mirror of
https://github.com/ceph/ceph
synced 2025-02-23 19:17:37 +00:00
Merge pull request #39917 from rhcs-dashboard/fix-43058-master
mgr/dashboard: warn password expiration in User Management
This commit is contained in:
commit
f2b7fb057a
24
src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.html
Normal file → Executable file
24
src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.html
Normal file → Executable file
@ -20,3 +20,27 @@
|
||||
{{ role }}{{ !isLast ? ", " : "" }}
|
||||
</span>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #warningTpl
|
||||
let-column="column"
|
||||
let-value="value"
|
||||
let-row="row">
|
||||
<div [class.border-danger]="row.remainingDays < this.expirationDangerAlert"
|
||||
[class.border-warning]="row.remainingDays < this.expirationWarningAlert && row.remainingDays >= this.expirationDangerAlert"
|
||||
class="border-margin">
|
||||
<div class="warning-content"> {{ value }} </div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #durationTpl
|
||||
let-column="column"
|
||||
let-value="value"
|
||||
let-row="row">
|
||||
<i *ngIf="row.remainingDays < this.expirationWarningAlert"
|
||||
i18n-title
|
||||
title="User's password is about to expire"
|
||||
[class.icon-danger-color]="row.remainingDays < this.expirationDangerAlert"
|
||||
[class.icon-warning-color]="row.remainingDays < this.expirationWarningAlert && row.remainingDays >= this.expirationDangerAlert"
|
||||
class="{{ icons.warning }}"></i>
|
||||
<span title="{{ value | cdDate }}">{{ row.remainingTimeWithoutSeconds / 1000 | duration }}</span>
|
||||
</ng-template>
|
||||
|
16
src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.scss
Normal file → Executable file
16
src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.scss
Normal file → Executable file
@ -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;
|
||||
}
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
72
src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.ts
Normal file → Executable file
72
src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.ts
Normal file → Executable file
@ -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<any>;
|
||||
@ViewChild('warningTpl', { static: true })
|
||||
warningTpl: TemplateRef<any>;
|
||||
@ViewChild('durationTpl', { static: true })
|
||||
durationTpl: TemplateRef<any>;
|
||||
|
||||
permission: Permission;
|
||||
tableActions: CdTableAction[];
|
||||
columns: CdTableColumn[];
|
||||
users: Array<any>;
|
||||
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<any>) => {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
|
@ -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'],
|
||||
|
@ -111,6 +111,7 @@ import { UpperFirstPipe } from './upper-first.pipe';
|
||||
MillisecondsPipe,
|
||||
NotAvailablePipe,
|
||||
UpperFirstPipe,
|
||||
DurationPipe,
|
||||
MapPipe,
|
||||
TruncatePipe
|
||||
]
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user