Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix login process #394

Merged
merged 4 commits into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions packages/client/src/components/form-inputs/PasswordForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ const formValidator = {
confirmPassword: passwordValidator,
};

export interface PasswordFormProps extends InputProps<string | null> {
passwordInHistory: boolean;
}

/**
* A password form
*
Expand Down Expand Up @@ -107,10 +111,19 @@ export default class PasswordForm extends React.Component<
<TextBox>
Please enter and confirm a password.
<br />
<br />
New update effective January 1st, 2024: All passwords must be at least 11 characters
in length. To read more about this change, please visit{' '}
<a href="https://github.com/cap-md089/evmplus-guides/wiki/Password-policy">
our user guide
</a>
.
<br />
<br />
Passwords must meet the following requirements:
<ul>
<li className={getClassFromShowLevel(this.state.showLengthError)}>
Length of at least 8 characters
Length of at least 11 characters
</li>
<li
className={getClassFromShowLevel(
Expand Down Expand Up @@ -138,8 +151,8 @@ export default class PasswordForm extends React.Component<
</li>
</ul>
* A special character is a space character or one of the following symbols:
<br />^ ! @ # $ % ^ &amp; * ( ) {'{'} {'}'} _ + - = {'<'} {'>'} , . ? / [ ] \ | ; '
"
<br />^ ! @ # $ % ^ &amp; * ( ) {'{'} {'}'} _ + - = &lt; &gt; , . ? / [ ] \ | ;
&apos; &quot;
<br />
<span className={getClassFromShowLevel(this.state.showMatchError)}>
Passwords must also match
Expand Down Expand Up @@ -213,7 +226,7 @@ export default class PasswordForm extends React.Component<
hasError = true;
}

if (fields.password.length >= 8) {
if (fields.password.length >= 11) {
update.showLengthError = ShowLevel.SHOWGOOD;
} else {
hasError = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,10 @@ export default class CreateProspectiveMember extends Page<PageProps, CreateAccou
seniorMember: !!form.seniorMember,
};

const password: CAPProspectiveMemberPasswordCreation = form.password;
const password: CAPProspectiveMemberPasswordCreation =
form.password.type !== CAPProspectiveMemberPasswordCreationType.EMAILLINK
? { ...form.password, username: form.password.username.trim() }
: form.password;

await fetchApi.member.account.capprospective
.requestProspectiveAccount(
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/pages/account/FinishSignup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default class FinishSignup extends Page<PageProps<{ token: string }>, Fin
public componentDidMount(): void {
this.props.deleteReduxState();
}

public render = (): JSX.Element => (
<SimpleForm<FormValues>
disableOnInvalid={true}
Expand Down Expand Up @@ -116,7 +116,7 @@ export default class FinishSignup extends Page<PageProps<{ token: string }>, Fin

const fetchResult = await fetchApi.member.account.finishAccountSetup(
{},
{ token, username, password },
{ token, username: username.trim(), password },
);

if (Either.isLeft(fetchResult)) {
Expand Down
26 changes: 15 additions & 11 deletions packages/client/src/pages/account/RequestPasswordReset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default class RequestPasswordResetForm extends Page<PageProps, RequestPas
public componentDidMount(): void {
this.props.deleteReduxState();
}

public render = (): JSX.Element => (
<SimpleForm<RequestPasswordResetFormValues>
values={this.state.form}
Expand All @@ -68,7 +68,7 @@ export default class RequestPasswordResetForm extends Page<PageProps, RequestPas
username: val => val?.length > 0,
}}
submitInfo={{
disabled: this.state.tryingSubmit,
disabled: this.state.tryingSubmit || this.state.success,
text: 'Request password reset',
}}
>
Expand All @@ -80,14 +80,18 @@ export default class RequestPasswordResetForm extends Page<PageProps, RequestPas
) : null}
{this.state.success ? (
<TextBox>
<p>
Password reset request succesful. Please check your inbox for a password
reset link.
</p>
<p>
If the email is not received in your inbox, please check your Spam or Junk
folder for the message
</p>
<div className="banner">
<p>
Password reset request succesful. Please check your inbox for a password
reset link. The email may take up to 5 minutes to appear in your inbox.
If nothing shows up after 5 minutes, please double check the username
provided
</p>
<p>
If the email is not received in your inbox, please check your Spam or
Junk folder for the message
</p>
</div>
</TextBox>
) : null}

Expand Down Expand Up @@ -136,7 +140,7 @@ export default class RequestPasswordResetForm extends Page<PageProps, RequestPas
const result = await fetchApi.member.account.passwordResetRequest(
{},
{
username: form.username,
username: form.username.trim(),
captchaToken: form.captchaToken,
},
);
Expand Down
23 changes: 13 additions & 10 deletions packages/client/src/pages/account/RequestUsername.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default class RequestUsernameForm extends Page<PageProps, RequestUsername
public componentDidMount(): void {
this.props.deleteReduxState();
}

public render = (): JSX.Element => (
<SimpleForm<RequestUsernameFormValues>
values={this.state.form}
Expand All @@ -68,7 +68,7 @@ export default class RequestUsernameForm extends Page<PageProps, RequestUsername
capid: val => val !== null && val >= 100000,
}}
submitInfo={{
disabled: this.state.tryingSubmit,
disabled: this.state.tryingSubmit || this.state.success,
text: 'Request login',
}}
>
Expand All @@ -80,14 +80,17 @@ export default class RequestUsernameForm extends Page<PageProps, RequestUsername
) : null}
{this.state.success ? (
<TextBox>
<p>
Login request successful. Please check the email associated with the CAP ID
entered for an email with your CAP account.
</p>
<p>
If the email is not received in your inbox, please check your Spam or Junk
folder for the message
</p>
<div className="banner">
<p>
Login request successful. Please check the email associated with the CAP
ID entered for an email with your CAP account. This email may take up to
5 minutes to appear in your inbox.
</p>
<p>
If the email is not received in your inbox, please check your Spam or
Junk folder for the message
</p>
</div>
</TextBox>
) : null}

Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/pages/account/Signin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export const RegularSignin: React.FC<SigninPageProps> = ({ state, authorizeUser,
.signin(
{},
{
username: state.signinFormValues.username,
username: state.signinFormValues.username.trim(),
password: state.signinFormValues.password,
token: {
type: SigninTokenType.RECAPTCHA,
Expand Down
8 changes: 5 additions & 3 deletions packages/client/src/pages/account/Signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default class Signup extends Page<PageProps, SignupFormState> {
public componentDidMount(): void {
this.props.deleteReduxState();
}

public constructor(props: PageProps) {
super(props);

Expand Down Expand Up @@ -99,8 +99,10 @@ export default class Signup extends Page<PageProps, SignupFormState> {
) : null}
{this.state.success ? (
<TextBox>
Account request successful. Check your inbox and spam folder for a link to
finish creating your account
<div className="banner">
Account request successful. Check your inbox and spam folder for a link to
finish creating your account
</div>
</TextBox>
) : null}

Expand Down
2 changes: 1 addition & 1 deletion packages/common-lib/src/lib/passwordComplexity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*/
export const passwordMeetsRequirements = (password: string): boolean =>
password.length > 32 ||
(password.length >= 8 &&
(password.length >= 11 &&
// lowercase letter
!!password.match(/[a-z]/g) &&
// uppercase letter
Expand Down
5 changes: 4 additions & 1 deletion packages/server-common/src/member/pam/Password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,10 @@ export const checkIfPasswordValid = async (

const passwordExpireDate = userInfo.passwordHistory[0].created + PASSWORD_MAX_AGE;
if (passwordExpireDate < Date.now()) {
return PasswordResult.VALID_EXPIRED;
if (passwordExpireDate < 1706763600000 + PASSWORD_MAX_AGE) {
return PasswordResult.VALID_EXPIRED;
}
return PasswordResult.VALID;
}

return PasswordResult.VALID;
Expand Down
6 changes: 4 additions & 2 deletions packages/server/src/api/member/account/finishpasswordreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* along with EvMPlus.org. If not, see <http://www.gnu.org/licenses/>.
*/

import { api, get, Maybe } from 'common-lib';
import { always, api, get, Maybe } from 'common-lib';
import { Backends, getCombinedPAMBackend, PAM, withBackends } from 'server-common';
import { Endpoint } from '../../..';

Expand All @@ -27,7 +27,9 @@ export const func: Endpoint<
> = backend => req =>
backend
.validatePasswordResetToken(req.body.token)
.tap(username => backend.addPasswordForUser([username, req.body.newPassword]))
.flatMap(username =>
backend.addPasswordForUser([username, req.body.newPassword]).map(always(username)),
)
.tap(() => backend.removePasswordValidationToken(req.body.token))
.flatMap(backend.getUserInformationForUser)
.filter(Maybe.isSome, {
Expand Down