Skip to content

Commit

Permalink
Password rules enhancement (#492)
Browse files Browse the repository at this point in the history
* update new rules, add visual indicators and more

* update

* adding check backend side for format

* Update routes/users.js

* add eye on login and profile page

* fix a bug

---------

Co-authored-by: mboudet <[email protected]>
  • Loading branch information
BobLamarley and mboudet authored Dec 10, 2024
1 parent 7590477 commit a2fe445
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 39 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@

## 1.4.30 (2024-08-02)

* Password enhancement
* Add an eye for visible/hidden on the password field in login/profile page
* Disable "update password" button until both fields are entered on profile page
* Centered input fields
* Added dynamic rules requirements in order for user to know what to add to his password
* Password rules is 12 char, with 1 spec char, 1 digit and no spaces
* Updated generated password via generate-password to 12 when account activated
* Updated also db user password generated to 12
* Added a red cross/green tick in the input for passwork and password confirmation

* Added a `project.terms_and_conditions_hds` setting in the config:
If true:
"Ask Admin" button to submit project creation form became available after 'project name' and 'expiration date' are filled, 'project description' is at least 30 char and a checkbox appears and 'terms and conditions hds' should also be checked
Expand Down
2 changes: 1 addition & 1 deletion bin/my-accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ program
process.exit(1);
}

