From 85545aba62546f219a9c9730945511412a3174ef Mon Sep 17 00:00:00 2001 From: jloup Date: Fri, 4 May 2018 11:55:15 +0200 Subject: Password reset. --- cmd/ansible/conf.toml.j2 | 3 ++ cmd/ansible/vars.yml | 50 ++++++++++++++++++++-------------- cmd/app/conf.toml | 3 ++ cmd/app/main.go | 11 ++++---- cmd/web/Makefile | 2 +- cmd/web/js/api.js | 21 ++++++++++++++ cmd/web/js/change_password.jsx | 62 ++++++++++++++++++++++++++++++++++++++++++ cmd/web/js/main.jsx | 34 +++++++++++++++++++++++ cmd/web/js/password_reset.jsx | 57 ++++++++++++++++++++++++++++++++++++++ cmd/web/package.json | 1 + cmd/web/yarn.lock | 4 +++ 11 files changed, 220 insertions(+), 28 deletions(-) create mode 100644 cmd/web/js/change_password.jsx create mode 100644 cmd/web/js/password_reset.jsx (limited to 'cmd') diff --git a/cmd/ansible/conf.toml.j2 b/cmd/ansible/conf.toml.j2 index b2c3137..7b048af 100644 --- a/cmd/ansible/conf.toml.j2 +++ b/cmd/ansible/conf.toml.j2 @@ -17,6 +17,9 @@ database=0 [api] domain="{{ app_domain }}" jwt_secret="{{ jwt_secret }}" +password_reset_secret="{{ reset_password_reset }}" +free_sms_user="20996747" +free_sms_pass="bM2ZPETB4zzWg3" [app] public_dir="/var/cryptoportfolio-app/static" diff --git a/cmd/ansible/vars.yml b/cmd/ansible/vars.yml index 1de7413..59a5eb8 100644 --- a/cmd/ansible/vars.yml +++ b/cmd/ansible/vars.yml @@ -1,22 +1,30 @@ $ANSIBLE_VAULT;1.1;AES256 -63613535333830393037646665363566636635366534636261623839326130663431653839346266 -3832643338623561313362663837323234663537663439350a313034326663383235663964626132 -38343964396265323539396439383731336464393337383833653666643736303539626136383431 -6536316338376538360a343862626636363031353037626462333364623433613861393137353336 -37396664663030363530333364633266653862393538313835326138663465626638326363656561 -30393836386664633834663838666432383836623432363936343635313835303166393531643966 -33313361383565363232373066306534613465386534386266306564383365373762613361366365 -61366530623863623336643531346463323233323539333139336335383439373132373233663031 -39666535633362383135376534376532333663636136366130653762643164333436313261646137 -37353139633361636163326366616234613466393731373631616138386263383131663537633533 -31393763316561623134623063623735356334363833623939313437386330323837626131356332 -30383863373535366137366138633832623566613061313138396539306536633763633934313562 -35383763653532336539346632623935303634353866636264373262363839326439313837313765 -36303539613734646238636432393166616438666665363363323331373437633362613838653564 -64393639346661646333383466363162633638643838386666383564366665656266333836363435 -35643231323362323566303535303561626139333830393538383635326631656666323166343863 -31393566346531653535393738326166303261376238316532373833616432306638326139353234 -32653132323764316231393634663262313765393230656232343833373438636430643663353965 -36333931303731646333316430646534383531313264353936396565336338663530303434643036 -34356663373533663137636235386164646334356262336464363862643332636661313339303531 -35663833656564393331636139663738323834373862623436633666306661373166 +66653734343938333339346336333430336239376539663338333431626461656430393931353165 +3564303163396337646466376265633537636362346434330a376630333634366165353832613063 +36353931626633306565613862396566353138353439666336376231643432343637613636633634 +6531666131343361340a633839363864643037373638356433613830666137626665343362303363 +64323766333830646463313130316234316161383031646132346633316162366335643137663835 +63663866613335643330653839393762373163366530376538643666653630636462363535613137 +63663966343335346564316462323463666130313733396164663761613966366165653162663432 +66616232633733613766336465336632373765326362383035613465303463306330623632356232 +38636136633661323137336431646666323464383033346239633032336561613433646632363564 +33323737313235313463643866373465366436376138303430633535623335393163343862366264 +35626263336462353264656637643437326635633237333932613666646666313235396561663762 +36373762363662613635633065383037336634323665333865363533616136373565633436653566 +34393033356566663433343864623664386330376339653463623834393036643733666237393964 +32316134653666306134623135636664643461303831376336373839356531663764636433323539 +62663938363437623235383666363163353562646165633564323635343534316130663062373332 +39303239363166366538623763396563616664663038346465336263666365306435376364366337 +66363137303731613638323839393731326539356262626634373136316265323937303863363637 +37313565663534353035313834626139353333386337643263663264376238393030386363613435 +33616432383131303761306265653566306266366263616162323363383365333363363334653132 +32343235666434636361656133636332303131313766326665393233316135323566633433303362 +62383431313861323036616331633134386238313034633936353931313838383038373435653235 +32333230646439613134613337333762313062613839613232663037363761353664373935356264 +61386239366330653939613637333264313532666363626633386632376138643265373432373233 +39313234326430663561343961313732666465613939343839663263353964663963356639633231 +64663330323761393932613039326339643737303939363033333666346439393631623038316561 +34313233626666663234613339306539343030616266333565326565346563353739613363363464 +65323763653436303030643765663739383965313335373265636336633762636134653536326364 +65353366643639663765663566663065316334383463343464366662373939343763356333343731 +6361 diff --git a/cmd/app/conf.toml b/cmd/app/conf.toml index 13e3e0b..16087e5 100644 --- a/cmd/app/conf.toml +++ b/cmd/app/conf.toml @@ -16,6 +16,9 @@ database=0 [api] domain="localhost" jwt_secret="secret" +password_reset_secret="resetsecret" +free_sms_user="20996747" +free_sms_pass="bM2ZPETB4zzWg3" [app] public_dir="../web/build/static" diff --git a/cmd/app/main.go b/cmd/app/main.go index 65e8b5a..a0463d2 100644 --- a/cmd/app/main.go +++ b/cmd/app/main.go @@ -21,8 +21,8 @@ type AppConfig struct { } type ApiConfig struct { - Domain string `toml:"domain"` - JwtSecret string `toml:"jwt_secret"` + api.Config + Domain string `toml:"domain"` } type Config struct { @@ -45,9 +45,6 @@ func (c *Config) SetToDefaults() { App: AppConfig{ PublicDir: "./public", }, - Api: ApiConfig{ - JwtSecret: "secret", - }, } c.LogConfiguration.SetToDefaults() @@ -63,7 +60,7 @@ func init() { panic(err) } - api.SetJwtSecretKey(C.Api.JwtSecret) + api.SetConfig(C.Api.Config) db.Init(C.Db, C.Redis) @@ -142,6 +139,8 @@ func main() { "/", "/signup", "/signin", + "/reset-password", + "/change-password", "/signout", "/me", "/otp/enroll", diff --git a/cmd/web/Makefile b/cmd/web/Makefile index c6bc2bd..c5d5d62 100644 --- a/cmd/web/Makefile +++ b/cmd/web/Makefile @@ -4,7 +4,7 @@ export PATH := $(PATH):./node_modules/.bin SRC_DIR=js BUILD_DIR=build/js -JSX_SRC= main.jsx signup.jsx signin.jsx otp.jsx poloniex.jsx +JSX_SRC= main.jsx signup.jsx signin.jsx otp.jsx poloniex.jsx password_reset.jsx change_password.jsx JS_SRC= cookies.js app.js api.js STATIC_FILES= index.html style.css JSX_OBJS=$(addprefix $(BUILD_DIR)/,$(JSX_SRC:.jsx=.js)) diff --git a/cmd/web/js/api.js b/cmd/web/js/api.js index 5cbf5eb..c9b4ef5 100644 --- a/cmd/web/js/api.js +++ b/cmd/web/js/api.js @@ -43,6 +43,27 @@ var ApiEndpoints = { return '/signin'; } }, + 'RESET_PASSWORD': { + 'type': 'POST', + 'auth': false, + 'parameters': [ + {'name': 'email', 'mandatory': true, 'inquery': true}, + ], + 'buildUrl': function() { + return '/passwordreset'; + } + }, + 'CHANGE_PASSWORD': { + 'type': 'POST', + 'auth': false, + 'parameters': [ + {'name': 'token', 'mandatory': true, 'inquery': true}, + {'name': 'password', 'mandatory': true, 'inquery': true}, + ], + 'buildUrl': function() { + return '/changepassword'; + } + }, 'MARKET': { 'type': 'GET', 'auth': true, diff --git a/cmd/web/js/change_password.jsx b/cmd/web/js/change_password.jsx new file mode 100644 index 0000000..aedf4af --- /dev/null +++ b/cmd/web/js/change_password.jsx @@ -0,0 +1,62 @@ +import Api from './api.js'; +import App from './app.js'; +import classNames from 'classnames'; +import React from 'react'; + +class ChangePasswordForm extends React.Component { + constructor(props) { + super(props); + this.state = {'hideMsg': true, 'msg': '', 'msgOk': false, 'password': ''}; + } + + handleSubmit = (e) => { + Api.Call( + 'CHANGE_PASSWORD', + { + 'password': this.state.password, + 'token': this.props.token + }, + function(err, status, data) { + if (err) { + console.error(err, data); + this.displayMessage(App.errorCodeToMessage(err.code), false); + return; + } + + this.displayMessage('You password has been reset.', true); + this.props.onSuccess(); + + }.bind(this) +); + e.preventDefault(); + } + + handlePasswordChange = (event) => { + this.setState({'password': event.target.value}); + } + + hideMessage = () => { + this.setState({'hideMsg': true}); + } + + displayMessage = (msg, ok) => { + this.setState({'msg': msg, 'msgOk': ok, 'hideMsg': false}); + } + + render = () => { + var cName = classNames('form-message', {'hidden': this.state.hideMsg, 'message-ok': this.state.msgOk}); + return ( +
+
+
+ + +
{this.state.msg}
+
+
+
+ ); + } +} + +export default ChangePasswordForm; diff --git a/cmd/web/js/main.jsx b/cmd/web/js/main.jsx index e64adc7..909f1bd 100644 --- a/cmd/web/js/main.jsx +++ b/cmd/web/js/main.jsx @@ -1,11 +1,14 @@ import SignupForm from './signup.js'; import SigninForm from './signin.js'; +import PasswordResetForm from './password_reset.js'; +import ChangePasswordForm from './change_password.js'; import OtpEnrollForm from './otp.js'; import PoloniexController from './poloniex.js'; import App from './app.js'; import Api from './api.js'; import cookies from './cookies.js'; import React from 'react'; +import qs from 'qs'; class Header extends React.Component { render = () => { @@ -60,6 +63,37 @@ App.page('/signin', false, function(context) { ); }); +App.page('/reset-password', false, function(context) { + if (App.isUserSignedIn()) { + App.go('/me'); + return; + } + + App.mount(
+
+ +
); +}); + +App.page('/change-password', false, function(context) { + if (App.isUserSignedIn()) { + App.go('/me'); + return; + } + + var token = qs.parse(context.querystring).token; + + if (token === undefined) { + App.go('/'); + return; + } + + App.mount(
+
+ +
); +}); + App.page('/signout', true, function(context) { cookies.removeItem('jwt'); diff --git a/cmd/web/js/password_reset.jsx b/cmd/web/js/password_reset.jsx new file mode 100644 index 0000000..8cbdc60 --- /dev/null +++ b/cmd/web/js/password_reset.jsx @@ -0,0 +1,57 @@ +import Api from './api.js'; +import App from './app.js'; +import classNames from 'classnames'; +import React from 'react'; + +class PasswordResetForm extends React.Component { + constructor(props) { + super(props); + this.state = {'hideMsg': true, 'msg': '', 'msgOk': false, 'email': ''}; + } + + handleSubmit = (e) => { + Api.Call('RESET_PASSWORD', {'email': this.state.email}, function(err, status, data) { + if (err) { + console.error(err, data); + this.displayMessage(App.errorCodeToMessage(err.code), false); + return; + } + + this.displayMessage('You will receive a reset link to reset your password.', true); + if (this.props.onSuccess) { + this.props.onSuccess(); + } + + }.bind(this)); + e.preventDefault(); + } + + handleEmailChange = (event) => { + this.setState({'email': event.target.value}); + } + + hideMessage = () => { + this.setState({'hideMsg': true}); + } + + displayMessage = (msg, ok) => { + this.setState({'msg': msg, 'msgOk': ok, 'hideMsg': false}); + } + + render = () => { + var cName = classNames('form-message', {'hidden': this.state.hideMsg, 'message-ok': this.state.msgOk}); + return ( +
+
+
+ + +
{this.state.msg}
+
+
+
+ ); + } +} + +export default PasswordResetForm; \ No newline at end of file diff --git a/cmd/web/package.json b/cmd/web/package.json index c9241f1..a867313 100644 --- a/cmd/web/package.json +++ b/cmd/web/package.json @@ -14,6 +14,7 @@ "localenvify": "^1.0.1", "page": "^1.8.3", "path-to-regexp": "^1.2.1", + "qs": "^6.5.1", "react": "^16.2.0" }, "devDependencies": { diff --git a/cmd/web/yarn.lock b/cmd/web/yarn.lock index 0d162a9..b2218ee 100644 --- a/cmd/web/yarn.lock +++ b/cmd/web/yarn.lock @@ -3536,6 +3536,10 @@ q@~1.0.0, q@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/q/-/q-1.0.1.tgz#11872aeedee89268110b10a718448ffb10112a14" +qs@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + qs@~1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/qs/-/qs-1.2.2.tgz#19b57ff24dc2a99ce1f8bdf6afcda59f8ef61f88" -- cgit v1.2.3