Skip to content

Commit

Permalink
Support change password policy (#186)
Browse files Browse the repository at this point in the history
  • Loading branch information
byn9826 authored Oct 31, 2024
1 parent 70de877 commit 330781b
Show file tree
Hide file tree
Showing 19 changed files with 494 additions and 19 deletions.
8 changes: 8 additions & 0 deletions server/src/__tests__/normal/identity-main.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
const { code } = json as { code: string }
const codeStore = JSON.parse(await mockedKV.get(`AC-${code}`) ?? '')
Expand Down Expand Up @@ -690,6 +691,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
const appRecord = await getApp(db)
const { code } = json as { code: string }
Expand Down Expand Up @@ -752,6 +754,7 @@ describe(
requireOtpSetup: true,
requireOtpMfa: true,
requireSmsMfa: false,
requireChangePassword: false,
})
global.process.env.OTP_MFA_IS_REQUIRED = false as unknown as string
},
Expand All @@ -774,6 +777,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
global.process.env.EMAIL_MFA_IS_REQUIRED = false as unknown as string
},
Expand All @@ -796,6 +800,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: true,
requireChangePassword: false,
})
global.process.env.SMS_MFA_IS_REQUIRED = false as unknown as string
},
Expand All @@ -818,6 +823,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
global.process.env.ENFORCE_ONE_MFA_ENROLLMENT = ['email', 'otp'] as unknown as string
},
Expand All @@ -840,6 +846,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
global.process.env.ENABLE_USER_APP_CONSENT = true as unknown as string
},
Expand Down Expand Up @@ -1006,6 +1013,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
const consent = db.prepare('SELECT * from user_app_consent WHERE "userId" = 1 AND "appId" = 1').get()
expect(consent).toBeTruthy()
Expand Down
8 changes: 8 additions & 0 deletions server/src/__tests__/normal/identity-mfa.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})

const user = await db.prepare('SELECT * from "user" WHERE id = 1').get() as userModel.Raw
Expand Down Expand Up @@ -322,6 +323,7 @@ describe(
requireOtpSetup: true,
requireOtpMfa: true,
requireSmsMfa: false,
requireChangePassword: false,
})

const user = await db.prepare('SELECT * from "user" WHERE id = 1').get() as userModel.Raw
Expand Down Expand Up @@ -544,6 +546,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
expect(await mockedKV.get(`${adapterConfig.BaseKVKey.OtpMfaCode}-${json.code}`)).toBe('1')
},
Expand Down Expand Up @@ -1516,6 +1519,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
expect(await mockedKV.get(`${adapterConfig.BaseKVKey.SmsMfaCode}-${json.code}`)).toBe('1')

Expand Down Expand Up @@ -1896,6 +1900,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
expect(await mockedKV.get(`${adapterConfig.BaseKVKey.EmailMfaCode}-${json.code}`)).toBe('1')
},
Expand Down Expand Up @@ -1994,6 +1999,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
expect(await mockedKV.get(`${adapterConfig.BaseKVKey.OtpMfaCode}-${json.code}`)).toBe('1')
},
Expand Down Expand Up @@ -2055,6 +2061,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
expect(await mockedKV.get(`${adapterConfig.BaseKVKey.SmsMfaCode}-${json.code}`)).toBe('1')
},
Expand Down Expand Up @@ -2235,6 +2242,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
expect(await mockedKV.get(`${adapterConfig.BaseKVKey.EmailMfaCode}-${json.code}`)).toBe('1')
},
Expand Down
176 changes: 176 additions & 0 deletions server/src/__tests__/normal/identity-policy.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import {
afterEach, beforeEach, describe, expect, test,
} from 'vitest'
import { Database } from 'better-sqlite3'
import { JSDOM } from 'jsdom'
import app from 'index'
import {
migrate, mock,
mockedKV,
} from 'tests/mock'
import { routeConfig } from 'configs'
import {
prepareFollowUpBody, prepareFollowUpParams,
insertUsers, postSignInRequest, getApp,
} from 'tests/identity'

let db: Database

beforeEach(async () => {
db = await migrate()
})

afterEach(async () => {
await db.close()
await mockedKV.empty()
})