let new_password = usrv.new_password(8);
let new_password = usrv.new_password(16);
user.password = new_password;
let fid = new Date().getTime();
try {
Expand Down
2 changes: 1 addition & 1 deletion core/tps.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ async function create_tp_users_db (owner, quantity, duration, end_date, userGrou
expiration: end_date + 1000*3600*24*(duration+CONFIG.tp.extra_expiration),
};
user = await usrsrv.create_user(user);
user.password = usrsrv.new_password(10);
user.password = usrsrv.new_password(16);
await usrsrv.activate_user(user);

users.push(user);
Expand Down
4 changes: 2 additions & 2 deletions core/user.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ function get_user_home(user) {

async function activate_user(user, action_owner = 'auto') {
if (!user.password) {
user.password = new_password(10);
user.password = new_password(16);
//user.password = Math.random().toString(36).slice(-10);
}
if (!user.created_at) {
Expand Down Expand Up @@ -217,7 +217,7 @@ async function create_user(user, action_owner = 'auto') {
// todo should be factorysed with "normal" user creation
async function create_extra_user(user_name, group, internal_user){
//let password = Math.random().toString(36).slice(-10);
let password = new_password(10);
let password = new_password(16);
if(process.env.MY_ADMIN_PASSWORD){
password = process.env.MY_ADMIN_PASSWORD;
}
Expand Down
2 changes: 1 addition & 1 deletion core/user_db.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ async function create_db(new_db, user, id) {
}

//let password = Math.random().toString(36).slice(-10);
let password = usrsrv.new_password(10);
let password = usrsrv.new_password(16);
let createuser = `CREATE USER '${id}'@'%' IDENTIFIED BY '${password}';\n`;
try {
await querydb(createuser);
Expand Down
34 changes: 31 additions & 3 deletions manager2/src/app/auth/login/login.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@
z-index: 2;
}

.form-signin input[type="text"]
#sign_user_id
{
margin-bottom: -1px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.form-signin input[type="password"]

#sign_password
{
margin-bottom: 10px;
border-top-left-radius: 0;
Expand All @@ -46,4 +47,31 @@
font-size: 18px;
font-weight: 400;
display: block;
}
}

.password-field {
position: relative;
}

.password-field .password-input {
width: 100%;
padding-right: 2rem;
}

.password-field .toggle-password {
position: absolute;
top: 50%;
right: 1rem;
transform: translateY(-50%);
cursor: pointer;
color: #aaa;
}

.password-field .toggle-password:hover {
color: #333;
}

/* Prevent icon from disappearing when input is focused */
.password-field:focus-within .toggle-password {
z-index: 9;
}
49 changes: 41 additions & 8 deletions manager2/src/app/auth/login/login.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,53 @@ <h1 class="text-center login-title"><strong>Sign in to access your account</stro
<div *ngIf="! double_auth">
<form class="form-signin form-group">
<div class="form-group">
<input [ngModelOptions]="{standalone: true}" [(ngModel)]="userId" id="sign_user_id" type="text" class="form-control" placeholder="User id" autocomplete="username" required autofocus>
<input
[ngModelOptions]="{standalone: true}"
[(ngModel)]="userId"
id="sign_user_id"
type="text"
class="form-control"
placeholder="User id"
autocomplete="username"
required
autofocus
/>
</div>
<div class="form-group">
<input [ngModelOptions]="{standalone: true}" [(ngModel)]="password" id="sign_password" type="password" placeholder="Password" class="form-control" autocomplete="current-password" required>
<div class="form-group password-field">
<input
[ngModelOptions]="{standalone: true}"
[(ngModel)]="password"
[type]="passwordVisible ? 'text' : 'password'"
id="sign_password"
placeholder="Password"
class="form-control password-input"
autocomplete="current-password"
required
/>
<i
class="toggle-password pi"
[ngClass]="passwordVisible ? 'pi-eye-slash' : 'pi-eye'"
(click)="togglePasswordVisibility()"
aria-label="Toggle password visibility"
role="button"
tabindex="0"
></i>
</div>
<div class="form-group">
<button class="p-button p-button-lg p-button-primary btn-block" [ngStyle]="{'display': 'block'}" type="submit" (click)="login()">
Sign in</button>
<button
class="p-button p-button-lg p-button-primary btn-block"
[ngStyle]="{ display: 'block' }"
type="submit"
(click)="login()"
>
Sign in
</button>
</div>
<div class="alert alert-warning" *ngIf="msg">{{msg}}</div>
<div class="alert alert-warning" *ngIf="msg">{{ msg }}</div>
<div class="form-group">
<span class="lost">Lost your password?</span><button class="btn btn-sm" (click)="password_reset_request()">RESET</button>
<span class="lost">Lost your password?</span>
<button class="btn btn-sm" (click)="password_reset_request()">RESET</button>
</div>

</form>
</div>
<div *ngIf="double_auth">
Expand Down
5 changes: 5 additions & 0 deletions manager2/src/app/auth/login/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class LoginComponent implements OnInit {
msg: string
error_msg: string
msgstatus: number
passwordVisible: boolean = false

u2f: any
uid: string
Expand Down Expand Up @@ -170,4 +171,8 @@ export class LoginComponent implements OnInit {
});
}

togglePasswordVisibility() {
this.passwordVisible = !this.passwordVisible;
}

}
68 changes: 68 additions & 0 deletions manager2/src/app/user/user.component.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@

.valid-indicator {
color: green;
}

.invalid-indicator {
color: red;
}

.invalid-feedback {
color: red;
}

.password-rules {
margin-top: 10px;
}

.rule-satisfied {
color: green;
}

.rule-not-satisfied {
color: red;
}

.btn-disabled {
background-color: #d6d6d6;
border-color: #d6d6d6;
cursor: not-allowed;
pointer-events: none;
color: #888888;
}

.btn-disabled:hover {
background-color: #d6d6d6;
border-color: #d6d6d6;
}

.password-field {
position: relative;
}

.password-field .password-input {
width: 100%;
padding-right: 2rem;
}

.password-field .toggle-password {
position: absolute;
top: 50%;
right: 1rem;
transform: translateY(-50%);
cursor: pointer;
color: #aaa;
}

.password-field:has(.input-group-append .valid-indicator, .input-group-append .invalid-indicator) .toggle-password {
right: 3rem;
}

.password-field .toggle-password:hover {
color: #333;
}

/* Prevent icon from disappearing when input is focused */
.password-field:focus-within .toggle-password {
z-index: 9;
}
117 changes: 111 additions & 6 deletions manager2/src/app/user/user.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -326,24 +326,129 @@ <h4>Update password</h4>
</div>
<div class="card-body">
<div *ngIf="update_passwd" class="alert alert-success">{{update_passwd}}</div>
<div *ngIf="wrong_confirm_passwd" class="alert alert-danger">{{wrong_confirm_passwd}}</div>
<form>
<div class="form-group row">
<div class="col-sm-8">
<input [value]="user.uid" id="user_id" type="text" autocomplete="username" hidden readonly>
</div>
</div>
<div class="form-group row">
<div class="col-sm-8">
<div class="col-sm-8 offset-sm-2">
<label for="password" class="col-form-label">New password</label>
<input autocomplete="new-password" placeholder="10 characters min" id="password" type="password" [ngModelOptions]="{standalone: true}" [(ngModel)]="password1" class="form-control"/>
<div class="input-group password-field">
<input
autocomplete="new-password"
id="password"
placeholder="New password"
[type]="passwordVisible ? 'text' : 'password'"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="password1"
#password1Model="ngModel"
name="password1"
class="form-control"
required
minlength="12"
(input)="checkPasswordRules(password1)"
(blur)="validateInput(password1Model)"
/>
<div class="input-group-append">
<i
class="toggle-password pi"
[ngClass]="passwordVisible ? 'pi-eye-slash' : 'pi-eye'"
(click)="togglePasswordVisibility(1)"
aria-label="Toggle password visibility"
role="button"
tabindex="0"
></i>
<span
*ngIf="isFormValid() && password1Model.dirty && password1.length > 0"
class="input-group-text valid-indicator"
>
&#10003;
</span>
<span
*ngIf="!isFormValid() && password1Model.dirty && password1.length > 0"
class="input-group-text invalid-indicator"
>
&#10007;
</span>
</div>
</div>
<div class="password-rules mt-2">
<p>Password rules requirements:</p>
<div [ngClass]="{ 'rule-satisfied': passwordLengthValid, 'rule-not-satisfied': !passwordLengthValid }">
- 12 characters minimum
</div>
<div [ngClass]="{ 'rule-satisfied': hasDigit, 'rule-not-satisfied': !hasDigit }">
- At least one digit
</div>
<div [ngClass]="{ 'rule-satisfied': hasLowercase, 'rule-not-satisfied': !hasLowercase }">
- At least one lowercase letter
</div>
<div [ngClass]="{ 'rule-satisfied': hasUppercase, 'rule-not-satisfied': !hasUppercase }">
- At least one uppercase letter
</div>
<div [ngClass]="{ 'rule-satisfied': hasSpecialChar, 'rule-not-satisfied': !hasSpecialChar }">
- At least one special character
</div>
<div [ngClass]="{ 'rule-satisfied': !hasSpaces, 'rule-not-satisfied': hasSpaces }">
- No spaces allowed
</div>
</div>
</div>
<div class="col-sm-8">

<div class="col-sm-8 offset-sm-2 mt-3">
<label for="confirm_password" class="col-form-label">Confirm password</label>
<input autocomplete="new-password" placeholder="10 characters min" id="confirm_password" type="password" [ngModelOptions]="{standalone: true}" [(ngModel)]="password2" class="form-control"/>
<div class="input-group password-field">
<input
autocomplete="new-password"
id="confirm_password"
placeholder="Confirm password"
[type]="passwordConfirmVisible ? 'text' : 'password'"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="password2"
#password2Model="ngModel"
name="password2"
class="form-control"
required
(blur)="validateInput(password2Model)"
(input)="checkPasswordMatch(password1Model, password2Model)"
/>
<div class="input-group-append">
<i
class="toggle-password pi"
[ngClass]="passwordConfirmVisible ? 'pi-eye-slash' : 'pi-eye'"
(click)="togglePasswordVisibility(2)"
aria-label="Toggle password visibility"
role="button"
tabindex="0"
></i>
<span
*ngIf="password1Model.valid && password2Model.valid && password1 === password2 && password2.length > 0"
class="input-group-text valid-indicator"
>
&#10003;
</span>
<span
*ngIf="(password2Model.dirty || password2Model.touched) && (password1Model.invalid || password1 !== password2) && password2.length > 0"
class="input-group-text invalid-indicator"
>
&#10007;
</span>
</div>
</div>
</div>
</div>
<button type="button" class="p-button p-button-sm p-button-primary" (click)="update_password()">Change password</button>

<button
type="button"
class="p-button p-button-sm p-button-primary mt-3"
(click)="update_password(password1Model, password2Model)"
[disabled]="!isFormValid() || password1 !== password2"
[ngClass]="{'btn-disabled': !isFormValid() || password1 !== password2}"
>
Change password
</button>
</form>
</div>
</div>
Expand Down
Loading

0 comments on commit a2fe445

Please sign in to comment.