Skip to content

Commit

Permalink
10007: WIP change password skeleton
Browse files Browse the repository at this point in the history
  • Loading branch information
TomElliottFlexion committed Jan 16, 2024
1 parent a62ec46 commit fd22cca
Show file tree
Hide file tree
Showing 12 changed files with 426 additions and 14 deletions.
129 changes: 129 additions & 0 deletions shared/src/business/entities/ChangePassword.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { JoiValidationConstants } from './JoiValidationConstants';
import { JoiValidationEntity } from './JoiValidationEntity';
import joi from 'joi';

type PasswordValidation = {
message: string;
valid: boolean;
};

export type ChangePasswordValidations = {
hasNoLeadingOrTrailingSpace: PasswordValidation;
hasOneLowercase: PasswordValidation;
hasOneNumber: PasswordValidation;
hasOneUppercase: PasswordValidation;
hasSpecialCharacterOrSpace: PasswordValidation;
isProperLength: PasswordValidation;
};

const ChangePasswordValidationErrorMessages = {
hasNoLeadingOrTrailingSpace: 'Must not contain leading or trailing space',
hasOneLowercase: 'Must contain lower case letter',
hasOneNumber: 'Must contain number',
hasOneUppercase: 'Must contain upper case letter',
hasSpecialCharacterOrSpace: 'Must contain special character or space',
isProperLength: 'Must be between 8-99 characters long',
};

export function getDefaultPasswordErrors(): ChangePasswordValidations {
return {
hasNoLeadingOrTrailingSpace: {
message:
ChangePasswordValidationErrorMessages.hasNoLeadingOrTrailingSpace,
valid: true,
},
hasOneLowercase: {
message: ChangePasswordValidationErrorMessages.hasOneLowercase,
valid: true,
},
hasOneNumber: {
message: ChangePasswordValidationErrorMessages.hasOneNumber,
valid: true,
},
hasOneUppercase: {
message: ChangePasswordValidationErrorMessages.hasOneUppercase,
valid: true,
},
hasSpecialCharacterOrSpace: {
message: ChangePasswordValidationErrorMessages.hasSpecialCharacterOrSpace,
valid: true,
},
isProperLength: {
message: ChangePasswordValidationErrorMessages.isProperLength,
valid: true,
},
};
}