describe(
'get /change-password',
() => {
test(
'should show change password page',
async () => {
await insertUsers(
db,
false,
)
const params = await prepareFollowUpParams(db)

const res = await app.request(
`${routeConfig.IdentityRoute.ChangePassword}${params}`,
{},
mock(db),
)

const html = await res.text()
const dom = new JSDOM(html)
const document = dom.window.document
expect(document.getElementsByName('password').length).toBe(1)
expect(document.getElementsByName('confirmPassword').length).toBe(1)
expect(document.getElementsByTagName('form').length).toBe(1)
expect(document.getElementsByTagName('select').length).toBe(1)
},
)

test(
'should redirect if use wrong auth code',
async () => {
await insertUsers(
db,
false,
)
await prepareFollowUpParams(db)

const res = await app.request(
`${routeConfig.IdentityRoute.ChangePassword}?locale=en&code=abc`,
{},
mock(db),
)
expect(res.status).toBe(302)
expect(res.headers.get('Location')).toBe(`${routeConfig.IdentityRoute.AuthCodeExpired}?locale=en`)
},
)

test(
'could disable locale selector',
async () => {
global.process.env.ENABLE_LOCALE_SELECTOR = false as unknown as string
await insertUsers(
db,
false,
)
const params = await prepareFollowUpParams(db)

const res = await app.request(
`${routeConfig.IdentityRoute.ChangePassword}${params}`,
{},
mock(db),
)

const html = await res.text()
const dom = new JSDOM(html)
const document = dom.window.document
expect(document.getElementsByTagName('select').length).toBe(0)
global.process.env.ENABLE_LOCALE_SELECTOR = true as unknown as string
},
)
},
)

describe(
'post /authorize-consent',
() => {
test(
'should change password',
async () => {
await insertUsers(
db,
false,
)
const body = await prepareFollowUpBody(db)

const res = await app.request(
routeConfig.IdentityRoute.ChangePassword,
{
method: 'POST',
body: JSON.stringify({
...body,
password: 'Password2!',
}),
},
mock(db),
)
const json = await res.json()
expect(json).toStrictEqual({ success: true })

const appRecord = await getApp(db)
const reLoginRes = await postSignInRequest(
db,
appRecord,
{ password: 'Password2!' },
)
const loginResJson = await reLoginRes.json() as { code: string }
expect(loginResJson).toStrictEqual({
code: expect.any(String),
redirectUri: 'http://localhost:3000/en/dashboard',
state: '123',
scopes: ['profile', 'openid', 'offline_access'],
requireConsent: true,
requireMfaEnroll: true,
requireEmailMfa: false,
requireSmsMfa: false,
requireOtpSetup: false,
requireOtpMfa: false,
requireChangePassword: false,
})
},
)

test(
'should redirect if use wrong auth code',
async () => {
await insertUsers(
db,
false,
)
await prepareFollowUpBody(db)

const res = await app.request(
routeConfig.IdentityRoute.ChangePassword,
{
method: 'POST',
body: JSON.stringify({
locale: 'en',
code: 'abc',
password: 'Password2!',
}),
},
mock(db),
)
expect(res.status).toBe(302)
expect(res.headers.get('Location')).toBe(`${routeConfig.IdentityRoute.AuthCodeExpired}?locale=en`)
},
)
},
)
2 changes: 2 additions & 0 deletions server/src/__tests__/normal/identity-social.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
}

Expand Down Expand Up @@ -281,6 +282,7 @@ describe(
requireOtpSetup: false,
requireOtpMfa: false,
requireSmsMfa: false,
requireChangePassword: false,
})
}

Expand Down
27 changes: 27 additions & 0 deletions server/src/configs/locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,33 @@ export const authorizeReset = Object.freeze({
},
})

export const changePassword = Object.freeze({
title: {
en: 'Update your password',
fr: 'Mettez à jour votre mot de passe',
},
success: {
en: 'Password updated!',
fr: 'Mot de passe mis à jour !',
},
newPassword: {
en: 'New Password',
fr: 'Nouveau mot de passe',
},
confirmNewPassword: {
en: 'Confirm New Password',
fr: 'Confirmez le nouveau mot de passe',
},
confirm: {
en: 'Confirm',
fr: 'Confirmer',
},
redirect: {
en: 'Redirect back',
fr: 'Rediriger en arrière',
},
})

export const verifyEmail = Object.freeze({
title: {
en: 'Verify your email',
Expand Down
1 change: 1 addition & 0 deletions server/src/configs/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ export enum IdentityRoute {
ResendResetCode = `${InternalRoute.Identity}/resend-reset-code`,
VerifyEmail = `${InternalRoute.Identity}/verify-email`,
Logout = `${InternalRoute.Identity}/logout`,
ChangePassword = `${InternalRoute.Identity}/change-password`,
}
11 changes: 11 additions & 0 deletions server/src/dtos/identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,17 @@ export class PostAuthorizeEnrollReqDto extends GetAuthorizeFollowUpReqDto {
}
}

export class PostChangePasswordReqDto extends GetAuthorizeFollowUpReqDto {
@IsString()
@IsNotEmpty()
password: string

constructor (dto: PostChangePasswordReqDto) {
super(dto)
this.password = dto.password
}
}

export class PostLogoutReqDto {
@IsString()
@IsNotEmpty()
Expand Down
Loading

0 comments on commit 330781b

Please sign in to comment.