From e21087aa501ce63b2405e25415afee2e7f0e41bb Mon Sep 17 00:00:00 2001 From: Yaodong Yu <2814461814@qq.com> Date: Wed, 1 Mar 2023 15:57:42 +0800 Subject: [PATCH] feat: refactor reset password api and forgetPage.js (#1601) --- authz/authz.go | 2 +- controllers/user.go | 21 +- object/user_util.go | 8 +- routers/router.go | 2 +- swagger/swagger.json | 168 +++++++++- swagger/swagger.yml | 118 ++++++- web/src/auth/AuthBackend.js | 7 +- web/src/auth/ForgetPage.js | 551 ++++++++++++++++----------------- web/src/backend/UserBackend.js | 2 +- 9 files changed, 539 insertions(+), 340 deletions(-) diff --git a/authz/authz.go b/authz/authz.go index d25f0879..afebea32 100644 --- a/authz/authz.go +++ b/authz/authz.go @@ -80,7 +80,7 @@ m = (r.subOwner == p.subOwner || p.subOwner == "*") && \ p, built-in, *, *, *, *, * p, app, *, *, *, *, * p, *, *, POST, /api/signup, *, * -p, *, *, POST, /api/get-email-and-phone, *, * +p, *, *, GET, /api/get-email-and-phone, *, * p, *, *, POST, /api/login, *, * p, *, *, GET, /api/get-app-login, *, * p, *, *, POST, /api/logout, *, * diff --git a/controllers/user.go b/controllers/user.go index c68f157f..d0a97d6e 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -231,24 +231,20 @@ func (c *ApiController) DeleteUser() { // @Param username formData string true "The username of the user" // @Param organization formData string true "The organization of the user" // @Success 200 {object} controllers.Response The Response object -// @router /get-email-and-phone [post] +// @router /get-email-and-phone [get] func (c *ApiController) GetEmailAndPhone() { - var form RequestForm - err := json.Unmarshal(c.Ctx.Input.RequestBody, &form) - if err != nil { - c.ResponseError(err.Error()) - return - } + organization := c.Ctx.Request.Form.Get("organization") + username := c.Ctx.Request.Form.Get("username") - user := object.GetUserByFields(form.Organization, form.Username) + user := object.GetUserByFields(organization, username) if user == nil { - c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(form.Organization, form.Username))) + c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(organization, username))) return } respUser := object.User{Name: user.Name} var contentType string - switch form.Username { + switch username { case user.Email: contentType = "email" respUser.Email = user.Email @@ -281,7 +277,7 @@ func (c *ApiController) SetPassword() { newPassword := c.Ctx.Request.Form.Get("newPassword") requestUserId := c.GetSessionUsername() - userId := fmt.Sprintf("%s/%s", userOwner, userName) + userId := util.GetId(userOwner, userName) hasPermission, err := object.CheckUserPermission(requestUserId, userId, userOwner, true, c.GetAcceptLanguage()) if !hasPermission { @@ -311,8 +307,7 @@ func (c *ApiController) SetPassword() { targetUser.Password = newPassword object.SetUserField(targetUser, "password", targetUser.Password) - c.Data["json"] = Response{Status: "ok"} - c.ServeJSON() + c.ResponseOk() } // CheckUserPassword diff --git a/object/user_util.go b/object/user_util.go index 35ff1827..625db421 100644 --- a/object/user_util.go +++ b/object/user_util.go @@ -53,9 +53,11 @@ func GetUserByFields(organization string, field string) *User { } // check email - user = GetUserByField(organization, "email", field) - if user != nil { - return user + if strings.Contains(field, "@") { + user = GetUserByField(organization, "email", field) + if user != nil { + return user + } } // check phone diff --git a/routers/router.go b/routers/router.go index 8faff02f..d1447194 100644 --- a/routers/router.go +++ b/routers/router.go @@ -112,7 +112,7 @@ func initAPI() { beego.Router("/api/set-password", &controllers.ApiController{}, "POST:SetPassword") beego.Router("/api/check-user-password", &controllers.ApiController{}, "POST:CheckUserPassword") - beego.Router("/api/get-email-and-phone", &controllers.ApiController{}, "POST:GetEmailAndPhone") + beego.Router("/api/get-email-and-phone", &controllers.ApiController{}, "GET:GetEmailAndPhone") beego.Router("/api/send-verification-code", &controllers.ApiController{}, "POST:SendVerificationCode") beego.Router("/api/verify-captcha", &controllers.ApiController{}, "POST:VerifyCaptcha") beego.Router("/api/reset-email-or-phone", &controllers.ApiController{}, "POST:ResetEmailOrPhone") diff --git a/swagger/swagger.json b/swagger/swagger.json index ccdca5c3..5514699f 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -339,6 +339,42 @@ } } }, + "/api/add-session": { + "post": { + "tags": [ + "Session API" + ], + "description": "Add session for one user in one application. If there are other existing sessions, join the session into the list.", + "operationId": "ApiController.AddSession", + "parameters": [ + { + "in": "query", + "name": "id", + "description": "The id(organization/application/user) of session", + "required": true, + "type": "string" + }, + { + "in": "query", + "name": "sessionId", + "description": "sessionId to be added", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The Response object", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, "/api/add-syncer": { "post": { "tags": [ @@ -889,13 +925,13 @@ "tags": [ "Session API" ], - "description": "Delete session by userId", + "description": "Delete session for one user in one application.", "operationId": "ApiController.DeleteSession", "parameters": [ { "in": "query", "name": "id", - "description": "The id ( owner/name )(owner/name) of user.", + "description": "The id(organization/application/user) of session", "required": true, "type": "string" } @@ -1233,7 +1269,7 @@ } }, "/api/get-email-and-phone": { - "post": { + "get": { "tags": [ "User API" ], @@ -1306,7 +1342,7 @@ } }, "/api/get-ldap": { - "post": { + "get": { "tags": [ "Account API" ], @@ -1322,7 +1358,7 @@ } }, "/api/get-ldaps": { - "post": { + "get": { "tags": [ "Account API" ], @@ -1884,12 +1920,41 @@ } } }, + "/api/get-session": { + "get": { + "tags": [ + "Session API" + ], + "description": "Get session for one user in one application.", + "operationId": "ApiController.GetSingleSession", + "parameters": [ + { + "in": "query", + "name": "id", + "description": "The id(organization/application/user) of session", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The Response object", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, "/api/get-sessions": { "get": { "tags": [ "Session API" ], - "description": "Get organization user sessions", + "description": "Get organization user sessions.", "operationId": "ApiController.GetSessions", "parameters": [ { @@ -2356,6 +2421,42 @@ } } }, + "/api/is-session-duplicated": { + "get": { + "tags": [ + "Session API" + ], + "description": "Check if there are other different sessions for one user in one application.", + "operationId": "ApiController.IsSessionDuplicated", + "parameters": [ + { + "in": "query", + "name": "id", + "description": "The id(organization/application/user) of session", + "required": true, + "type": "string" + }, + { + "in": "query", + "name": "sessionId", + "description": "sessionId to be checked", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The Response object", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, "/api/login": { "post": { "tags": [ @@ -3224,6 +3325,35 @@ } } }, + "/api/update-session": { + "post": { + "tags": [ + "Session API" + ], + "description": "Update session for one user in one application.", + "operationId": "ApiController.UpdateSession", + "parameters": [ + { + "in": "query", + "name": "id", + "description": "The id(organization/application/user) of session", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The Response object", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, "/api/update-syncer": { "post": { "tags": [ @@ -3505,11 +3635,11 @@ } }, "definitions": { - "2346.0xc0001ce990.false": { + "2346.0xc000278ab0.false": { "title": "false", "type": "object" }, - "2381.0xc0001ce9c0.false": { + "2381.0xc000278ae0.false": { "title": "false", "type": "object" }, @@ -3566,6 +3696,9 @@ "code": { "type": "string" }, + "countryCode": { + "type": "string" + }, "email": { "type": "string" }, @@ -3599,9 +3732,6 @@ "phoneCode": { "type": "string" }, - "phonePrefix": { - "type": "string" - }, "provider": { "type": "string" }, @@ -3636,10 +3766,10 @@ "type": "object", "properties": { "data": { - "$ref": "#/definitions/2346.0xc0001ce990.false" + "$ref": "#/definitions/2346.0xc000278ab0.false" }, "data2": { - "$ref": "#/definitions/2381.0xc0001ce9c0.false" + "$ref": "#/definitions/2381.0xc000278ae0.false" }, "msg": { "type": "string" @@ -4091,6 +4221,12 @@ "$ref": "#/definitions/object.AccountItem" } }, + "countryCodes": { + "type": "array", + "items": { + "type": "string" + } + }, "createdTime": { "type": "string" }, @@ -4137,9 +4273,6 @@ "passwordType": { "type": "string" }, - "phonePrefix": { - "type": "string" - }, "tags": { "type": "array", "items": { @@ -4906,6 +5039,9 @@ "cloudfoundry": { "type": "string" }, + "countryCode": { + "type": "string" + }, "createdIp": { "type": "string" }, diff --git a/swagger/swagger.yml b/swagger/swagger.yml index 569bbd69..4c3ba10f 100644 --- a/swagger/swagger.yml +++ b/swagger/swagger.yml @@ -218,6 +218,30 @@ paths: description: The Response object schema: $ref: '#/definitions/controllers.Response' + /api/add-session: + post: + tags: + - Session API + description: Add session for one user in one application. If there are other existing sessions, join the session into the list. + operationId: ApiController.AddSession + parameters: + - in: query + name: id + description: The id(organization/application/user) of session + required: true + type: string + - in: query + name: sessionId + description: sessionId to be added + required: true + type: string + responses: + "200": + description: The Response object + schema: + type: array + items: + type: string /api/add-syncer: post: tags: @@ -574,12 +598,12 @@ paths: post: tags: - Session API - description: Delete session by userId + description: Delete session for one user in one application. operationId: ApiController.DeleteSession parameters: - in: query name: id - description: The id ( owner/name )(owner/name) of user. + description: The id(organization/application/user) of session required: true type: string responses: @@ -799,7 +823,7 @@ paths: schema: $ref: '#/definitions/Response' /api/get-email-and-phone: - post: + get: tags: - User API description: get email and phone by username @@ -847,7 +871,7 @@ paths: items: $ref: '#/definitions/object.User' /api/get-ldap: - post: + get: tags: - Account API operationId: ApiController.GetLdap @@ -857,7 +881,7 @@ paths: - Account API operationId: ApiController.GetLdapser /api/get-ldaps: - post: + get: tags: - Account API operationId: ApiController.GetLdaps @@ -1224,11 +1248,30 @@ paths: type: array items: $ref: '#/definitions/object.Role' + /api/get-session: + get: + tags: + - Session API + description: Get session for one user in one application. + operationId: ApiController.GetSingleSession + parameters: + - in: query + name: id + description: The id(organization/application/user) of session + required: true + type: string + responses: + "200": + description: The Response object + schema: + type: array + items: + type: string /api/get-sessions: get: tags: - Session API - description: Get organization user sessions + description: Get organization user sessions. operationId: ApiController.GetSessions parameters: - in: query @@ -1535,6 +1578,30 @@ paths: description: The Response object schema: $ref: '#/definitions/controllers.Response' + /api/is-session-duplicated: + get: + tags: + - Session API + description: Check if there are other different sessions for one user in one application. + operationId: ApiController.IsSessionDuplicated + parameters: + - in: query + name: id + description: The id(organization/application/user) of session + required: true + type: string + - in: query + name: sessionId + description: sessionId to be checked + required: true + type: string + responses: + "200": + description: The Response object + schema: + type: array + items: + type: string /api/login: post: tags: @@ -2110,6 +2177,25 @@ paths: description: The Response object schema: $ref: '#/definitions/controllers.Response' + /api/update-session: + post: + tags: + - Session API + description: Update session for one user in one application. + operationId: ApiController.UpdateSession + parameters: + - in: query + name: id + description: The id(organization/application/user) of session + required: true + type: string + responses: + "200": + description: The Response object + schema: + type: array + items: + type: string /api/update-syncer: post: tags: @@ -2293,10 +2379,10 @@ paths: schema: $ref: '#/definitions/Response' definitions: - 2346.0xc0001ce990.false: + 2346.0xc000278ab0.false: title: "false" type: object - 2381.0xc0001ce9c0.false: + 2381.0xc000278ae0.false: title: "false" type: object Response: @@ -2336,6 +2422,8 @@ definitions: type: string code: type: string + countryCode: + type: string email: type: string emailCode: @@ -2358,8 +2446,6 @@ definitions: type: string phoneCode: type: string - phonePrefix: - type: string provider: type: string redirectUri: @@ -2383,9 +2469,9 @@ definitions: type: object properties: data: - $ref: '#/definitions/2346.0xc0001ce990.false' + $ref: '#/definitions/2346.0xc000278ab0.false' data2: - $ref: '#/definitions/2381.0xc0001ce9c0.false' + $ref: '#/definitions/2381.0xc000278ae0.false' msg: type: string name: @@ -2689,6 +2775,10 @@ definitions: type: array items: $ref: '#/definitions/object.AccountItem' + countryCodes: + type: array + items: + type: string createdTime: type: string defaultApplication: @@ -2720,8 +2810,6 @@ definitions: type: string passwordType: type: string - phonePrefix: - type: string tags: type: array items: @@ -3237,6 +3325,8 @@ definitions: type: string cloudfoundry: type: string + countryCode: + type: string createdIp: type: string createdTime: diff --git a/web/src/auth/AuthBackend.js b/web/src/auth/AuthBackend.js index 2c239a2a..b856172a 100644 --- a/web/src/auth/AuthBackend.js +++ b/web/src/auth/AuthBackend.js @@ -36,11 +36,10 @@ export function signup(values) { }).then(res => res.json()); } -export function getEmailAndPhone(values) { - return fetch(`${authConfig.serverUrl}/api/get-email-and-phone`, { - method: "POST", +export function getEmailAndPhone(organization, username) { + return fetch(`${authConfig.serverUrl}/api/get-email-and-phone?organization=${organization}&username=${username}`, { + method: "GET", credentials: "include", - body: JSON.stringify(values), headers: { "Accept-Language": Setting.getAcceptLanguage(), }, diff --git a/web/src/auth/ForgetPage.js b/web/src/auth/ForgetPage.js index 7f5000fe..92eba8ea 100644 --- a/web/src/auth/ForgetPage.js +++ b/web/src/auth/ForgetPage.js @@ -24,8 +24,6 @@ import * as UserBackend from "../backend/UserBackend"; import {CheckCircleOutlined, KeyOutlined, LockOutlined, SolutionOutlined, UserOutlined} from "@ant-design/icons"; import CustomGithubCorner from "../CustomGithubCorner"; import {withRouter} from "react-router-dom"; - -const {Step} = Steps; const {Option} = Select; class ForgetPage extends React.Component { @@ -33,23 +31,21 @@ class ForgetPage extends React.Component { super(props); this.state = { classes: props, - account: props.account, applicationName: props.applicationName ?? props.match.params?.applicationName, application: null, msg: null, userId: "", username: "", name: "", - email: "", - isFixed: false, - fixedContent: "", - token: "", phone: "", - emailCode: "", - phoneCode: "", - verifyType: null, // "email" or "phone" + email: "", + dest: "", + isVerifyTypeFixed: false, + verifyType: "", // "email", "phone" current: 0, }; + + this.form = React.createRef(); } componentDidMount() { @@ -88,72 +84,67 @@ class ForgetPage extends React.Component { switch (name) { case "step1": const username = forms.step1.getFieldValue("username"); - AuthBackend.getEmailAndPhone({ - application: forms.step1.getFieldValue("application"), - organization: forms.step1.getFieldValue("organization"), - username: username, - }).then((res) => { - if (res.status === "ok") { - const phone = res.data.phone; - const email = res.data.email; - const saveFields = () => { - if (this.state.isFixed) { - forms.step2.setFieldsValue({email: this.state.fixedContent}); - this.setState({username: this.state.fixedContent}); + AuthBackend.getEmailAndPhone(forms.step1.getFieldValue("organization"), username) + .then((res) => { + if (res.status === "ok") { + const phone = res.data.phone; + const email = res.data.email; + + if (phone === "" && email === "") { + Setting.showMessage("error", "no verification method!"); + } else { + this.setState({ + name: res.data.name, + phone: phone, + email: email, + }); + + const saveFields = (type, dest, fixed) => { + this.setState({ + verifyType: type, + isVerifyTypeFixed: fixed, + dest: dest, + }); + }; + + switch (res.data2) { + case "email": + saveFields("email", email, true); + break; + case "phone": + saveFields("phone", phone, true); + break; + case "username": + phone !== "" ? saveFields("phone", phone, false) : saveFields("email", email, false); + } + + this.setState({ + current: 1, + }); } - this.setState({current: 1}); - }; - this.setState({phone: phone, email: email, username: res.data.name, name: res.data.name}); - - if (phone !== "" && email === "") { - this.setState({ - verifyType: "phone", - }); - } else if (phone === "" && email !== "") { - this.setState({ - verifyType: "email", - }); + } else { + Setting.showMessage("error", res.msg); } - - switch (res.data2) { - case "email": - this.setState({isFixed: true, fixedContent: email, verifyType: "email"}, () => {saveFields();}); - break; - case "phone": - this.setState({isFixed: true, fixedContent: phone, verifyType: "phone"}, () => {saveFields();}); - break; - default: - saveFields(); - break; - } - } else { - Setting.showMessage("error", i18next.t(`signup:${res.msg}`)); - } - }); + }); break; case "step2": const oAuthParams = Util.getOAuthGetParameters(); - const login = () => { - AuthBackend.login({ - application: forms.step2.getFieldValue("application"), - organization: forms.step2.getFieldValue("organization"), - username: this.state.username, - name: this.state.name, - code: forms.step2.getFieldValue("emailCode"), - type: "login", - }, oAuthParams).then(res => { - if (res.status === "ok") { - this.setState({current: 2, userId: res.data, username: res.data.split("/")[1]}); - } else { - Setting.showMessage("error", i18next.t(`signup:${res.msg}`)); - } - }); - }; - if (this.state.verifyType === "email") { - this.setState({username: this.state.email}, () => {login();}); - } else if (this.state.verifyType === "phone") { - this.setState({username: this.state.phone}, () => {login();}); - } + + AuthBackend.login({ + application: forms.step2.getFieldValue("application"), + organization: forms.step2.getFieldValue("organization"), + username: forms.step2.getFieldValue("dest"), + name: this.state.name, + code: forms.step2.getFieldValue("code"), + type: "login", + }, oAuthParams).then(res => { + if (res.status === "ok") { + this.setState({current: 2, userId: res.data}); + } else { + Setting.showMessage("error", res.msg); + } + }); + break; default: break; @@ -161,13 +152,13 @@ class ForgetPage extends React.Component { } onFinish(values) { - values.username = this.state.username; + values.username = this.state.name; values.userOwner = this.getApplicationObj()?.organizationObj.name; UserBackend.setPassword(values.userOwner, values.username, "", values?.newPassword).then(res => { if (res.status === "ok") { Setting.redirectToLoginPage(this.getApplicationObj(), this.props.history); } else { - Setting.showMessage("error", i18next.t(`signup:${res.msg}`)); + Setting.showMessage("error", res.msg); } }); } @@ -179,7 +170,7 @@ class ForgetPage extends React.Component { if (this.state.phone !== "") { options.push( - ); @@ -187,7 +178,7 @@ class ForgetPage extends React.Component { if (this.state.email !== "") { options.push( - ); @@ -202,76 +193,70 @@ class ForgetPage extends React.Component { this.onFormFinish(name, info, forms); }}> {/* STEP 1: input username -> get email & phone */} -