From: Pere Diaz Bou Date: Thu, 25 Mar 2021 17:40:40 +0000 (+0100) Subject: mgr/dashboard: warn user's password expiration in user management X-Git-Tag: v17.1.0~2447^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=refs%2Fpull%2F39917%2Fhead;p=ceph.git mgr/dashboard: warn user's password expiration in user management Warn about user's incoming password expiration on the User Management with an icon. Fixes: https://tracker.ceph.com/issues/43058 Signed-off-by: Pere Diaz Bou --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.html old mode 100644 new mode 100755 index 89ed21be8331..5676f3fbc6f1 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.html @@ -20,3 +20,27 @@ {{ role }}{{ !isLast ? ", " : "" }} + + +
+
{{ value }}
+
+
+ + + + {{ row.remainingTimeWithoutSeconds / 1000 | duration }} + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.scss old mode 100644 new mode 100755 index e69de29bb2d1..4aada4145c29 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.scss +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.scss @@ -0,0 +1,16 @@ +@use './src/styles/vendor/variables' as vv; + +.border-margin { + border-left: 3px solid transparent; + height: calc(100% + 10px); + margin-bottom: -5px; + margin-left: -5px; + margin-top: -5px; +} + +.warning-content { + height: 100%; + padding-bottom: 5px; + padding-left: 5px; + padding-top: 5px; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.spec.ts index a1b9cfd14e1b..01e68e6d9296 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.spec.ts @@ -79,4 +79,19 @@ describe('UserListComponent', () => { } }); }); + it('should calculate remaining days', () => { + const day = 60 * 60 * 24 * 1000; + let today = Date.now(); + expect(component.getRemainingDays(today + day * 2 + 1000)).toBe(2); + today = Date.now(); + expect(component.getRemainingDays(today + day * 2 - 1000)).toBe(1); + today = Date.now(); + expect(component.getRemainingDays(today + day + 1000)).toBe(1); + today = Date.now(); + expect(component.getRemainingDays(today + 1)).toBe(0); + today = Date.now(); + expect(component.getRemainingDays(today - (day + 1))).toBe(0); + expect(component.getRemainingDays(null)).toBe(undefined); + expect(component.getRemainingDays(undefined)).toBe(undefined); + }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.ts old mode 100644 new mode 100755 index 09c0d82fcc40..3a16fdce610d --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { SettingsService } from '~/app/shared/api/settings.service'; import { UserService } from '~/app/shared/api/user.service'; import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component'; import { ActionLabelsI18n } from '~/app/shared/constants/app.constants'; @@ -12,7 +13,6 @@ import { CdTableAction } from '~/app/shared/models/cd-table-action'; import { CdTableColumn } from '~/app/shared/models/cd-table-column'; import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; import { Permission } from '~/app/shared/models/permissions'; -import { CdDatePipe } from '~/app/shared/pipes/cd-date.pipe'; import { EmptyPipe } from '~/app/shared/pipes/empty.pipe'; import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; import { ModalService } from '~/app/shared/services/modal.service'; @@ -30,12 +30,19 @@ const BASE_URL = 'user-management/users'; export class UserListComponent implements OnInit { @ViewChild('userRolesTpl', { static: true }) userRolesTpl: TemplateRef; + @ViewChild('warningTpl', { static: true }) + warningTpl: TemplateRef; + @ViewChild('durationTpl', { static: true }) + durationTpl: TemplateRef; permission: Permission; tableActions: CdTableAction[]; columns: CdTableColumn[]; users: Array; + expirationWarningAlert: number; + expirationDangerAlert: number; selection = new CdTableSelection(); + icons = Icons; modalRef: NgbModalRef; @@ -46,7 +53,7 @@ export class UserListComponent implements OnInit { private notificationService: NotificationService, private authStorageService: AuthStorageService, private urlBuilder: URLBuilderService, - private cdDatePipe: CdDatePipe, + private settingsService: SettingsService, public actionLabels: ActionLabelsI18n ) { this.permission = this.authStorageService.getPermissions().user; @@ -77,7 +84,8 @@ export class UserListComponent implements OnInit { { name: $localize`Username`, prop: 'username', - flexGrow: 1 + flexGrow: 1, + cellTemplate: this.warningTpl }, { name: $localize`Name`, @@ -104,19 +112,29 @@ export class UserListComponent implements OnInit { cellTransformation: CellTemplate.checkIcon }, { - name: $localize`Password expiration date`, + name: $localize`Password expires`, prop: 'pwdExpirationDate', flexGrow: 1, - pipe: this.cdDatePipe + cellTemplate: this.durationTpl } ]; + const settings: string[] = ['USER_PWD_EXPIRATION_WARNING_1', 'USER_PWD_EXPIRATION_WARNING_2']; + this.settingsService.getValues(settings).subscribe((data) => { + this.expirationWarningAlert = data['USER_PWD_EXPIRATION_WARNING_1']; + this.expirationDangerAlert = data['USER_PWD_EXPIRATION_WARNING_2']; + }); } getUsers() { this.userService.list().subscribe((users: Array) => { users.forEach((user) => { + user['remainingTimeWithoutSeconds'] = 0; if (user['pwdExpirationDate'] && user['pwdExpirationDate'] > 0) { user['pwdExpirationDate'] = user['pwdExpirationDate'] * 1000; + user['remainingTimeWithoutSeconds'] = this.getRemainingTimeWithoutSeconds( + user.pwdExpirationDate + ); + user['remainingDays'] = this.getRemainingDays(user.pwdExpirationDate); } }); this.users = users; @@ -161,4 +179,48 @@ export class UserListComponent implements OnInit { submitAction: () => this.deleteUser(username) }); } + + getWarningIconClass(expirationDays: number): any { + if (expirationDays === null || this.expirationWarningAlert > 10) { + return ''; + } + const remainingDays = this.getRemainingDays(expirationDays); + if (remainingDays <= this.expirationDangerAlert) { + return 'icon-danger-color'; + } else { + return 'icon-warning-color'; + } + } + + getWarningClass(expirationDays: number): any { + if (expirationDays === null || this.expirationWarningAlert > 10) { + return ''; + } + const remainingDays = this.getRemainingDays(expirationDays); + if (remainingDays <= this.expirationDangerAlert) { + return 'border-danger'; + } else { + return 'border-warning'; + } + } + + getRemainingDays(time: number): number { + if (time === undefined || time == null) { + return undefined; + } + if (time < 0) { + return 0; + } + const toDays = 1000 * 60 * 60 * 24; + return Math.max(0, Math.floor(this.getRemainingTime(time) / toDays)); + } + + getRemainingTimeWithoutSeconds(time: number): number { + const withSeconds = this.getRemainingTime(time); + return Math.floor(withSeconds / (1000 * 60)) * 60 * 1000; + } + + getRemainingTime(time: number): number { + return time - Date.now(); + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/duration.pipe.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/duration.pipe.spec.ts index 1b0e22578aaf..d939e2e58594 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/duration.pipe.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/duration.pipe.spec.ts @@ -8,7 +8,7 @@ describe('DurationPipe', () => { }); it('transforms seconds into a human readable duration', () => { - expect(pipe.transform(0)).toBe('1 second'); + expect(pipe.transform(0)).toBe(''); expect(pipe.transform(6)).toBe('6 seconds'); expect(pipe.transform(60)).toBe('1 minute'); expect(pipe.transform(600)).toBe('10 minutes'); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/duration.pipe.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/duration.pipe.ts index 4675fc0f6c6d..687153b21b6e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/duration.pipe.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/duration.pipe.ts @@ -13,6 +13,9 @@ export class DurationPipe implements PipeTransform { * @return {string} The phrase describing the the amount of time */ transform(seconds: number): string { + if (seconds === null || seconds <= 0) { + return ''; + } const levels = [ [`${Math.floor(seconds / 31536000)}`, 'years'], [`${Math.floor((seconds % 31536000) / 86400)}`, 'days'], diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/pipes.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/pipes.module.ts index 3deb535929d3..5b94f2d7d9de 100755 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/pipes.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/pipes.module.ts @@ -111,6 +111,7 @@ import { UpperFirstPipe } from './upper-first.pipe'; MillisecondsPipe, NotAvailablePipe, UpperFirstPipe, + DurationPipe, MapPipe, TruncatePipe ] diff --git a/src/pybind/mgr/dashboard/frontend/src/styles/ceph-custom/_basics.scss b/src/pybind/mgr/dashboard/frontend/src/styles/ceph-custom/_basics.scss index 04f365e6daac..29cd040e8728 100644 --- a/src/pybind/mgr/dashboard/frontend/src/styles/ceph-custom/_basics.scss +++ b/src/pybind/mgr/dashboard/frontend/src/styles/ceph-custom/_basics.scss @@ -74,6 +74,18 @@ option { white-space: pre; } +.icon-danger-color { + color: vv.$danger; +} + +.icon-warning-color { + color: vv.$warning; +} + +.border-warning { + border-left: 4px solid vv.$warning; +} + .border-danger { border-left: 4px solid vv.$danger; }