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

NAS-129885 / 24.10 / Add customer designed login banner #10311

Merged
merged 9 commits into from
Jul 24, 2024
1 change: 1 addition & 0 deletions src/app/core/testing/utils/mock-auth.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export function mockAuth(
}),
createSpyObject(Store),
createSpyObject(WebSocketService),
createSpyObject(Window),
);

mockService.setUser(user as LoggedInUser);
Expand Down
5 changes: 5 additions & 0 deletions src/app/enums/system-state.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum SystemState {
Ready = 'READY',
Booting = 'BOOTING',
ShuttingDown = 'SHUTTING_DOWN',
}
1 change: 1 addition & 0 deletions src/app/interfaces/advanced-config.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface AdvancedConfig {
isolated_gpu_pci_ids: string[];
kdump_enabled: boolean;
motd: string;
login_banner: string;
overprovision: number;
powerdaemon: boolean;
sed_user: SedUser;
Expand Down
6 changes: 6 additions & 0 deletions src/app/interfaces/api/api-call-directory.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ProductType } from 'app/enums/product-type.enum';
import { ServiceName } from 'app/enums/service-name.enum';
import { SmbInfoLevel } from 'app/enums/smb-info-level.enum';
import { SystemEnvironment } from 'app/enums/system-environment.enum';
import { SystemState } from 'app/enums/system-state.enum';
import { TransportMode } from 'app/enums/transport-mode.enum';
import {
Acl,
Expand Down Expand Up @@ -794,6 +795,7 @@ export interface ApiCallDirectory {
'system.advanced.syslog_certificate_choices': { params: void; response: Choices };
'system.advanced.update': { params: [Partial<AdvancedConfigUpdate>]; response: AdvancedConfig };
'system.advanced.update_gpu_pci_ids': { params: [isolated_gpu_pci_ids: string[]]; response: void };
'system.advanced.login_banner': { params: void; response: string };
'system.build_time': { params: void; response: ApiTimestamp };
'system.boot_id': { params: void; response: string };
'system.environment': { params: void; response: SystemEnvironment };
Expand All @@ -820,6 +822,10 @@ export interface ApiCallDirectory {
'system.security.config': { params: void; response: SystemSecurityConfig };
'system.security.info.fips_available': { params: void; response: boolean };
'system.set_time': { params: [number]; response: void };
'system.ready': { params: void; response: boolean };
'system.state': { params: void; response: SystemState };
'system.version': { params: void; response: string };
'system.version_short': { params: void; response: string };

// Systemdataset
'systemdataset.config': { params: void; response: SystemDatasetConfig };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<div class="wrapper">
<div class="container">
<div class="title">{{ title | translate }}</div>
<p class="message">{{ message | translate }}</p>
@if (title) {
<div class="title">{{ title | translate }}</div>
}
@if (message) {
<p class="message" [class.pre]="data.pre">{{ message | translate }}</p>
}
</div>
@if (data.showClose) {
<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
z-index: 99999;
}

.cdk-overlay-pane.full-screen-modal,
.mat-mdc-dialog-container {
max-width: none !important;
}
Expand All @@ -13,11 +14,21 @@
display: flex;
flex-direction: column;
justify-content: center;
margin-bottom: 16px;
width: 50%;

.title {
font-size: xx-large;
}

.message {
&.pre {
display: block;
font-family: 'Droid Sans Mono', 'Courier New', Courier, monospace;
unicode-bidi: isolate;
white-space: pre;
}
}
}

.wrapper {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ export class FullScreenDialogComponent {

constructor(
public dialogRef: MatDialogRef<FullScreenDialogComponent>,
@Inject(MAT_DIALOG_DATA) protected data: { showClose: boolean },
@Inject(MAT_DIALOG_DATA) protected data: {
showClose: boolean;
pre: boolean;
},
) {}

close(): void {
this.dialogRef.close();
this.dialogRef.close(true);
}
}
4 changes: 2 additions & 2 deletions src/app/modules/dialog/dialog.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,15 @@ export class DialogService {
return dialogRef.afterClosed();
}

fullScreenDialog(title: string, message: string, showClose = false): Observable<boolean> {
fullScreenDialog(title: string, message: string, showClose = false, pre = false): Observable<boolean> {
const dialogRef = this.matDialog.open(FullScreenDialogComponent, {
maxWidth: '100vw',
maxHeight: '100vh',
height: '100%',
width: '100%',
panelClass: 'full-screen-modal',
disableClose: true,
data: { showClose },
data: { showClose, pre },
});
dialogRef.componentInstance.title = title;
dialogRef.componentInstance.message = message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ <h3>{{ 'Access' | translate }}</h3>
</span>
</mat-list-item>
}
<mat-list-item [ixUiSearch]="searchableElements.elements.loginBanner">
<span class="label">{{ 'Login Banner' | translate }}:</span>
<span *ixWithLoadingState="loginBanner$ as loginBanner" class="value">
{{ loginBanner || '–' }}
</span>
</mat-list-item>
</mat-list>
</mat-card-content>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { LocaleService } from 'app/services/locale.service';
import { SystemGeneralService } from 'app/services/system-general.service';
import { WebSocketService } from 'app/services/ws.service';
import { selectPreferences } from 'app/store/preferences/preferences.selectors';
import { selectGeneralConfig } from 'app/store/system-config/system-config.selectors';
import { selectAdvancedConfig, selectGeneralConfig } from 'app/store/system-config/system-config.selectors';

describe('AccessCardComponent', () => {
let spectator: Spectator<AccessCardComponent>;
Expand Down Expand Up @@ -79,6 +79,12 @@ describe('AccessCardComponent', () => {
ds_auth: true,
},
},
{
selector: selectAdvancedConfig,
value: {
login_banner: 'Hello World!',
},
},
],
}),
mockProvider(DialogService, {
Expand Down Expand Up @@ -113,6 +119,11 @@ describe('AccessCardComponent', () => {
expect(await allowed.getFullText()).toBe('Allow Directory Service users to access WebUI: Yes');
});

it('shows current login banner', async () => {
const loginBanner = (await loader.getAllHarnesses(MatListItemHarness))[2];
expect(await loginBanner.getFullText()).toBe('Login Banner: Hello World!');
});

it('opens Token settings form when Configure is pressed', async () => {
const configure = await loader.getHarness(MatButtonHarness.with({ text: 'Configure' }));
await configure.click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { WebSocketService } from 'app/services/ws.service';
import { AppState } from 'app/store';
import { defaultPreferences } from 'app/store/preferences/default-preferences.constant';
import { waitForPreferences } from 'app/store/preferences/preferences.selectors';
import { waitForGeneralConfig } from 'app/store/system-config/system-config.selectors';
import { waitForAdvancedConfig, waitForGeneralConfig } from 'app/store/system-config/system-config.selectors';

@UntilDestroy()
@Component({
Expand All @@ -56,6 +56,12 @@ export class AccessCardComponent implements OnInit {
toLoadingState(),
);

readonly loginBanner$ = this.store$.pipe(
waitForAdvancedConfig,
map((advancedConfig) => advancedConfig.login_banner),
toLoadingState(),
);

dataProvider: AsyncDataProvider<AuthSession>;

columns = createTable<AuthSession>([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,9 @@ export const accessCardElements = {
hierarchy: [T('Token Lifetime')],
synonyms: [T('Session Token Lifetime')],
},
loginBanner: {
hierarchy: [T('Login Banner')],
synonyms: [T('MOTD')],
},
},
} satisfies UiSearchableElement;
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
[label]="'Allow Directory Service users to access WebUI' | translate"
></ix-checkbox>
}
<ix-textarea
formControlName="login_banner"
[label]="'Login Banner' | translate"
[tooltip]="'When set, the following text will be shown prior to showing login page to the user' | translate"
></ix-textarea>
</ix-fieldset>

<ix-form-actions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import { SystemGeneralService } from 'app/services/system-general.service';
import { WebSocketService } from 'app/services/ws.service';
import { lifetimeTokenUpdated } from 'app/store/preferences/preferences.actions';
import { selectPreferences } from 'app/store/preferences/preferences.selectors';
import { generalConfigUpdated } from 'app/store/system-config/system-config.actions';
import { selectGeneralConfig } from 'app/store/system-config/system-config.selectors';
import { advancedConfigUpdated, generalConfigUpdated, loginBannerUpdated } from 'app/store/system-config/system-config.actions';
import { selectAdvancedConfig, selectGeneralConfig } from 'app/store/system-config/system-config.selectors';

describe('AccessFormComponent', () => {
let spectator: Spectator<AccessFormComponent>;
Expand All @@ -37,9 +37,13 @@ describe('AccessFormComponent', () => {
localStorage: {
setItem: jest.fn,
},
sessionStorage: {
setItem: jest.fn,
},
}),
mockWebSocket([
mockCall('system.general.update'),
mockCall('system.advanced.update'),
]),
mockProvider(IxChainedSlideInService, {
open: jest.fn(() => of(true)),
Expand All @@ -55,6 +59,9 @@ describe('AccessFormComponent', () => {
}, {
selector: selectGeneralConfig,
value: { ds_auth: true },
}, {
selector: selectAdvancedConfig,
value: { login_banner: 'test' },
}],
}),
mockProvider(ChainedRef, chainedRef),
Expand All @@ -73,6 +80,7 @@ describe('AccessFormComponent', () => {

expect(values).toEqual({
'Token Lifetime': '300',
'Login Banner': 'test',
'Allow Directory Service users to access WebUI': true,
});
});
Expand All @@ -84,6 +92,7 @@ describe('AccessFormComponent', () => {
const form = await loader.getHarness(IxFormHarness);
await form.fillForm({
'Token Lifetime': '60',
'Login Banner': '',
'Allow Directory Service users to access WebUI': false,
});

Expand All @@ -94,7 +103,12 @@ describe('AccessFormComponent', () => {
expect(spectator.inject(WebSocketService).call).toHaveBeenCalledWith('system.general.update', [{
ds_auth: false,
}]);
expect(spectator.inject(WebSocketService).call).toHaveBeenCalledWith('system.advanced.update', [{
login_banner: '',
}]);
expect(store$.dispatch).toHaveBeenCalledWith(generalConfigUpdated());
expect(store$.dispatch).toHaveBeenCalledWith(advancedConfigUpdated());
expect(store$.dispatch).toHaveBeenCalledWith(loginBannerUpdated({ loginBanner: '' }));
expect(chainedRef.close).toHaveBeenCalled();
});
});
Loading
Loading