Skip to content

Commit

Permalink
feat: change own password dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
NGPixel committed Oct 2, 2023
1 parent c5a441c commit 7f9c351
Show file tree
Hide file tree
Showing 12 changed files with 350 additions and 20 deletions.
15 changes: 11 additions & 4 deletions server/graph/resolvers/authentication.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,17 @@ export default {
*/
async changePassword (obj, args, context) {
try {
const authResult = await WIKI.db.users.loginChangePassword(args, context)
return {
...authResult,
operation: generateSuccess('Password changed successfully')
if (args.continuationToken) {
const authResult = await WIKI.db.users.loginChangePassword(args, context)
return {
...authResult,
operation: generateSuccess('Password set successfully')
}
} else {
await WIKI.db.users.changePassword(args, context)
return {
operation: generateSuccess('Password changed successfully')
}
}
} catch (err) {
WIKI.logger.debug(err)
Expand Down
11 changes: 6 additions & 5 deletions server/graph/resolvers/user.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default {
const usr = await WIKI.db.users.query().findById(args.id)

if (!usr) {
throw new Error('Invalid User')
throw new Error('ERR_INVALID_USER')
}

// const str = _.get(WIKI.auth.strategies, usr.providerKey)
Expand All @@ -51,10 +51,11 @@ export default {

usr.auth = _.mapValues(usr.auth, (auth, providerKey) => {
if (auth.password) {
auth.password = '***'
auth.password = 'redacted'
}
if (auth.tfaSecret) {
auth.tfaSecret = 'redacted'
}
auth.module = providerKey === '00910749-8ab6-498a-9be0-f4ca28ea5e52' ? 'google' : 'local'
auth._moduleName = providerKey === '00910749-8ab6-498a-9be0-f4ca28ea5e52' ? 'Google' : 'Local'
return auth
})

Expand Down Expand Up @@ -211,7 +212,7 @@ export default {
},
async changeUserPassword (obj, args, context) {
try {
if (args.newPassword?.length < 6) {
if (args.newPassword?.length < 8) {
throw new Error('ERR_PASSWORD_TOO_SHORT')
}

Expand Down
3 changes: 1 addition & 2 deletions server/graph/schemas/authentication.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,11 @@ extend type Mutation {
): AuthenticationAuthResponse @rateLimit(limit: 5, duration: 60)

changePassword(
userId: UUID
continuationToken: String
currentPassword: String
newPassword: String!
strategyId: UUID!
siteId: UUID
siteId: UUID!
): AuthenticationAuthResponse @rateLimit(limit: 5, duration: 60)

forgotPassword(
Expand Down
7 changes: 7 additions & 0 deletions server/graph/schemas/user.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,15 @@ input UserUpdateInput {
email: String
name: String
groups: [UUID!]
auth: UserAuthUpdateInput
isActive: Boolean
isVerified: Boolean
meta: JSON
prefs: JSON
}

input UserAuthUpdateInput {
tfaRequired: Boolean
mustChangePwd: Boolean
restrictLogin: Boolean
}
4 changes: 3 additions & 1 deletion server/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,7 @@
"auth.errors.tooManyAttempts": "Too many attempts!",
"auth.errors.tooManyAttemptsMsg": "You've made too many failed attempts in a short period of time, please try again {time}.",
"auth.errors.userNotFound": "User not found",
"auth.errors.fields": "One or more fields are invalid.",
"auth.fields.email": "Email Address",
"auth.fields.emailUser": "Email / Username",
"auth.fields.name": "Name",
Expand Down Expand Up @@ -1197,9 +1198,9 @@
"auth.tfaFormTitle": "Enter the security code generated from your trusted device:",
"auth.tfaSetupInstrFirst": "Scan the QR code below from your mobile 2FA application:",
"auth.tfaSetupInstrSecond": "Enter the security code generated from your trusted device:",
"auth.tfaSetupSuccess": "2FA enabled successfully on your account.",
"auth.tfaSetupTitle": "Your administrator has required Two-Factor Authentication (2FA) to be enabled on your account.",
"auth.tfaSetupVerifying": "Verifying...",
"auth.tfaSetupSuccess": "2FA enabled successfully on your account.",
"common.actions.activate": "Activate",
"common.actions.add": "Add",
"common.actions.apply": "Apply",
Expand Down Expand Up @@ -1746,6 +1747,7 @@
"profile.appearanceLight": "Light",
"profile.auth": "Authentication",
"profile.authChangePassword": "Change Password",
"profile.authDisableTfa": "Turn Off 2FA",
"profile.authInfo": "Your account is associated with the following authentication methods:",
"profile.authLoadingFailed": "Failed to load authentication methods.",
"profile.authModifyTfa": "Modify 2FA",
Expand Down
52 changes: 50 additions & 2 deletions server/models/users.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,42 @@ export class User extends Model {
}
}

/**
* Change Password from Profile
*/
static async changePassword ({ strategyId, siteId, currentPassword, newPassword }, context) {
const userId = context.req.user?.id
if (!userId) {
throw new Error('ERR_USER_NOT_AUTHENTICATED')
}

const user = await WIKI.db.users.query().findById(userId)
if (!user) {
throw new Error('ERR_USER_NOT_FOUND')
}

if (!newPassword || newPassword.length < 8) {
throw new Error('ERR_PASSWORD_TOO_SHORT')
}

if (!user.auth[strategyId]?.password) {
throw new Error('ERR_UNEXPECTED_STRATEGY_ID')
}

if (await bcrypt.compare(currentPassword, user.auth[strategyId].password) !== true) {
throw new Error('ERR_INCORRECT_CURRENT_PASSWORD')
}

user.auth[strategyId].password = await bcrypt.hash(newPassword, 12)
user.auth[strategyId].mustChangePwd = false

await user.$query().patch({
auth: user.auth
})

return true
}

/**
* Send a password reset request
*/
Expand Down Expand Up @@ -686,14 +722,14 @@ export class User extends Model {
*
* @param {Object} param0 User ID and fields to update
*/
static async updateUser (id, { email, name, groups, isVerified, isActive, meta, prefs }) {
static async updateUser (id, { email, name, groups, auth, isVerified, isActive, meta, prefs }) {
const usr = await WIKI.db.users.query().findById(id)
if (usr) {
let usrData = {}
if (!isEmpty(email) && email !== usr.email) {
const dupUsr = await WIKI.db.users.query().select('id').where({ email }).first()
if (dupUsr) {
throw new WIKI.Error.AuthAccountAlreadyExists()
throw new Error('ERR_DUPLICATE_ACCOUNT_EMAIL')
}
usrData.email = email.toLowerCase()
}
Expand All @@ -714,6 +750,18 @@ export class User extends Model {
await usr.$relatedQuery('groups').unrelate().where('groupId', grp)
}
}
if (!isNil(auth?.tfaRequired)) {
usr.auth[WIKI.data.systemIds.localAuthId].tfaRequired = auth.tfaRequired
usrData.auth = usr.auth
}
if (!isNil(auth?.mustChangePwd)) {
usr.auth[WIKI.data.systemIds.localAuthId].mustChangePwd = auth.mustChangePwd
usrData.auth = usr.auth
}
if (!isNil(auth?.restrictLogin)) {
usr.auth[WIKI.data.systemIds.localAuthId].restrictLogin = auth.restrictLogin
usrData.auth = usr.auth
}
if (!isNil(isVerified)) {
usrData.isVerified = isVerified
}
Expand Down
1 change: 1 addition & 0 deletions ux/public/_assets/icons/ultraviolet-good-pincode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions ux/public/_assets/icons/ultraviolet-lock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion ux/src/components/AuthLoginPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ async function changePwd () {
$continuationToken: String
$newPassword: String!
$strategyId: UUID!
$siteId: UUID
$siteId: UUID!
) {
changePassword (
continuationToken: $continuationToken
Expand Down
Loading

0 comments on commit 7f9c351

Please sign in to comment.