export class ChangePasswordForm extends JoiValidationEntity {
public password: string;
public confirmPassword: string;

constructor(rawProps) {
super('ChangePasswordForm');
this.password = rawProps.password;
this.confirmPassword = rawProps.confirmPassword;
}

static VALIDATION_RULES = joi.object().keys({
confirmPassword: joi
.valid(joi.ref('password'))
.required()
.messages({ '*': 'Passwords must match' }),
entityName:
JoiValidationConstants.STRING.valid('ChangePasswordForm').required(),
password: JoiValidationConstants.STRING.custom((value, helper) => {
const errors = getDefaultPasswordErrors();

if (value.length < 8 || value.length > 99) {
errors.isProperLength.valid = false;
}

if (!/[a-z]/.test(value)) {
errors.hasOneLowercase.valid = false;
}

if (!/[A-Z]/.test(value)) {
errors.hasOneUppercase.valid = false;
}

if (!/[\^$*.[\]{}()?\-!@#%&/,><:;|_~`]/.test(value)) {
errors.hasSpecialCharacterOrSpace.valid = false;
}

if (!/[0-9]/.test(value)) {
errors.hasOneNumber.valid = false;
}

if (/^\s/.test(value) || /\s$/.test(value)) {
errors.hasNoLeadingOrTrailingSpace.valid = false;
}

const noErrors = Object.values(errors).reduce(
(accumulator, currentValue) => {
return accumulator && currentValue.valid;
},
true,
);

if (noErrors) {
return value;
} else {
return helper.message(
Object.entries(errors)
.filter(([, curValue]) => !curValue.valid)
.map(([key]) => key)
.join('|') as any,
);
}
}).description(
'Password for the account. Contains a custom validation because we want to construct a string with all the keys that failed which later we parse out to an object',
),
});

getValidationRules() {
return ChangePasswordForm.VALIDATION_RULES;
}
}

export type RawChangePasswordForm = ExcludeMethods<ChangePasswordForm>;
4 changes: 2 additions & 2 deletions shared/src/business/entities/NewPetitionerUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type PasswordValidation = {
valid: boolean;
};

export type NewPetitionerUserPasswordValidations = {
export type PasswordValidations = {
hasNoLeadingOrTrailingSpace: PasswordValidation;
hasOneLowercase: PasswordValidation;
hasOneNumber: PasswordValidation;
Expand All @@ -24,7 +24,7 @@ const NewPetitionerUserPasswordValidationErrorMessages = {
isProperLength: 'Must be between 8-99 characters long',
};

export function getDefaultPasswordErrors(): NewPetitionerUserPasswordValidations {
export function getDefaultPasswordErrors(): PasswordValidations {
return {
hasNoLeadingOrTrailingSpace: {
message:
Expand Down
25 changes: 25 additions & 0 deletions web-api/src/business/useCases/auth/changePasswordInteractor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const changePasswordInteractor = async (
applicationContext: IApplicationContext,
{
newPassword,
sessionId,
userEmail,
}: { newPassword: string; sessionId: string; userEmail: string },
) => {
const params = {
ChallengeName: 'NEW_PASSWORD_REQUIRED',
ChallengeResponses: {
NEW_PASSWORD: newPassword,
USERNAME: userEmail,
},
ClientId: process.env.COGNITO_CLIENT_ID,
Session: sessionId,
};

const result = await applicationContext
.getCognito()
.respondToAuthChallenge(params)
.promise();

return result;
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
import { state } from '@web-client/presenter/app.cerebral';

export const redirectToChangePasswordAction = ({ get, router }) => {
const a = get(state.cognitoPasswordChange);
console.log(`get(state.cognitoPasswordChange)[${a}]`);

router.externalRoute(get(state.cognitoPasswordChange));
export const redirectToChangePasswordAction = async ({ router }) => {
await router.route('/change-password');
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Get } from 'cerebral';
import {
NewPetitionerUser,
NewPetitionerUserPasswordValidations,
PasswordValidations,
getDefaultPasswordErrors,
} from '@shared/business/entities/NewPetitionerUser';
import { state } from '@web-client/presenter/app.cerebral';
Expand All @@ -11,12 +11,12 @@ export type CreateAccountHelperResults = {
email?: string;
formIsValid: boolean;
name?: string;
passwordErrors?: NewPetitionerUserPasswordValidations;
passwordErrors?: PasswordValidations;
};

const convertErrorMessageToPasswordValidationObject = (
export const convertErrorMessageToPasswordValidationObject = (
stringToParse: string | undefined,
): NewPetitionerUserPasswordValidations => {
): PasswordValidations => {
const errorObjects = getDefaultPasswordErrors();
if (!stringToParse) return errorObjects;

Expand All @@ -33,7 +33,7 @@ export const createAccountHelper = (get: Get): CreateAccountHelperResults => {
const formEntity = new NewPetitionerUser(form);
const errors = formEntity.getFormattedValidationErrors();

const passwordErrors: NewPetitionerUserPasswordValidations =
const passwordErrors: PasswordValidations =
convertErrorMessageToPasswordValidationObject(errors?.password);

return {
Expand Down
26 changes: 26 additions & 0 deletions web-client/src/presenter/computeds/Login/changePasswordHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ChangePasswordForm } from '@shared/business/entities/ChangePassword';
import { Get } from 'cerebral';
import { PasswordValidations } from '@shared/business/entities/NewPetitionerUser';
import { convertErrorMessageToPasswordValidationObject } from '@web-client/presenter/computeds/CreatePetitionerAccount/createAccountHelper';
import { state } from '@web-client/presenter/app.cerebral';

export type ChangePasswordHelperResults = {
confirmPassword: boolean;
formIsValid: boolean;
passwordErrors?: PasswordValidations;
};

export const changePasswordHelper = (get: Get): ChangePasswordHelperResults => {
const form = get(state.form);
const formEntity = new ChangePasswordForm(form);
const errors = formEntity.getFormattedValidationErrors();

const passwordErrors: PasswordValidations =
convertErrorMessageToPasswordValidationObject(errors?.password);

return {
confirmPassword: !errors?.confirmPassword,
formIsValid: formEntity.isValid(),
passwordErrors,
};
};
2 changes: 2 additions & 0 deletions web-client/src/presenter/presenter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ import { getCaseInventoryReportSequence } from './sequences/getCaseInventoryRepo
import { getCustomCaseReportSequence } from './sequences/getCustomCaseReportSequence';
import { getUsersInSectionSequence } from './sequences/getUsersInSectionSequence';
import { goToApplyStampSequence } from './sequences/gotoApplyStampSequence';
import { goToChangePasswordSequence } from '@web-client/presenter/sequences/Login/goToChangePasswordSequence';
import { goToCreatePetitionerAccountSequence } from '@web-client/presenter/sequences/Public/goToCreatePetitionerAccountSequence';
import { goToVerificationSentSequence } from '@web-client/presenter/sequences/goToVerificationSentSequence';
import { gotoAccessibilityStatementSequence } from './sequences/gotoAccessibilityStatementSequence';
Expand Down Expand Up @@ -748,6 +749,7 @@ export const presenterSequences = {
getCustomCaseReportSequence,
getUsersInSectionSequence: getUsersInSectionSequence as unknown as Function,
goToApplyStampSequence: goToApplyStampSequence as unknown as Function,
goToChangePasswordSequence,
goToCreatePetitionerAccountSequence,
goToVerificationSentSequence:
goToVerificationSentSequence as unknown as Function,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { setupCurrentPageAction } from '../../actions/setupCurrentPageAction';

export const goToChangePasswordSequence = [
setupCurrentPageAction('ChangePassword'),
] as unknown as () => void;
4 changes: 4 additions & 0 deletions web-client/src/presenter/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { caseSearchNoMatchesHelper } from './computeds/caseSearchNoMatchesHelper
import { caseStatusHistoryHelper } from './computeds/caseStatusHistoryHelper';
import { caseTypeDescriptionHelper } from './computeds/caseTypeDescriptionHelper';
import { caseWorksheetsHelper } from '@web-client/presenter/computeds/CaseWorksheets/caseWorksheetsHelper';
import { changePasswordHelper } from '@web-client/presenter/computeds/Login/changePasswordHelper';
import { cloneDeep } from 'lodash';
import { completeDocumentTypeSectionHelper } from './computeds/completeDocumentTypeSectionHelper';
import { confirmInitiateServiceModalHelper } from './computeds/confirmInitiateServiceModalHelper';
Expand Down Expand Up @@ -241,6 +242,9 @@ export const computeds = {
caseWorksheetsHelper: caseWorksheetsHelper as unknown as ReturnType<
typeof caseWorksheetsHelper
>,
changePasswordHelper: changePasswordHelper as unknown as ReturnType<
typeof changePasswordHelper
>,
completeDocumentTypeSectionHelper:
completeDocumentTypeSectionHelper as unknown as ReturnType<
typeof completeDocumentTypeSectionHelper
Expand Down
5 changes: 5 additions & 0 deletions web-client/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,11 @@ const router = {
app.getSequence('gotoLoginSequence')();
});

registerRoute('/change-password', () => {
setPageTitle('Change Password');
app.getSequence('goToChangePasswordSequence')();
});

registerRoute('/create-account/petitioner', () => {
setPageTitle('Account Registration');
app.getSequence('goToCreatePetitionerAccountSequence')();
Expand Down
3 changes: 3 additions & 0 deletions web-client/src/views/AppComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { CaseInventoryReport } from './CaseInventoryReport/CaseInventoryReport';
import { CaseInventoryReportModal } from './CaseInventoryReport/CaseInventoryReportModal';
import { CaseSearchNoMatches } from './CaseSearchNoMatches';
import { ChangeLoginAndServiceEmail } from './ChangeLoginAndServiceEmail';
import { ChangePassword } from '@web-client/views/Login/ChangePassword';
import { Contact } from './Contact';
import { ContactEdit } from './ContactEdit';
import { CourtIssuedDocketEntry } from './CourtIssuedDocketEntry/CourtIssuedDocketEntry';
Expand Down Expand Up @@ -119,6 +120,7 @@ const pages = {
CaseInventoryReport,
CaseSearchNoMatches,
ChangeLoginAndServiceEmail,
ChangePassword,
Contact,
ContactEdit,
CourtIssuedDocketEntry,
Expand Down Expand Up @@ -190,6 +192,7 @@ const pages = {
};

const pagesWithBlueBackground = {
ChangePassword,
CreatePetitionerAccount,
Login,
VerificationSent,
Expand Down
Loading

0 comments on commit fd22cca

Please sign in to comment.