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

FOGL-8757: Blocked user GUI support incase of invalid password attempt #428

Merged
merged 15 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from 13 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
18 changes: 10 additions & 8 deletions src/app/components/common/alert-dialog/alert-dialog.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ export class AlertDialogComponent implements OnInit, OnChanges {
@Output() deleteNotification = new EventEmitter<Object>();
@Output() deleteNotificationService = new EventEmitter<Object>();
@Output() deleteTask = new EventEmitter<Object>();
@Output() deleteUserService = new EventEmitter<Number>();

@Output() deletePipeline = new EventEmitter<Number>();
@Output() deleteCertificate = new EventEmitter<Object>();
@Output() logoutUserService = new EventEmitter<Number>();
@Output() createBackup = new EventEmitter<Number>();
@Output() restoreBackup = new EventEmitter<Number>();
@Output() deleteBackup = new EventEmitter<Number>();
@Output() logoutAllUserSessionsService = new EventEmitter<Number>();
@Output() userActionService = new EventEmitter<any>();
modalId = '';

constructor() { }
Expand Down Expand Up @@ -55,6 +55,12 @@ export class AlertDialogComponent implements OnInit, OnChanges {
this.childData.actionButtonValue = 'Deactivate';
this.childData.headerTextValue = 'Deactivate User';
}

if (this.childData.key === 'unblockUser') {
this.childData.actionButtonValue = 'Unblock';
this.childData.headerTextValue = 'Unblock User';
}

if (this.childData.key === 'logout') {
this.childData.actionButtonValue = 'Log Out';
}
Expand Down Expand Up @@ -107,8 +113,8 @@ export class AlertDialogComponent implements OnInit, OnChanges {
this.delete.emit(this.childData.id);
this.toggleModal(false);
}
if (this.childData.key === 'deactivateUser') {
this.deleteUserService.emit(this.childData.id);
if (['deactivateUser', 'enableUser', 'unblockUser', 'clearSessions'].includes(this.childData.key)) {
this.userActionService.emit({ key: this.childData.key, id: this.childData.id });
this.toggleModal(false);
}
if (this.childData.key === 'deleteCertificate') {
Expand All @@ -119,10 +125,6 @@ export class AlertDialogComponent implements OnInit, OnChanges {
this.deleteCertificate.emit({ name: this.childData.name, type: 'key' });
this.toggleModal(false);
}
if (this.childData.key === 'clearSessions') {
this.logoutAllUserSessionsService.emit(this.childData.id);
this.toggleModal(false);
}
if (this.childData.key === 'logout') {
this.logoutUserService.emit();
this.toggleModal(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,44 @@
.fa::before {
display: inline-block;
display: inline-block;
}

.tabs {
position: relative;
}

.columns {
margin-top: 10px;
}

.column {
padding-top: 0px;
position: relative;
}

.context-menu {
border: none;
}

.user-col {
vertical-align: middle;
border: none;
}

@media only screen and (min-width: 1024px) {
.user-table, .card-data {
overflow: visible;
}

.user-table,
.card-data {
overflow: visible;
}
}

.user-table {
width: 92%;
width: 92%;
}

.tags-groups {
white-space: nowrap;
white-space: nowrap;
}

.desc-col {
max-width: 18rem;
max-width: 18rem;
}

.is-hoverable.user-table tr:hover .context-menu {
background-color: #fafafa;
background-color: #fafafa;
}

.action-items {
position: absolute;
right: 0;
}

#add-user {
border-bottom-width: 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,20 @@ <h6 class="is-6 has-text-weight-semibold">
<a (click)="showDiv(2)">Roles</a>
</h6>
</li>
<li style="position: absolute;right: 0;">
<a class="is-light" (click)="openCreateUserModal()" style="border-bottom-width: 0">
<p *ngIf="viewPort !== 'mobile'" class="add-btn">&nbsp; Add User &nbsp;</p>
<i class="bi bi-person-plus bi-sm" aria-hidden="true"></i>
</a>
<li class="action-items">
<div class="columns is-vcentered m-0 p-0">
<div *ngIf="seletedTab == 1" class="column is-narrow px-0">
<a type="button" (click)="getUsers()" class="button is-small" title="Reload" id="refresh-check">
<i class="fa fa-sm fa-sync" aria-hidden="true"></i>
</a>
</div>
<div class="column is-narrow px-0">
<a id="add-user" class="is-light pl-0" (click)="openCreateUserModal()">
<p *ngIf="viewPort !== 'mobile'" class="add-btn">&nbsp; Add User &nbsp;</p>
<i class="bi bi-person-plus bi-sm" aria-hidden="true"></i>
</a>
</div>
</div>
</li>
</ul>
</div>
Expand All @@ -28,30 +37,36 @@ <h6 class="is-6 has-text-weight-semibold">
<table class="table is-hoverable user-table">
<thead id="head">
<tr>
<!-- <th>ID</th> -->
<th>Name</th>
<th></th>
<th class="px-0">Name</th>
<th>Username</th>
<th>Role</th>
<th>Authentication Method</th>
<th>Description</th>
<th>Description</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of userRecord; let i = index;">
<!-- <td class="user-col">{{user.userId}}</td> -->
<td class="user-col">
<td class="pr-0">
<span
[title]="'User blocked temporarily ' + user.blockUntil + ', due to multiple login attempts with invalid credentials.'">
<i *ngIf="user.status == 'blocked'" class="bi bi-person-lock has-text-danger"
aria-hidden="true"></i>
</span>
</td>
<td class="px-0">
{{user.realName}}
</td>
<td class="user-col">
<td>
{{user.userName}}
</td>
<td class="user-col">
<td>
{{user.roleName}}
</td>
<td class="user-col">{{getAccessMethod(user.accessMethod)}}</td>
<td class="user-col desc-col desc-text">{{user.description}}</td>
<td>{{getAccessMethod(user.accessMethod)}}</td>
<td class="desc-col desc-text">{{user.description}}</td>
<td class="tags-groups">
<span *ngIf='user.userId == 1' class="tag is-rounded">super admin</span>&nbsp;
<span *ngIf='user.userId == uid' class="tag is-rounded">active</span>
Expand Down Expand Up @@ -80,8 +95,13 @@ <h6 class="is-6 has-text-weight-semibold">
<i class="bi bi-key bi-xs" aria-hidden="true"></i>
Reset Password
</a>
<a *ngIf="user.status == 'blocked'" class="dropdown-item"
(click)="openModal(user.userId, user.userName, 'unblockUser', 'Are you sure, You want to unblock the user')">
<i class="bi bi-unlock bi-xs" aria-hidden="true"></i>
Unblock
</a>
<a class="dropdown-item"
(click)="openModal(user.userId, user.userName, 'deactivateUser', 'Are You sure, you want to deactivate the user')">
(click)="openModal(user.userId, user.userName, 'deactivateUser', 'Are you sure, You want to deactivate the user')">
<i class="bi bi-trash bi-xs" aria-hidden="true"></i>
Deactivate
</a>
Expand All @@ -93,7 +113,7 @@ <h6 class="is-6 has-text-weight-semibold">
<a class="dropdown-item"
(click)="openModal(user.userId, user.userName, 'clearSessions', 'Are you sure, You want to clear all active sessions for')">
<i class="bi bi-box-arrow-right bi-xs" aria-hidden="true"></i>
Logout Active Sessions
Log out active sessions
</a>
</div>
</div>
Expand Down Expand Up @@ -124,8 +144,7 @@ <h6 class="is-6 has-text-weight-semibold">
</div>
</div>
</div>
<app-alert-dialog (enabledService)='enableUser($event)' (deleteUserService)='deleteUser($event)'
(logoutAllUserSessionsService)='clearAllSessions($event)' [childData]='childData'></app-alert-dialog>
<app-alert-dialog (userActionService)="action($event)" [childData]='childData'></app-alert-dialog>
<app-create-user [userRoles]="roles" (notify)='onNotify()'></app-create-user>
<app-update-user [userRoles]="roles" (notify)='onNotify()'></app-update-user>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core';

import { AlertService, AuthService, UserService, ProgressBarService, SharedService, RolesService } from '../../../services';
import { AlertDialogComponent } from '../../common/alert-dialog/alert-dialog.component';
import { CreateUserComponent } from './create-user/create-user.component';
import { UpdateUserComponent } from './update-user/update-user.component';
import { Subscription } from 'rxjs';
import { DateFormatterPipe } from '../../../pipes';
import { User } from '../../../models';
import moment from 'moment';

@Component({
selector: 'app-user-management',
Expand All @@ -31,6 +33,7 @@ export class UserManagementComponent implements OnInit, OnDestroy {
private userService: UserService,
public ngProgress: ProgressBarService,
private sharedService: SharedService,
private dateFormatter: DateFormatterPipe,
private roleService: RolesService
) { }

Expand Down Expand Up @@ -76,6 +79,13 @@ export class UserManagementComponent implements OnInit, OnDestroy {
});
});
this.userRecord = users.sort();
this.userRecord = this.userRecord.map((user: User) => {
if (user.blockUntil) {
user.blockUntil = this.calculateBlockUserTime(user.blockUntil);
}
return user;
})

this.ngProgress.done();
},
error => {
Expand All @@ -90,17 +100,27 @@ export class UserManagementComponent implements OnInit, OnDestroy {
}

getAccessMethod(accessMethod) {
let method;
switch (accessMethod) {
case 'cert':
return 'Certificate';
method = 'Certificate';
break;
case 'pwd':
return 'Password';
method = 'Password';
break;
default:
return 'Any';
method = 'Any';
break;
}
return method;
}

calculateBlockUserTime(time: string): string {
const blockUntilTime = this.dateFormatter.transform(time, 'YYYY-MM-DD HH:mm:ss');
const blockUntilTimestamp = moment(blockUntilTime);
let timeDifference = blockUntilTimestamp.fromNow();
timeDifference = timeDifference.toString().replace('in', 'for')
return timeDifference;
}

/**
Expand Down Expand Up @@ -142,6 +162,18 @@ export class UserManagementComponent implements OnInit, OnDestroy {
this.updateUserModal.toggleModal(true);
}

action(userData: any) {
if (userData.key == 'deactivateUser') {
this.deleteUser(userData.id);
} else if (userData.key == 'enableUser') {
this.enableUser(userData.id);
} else if (userData.key == 'clearSessions') {
this.clearAllSessions(userData.key);
} else if (userData.key == 'unblockUser') {
this.unblockUser(userData.id);
}
}

deleteUser(userId) {
console.log('Deleting User:', userId);
/** request started */
Expand Down Expand Up @@ -192,6 +224,28 @@ export class UserManagementComponent implements OnInit, OnDestroy {
// });
}

unblockUser(userId: string) {
/** request started */
this.ngProgress.start();
this.userService.unblockUser(userId).
subscribe(
(data) => {
/** request completed */
this.ngProgress.done();
this.alertService.success(data['message']);
this.getUsers();
},
error => {
/** request completed */
this.ngProgress.done();
if (error.status === 0) {
console.log('service down ', error);
} else {
this.alertService.error(error.statusText);
}
});
}

public toggleDropdown(contextMenu) {
const id = 'dropdown-' + contextMenu;
const activeDropDowns = Array.prototype.slice.call(document.querySelectorAll('.dropdown.is-active'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
<div id="edit_profile">
<div class="columns">
<div class="column is-6">
<label class="label is-small">Name</label>
<p class="control is-fullwidth">
<input type="text" class="input is-small" placeholder="Real Name" name='real_name'
[(ngModel)]="userRecord.real_name" />
</p>
<form #profileForm="ngForm">
<label class="label is-small">Name</label>
<p class="control is-fullwidth">
<input type="text" class="input is-small" placeholder="Real Name" name='real_name'
[(ngModel)]="userRecord.real_name" required />
<small
*ngIf="profileForm?.controls['real_name']?.hasError('required') && profileForm?.controls['real_name']?.touched"
class="help is-danger">Name is required</small>
</p>
</form>
</div>

</div>
<div class="columns">
<div class="column is-6">
Expand Down Expand Up @@ -63,7 +69,8 @@
<div class="columns">
<div class="column is-3">
<div class="control">
<span (click)="update()" class="button is-info is-small">Update</span>
<button [disabled]="profileForm.invalid || profileForm.pristine" (click)="update()"
class="button is-info is-small">Update</button>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class UserProfileComponent implements OnInit {
public childData = {};
isShow = false;
@ViewChild(AlertDialogComponent, { static: true }) child: AlertDialogComponent;

@ViewChild('profileForm', { static: true }) profileForm: NgForm;

constructor(private authService: AuthService,
private alertService: AlertService,
Expand Down Expand Up @@ -185,6 +185,7 @@ export class UserProfileComponent implements OnInit {
() => {
this.ngProgress.done();
this.alertService.success('User updated successfully');
this.profileForm.form.markAsPristine();
},
error => {
this.ngProgress.done();
Expand Down
2 changes: 2 additions & 0 deletions src/app/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ export class User {
description: string;
confirmPassword: string;
role_id: number;
status?: string;
blockUntil?: string;
}
Loading