From afb315ce412a2e7f095dd5073e03eab8afebd247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= Date: Mon, 5 Nov 2018 14:42:28 +0100 Subject: [PATCH 01/11] create branch From 67d6a8190e8be68e5356c6c7a9e73932f894c10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= Date: Mon, 5 Nov 2018 14:56:56 +0100 Subject: [PATCH 02/11] remove password fields if user is edited --- scm-ui/src/repos/containers/RepositoryRoot.js | 27 ++++++++---- scm-ui/src/users/components/UserForm.js | 42 +++++++++++-------- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js index e3043b1041..275bee8c8a 100644 --- a/scm-ui/src/repos/containers/RepositoryRoot.js +++ b/scm-ui/src/repos/containers/RepositoryRoot.js @@ -1,19 +1,32 @@ //@flow import React from "react"; -import {deleteRepo, fetchRepo, getFetchRepoFailure, getRepository, isFetchRepoPending} from "../modules/repos"; +import { + deleteRepo, + fetchRepo, + getFetchRepoFailure, + getRepository, + isFetchRepoPending +} from "../modules/repos"; -import {connect} from "react-redux"; -import {Route, Switch} from "react-router-dom"; -import type {Repository} from "@scm-manager/ui-types"; +import { connect } from "react-redux"; +import { Route, Switch } from "react-router-dom"; +import type { Repository } from "@scm-manager/ui-types"; -import {ErrorPage, Loading, Navigation, NavLink, Page, Section} from "@scm-manager/ui-components"; -import {translate} from "react-i18next"; +import { + ErrorPage, + Loading, + Navigation, + NavLink, + Page, + Section +} from "@scm-manager/ui-components"; +import { translate } from "react-i18next"; import RepositoryDetails from "../components/RepositoryDetails"; import DeleteNavAction from "../components/DeleteNavAction"; import Edit from "../containers/Edit"; import Permissions from "../permissions/containers/Permissions"; -import type {History} from "history"; +import type { History } from "history"; import EditNavLink from "../components/EditNavLink"; import BranchRoot from "./ChangesetsRoot"; diff --git a/scm-ui/src/users/components/UserForm.js b/scm-ui/src/users/components/UserForm.js index 80ade5e070..94daa53b63 100644 --- a/scm-ui/src/users/components/UserForm.js +++ b/scm-ui/src/users/components/UserForm.js @@ -89,6 +89,7 @@ class UserForm extends React.Component { const user = this.state.user; let nameField = null; + let passwordFields = null; if (!this.props.user) { nameField = ( { helpText={t("help.usernameHelpText")} /> ); + passwordFields = ( + <> + + + + ); } return (
@@ -120,24 +143,7 @@ class UserForm extends React.Component { errorMessage={t("validation.mail-invalid")} helpText={t("help.mailHelpText")} /> - - + {passwordFields} Date: Mon, 5 Nov 2018 15:08:58 +0100 Subject: [PATCH 03/11] add route to set password component --- scm-ui/public/locales/en/users.json | 3 ++ .../src/users/components/SetUserPassword.js | 17 ++++++++++ .../components/navLinks/SetPasswordNavLink.js | 28 +++++++++++++++++ .../navLinks/SetPasswordNavLink.test.js | 31 +++++++++++++++++++ scm-ui/src/users/components/navLinks/index.js | 1 + scm-ui/src/users/containers/SingleUser.js | 15 ++++++++- 6 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 scm-ui/src/users/components/SetUserPassword.js create mode 100644 scm-ui/src/users/components/navLinks/SetPasswordNavLink.js create mode 100644 scm-ui/src/users/components/navLinks/SetPasswordNavLink.test.js diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index 07540134be..34f2a9deb4 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -29,6 +29,9 @@ "edit-user-button": { "label": "Edit" }, + "set-password-button": { + "label": "Set password" + }, "user-form": { "submit": "Submit" }, diff --git a/scm-ui/src/users/components/SetUserPassword.js b/scm-ui/src/users/components/SetUserPassword.js new file mode 100644 index 0000000000..fea841b985 --- /dev/null +++ b/scm-ui/src/users/components/SetUserPassword.js @@ -0,0 +1,17 @@ +// @flow +import React from "react"; +import type { User } from "@scm-manager/ui-types"; + +type Props = { + user: User +}; + +export default class SetUserPassword extends React.Component { + + render() { + + return ( + "Hey, Change Password!" + ); + } +} diff --git a/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js b/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js new file mode 100644 index 0000000000..43b7a4b5a4 --- /dev/null +++ b/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js @@ -0,0 +1,28 @@ +//@flow +import React from "react"; +import { translate } from "react-i18next"; +import type { User } from "@scm-manager/ui-types"; +import { NavLink } from "@scm-manager/ui-components"; + +type Props = { + t: string => string, + user: User, + passwordUrl: String +}; + +class ChangePasswordNavLink extends React.Component { + render() { + const { t, passwordUrl } = this.props; + + if (!this.hasPermissionToSetPassword()) { + return null; + } + return ; + } + + hasPermissionToSetPassword = () => { + return this.props.user._links.password; + }; +} + +export default translate("users")(ChangePasswordNavLink); diff --git a/scm-ui/src/users/components/navLinks/SetPasswordNavLink.test.js b/scm-ui/src/users/components/navLinks/SetPasswordNavLink.test.js new file mode 100644 index 0000000000..75ce4e58cf --- /dev/null +++ b/scm-ui/src/users/components/navLinks/SetPasswordNavLink.test.js @@ -0,0 +1,31 @@ +import React from "react"; +import { shallow } from "enzyme"; +import "../../../tests/enzyme"; +import "../../../tests/i18n"; +import ChangePasswordNavLink from "./SetPasswordNavLink"; + +it("should render nothing, if the password link is missing", () => { + const user = { + _links: {} + }; + + const navLink = shallow( + + ); + expect(navLink.text()).toBe(""); +}); + +it("should render the navLink", () => { + const user = { + _links: { + password: { + href: "/password" + } + } + }; + + const navLink = shallow( + + ); + expect(navLink.text()).not.toBe(""); +}); diff --git a/scm-ui/src/users/components/navLinks/index.js b/scm-ui/src/users/components/navLinks/index.js index a3ccd16a32..a6d8370c00 100644 --- a/scm-ui/src/users/components/navLinks/index.js +++ b/scm-ui/src/users/components/navLinks/index.js @@ -1,2 +1,3 @@ export { default as DeleteUserNavLink } from "./DeleteUserNavLink"; export { default as EditUserNavLink } from "./EditUserNavLink"; +export { default as SetPasswordNavLink } from "./SetPasswordNavLink"; diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index f581874742..a88d517c55 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -24,9 +24,14 @@ import { getDeleteUserFailure } from "../modules/users"; -import { DeleteUserNavLink, EditUserNavLink } from "./../components/navLinks"; +import { + DeleteUserNavLink, + EditUserNavLink, + SetPasswordNavLink +} from "./../components/navLinks"; import { translate } from "react-i18next"; import { getUsersLink } from "../../modules/indexResource"; +import SetUserPassword from "../components/SetUserPassword"; type Props = { name: string, @@ -97,6 +102,10 @@ class SingleUser extends React.Component { path={`${url}/edit`} component={() => } /> + } + />
@@ -106,6 +115,10 @@ class SingleUser extends React.Component { label={t("single-user.information-label")} /> +
From 4e444c14494c82dfd33bdfa0fc2baccf342f25d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= Date: Mon, 5 Nov 2018 15:18:44 +0100 Subject: [PATCH 04/11] add view for passwords (without functionality) --- .../src/users/components/SetUserPassword.js | 98 ++++++++++++++++++- 1 file changed, 94 insertions(+), 4 deletions(-) diff --git a/scm-ui/src/users/components/SetUserPassword.js b/scm-ui/src/users/components/SetUserPassword.js index fea841b985..d47336bdef 100644 --- a/scm-ui/src/users/components/SetUserPassword.js +++ b/scm-ui/src/users/components/SetUserPassword.js @@ -1,17 +1,107 @@ // @flow import React from "react"; import type { User } from "@scm-manager/ui-types"; +import { InputField, SubmitButton } from "@scm-manager/ui-components"; +import * as userValidator from "./userValidation"; +import { translate } from "react-i18next"; type Props = { - user: User + user: User, + t: string => string }; -export default class SetUserPassword extends React.Component { +type State = { + password: string, + loading: boolean, + passwordValidationError: boolean, + validatePasswordError: boolean, + validatePassword: string +}; + +class SetUserPassword extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { + password: "", + loading: false, + passwordValidationError: false, + validatePasswordError: false, + validatePassword: "" + }; + } + + isValid = () => { + return !( + this.state.validatePasswordError || this.state.passwordValidationError + ); + }; + + submit = (event: Event) => { + event.preventDefault(); + if (this.isValid()) { + //TODO:hier update pw! + } + }; render() { - + const { user, t } = this.props; + const { loading } = this.state; return ( - "Hey, Change Password!" + + + + + ); } + + handlePasswordChange = (password: string) => { + const validatePasswordError = !this.checkPasswords( + password, + this.state.validatePassword + ); + this.setState({ + validatePasswordError: !userValidator.isPasswordValid(password), + passwordValidationError: validatePasswordError, + password: password + }); + }; + + handlePasswordValidationChange = (validatePassword: string) => { + const validatePasswordError = this.checkPasswords( + this.state.password, + validatePassword + ); + this.setState({ + validatePassword, + passwordValidationError: !validatePasswordError + }); + }; + + checkPasswords = (password1: string, password2: string) => { + return password1 === password2; + }; } + +export default translate("users")(SetUserPassword); From c6bc385b2f2ed513e54d9fc49bc52f913e461d28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= Date: Mon, 5 Nov 2018 16:14:54 +0100 Subject: [PATCH 05/11] update password function --- scm-ui/public/locales/en/users.json | 3 + .../src/users/components/SetUserPassword.js | 62 +++++++++++++++++-- scm-ui/src/users/components/updatePassword.js | 16 +++++ .../users/components/updatePassword.test.js | 23 +++++++ 4 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 scm-ui/src/users/components/updatePassword.js create mode 100644 scm-ui/src/users/components/updatePassword.test.js diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index 34f2a9deb4..b5b98fc915 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -55,6 +55,9 @@ "passwordValidation-invalid": "Passwords have to be the same", "validatePassword": "Please validate password here" }, + "password": { + "set-password-successful": "Password is set successful" + }, "help": { "usernameHelpText": "Unique name of the user.", "displayNameHelpText": "Display name of the user.", diff --git a/scm-ui/src/users/components/SetUserPassword.js b/scm-ui/src/users/components/SetUserPassword.js index d47336bdef..0ceab52878 100644 --- a/scm-ui/src/users/components/SetUserPassword.js +++ b/scm-ui/src/users/components/SetUserPassword.js @@ -1,9 +1,14 @@ // @flow import React from "react"; import type { User } from "@scm-manager/ui-types"; -import { InputField, SubmitButton } from "@scm-manager/ui-components"; +import { + InputField, + SubmitButton, + Notification +} from "@scm-manager/ui-components"; import * as userValidator from "./userValidation"; import { translate } from "react-i18next"; +import { updatePassword } from "./updatePassword"; type Props = { user: User, @@ -15,7 +20,9 @@ type State = { loading: boolean, passwordValidationError: boolean, validatePasswordError: boolean, - validatePassword: string + validatePassword: string, + error?: Error, + passwordChanged: boolean }; class SetUserPassword extends React.Component { @@ -27,7 +34,8 @@ class SetUserPassword extends React.Component { loading: false, passwordValidationError: false, validatePasswordError: false, - validatePassword: "" + validatePassword: "", + passwordChanged: false }; } @@ -38,22 +46,57 @@ class SetUserPassword extends React.Component { }; submit = (event: Event) => { + //TODO: set loading event.preventDefault(); if (this.isValid()) { - //TODO:hier update pw! + const { user } = this.props; + const { password } = this.state; + updatePassword(user._links.password.href, password) + .then(result => { + if (result.error || result.status !== 204) { + this.setState({ + ...this.state, + error: result.error, + loading: false + }); + } else { + this.setState({ + ...this.state, + loading: false, + passwordChanged: true, + password: "", + validatePassword: "" + }); + } + }) + .catch(err => {}); } }; render() { const { user, t } = this.props; - const { loading } = this.state; + const { loading, passwordChanged } = this.state; + + let passwordChangedSuccessful = null; + + if (passwordChanged) { + passwordChangedSuccessful = ( + this.onClose()} + /> + ); + } + return (
+ {passwordChangedSuccessful} { checkPasswords = (password1: string, password2: string) => { return password1 === password2; }; + + onClose = () => { + this.setState({ + ...this.state, + passwordChanged: false + }); + }; } export default translate("users")(SetUserPassword); diff --git a/scm-ui/src/users/components/updatePassword.js b/scm-ui/src/users/components/updatePassword.js new file mode 100644 index 0000000000..fb5adfaf2a --- /dev/null +++ b/scm-ui/src/users/components/updatePassword.js @@ -0,0 +1,16 @@ +//@flow +import { apiClient } from "@scm-manager/ui-components"; +const CONTENT_TYPE_USER = "application/vnd.scmm-passwordOverwrite+json;v=2"; + +export function updatePassword(url: string, password: string) { + return apiClient + .put(url, { newPassword: password }, CONTENT_TYPE_USER) + .then(response => { + return { + status: response.status + }; + }) + .catch(err => { + return { error: err }; + }); +} diff --git a/scm-ui/src/users/components/updatePassword.test.js b/scm-ui/src/users/components/updatePassword.test.js new file mode 100644 index 0000000000..a5762406b2 --- /dev/null +++ b/scm-ui/src/users/components/updatePassword.test.js @@ -0,0 +1,23 @@ +//@flow +import fetchMock from "fetch-mock"; +import { updatePassword } from "./updatePassword"; + +describe("get content type", () => { + const PASSWORD_URL = "/users/testuser/password"; + const password = "testpw123"; + + afterEach(() => { + fetchMock.reset(); + fetchMock.restore(); + }); + + it("should update password", done => { + + fetchMock.put("/api/v2" + PASSWORD_URL, 204); + + updatePassword(PASSWORD_URL, password).then(content => { + + done(); + }); + }); +}); From 3c63d994c6e240fc4c4677a86a59515d6cf5e3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= Date: Mon, 5 Nov 2018 16:28:46 +0100 Subject: [PATCH 06/11] show error and loading state --- .../src/users/components/SetUserPassword.js | 59 +++++++++++++------ scm-ui/src/users/components/updatePassword.js | 4 +- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/scm-ui/src/users/components/SetUserPassword.js b/scm-ui/src/users/components/SetUserPassword.js index 0ceab52878..a4a91c4083 100644 --- a/scm-ui/src/users/components/SetUserPassword.js +++ b/scm-ui/src/users/components/SetUserPassword.js @@ -4,7 +4,8 @@ import type { User } from "@scm-manager/ui-types"; import { InputField, SubmitButton, - Notification + Notification, + ErrorNotification } from "@scm-manager/ui-components"; import * as userValidator from "./userValidation"; import { translate } from "react-i18next"; @@ -45,28 +46,46 @@ class SetUserPassword extends React.Component { ); }; + setLoadingState = () => { + this.setState({ + ...this.state, + loading: true + }); + }; + + setErrorState = (error: Error) => { + this.setState({ + ...this.state, + error: error, + loading: false + }); + }; + + setSuccessfulState = () => { + this.setState({ + ...this.state, + loading: false, + passwordChanged: true, + password: "", + validatePassword: "", + validatePasswordError: false, + passwordValidationError: false + }); + }; + submit = (event: Event) => { //TODO: set loading event.preventDefault(); if (this.isValid()) { const { user } = this.props; const { password } = this.state; + this.setLoadingState(); updatePassword(user._links.password.href, password) .then(result => { - if (result.error || result.status !== 204) { - this.setState({ - ...this.state, - error: result.error, - loading: false - }); + if (result.error) { + this.setErrorState(result.error); } else { - this.setState({ - ...this.state, - loading: false, - passwordChanged: true, - password: "", - validatePassword: "" - }); + this.setSuccessfulState(); } }) .catch(err => {}); @@ -74,24 +93,26 @@ class SetUserPassword extends React.Component { }; render() { - const { user, t } = this.props; - const { loading, passwordChanged } = this.state; + const { t } = this.props; + const { loading, passwordChanged, error } = this.state; - let passwordChangedSuccessful = null; + let message = null; if (passwordChanged) { - passwordChangedSuccessful = ( + message = ( this.onClose()} /> ); + } else if (error) { + message = ; } return ( - {passwordChangedSuccessful} + {message} { - return { - status: response.status - }; + return response; }) .catch(err => { return { error: err }; From 1a3506ce50d9ef77663bef3d5bbd5381f642fe23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= Date: Mon, 5 Nov 2018 16:43:42 +0100 Subject: [PATCH 07/11] remove todo --- scm-ui/src/users/components/SetUserPassword.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scm-ui/src/users/components/SetUserPassword.js b/scm-ui/src/users/components/SetUserPassword.js index a4a91c4083..dd826c2b90 100644 --- a/scm-ui/src/users/components/SetUserPassword.js +++ b/scm-ui/src/users/components/SetUserPassword.js @@ -74,7 +74,6 @@ class SetUserPassword extends React.Component { }; submit = (event: Event) => { - //TODO: set loading event.preventDefault(); if (this.isValid()) { const { user } = this.props; From 4086622647d1843aa0e78ed2e4d20ca714c80582 Mon Sep 17 00:00:00 2001 From: Philipp Czora Date: Tue, 6 Nov 2018 15:25:49 +0100 Subject: [PATCH 08/11] Renamed function for clarity --- scm-ui/src/users/components/SetUserPassword.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scm-ui/src/users/components/SetUserPassword.js b/scm-ui/src/users/components/SetUserPassword.js index a4a91c4083..19872cb9a2 100644 --- a/scm-ui/src/users/components/SetUserPassword.js +++ b/scm-ui/src/users/components/SetUserPassword.js @@ -40,7 +40,7 @@ class SetUserPassword extends React.Component { }; } - isValid = () => { + passwordIsValid = () => { return !( this.state.validatePasswordError || this.state.passwordValidationError ); @@ -74,9 +74,8 @@ class SetUserPassword extends React.Component { }; submit = (event: Event) => { - //TODO: set loading event.preventDefault(); - if (this.isValid()) { + if (this.passwordIsValid()) { const { user } = this.props; const { password } = this.state; this.setLoadingState(); @@ -132,7 +131,7 @@ class SetUserPassword extends React.Component { helpText={t("help.passwordConfirmHelpText")} /> From 6e32d354cf5d8230023f1a6a7ed1f597d747d1f1 Mon Sep 17 00:00:00 2001 From: Philipp Czora Date: Tue, 6 Nov 2018 15:26:25 +0100 Subject: [PATCH 09/11] Renamed incorrectly named variable --- scm-ui/src/users/components/updatePassword.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scm-ui/src/users/components/updatePassword.js b/scm-ui/src/users/components/updatePassword.js index e2ef8c0a02..3915c90bd9 100644 --- a/scm-ui/src/users/components/updatePassword.js +++ b/scm-ui/src/users/components/updatePassword.js @@ -1,10 +1,11 @@ //@flow import { apiClient } from "@scm-manager/ui-components"; -const CONTENT_TYPE_USER = "application/vnd.scmm-passwordOverwrite+json;v=2"; +const CONTENT_TYPE_PASSWORD_OVERWRITE = + "application/vnd.scmm-passwordOverwrite+json;v=2"; export function updatePassword(url: string, password: string) { return apiClient - .put(url, { newPassword: password }, CONTENT_TYPE_USER) + .put(url, { newPassword: password }, CONTENT_TYPE_PASSWORD_OVERWRITE) .then(response => { return response; }) From 081cd93c51eaedf9ee7101bcaec526d12f094085 Mon Sep 17 00:00:00 2001 From: Philipp Czora Date: Tue, 6 Nov 2018 15:27:01 +0100 Subject: [PATCH 10/11] Polished pw labels --- scm-ui/public/locales/en/users.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index b5b98fc915..7199cb2135 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -52,11 +52,11 @@ "name-invalid": "This name is invalid", "displayname-invalid": "This displayname is invalid", "password-invalid": "Password has to be between 6 and 32 characters", - "passwordValidation-invalid": "Passwords have to be the same", - "validatePassword": "Please validate password here" + "passwordValidation-invalid": "Passwords have to be identical", + "validatePassword": "Confirm password" }, "password": { - "set-password-successful": "Password is set successful" + "set-password-successful": "Password successfully set" }, "help": { "usernameHelpText": "Unique name of the user.", From 21793153011619cc15f2d06dd3089811c5189ca0 Mon Sep 17 00:00:00 2001 From: Philipp Czora Date: Tue, 6 Nov 2018 15:31:04 +0000 Subject: [PATCH 11/11] Close branch feature/ui-AdminPasswordChange