Skip to content
Open
6 changes: 6 additions & 0 deletions application/admin-client/src/pages/participants/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ export const ParticipantEdit = () => {
const { externalId, profile } = values
// eslint-disable-next-line
const { familyId, familyMembers, id, ...rest } = profile
if (rest.email) {
rest.email = rest.email.trim()
}
if (rest.nextOfKin?.email) {
rest.nextOfKin.email = rest.nextOfKin.email.trim()
}
onFinish({
externalId: externalId || '',
profile: rest,
Expand Down
8 changes: 5 additions & 3 deletions application/admin-client/src/pages/password/update.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,16 @@ export const UpdatePassword = () => {
}
}

if (data.password !== data.confirmPassword) {
const password = data.password.trim()

if (password !== data.confirmPassword?.trim()) {
return {
values: {},
errors: { password: { type: 'validate', message: "Passwords don't match" } },
}
}

const { isValid, fields } = checkPasswordStrength(data.password)
const { isValid, fields } = checkPasswordStrength(password)
if (!isValid) {
return {
values: {},
Expand All @@ -58,7 +60,7 @@ export const UpdatePassword = () => {
}
}
return {
values: data,
values: { ...data, password },
errors: {},
}
},
Expand Down
11 changes: 7 additions & 4 deletions application/admin-client/src/pages/setup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,22 @@ export const SetupPage = () => {
const { mutate: login } = useLogin()

const onSubmit = (data: any) => {
const password = data.password.trim()
const email = data.email.trim()
const payload = { ...data, email, password }
fetch(import.meta.env.VITE_BACKEND_URL + '/auth/register/setup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
body: JSON.stringify(payload),
})
.then((res) => {
if (res.ok) {
res.json().then((rdata: RegisterParticipantResponse) => {
if (!rdata.token) throw new Error('No token provided')
login({
loginType: 'Password',
email: data.email,
password: data.password,
email,
password,
})
})
} else {
Expand Down Expand Up @@ -87,7 +90,7 @@ export const SetupPage = () => {
{...register('password', {
required: true,
validate: (val) => {
const { isValid, fields } = checkPasswordStrength(val)
const { isValid, fields } = checkPasswordStrength(val.trim())
if (!isValid) {
return `Invalid password. ${Object.values(fields).map((f) => ' ' + f.message)}`
}
Expand Down
12 changes: 10 additions & 2 deletions application/admin-client/src/pages/users/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,24 @@ import { Controller } from 'react-hook-form'
export const UserCreate = () => {
const {
saveButtonProps,
refineCore: { formLoading },
refineCore: { formLoading, onFinish },
register,
handleSubmit,
control,
formState: { errors },
} = useForm({})

const { data: identity } = useGetIdentity<{ role: string; id: number }>()

const handleSubmitCustom = (values: any) => {
onFinish({ ...values, email: values.email?.trim() })
}

return (
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
<Create
isLoading={formLoading}
saveButtonProps={{ ...saveButtonProps, onClick: handleSubmit(handleSubmitCustom) }}
>
<Box component="form" sx={{ display: 'flex', flexDirection: 'column' }} autoComplete="off">
<TextField
{...register('firstName', {
Expand Down
9 changes: 7 additions & 2 deletions application/admin-client/src/pages/users/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ export const UserEdit = () => {
type FieldValues = UpdateUserRequest
const {
saveButtonProps,
refineCore: { formLoading },
refineCore: { formLoading, onFinish },
register,
handleSubmit,
control,
formState: { errors },
} = useForm<any, any, FieldValues>({ refineCoreProps: { redirect: false } })

const handleSubmitCustom = (values: any) => {
onFinish({ ...values, email: values.email?.trim() })
}

const { query } = useShow()

const { data } = query
Expand Down Expand Up @@ -71,7 +76,7 @@ export const UserEdit = () => {
return (
<Edit
isLoading={formLoading}
saveButtonProps={saveButtonProps}
saveButtonProps={{ ...saveButtonProps, onClick: handleSubmit(handleSubmitCustom) }}
canDelete={identity?.role == 'OrganisationAdmin'}
footerButtonProps={{
sx: { display: 'flex', justifyContent: 'flex-start', width: '100%', pl: 2, pb: 2 },
Expand Down
9 changes: 6 additions & 3 deletions application/admin-client/src/providers/authProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ export const authProvider: AuthProvider = {

// EMAIL / PASSWORD CREDENTIALS
case 'Password': {
const { email, password } = params
const { email: rawEmail, password: rawPassword } = params
const email = rawEmail.trim()
const password = rawPassword.trim()

const res = await fetch(BACKEND_URL + '/auth/login', {
method: 'POST',
Expand Down Expand Up @@ -153,7 +155,7 @@ export const authProvider: AuthProvider = {
return { error }
},
forgotPassword: async ({ email }) => {
const req: GeneratePasswordResetLinkRequest = { email: email }
const req: GeneratePasswordResetLinkRequest = { email: email.trim() }
try {
await axiosInstance.post('/users/password/generate-reset-link', req, {
headers: { 'Content-Type': 'application/json', 'x-client-type': clientType },
Expand All @@ -175,7 +177,8 @@ export const authProvider: AuthProvider = {
},
}
},
updatePassword: async ({ password, token }) => {
updatePassword: async ({ password: rawPassword, token }) => {
const password = rawPassword.trim()
const reqData: ResetPasswordRequest = {
newPassword: password,
token: token,
Expand Down
16 changes: 10 additions & 6 deletions application/backend/prisma/seed/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,31 +330,35 @@ const main = async () => {
},
})

const exampleAdminEmail = (process.env.EXAMPLE_ORG_ADMIN_EMAIL ?? '').trim()
const exampleAdminPassword = (process.env.EXAMPLE_ORG_ADMIN_PASSWORD ?? '').trim()
const exampleAdmin = await prisma.user.upsert({
where: { email: String(process.env.EXAMPLE_ORG_ADMIN_EMAIL) },
where: { email: exampleAdminEmail },
update: {},
create: {
email: String(process.env.EXAMPLE_ORG_ADMIN_EMAIL),
email: exampleAdminEmail,
firstName: 'Example',
lastName: 'Admin',
role: 'OrganisationAdmin',
password: hashPassword(String(process.env.EXAMPLE_ORG_ADMIN_PASSWORD)),
password: hashPassword(exampleAdminPassword),
},
})
console.log('Added the following users:', exampleAdmin)

const exampleAnswers = createDefaultAnswers(SeedSurveyStepData)
exampleAnswers[1].answers[0] = false //For DUO testing
const exampleParticipantEmail = (process.env.EXAMPLE_PARTICIPANT_EMAIL ?? '').trim()
const exampleParticipantPassword = (process.env.EXAMPLE_PARTICIPANT_PASSWORD ?? '').trim()
const exampleUser = await prisma.user.upsert({
where: { email: String(process.env.EXAMPLE_PARTICIPANT_EMAIL) },
where: { email: exampleParticipantEmail },
update: {},
create: {
email: String(process.env.EXAMPLE_PARTICIPANT_EMAIL),
email: exampleParticipantEmail,
firstName: 'Judith',
middleName: 'Arundell',
lastName: 'Wright',
role: 'Participant',
password: hashPassword(String(process.env.EXAMPLE_PARTICIPANT_PASSWORD)),
password: hashPassword(exampleParticipantPassword),
profiles: {
create: [
{
Expand Down
33 changes: 27 additions & 6 deletions application/backend/src/controllers/AuthController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ export class AuthController extends Controller {
@Security('jwt', ['OrganisationAdmin'])
@SuccessResponse('201', 'User Created')
public async registerUser(@Body() bodyRequest: RegisterRequest): Promise<RegisterResponse> {
const { password, ...userDetails } = bodyRequest
const { password: rawPassword, email: rawEmail, ...userDetails } = bodyRequest
const password = rawPassword.trim()
const email = rawEmail.trim()

const { isValid, fields } = await checkPasswordStrength(password)

Expand All @@ -93,6 +95,7 @@ export class AuthController extends Controller {
const insertedUser: User = await this.userRepo.create({
data: {
...userDetails,
email,
password: hashedPassword,
},
})
Expand Down Expand Up @@ -143,7 +146,9 @@ export class AuthController extends Controller {
public async registerInitialUser(
@Body() bodyRequest: RegisterSetupRequest,
): Promise<RegisterResponse> {
const { password, email } = bodyRequest
const { password: rawPassword, email: rawEmail } = bodyRequest
const password = rawPassword.trim()
const email = rawEmail.trim()

const { isValid, fields } = await checkPasswordStrength(password)

Expand Down Expand Up @@ -205,7 +210,16 @@ export class AuthController extends Controller {
@Body() bodyRequest: RegisterParticipantRequest,
): Promise<RegisterParticipantResponse> {
// Extract info for user creation
const { firstName, middleName, lastName, email, password, ...participantInfo } = bodyRequest
const {
firstName,
middleName,
lastName,
email: rawEmail,
password: rawPassword,
...participantInfo
} = bodyRequest
const password = rawPassword.trim()
const email = rawEmail.trim()

// Check that the Participant has an invitation
const invite = await this.inviteRepo.findFirst({ where: { id: inviteId, email } })
Expand Down Expand Up @@ -319,7 +333,8 @@ export class AuthController extends Controller {
method: 'POST',
})

const { email } = await userinfo_res.json()
const { email: rawEmail } = await userinfo_res.json()
const email = rawEmail?.trim()
user = await this.userRepo.findFirst({ where: { email } })
} catch {
throw new Error('Error authenticating with OIDC')
Expand Down Expand Up @@ -353,8 +368,11 @@ export class AuthController extends Controller {
@Body() bodyRequest: LoginRequest,
@Header('x-client-type') clientType?: string,
): Promise<LoginResponse> {
const { email: rawEmail, password: rawPassword } = bodyRequest
const email = rawEmail.trim()
const password = rawPassword.trim()
// Check if user exists and password matches
const user = await this.userRepo.findUnique({ where: { email: bodyRequest.email } })
const user = await this.userRepo.findUnique({ where: { email } })

if (!user) {
throw new InvalidCredentialsError('User not found')
Expand All @@ -380,7 +398,7 @@ export class AuthController extends Controller {
throw new IncorrectPermissionsError('User is not a participant')
}

if (!(await verifyPassword(user.password, bodyRequest.password))) {
if (!(await verifyPassword(user.password, password))) {
await this.userRepo.update({
where: { id: user.id },
data: { retriesRemaining: user.retriesRemaining - 1 },
Expand Down Expand Up @@ -538,6 +556,9 @@ export class AuthController extends Controller {
// Extract user and profile data
const { firstName, lastName, dob, externalId, ...profileData } = participantData
const { nextOfKin, dependents, ...noNextOfKinProfileData } = profileData
if (nextOfKin?.email) {
nextOfKin.email = nextOfKin.email.trim()
}
const nextOfKinCreateData = { nextOfKin: { create: { ...nextOfKin } } }

// Check for existing dependents
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@ export class IntegrationsController extends Controller {
const existingUsers: string[] = []

for (const participant of data) {
const { email, ...participantData } = participant
const { email: rawEmail, ...participantData } = participant
const email = rawEmail.trim()

// eslint-disable-next-line @typescript-eslint/no-explicit-any
delete (participantData as any).password
Expand Down
9 changes: 8 additions & 1 deletion application/backend/src/controllers/ProfilesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ export class ProfilesController extends Controller {
where: { userId: request.user?.userId },
})
const { nextOfKin, ...updateData } = { ...bodyRequest }
if (nextOfKin?.email) {
nextOfKin.email = nextOfKin.email.trim()
}

const hasNok = Boolean(nextOfKin)

Expand Down Expand Up @@ -186,7 +189,11 @@ export class ProfilesController extends Controller {
studies: { select: { studyId: true } },
},
})
const { nextOfKin, email, ...updateData } = { ...bodyRequest }
const { nextOfKin, email: rawEmail, ...updateData } = { ...bodyRequest }
const email = rawEmail?.trim()
if (nextOfKin?.email) {
nextOfKin.email = nextOfKin.email.trim()
}

const hasNok = Boolean(nextOfKin)

Expand Down
Loading
Loading