From c630095353596a8327c37e3f3d3e7c190dfdf1db Mon Sep 17 00:00:00 2001 From: Tatjana Dehler Date: Mon, 16 Sep 2019 15:30:00 +0200 Subject: [PATCH] mgr/dashboard: add password expiration warning banner It adds a new component to notify the user about the expiration of the password. The banner is shown on top of the page and can be dismissed by the user. Fixes: https://tracker.ceph.com/issues/40329 Signed-off-by: Tatjana Dehler --- .../frontend/src/app/app.component.html | 1 + .../shared/components/components.module.ts | 11 +- ...pwd-expiration-notification.component.html | 13 ++ ...pwd-expiration-notification.component.scss | 3 + ...-expiration-notification.component.spec.ts | 120 ++++++++++++++++++ .../pwd-expiration-notification.component.ts | 44 +++++++ 6 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.html create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.scss create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.spec.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.ts diff --git a/src/pybind/mgr/dashboard/frontend/src/app/app.component.html b/src/pybind/mgr/dashboard/frontend/src/app/app.component.html index 7e08eef685f11..6cfd22b416436 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/app.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/app.component.html @@ -15,6 +15,7 @@
+
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts index 2edff8c53ab19..8d1d1ae644ea1 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts @@ -1,6 +1,7 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterModule } from '@angular/router'; import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation'; import { ChartsModule } from 'ng2-charts'; @@ -25,6 +26,7 @@ import { LanguageSelectorComponent } from './language-selector/language-selector import { LoadingPanelComponent } from './loading-panel/loading-panel.component'; import { ModalComponent } from './modal/modal.component'; import { NotificationsSidebarComponent } from './notifications-sidebar/notifications-sidebar.component'; +import { PwdExpirationNotificationComponent } from './pwd-expiration-notification/pwd-expiration-notification.component'; import { RefreshSelectorComponent } from './refresh-selector/refresh-selector.component'; import { SelectBadgesComponent } from './select-badges/select-badges.component'; import { SelectComponent } from './select/select.component'; @@ -48,7 +50,8 @@ import { ViewCacheComponent } from './view-cache/view-cache.component'; ModalModule.forRoot(), DirectivesModule, BsDropdownModule, - NgBootstrapFormValidationModule + NgBootstrapFormValidationModule, + RouterModule ], declarations: [ ViewCacheComponent, @@ -69,7 +72,8 @@ import { ViewCacheComponent } from './view-cache/view-cache.component'; RefreshSelectorComponent, ConfigOptionComponent, AlertPanelComponent, - FormModalComponent + FormModalComponent, + PwdExpirationNotificationComponent ], providers: [], exports: [ @@ -88,7 +92,8 @@ import { ViewCacheComponent } from './view-cache/view-cache.component'; SelectComponent, RefreshSelectorComponent, ConfigOptionComponent, - AlertPanelComponent + AlertPanelComponent, + PwdExpirationNotificationComponent ], entryComponents: [ ModalComponent, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.html new file mode 100644 index 0000000000000..d524a900573cb --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.html @@ -0,0 +1,13 @@ + +
Your password will expire in less than 1 day. Click + here to change it now.
+
Your password will expire in {{ expirationDays }} day(s). Click + here to change it now.
+
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.scss new file mode 100644 index 0000000000000..85467dd1700f0 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.scss @@ -0,0 +1,3 @@ +::ng-deep .no-margin-bottom .alert { + margin-bottom: 0; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.spec.ts new file mode 100644 index 0000000000000..c756074f1ea3b --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.spec.ts @@ -0,0 +1,120 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { Component } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { Routes } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { of as observableOf } from 'rxjs'; + +import { AlertModule } from 'ngx-bootstrap/alert'; + +import { configureTestBed } from '../../../../testing/unit-test-helper'; + +import { SettingsService } from '../../api/settings.service'; +import { AuthStorageService } from '../../services/auth-storage.service'; +import { PwdExpirationNotificationComponent } from './pwd-expiration-notification.component'; + +describe('PwdExpirationNotificationComponent', () => { + let component: PwdExpirationNotificationComponent; + let fixture: ComponentFixture; + let settingsService: SettingsService; + let authStorageService: AuthStorageService; + + @Component({ selector: 'cd-fake', template: '' }) + class FakeComponent {} + + const routes: Routes = [{ path: 'login', component: FakeComponent }]; + + configureTestBed({ + declarations: [PwdExpirationNotificationComponent, FakeComponent], + imports: [ + AlertModule.forRoot(), + HttpClientTestingModule, + RouterTestingModule.withRoutes(routes) + ], + providers: [SettingsService, AuthStorageService] + }); + + describe('password expiration date has been set', () => { + beforeEach(() => { + authStorageService = TestBed.get(AuthStorageService); + settingsService = TestBed.get(SettingsService); + spyOn(authStorageService, 'getPwdExpirationDate').and.returnValue(1645488000); + spyOn(settingsService, 'pwdExpirationSettings').and.returnValue( + observableOf({ + user_pwd_expiration_warning_1: 10, + user_pwd_expiration_warning_2: 5, + user_pwd_expiration_span: 90 + }) + ); + fixture = TestBed.createComponent(PwdExpirationNotificationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + component.ngOnInit(); + expect(component).toBeTruthy(); + }); + + it('should set warning levels', () => { + component.ngOnInit(); + expect(component.pwdExpirationSettings.pwdExpirationWarning1).toBe(10); + expect(component.pwdExpirationSettings.pwdExpirationWarning2).toBe(5); + }); + + it('should calculate password expiration in days', () => { + const dateValue = Date; + spyOn(global, 'Date').and.callFake((date) => { + if (date) { + return new dateValue(date); + } else { + return new Date('2022-02-18T00:00:00.000Z'); + } + }); + component.ngOnInit(); + expect(component['expirationDays']).toBe(4); + }); + + it('should set alert type warning correctly', () => { + const dateValue = Date; + spyOn(global, 'Date').and.callFake((date) => { + if (date) { + return new dateValue(date); + } else { + return new Date('2022-02-14T00:00:00.000Z'); + } + }); + component.ngOnInit(); + expect(component['alertType']).toBe('warning'); + }); + + it('should set alert type danger correctly', () => { + const dateValue = Date; + spyOn(global, 'Date').and.callFake((date) => { + if (date) { + return new dateValue(date); + } else { + return new Date('2022-02-18T00:00:00.000Z'); + } + }); + component.ngOnInit(); + expect(component['alertType']).toBe('danger'); + }); + }); + + describe('password expiration date has not been set', () => { + beforeEach(() => { + authStorageService = TestBed.get(AuthStorageService); + spyOn(authStorageService, 'getPwdExpirationDate').and.returnValue(null); + fixture = TestBed.createComponent(PwdExpirationNotificationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should calculate no expirationDays', () => { + component.ngOnInit(); + expect(component['expirationDays']).toBeUndefined(); + }); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.ts new file mode 100644 index 0000000000000..b8b1ce79d5116 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/pwd-expiration-notification/pwd-expiration-notification.component.ts @@ -0,0 +1,44 @@ +import { Component, OnInit } from '@angular/core'; + +import { SettingsService } from '../../api/settings.service'; +import { CdPwdExpirationSettings } from '../../models/cd-pwd-expiration-settings'; +import { AuthStorageService } from '../../services/auth-storage.service'; + +@Component({ + selector: 'cd-pwd-expiration-notification', + templateUrl: './pwd-expiration-notification.component.html', + styleUrls: ['./pwd-expiration-notification.component.scss'] +}) +export class PwdExpirationNotificationComponent implements OnInit { + alertType: string; + expirationDays: number; + pwdExpirationSettings: CdPwdExpirationSettings; + + constructor( + private settingsService: SettingsService, + private authStorageService: AuthStorageService + ) {} + + ngOnInit() { + this.settingsService.pwdExpirationSettings().subscribe((pwdExpirationSettings) => { + this.pwdExpirationSettings = new CdPwdExpirationSettings(pwdExpirationSettings); + const pwdExpirationDate = this.authStorageService.getPwdExpirationDate(); + if (pwdExpirationDate) { + this.expirationDays = this.getExpirationDays(pwdExpirationDate); + if (this.expirationDays <= this.pwdExpirationSettings.pwdExpirationWarning2) { + this.alertType = 'danger'; + } else { + this.alertType = 'warning'; + } + } + }); + } + + private getExpirationDays(pwdExpirationDate: number): number { + if (pwdExpirationDate) { + const current = new Date(); + const expiration = new Date(pwdExpirationDate * 1000); + return Math.floor((expiration.valueOf() - current.valueOf()) / (1000 * 3600 * 24)); + } + } +} -- 2.39.5