Browse Source

Added SetPassword page.

KrisVos130 7 years ago
parent
commit
f7c35ece30

+ 4 - 4
backend/logic/actions/users.js

@@ -748,12 +748,12 @@ module.exports = {
 	verifyPasswordCode: hooks.loginRequired((session, code, cb, userId) => {
 		async.waterfall([
 			(next) => {
-				if (!code || typeof code !== 'string') return next('Invalid code1.');
+				if (!code || typeof code !== 'string') return next('Invalid code.');
 				db.models.user.findOne({"services.password.set.code": code, _id: userId}, next);
 			},
 
 			(user, next) => {
-				if (!user) return next('Invalid code2.');
+				if (!user) return next('Invalid code.');
 				if (user.services.password.set.expires < new Date()) return next('That code has expired.');
 				next(null);
 			}
@@ -784,12 +784,12 @@ module.exports = {
 	changePasswordWithCode: hooks.loginRequired((session, code, newPassword, cb, userId) => {
 		async.waterfall([
 			(next) => {
-				if (!code || typeof code !== 'string') return next('Invalid code1.');
+				if (!code || typeof code !== 'string') return next('Invalid code.');
 				db.models.user.findOne({"services.password.set.code": code}, next);
 			},
 
 			(user, next) => {
-				if (!user) return next('Invalid code2.');
+				if (!user) return next('Invalid code.');
 				if (!user.services.password.set.expires > new Date()) return next('That code has expired.');
 				next();
 			},

+ 4 - 4
frontend/app/js/components/Global/Menu.jsx

@@ -24,10 +24,10 @@ export default class Menu extends Component {
 		return (
 			<div className="Menu">
 				{this.getLink("/", "Home")}
-				{this.getLink("login", "Login", !this.props.loggedIn)}
-				{this.getLink("register", "Register", !this.props.loggedIn)}
-				{this.getLink("settings", "Settings", this.props.loggedIn)}
-				{this.getLink("Logout", "Logout", this.props.loggedIn)}
+				{this.getLink("/login", "Login", !this.props.loggedIn)}
+				{this.getLink("/register", "Register", !this.props.loggedIn)}
+				{this.getLink("/settings", "Settings", this.props.loggedIn)}
+				{this.getLink("/logout", "Logout", this.props.loggedIn)}
 			</div>
 		);
 	}

+ 8 - 0
frontend/app/js/views/App/index.jsx

@@ -106,6 +106,14 @@ class App extends Component { // eslint-disable-line react/no-multi-comp
 							) }
 							authRequired={ true }
 						/>
+						<AuthRoute
+							exact
+							path="/settings/setpassword"
+							component={ asyncComponent(() =>
+								System.import("views/Auth/SetPassword").then(module => module.default)
+							) }
+							authRequired={ true }
+						/>
 						<AuthRoute
 							exact
 							path="/reset_password"

+ 144 - 0
frontend/app/js/views/Auth/SetPassword.jsx

@@ -0,0 +1,144 @@
+import React, { Component } from "react";
+import PropTypes from "prop-types";
+import { connect } from "react-redux";
+
+import CustomInput from "./CustomInput.jsx";
+import CustomErrors from "./CustomErrors.jsx";
+
+import io from "../../io";
+
+@connect(state => ({
+	user: {
+		userId: state.user.get("userId"),
+	},
+}))
+
+export default class Settings extends Component {
+	static propTypes = {
+		user: PropTypes.object,
+	};
+
+	static defaultProps = {
+		user: {
+			userId: "",
+		},
+	};
+
+	constructor(props) {
+		super(props);
+
+		CustomInput.initialize(this);
+
+		this.state = {
+			passwordLinked: false,
+			step: 1,
+		};
+
+		io.getSocket(socket => {
+			socket.emit("users.findBySession", res => {
+				if (res.status === "success") {
+					this.setState({
+						passwordLinked: res.data.password,
+					});
+				} else {
+					this.errors.addError("You are currently not logged in.");
+				}
+			});
+
+			socket.on("event:user.linkPassword", () => {
+				alert("A password for your account has been set. We will now redirect you to the settings page.");
+				location.href = "/settings";
+			});
+		});
+	}
+
+	getActions = () => {
+		const requestCodeButton = (<button key="requestCode" onClick={ this.requestCode }>
+			Request code
+		</button>);
+
+		const codeInput = <CustomInput key="code" type="uniqueCode" name="code" label="Code" placeholder="Code" onRef={ ref => (this.input.code = ref) } />;
+		const verifyCodeButton = (<button key="verifyCode" onClick={ this.verifyCode }>
+			Verify code
+		</button>);
+
+		const newPasswordInput = <CustomInput key="newPassword" type="password" name="newPassword" label="New password" placeholder="New password" onRef={ ref => (this.input.newPassword = ref) } />;
+		const newPasswordAgainInput = <CustomInput key="newPasswordAgain" type="password" name="newPasswordAgain" label="New password again" placeholder="New password again" onRef={ ref => (this.input.newPasswordAgain = ref) } />;
+		const setPassword = (<button key="setPassword" onClick={ this.setPassword }>
+			Change password
+		</button>);
+
+		if (this.state.step === 1) {
+			return [requestCodeButton];
+		} if (this.state.step === 2) {
+			return [codeInput, verifyCodeButton];
+		} return [newPasswordInput, newPasswordAgainInput, setPassword];
+	};
+
+	setPassword = () => {
+		if (CustomInput.hasInvalidInput(this.input, ["newPassword", "newPasswordAgain"])) {
+			this.errors.clearAddError("Some fields are incorrect. Please fix them before continuing.");
+		} else if (CustomInput.isTheSame(this.input, ["newPassword", "newPasswordAgain"])) {
+			this.errors.clearAddError("New password and new password again need to be the same.");
+		} else {
+			this.errors.clearErrors();
+			io.getSocket(socket => {
+				socket.emit("users.changePasswordWithCode", this.state.code, this.input.newPassword.getValue(), res => {
+					if (res.status === "success") {
+						alert("Success!");
+						location.href = "/settings";
+					} else {
+						this.errors.addError(res.message);
+					}
+				});
+			});
+		}
+	};
+
+	requestCode = () => {
+		this.errors.clearErrors();
+		io.getSocket(socket => {
+			socket.emit("users.requestPassword", res => {
+				if (res.status === "success") {
+					alert("Success!");
+					this.setState({
+						step: 2,
+					});
+				} else {
+					this.errors.addError(res.message);
+				}
+			});
+		});
+	};
+
+	verifyCode = () => {
+		if (CustomInput.hasInvalidInput(this.input, ["code"])) {
+			this.errors.clearAddError("Some fields are incorrect. Please fix them before continuing.");
+		} else {
+			this.errors.clearErrors();
+			io.getSocket(socket => {
+				socket.emit("users.verifyPasswordCode", this.input.code.getValue(), res => {
+					if (res.status === "success") {
+						alert("Success!");
+						this.setState({
+							step: 3,
+							code: this.input.code.getValue(),
+						});
+					} else {
+						this.errors.addError(res.message);
+					}
+				});
+			});
+		}
+	};
+
+	render() {
+		return (
+			<div>
+				<h1>Set Password</h1>
+				<CustomErrors onRef={ ref => (this.errors = ref) } />
+				{ this.getActions() }
+			</div>
+		);
+	}
+}

+ 5 - 3
frontend/app/js/views/Auth/Settings.jsx

@@ -2,6 +2,7 @@ import React, { Component } from "react";
 import async from "async";
 import PropTypes from "prop-types";
 import { connect } from "react-redux";
+import { NavLink } from "react-router-dom";
 
 import CustomInput from "./CustomInput.jsx";
 import CustomErrors from "./CustomErrors.jsx";
@@ -188,7 +189,9 @@ export default class Settings extends Component {
 	};
 
 	linkButtons = () => {
-		const linkPassword = <button key="linkPassword">TODO</button>;
+		const newPassword = <CustomInput key="newPassword" type="password" name="newPassword" label="New password" placeholder="New password" onRef={ ref => (this.input.newPassword = ref) } />;
+		const changePasswordButton = <button key="changePassword" onClick={ this.changePassword }>Change password</button>;
+		const linkPassword = <NavLink key="linkPassword" to="/settings/setpassword" >Add a password to account</NavLink>;
 		const linkGitHub = <a key="linkGitHub" href="http://localhost:8080/auth/github/link">Link GitHub to account</a>;
 		const unlinkGitHub = (<button key="unlinkGitHub" onClick={ this.unlinkGitHub }>
 				Remove logging in with GitHub
@@ -196,6 +199,7 @@ export default class Settings extends Component {
 		const unlinkPassword = (<button key="unlinkPassword" onClick={ this.unlinkPassword }>
 			Remove logging in with password
 		</button>);
+		if (this.state.passwordLinked) return [newPassword, changePasswordButton];
 		if (this.state.passwordLinked && this.state.gitHubLinked) {
 			return [unlinkGitHub, unlinkPassword];
 		} else if (!this.state.passwordLinked) {
@@ -215,8 +219,6 @@ export default class Settings extends Component {
 				</div>
 				<div>
 					<h2>Security</h2>
-					<CustomInput type="password" name="newPassword" label="New password" placeholder="New password" onRef={ ref => (this.input.newPassword = ref) } />
-					<button onClick={ this.changePassword }>Change password</button>
 					{ this.linkButtons() }
 					<button onClick={ this.logOutEverywhere }>Log out everywhere</button>
 				</div>