feat(console): only use one save button in SMTP Settings (#6561)

seperate smtp settings from notification providers
This commit is contained in:
Miguel Cabrerizo 2023-10-09 13:14:17 +02:00 committed by GitHub
parent 827ce8809d
commit 8c68f8ed3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 464 additions and 278 deletions

View File

@ -26,6 +26,7 @@
color="primary"
name="hasUppercase"
ngDefaultControl
data-e2e="notification-policy-checkbox"
[(ngModel)]="notificationData.passwordChange"
[disabled]="(['policy.write'] | hasRole | async) === false"
>
@ -43,6 +44,7 @@
color="primary"
type="submit"
mat-raised-button
data-e2e="save-notification-policy-button"
>
{{ 'ACTIONS.SAVE' | translate }}
</button>

View File

@ -1,24 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NotificationSettingsComponent } from './notification-settings.component';
describe('NotificationSettingsComponent', () => {
let component: NotificationSettingsComponent;
let fixture: ComponentFixture<NotificationSettingsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [NotificationSettingsComponent],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(NotificationSettingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -33,6 +33,7 @@
class="ok-button"
color="primary"
(click)="closeDialogWithRequest()"
data-e2e="save-sms-settings-button"
>
<span>{{ 'ACTIONS.SAVE' | translate }}</span>
</button>

View File

@ -14,7 +14,6 @@ import {
import { SMSProvider, TwilioConfig } from 'src/app/proto/generated/zitadel/settings_pb';
import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service';
import { PasswordDialogComponent } from '../password-dialog/password-dialog.component';
enum SMSProviderType {

View File

@ -0,0 +1,56 @@
<h2>{{ 'SETTING.SMS.TITLE' | translate }}</h2>
<div class="spinner-wr">
<mat-spinner diameter="30" *ngIf="smsProvidersLoading" color="primary"></mat-spinner>
</div>
<div class="sms-providers">
<cnsl-card class="sms-card" [nomargin]="true">
<div class="sms-provider">
<h4 class="title">Twilio</h4>
<span
*ngIf="twilio"
class="state"
[ngClass]="{
active: twilio.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_ACTIVE,
inactive: twilio.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_INACTIVE
}"
>{{ 'SETTING.SMS.SMSPROVIDERSTATE.' + twilio.state | translate }}</span
>
<span class="fill-space"></span>
<button
*ngIf="twilio && twilio.id"
[disabled]="(['iam.write'] | hasRole | async) === false"
mat-stroked-button
(click)="toggleSMSProviderState(twilio.id)"
>
<span *ngIf="twilio.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_ACTIVE">{{
'ACTIONS.DEACTIVATE' | translate
}}</span>
<span *ngIf="twilio.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_INACTIVE">{{
'ACTIONS.ACTIVATE' | translate
}}</span>
</button>
<button
*ngIf="twilio && twilio.id"
color="warn"
[disabled]="(['iam.write'] | hasRole | async) === false"
mat-icon-button
(click)="removeSMSProvider(twilio.id)"
data-e2e="remove-sms-provider-button"
>
<i class="las la-trash"></i>
</button>
<button
[disabled]="(['iam.write'] | hasRole | async) === false"
mat-icon-button
(click)="editSMSProvider()"
data-e2e="new-twilio-button"
>
<i class="las la-pen"></i>
</button>
</div>
</cnsl-card>
</div>

View File

@ -2,35 +2,6 @@
margin: 0.5rem 0;
}
.smtp-form-field,
.info-section-warn {
max-width: 400px;
display: block;
}
.info-section-warn {
margin-bottom: 0.5rem;
}
.smtp-checkbox {
max-width: 400px;
display: block;
margin: 1rem 0;
}
.set-password-btn {
margin-bottom: 1rem;
}
.general-btn-container {
display: flex;
justify-content: flex-start;
.save-button {
display: block;
}
}
.sms-providers {
display: flex;
align-items: center;

View File

@ -0,0 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NotificationSMSProviderComponent } from './notification-sms-provider.component';
describe('NotificationSMSProviderComponent', () => {
let component: NotificationSMSProviderComponent;
let fixture: ComponentFixture<NotificationSMSProviderComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [NotificationSMSProviderComponent],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(NotificationSMSProviderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,140 @@
import { Component, Input } from '@angular/core';
import { AddSMSProviderTwilioRequest, UpdateSMSProviderTwilioRequest } from 'src/app/proto/generated/zitadel/admin_pb';
import { SMSProvider, SMSProviderConfigState } from 'src/app/proto/generated/zitadel/settings_pb';
import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service';
import { InfoSectionType } from '../../info-section/info-section.component';
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
import { PolicyComponentServiceType } from '../policy-component-types.enum';
import { DialogAddSMSProviderComponent } from './dialog-add-sms-provider/dialog-add-sms-provider.component';
@Component({
selector: 'cnsl-notification-sms-provider',
templateUrl: './notification-sms-provider.component.html',
styleUrls: ['./notification-sms-provider.component.scss'],
})
export class NotificationSMSProviderComponent {
@Input() public serviceType!: PolicyComponentServiceType;
public smsProviders: SMSProvider.AsObject[] = [];
public smsProvidersLoading: boolean = false;
public SMSProviderConfigState: any = SMSProviderConfigState;
public InfoSectionType: any = InfoSectionType;
constructor(
private service: AdminService,
private dialog: MatDialog,
private toast: ToastService,
) {}
private fetchData(): void {
this.smsProvidersLoading = true;
this.service
.listSMSProviders()
.then((smsProviders) => {
this.smsProvidersLoading = false;
if (smsProviders.resultList) {
this.smsProviders = smsProviders.resultList;
}
})
.catch((error) => {
this.smsProvidersLoading = false;
this.toast.showError(error);
});
}
public editSMSProvider(): void {
const dialogRef = this.dialog.open(DialogAddSMSProviderComponent, {
width: '400px',
data: {
smsProviders: this.smsProviders,
},
});
dialogRef.afterClosed().subscribe((req: AddSMSProviderTwilioRequest | UpdateSMSProviderTwilioRequest) => {
if (req) {
if (!!this.twilio) {
this.service
.updateSMSProviderTwilio(req as UpdateSMSProviderTwilioRequest)
.then(() => {
this.toast.showInfo('SETTING.SMS.TWILIO.ADDED', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
} else {
this.service
.addSMSProviderTwilio(req as AddSMSProviderTwilioRequest)
.then(() => {
this.toast.showInfo('SETTING.SMS.TWILIO.ADDED', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
}
}
});
}
public toggleSMSProviderState(id: string): void {
const provider = this.smsProviders.find((p) => p.id === id);
if (provider) {
if (provider.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_ACTIVE) {
this.service
.deactivateSMSProvider(id)
.then(() => {
this.toast.showInfo('SETTING.SMS.DEACTIVATED', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
} else if (provider.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_INACTIVE) {
this.service
.activateSMSProvider(id)
.then(() => {
this.toast.showInfo('SETTING.SMS.ACTIVATED', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
}
}
}
public removeSMSProvider(id: string): void {
const dialogRef = this.dialog.open(WarnDialogComponent, {
data: {
confirmKey: 'ACTIONS.DELETE',
cancelKey: 'ACTIONS.CANCEL',
titleKey: 'SETTING.SMS.REMOVEPROVIDER',
descriptionKey: 'SETTING.SMS.REMOVEPROVIDER_DESC',
},
width: '400px',
});
dialogRef.afterClosed().subscribe((resp) => {
if (resp) {
this.service
.removeSMSProvider(id)
.then(() => {
this.toast.showInfo('SETTING.SMS.TWILIO.REMOVED', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
}
});
}
public get twilio(): SMSProvider.AsObject | undefined {
return this.smsProviders.find((p) => p.twilio);
}
}

View File

@ -15,11 +15,10 @@ import { InfoSectionModule } from '../../info-section/info-section.module';
import { InputModule } from '../../input/input.module';
import { WarnDialogModule } from '../../warn-dialog/warn-dialog.module';
import { DialogAddSMSProviderComponent } from './dialog-add-sms-provider/dialog-add-sms-provider.component';
import { NotificationSettingsComponent } from './notification-settings.component';
import { PasswordDialogComponent } from './password-dialog/password-dialog.component';
import { NotificationSMSProviderComponent } from './notification-sms-provider.component';
@NgModule({
declarations: [NotificationSettingsComponent, DialogAddSMSProviderComponent, PasswordDialogComponent],
declarations: [NotificationSMSProviderComponent, DialogAddSMSProviderComponent],
imports: [
CommonModule,
CardModule,
@ -38,6 +37,6 @@ import { PasswordDialogComponent } from './password-dialog/password-dialog.compo
MatSelectModule,
TranslateModule,
],
exports: [NotificationSettingsComponent],
exports: [NotificationSMSProviderComponent],
})
export class NotificationSettingsModule {}
export class NotificationSMSProviderModule {}

View File

@ -4,7 +4,13 @@
<div mat-dialog-content>
<cnsl-form-field class="formfield">
<cnsl-label>{{ data.i18nLabel | translate }}</cnsl-label>
<input cnslInput [(ngModel)]="password" type="password" autocomplete="new-password" />
<input
cnslInput
[(ngModel)]="password"
type="password"
autocomplete="new-password"
data-e2e="notification-setting-password"
/>
</cnsl-form-field>
</div>
<div mat-dialog-actions class="action">
@ -19,6 +25,7 @@
mat-raised-button
class="ok-button"
(click)="closeDialog(password)"
data-e2e="save-notification-setting-password-button"
>
{{ 'ACTIONS.OK' | translate }}
</button>

View File

@ -1,7 +1,7 @@
<h2>{{ 'SETTING.SMTP.TITLE' | translate }}</h2>
<div class="spinner-wr">
<mat-spinner diameter="30" *ngIf="smtpLoading || smsProvidersLoading" color="primary"></mat-spinner>
<mat-spinner diameter="30" *ngIf="smtpLoading" color="primary"></mat-spinner>
</div>
<cnsl-info-section
@ -48,6 +48,7 @@
(click)="setSMTPPassword()"
type="button"
mat-stroked-button
data-e2e="add-smtp-password-button"
>
{{ 'SETTING.SMTP.SETPASSWORD' | translate }}
</button>
@ -60,55 +61,9 @@
color="primary"
type="submit"
mat-raised-button
data-e2e="save-smtp-settings-button"
>
{{ 'ACTIONS.SAVE' | translate }}
</button>
</div>
</form>
<br />
<h2>{{ 'SETTING.SMS.TITLE' | translate }}</h2>
<div class="sms-providers">
<cnsl-card class="sms-card" [nomargin]="true">
<div class="sms-provider">
<h4 class="title">Twilio</h4>
<span
*ngIf="twilio"
class="state"
[ngClass]="{
active: twilio.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_ACTIVE,
inactive: twilio.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_INACTIVE
}"
>{{ 'SETTING.SMS.SMSPROVIDERSTATE.' + twilio.state | translate }}</span
>
<span class="fill-space"></span>
<button
*ngIf="twilio && twilio.id"
[disabled]="(['iam.write'] | hasRole | async) === false"
mat-stroked-button
(click)="toggleSMSProviderState(twilio.id)"
>
<span *ngIf="twilio.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_ACTIVE">{{
'ACTIONS.DEACTIVATE' | translate
}}</span>
<span *ngIf="twilio.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_INACTIVE">{{
'ACTIONS.ACTIVATE' | translate
}}</span>
</button>
<button
*ngIf="twilio && twilio.id"
color="warn"
[disabled]="(['iam.write'] | hasRole | async) === false"
mat-icon-button
(click)="removeSMSProvider(twilio.id)"
>
<i class="las la-trash"></i>
</button>
<button [disabled]="(['iam.write'] | hasRole | async) === false" mat-icon-button (click)="editSMSProvider()">
<i class="las la-pen"></i>
</button>
</div>
</cnsl-card>
</div>

View File

@ -0,0 +1,32 @@
.spinner-wr {
margin: 0.5rem 0;
}
.smtp-form-field,
.info-section-warn {
max-width: 400px;
display: block;
}
.info-section-warn {
margin-bottom: 0.5rem;
}
.smtp-checkbox {
max-width: 400px;
display: block;
margin: 1rem 0;
}
.set-password-btn {
margin-bottom: 1rem;
}
.general-btn-container {
display: flex;
justify-content: flex-start;
.save-button {
display: block;
}
}

View File

@ -0,0 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NotificationSMTPProviderComponent } from './notification-smtp-provider.component';
describe('NotificationSMTPProviderComponent', () => {
let component: NotificationSMTPProviderComponent;
let fixture: ComponentFixture<NotificationSMTPProviderComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [NotificationSMTPProviderComponent],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(NotificationSMTPProviderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -3,45 +3,33 @@ import { AbstractControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/
import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { take } from 'rxjs';
import {
AddSMSProviderTwilioRequest,
AddSMTPConfigRequest,
AddSMTPConfigResponse,
UpdateSMSProviderTwilioRequest,
UpdateSMTPConfigPasswordRequest,
UpdateSMTPConfigRequest,
UpdateSMTPConfigResponse,
} from 'src/app/proto/generated/zitadel/admin_pb';
import { DebugNotificationProvider, SMSProvider, SMSProviderConfigState } from 'src/app/proto/generated/zitadel/settings_pb';
import { AdminService } from 'src/app/services/admin.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ToastService } from 'src/app/services/toast.service';
import { requiredValidator } from '../../form-field/validators/validators';
import { InfoSectionType } from '../../info-section/info-section.component';
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
import { PasswordDialogComponent } from '../notification-sms-provider/password-dialog/password-dialog.component';
import { PolicyComponentServiceType } from '../policy-component-types.enum';
import { DialogAddSMSProviderComponent } from './dialog-add-sms-provider/dialog-add-sms-provider.component';
import { PasswordDialogComponent } from './password-dialog/password-dialog.component';
@Component({
selector: 'cnsl-notification-settings',
templateUrl: './notification-settings.component.html',
styleUrls: ['./notification-settings.component.scss'],
selector: 'cnsl-notification-smtp-provider',
templateUrl: './notification-smtp-provider.component.html',
styleUrls: ['./notification-smtp-provider.component.scss'],
})
export class NotificationSettingsComponent implements OnInit {
export class NotificationSMTPProviderComponent implements OnInit {
@Input() public serviceType!: PolicyComponentServiceType;
public smsProviders: SMSProvider.AsObject[] = [];
public logNotificationProvider!: DebugNotificationProvider.AsObject;
public fileNotificationProvider!: DebugNotificationProvider.AsObject;
public smtpLoading: boolean = false;
public smsProvidersLoading: boolean = false;
public logProviderLoading: boolean = false;
public fileProviderLoading: boolean = false;
public form!: UntypedFormGroup;
public SMSProviderConfigState: any = SMSProviderConfigState;
public InfoSectionType: any = InfoSectionType;
public hasSMTPConfig: boolean = false;
@ -96,46 +84,6 @@ export class NotificationSettingsComponent implements OnInit {
this.hasSMTPConfig = false;
}
});
this.smsProvidersLoading = true;
this.service
.listSMSProviders()
.then((smsProviders) => {
this.smsProvidersLoading = false;
if (smsProviders.resultList) {
this.smsProviders = smsProviders.resultList;
}
})
.catch((error) => {
this.smsProvidersLoading = false;
this.toast.showError(error);
});
this.logProviderLoading = true;
this.service
.getLogNotificationProvider()
.then((logNotificationProvider) => {
this.logProviderLoading = false;
if (logNotificationProvider.provider) {
this.logNotificationProvider = logNotificationProvider.provider;
}
})
.catch(() => {
this.logProviderLoading = false;
});
this.fileProviderLoading = true;
this.service
.getFileSystemNotificationProvider()
.then((fileNotificationProvider) => {
this.fileProviderLoading = false;
if (fileNotificationProvider.provider) {
this.fileNotificationProvider = fileNotificationProvider.provider;
}
})
.catch(() => {
this.fileProviderLoading = false;
});
}
private updateData(): Promise<UpdateSMTPConfigResponse.AsObject | AddSMTPConfigResponse> {
@ -175,41 +123,6 @@ export class NotificationSettingsComponent implements OnInit {
});
}
public editSMSProvider(): void {
const dialogRef = this.dialog.open(DialogAddSMSProviderComponent, {
width: '400px',
data: {
smsProviders: this.smsProviders,
},
});
dialogRef.afterClosed().subscribe((req: AddSMSProviderTwilioRequest | UpdateSMSProviderTwilioRequest) => {
if (req) {
if (!!this.twilio) {
this.service
.updateSMSProviderTwilio(req as UpdateSMSProviderTwilioRequest)
.then(() => {
this.toast.showInfo('SETTING.SMS.TWILIO.ADDED', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
} else {
this.service
.addSMSProviderTwilio(req as AddSMSProviderTwilioRequest)
.then(() => {
this.toast.showInfo('SETTING.SMS.TWILIO.ADDED', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
}
}
});
}
public setSMTPPassword(): void {
const dialogRef = this.dialog.open(PasswordDialogComponent, {
width: '400px',
@ -236,63 +149,6 @@ export class NotificationSettingsComponent implements OnInit {
});
}
public toggleSMSProviderState(id: string): void {
const provider = this.smsProviders.find((p) => p.id === id);
if (provider) {
if (provider.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_ACTIVE) {
this.service
.deactivateSMSProvider(id)
.then(() => {
this.toast.showInfo('SETTING.SMS.DEACTIVATED', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
} else if (provider.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_INACTIVE) {
this.service
.activateSMSProvider(id)
.then(() => {
this.toast.showInfo('SETTING.SMS.ACTIVATED', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
}
}
}
public removeSMSProvider(id: string): void {
const dialogRef = this.dialog.open(WarnDialogComponent, {
data: {
confirmKey: 'ACTIONS.DELETE',
cancelKey: 'ACTIONS.CANCEL',
titleKey: 'SETTING.SMS.REMOVEPROVIDER',
descriptionKey: 'SETTING.SMS.REMOVEPROVIDER_DESC',
},
width: '400px',
});
dialogRef.afterClosed().subscribe((resp) => {
if (resp) {
this.service
.removeSMSProvider(id)
.then(() => {
this.toast.showInfo('SETTING.SMS.TWILIO.REMOVED', true);
this.fetchData();
})
.catch((error) => {
this.toast.showError(error);
});
}
});
}
public get twilio(): SMSProvider.AsObject | undefined {
return this.smsProviders.find((p) => p.twilio);
}
public get senderAddress(): AbstractControl | null {
return this.form.get('senderAddress');
}

View File

@ -0,0 +1,42 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatIconModule } from '@angular/material/icon';
import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatLegacyCheckboxModule as MatCheckboxModule } from '@angular/material/legacy-checkbox';
import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner';
import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';
import { TranslateModule } from '@ngx-translate/core';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { CardModule } from '../../card/card.module';
import { FormFieldModule } from '../../form-field/form-field.module';
import { InfoSectionModule } from '../../info-section/info-section.module';
import { InputModule } from '../../input/input.module';
import { WarnDialogModule } from '../../warn-dialog/warn-dialog.module';
import { PasswordDialogComponent } from '../notification-sms-provider/password-dialog/password-dialog.component';
import { NotificationSMTPProviderComponent } from './notification-smtp-provider.component';
@NgModule({
declarations: [NotificationSMTPProviderComponent, PasswordDialogComponent],
imports: [
CommonModule,
CardModule,
InfoSectionModule,
FormsModule,
ReactiveFormsModule,
HasRolePipeModule,
MatButtonModule,
MatCheckboxModule,
InputModule,
MatIconModule,
FormFieldModule,
WarnDialogModule,
MatSelectModule,
MatProgressSpinnerModule,
MatSelectModule,
TranslateModule,
],
exports: [NotificationSMTPProviderComponent],
})
export class NotificationSMTPProviderModule {}

View File

@ -51,7 +51,7 @@ export const NOTIFICATION_GROUP: SettingLinks = {
i18nTitle: 'SETTINGS.GROUPS.NOTIFICATIONS',
i18nDesc: 'SETTINGS.LIST.NOTIFICATIONS_DESC',
iamRouterLink: ['/settings'],
queryParams: { id: 'notifications' },
queryParams: { id: 'smtpprovider' },
iamWithRole: ['iam.policy.read'],
icon: 'las la-bell',
color: 'red',

View File

@ -27,19 +27,18 @@
<ng-container *ngIf="currentSetting === 'idp'">
<cnsl-idp-settings [serviceType]="serviceType"></cnsl-idp-settings>
</ng-container>
<ng-container *ngIf="currentSetting === 'notifications' && serviceType === PolicyComponentServiceType.ADMIN">
<ng-container *ngIf="currentSetting === 'notifications'">
<cnsl-notification-policy [serviceType]="serviceType"></cnsl-notification-policy>
<cnsl-notification-settings [serviceType]="serviceType"></cnsl-notification-settings>
</ng-container>
<ng-container *ngIf="currentSetting === 'notifications' && serviceType === PolicyComponentServiceType.MGMT">
<cnsl-notification-policy [serviceType]="serviceType"></cnsl-notification-policy
></ng-container>
<ng-container *ngIf="currentSetting === 'smtpprovider' && serviceType === PolicyComponentServiceType.ADMIN">
<cnsl-notification-smtp-provider [serviceType]="serviceType"></cnsl-notification-smtp-provider>
</ng-container>
<ng-container *ngIf="currentSetting === 'smsprovider' && serviceType === PolicyComponentServiceType.ADMIN">
<cnsl-notification-sms-provider [serviceType]="serviceType"></cnsl-notification-sms-provider>
</ng-container>
<ng-container *ngIf="currentSetting === 'oidc' && serviceType === PolicyComponentServiceType.ADMIN">
<cnsl-oidc-configuration></cnsl-oidc-configuration>
</ng-container>
<ng-container *ngIf="currentSetting === 'secrets' && serviceType === PolicyComponentServiceType.ADMIN">
<cnsl-secret-generator></cnsl-secret-generator>
</ng-container>

View File

@ -13,7 +13,8 @@ import { LoginPolicyModule } from '../policies/login-policy/login-policy.module'
import { LoginTextsPolicyModule } from '../policies/login-texts/login-texts.module';
import { MessageTextsPolicyModule } from '../policies/message-texts/message-texts.module';
import { NotificationPolicyModule } from '../policies/notification-policy/notification-policy.module';
import { NotificationSettingsModule } from '../policies/notification-settings/notification-settings.module';
import { NotificationSMSProviderModule } from '../policies/notification-sms-provider/notification-sms-provider.module';
import { NotificationSMTPProviderModule } from '../policies/notification-smtp-provider/notification-smtp-provider.module';
import { OIDCConfigurationModule } from '../policies/oidc-configuration/oidc-configuration.module';
import { PasswordComplexityPolicyModule } from '../policies/password-complexity-policy/password-complexity-policy.module';
import { PasswordLockoutPolicyModule } from '../policies/password-lockout-policy/password-lockout-policy.module';
@ -46,7 +47,8 @@ import { SettingsListComponent } from './settings-list.component';
DomainPolicyModule,
TranslateModule,
HasRolePipeModule,
NotificationSettingsModule,
NotificationSMTPProviderModule,
NotificationSMSProviderModule,
OIDCConfigurationModule,
SecretGeneratorModule,
],

View File

@ -98,15 +98,25 @@ export const NOTIFICATIONS: SidenavSetting = {
groupI18nKey: 'SETTINGS.GROUPS.NOTIFICATIONS',
requiredRoles: {
[PolicyComponentServiceType.ADMIN]: ['iam.policy.read'],
[PolicyComponentServiceType.MGMT]: ['policy.read'],
},
};
export const NOTIFICATION_POLICY: SidenavSetting = {
id: 'notifications',
i18nKey: 'SETTINGS.LIST.NOTIFICATIONS',
export const SMTP_PROVIDER: SidenavSetting = {
id: 'smtpprovider',
i18nKey: 'SETTINGS.LIST.SMTP_PROVIDER',
groupI18nKey: 'SETTINGS.GROUPS.NOTIFICATIONS',
requiredRoles: {
[PolicyComponentServiceType.MGMT]: ['policy.read'],
[PolicyComponentServiceType.ADMIN]: ['iam.policy.read'],
},
};
export const SMS_PROVIDER: SidenavSetting = {
id: 'smsprovider',
i18nKey: 'SETTINGS.LIST.SMS_PROVIDER',
groupI18nKey: 'SETTINGS.GROUPS.NOTIFICATIONS',
requiredRoles: {
[PolicyComponentServiceType.ADMIN]: ['iam.policy.read'],
},
};

View File

@ -21,6 +21,8 @@ import {
PRIVACYPOLICY,
SECRETS,
SECURITY,
SMS_PROVIDER,
SMTP_PROVIDER,
} from '../../modules/settings-list/settings';
@Component({
@ -36,6 +38,8 @@ export class InstanceSettingsComponent implements OnInit, OnDestroy {
// notifications
// { showWarn: true, ...NOTIFICATIONS },
NOTIFICATIONS,
SMTP_PROVIDER,
SMS_PROVIDER,
// login
LOGIN,
IDP,
@ -80,7 +84,10 @@ export class InstanceSettingsComponent implements OnInit, OnDestroy {
}
ngOnInit(): void {
this.settingsList = this.authService.isAllowedMapper(this.defaultSettingsList, (setting) => setting.requiredRoles.admin);
this.settingsList = this.authService.isAllowedMapper(
this.defaultSettingsList,
(setting) => setting.requiredRoles.admin || [],
);
}
ngOnDestroy(): void {

View File

@ -15,7 +15,7 @@ import {
LOGIN,
LOGINTEXTS,
MESSAGETEXTS,
NOTIFICATION_POLICY,
NOTIFICATIONS,
PRIVACYPOLICY,
VERIFIED_DOMAINS,
} from '../../modules/settings-list/settings';
@ -34,7 +34,7 @@ export class OrgSettingsComponent implements OnInit {
IDP,
COMPLEXITY,
LOCKOUT,
NOTIFICATION_POLICY,
NOTIFICATIONS,
VERIFIED_DOMAINS,
DOMAIN,
BRANDING,
@ -68,7 +68,7 @@ export class OrgSettingsComponent implements OnInit {
ngOnInit(): void {
this.settingsList = this.authService
.isAllowedMapper(this.defaultSettingsList, (setting) => setting.requiredRoles.mgmt)
.isAllowedMapper(this.defaultSettingsList, (setting) => setting.requiredRoles.mgmt || [])
.pipe(take(1));
}
}

View File

@ -71,7 +71,7 @@ export const ONBOARDING_EVENTS: OnboardingActions[] = [
eventType: 'instance.smtp.config.added',
oneof: ['instance.smtp.config.added', 'instance.smtp.config.changed'],
link: ['/settings'],
fragment: 'notifications',
fragment: 'smtpprovider',
iconClasses: 'las la-envelope',
darkcolor: yellowdark,
lightcolor: yellowlight,

View File

@ -1013,6 +1013,8 @@
"LOCKOUT": "Блокиране",
"COMPLEXITY": "Сложност на паролата",
"NOTIFICATIONS": "Настройки за известията",
"SMTP_PROVIDER": "SMTP доставчик",
"SMS_PROVIDER": "Доставчик на SMS/телефон",
"NOTIFICATIONS_DESC": "Настройки за SMTP и SMS",
"MESSAGETEXTS": "Текстове на съобщения",
"IDP": "Доставчици на идентичност",

View File

@ -1019,6 +1019,8 @@
"LOCKOUT": "Sperrmechanismen",
"COMPLEXITY": "Passwordkomplexität",
"NOTIFICATIONS": "Benachrichtigungseinstellungen",
"SMTP_PROVIDER": "SMTP-Anbieter",
"SMS_PROVIDER": "SMS / Telefon Anbieter",
"NOTIFICATIONS_DESC": "SMTP und SMS Einstellungen",
"MESSAGETEXTS": "Benachrichtigungstexte",
"IDP": "Identitätsanbieter",

View File

@ -1019,7 +1019,9 @@
"LOGIN": "Login Behavior and Security",
"LOCKOUT": "Lockout",
"COMPLEXITY": "Password complexity",
"NOTIFICATIONS": "Notification settings",
"NOTIFICATIONS": "Notifications",
"SMTP_PROVIDER": "SMTP Provider",
"SMS_PROVIDER": "SMS/Phone Provider",
"NOTIFICATIONS_DESC": "SMTP and SMS Settings",
"MESSAGETEXTS": "Message Texts",
"IDP": "Identity Providers",

View File

@ -1020,6 +1020,8 @@
"LOCKOUT": "Bloqueo",
"COMPLEXITY": "Complejidad de contraseña",
"NOTIFICATIONS": "Ajustes de notificación",
"SMTP_PROVIDER": "Proveedor SMTP",
"SMS_PROVIDER": "Proveedor SMS/Teléfono",
"NOTIFICATIONS_DESC": "Ajustes SMTP y SMS",
"MESSAGETEXTS": "Mensajes de texto",
"IDP": "Proveedores de identidad",

View File

@ -1019,6 +1019,8 @@
"LOCKOUT": "Verrouillage",
"COMPLEXITY": "Complexité du mot de passe",
"NOTIFICATIONS": "Paramètres de notification",
"SMTP_PROVIDER": "Fournisseur SMTP",
"SMS_PROVIDER": "SMS/Téléphone Fournisseur",
"NOTIFICATIONS_DESC": "Paramètres SMTP et SMS",
"MESSAGETEXTS": "Textes des messages",
"IDP": "Fournisseurs d'identité",

View File

@ -1019,6 +1019,8 @@
"LOCKOUT": "Meccanismi di bloccaggio",
"COMPLEXITY": "Complessità della password",
"NOTIFICATIONS": "Impostazioni di notifica",
"SMTP_PROVIDER": "Fornitore SMTP",
"SMS_PROVIDER": "Fornitore di servizi SMS/telefonici",
"NOTIFICATIONS_DESC": "Impostazioni SMTP e SMS",
"MESSAGETEXTS": "Testi di notifica",
"IDP": "Fornitori di identità",

View File

@ -1020,6 +1020,8 @@
"LOCKOUT": "ロックアウト",
"COMPLEXITY": "パスワードの複雑さ",
"NOTIFICATIONS": "通知設定",
"SMTP_PROVIDER": "SMTPプロバイダー",
"SMS_PROVIDER": "SMS/電話プロバイダー",
"NOTIFICATIONS_DESC": "SMTPおよびSMS設定",
"MESSAGETEXTS": "メッセージテキスト",
"IDP": "IDプロバイダー",

View File

@ -1021,6 +1021,8 @@
"LOCKOUT": "Забрана на пристап",
"COMPLEXITY": "Сложеност на лозинката",
"NOTIFICATIONS": "Подесувања за известувања",
"SMTP_PROVIDER": "SMTP провајдер",
"SMS_PROVIDER": "СМС/Провајдер на телефон",
"NOTIFICATIONS_DESC": "Подесувања за SMTP и SMS",
"MESSAGETEXTS": "Текстови на пораки",
"IDP": "Identity Providers",

View File

@ -1019,6 +1019,8 @@
"LOCKOUT": "Blokada",
"COMPLEXITY": "Złożoność hasła",
"NOTIFICATIONS": "Ustawienia powiadomień",
"SMTP_PROVIDER": "Dostawca SMTP",
"SMS_PROVIDER": "Dostawca SMS-ów/telefonów",
"NOTIFICATIONS_DESC": "Ustawienia SMTP i SMS",
"MESSAGETEXTS": "Teksty wiadomości",
"IDP": "Dostawcy tożsamości",

View File

@ -1021,6 +1021,8 @@
"LOCKOUT": "Bloqueio",
"COMPLEXITY": "Complexidade de Senha",
"NOTIFICATIONS": "Configurações de Notificação",
"SMTP_PROVIDER": "Provedor SMTP",
"SMS_PROVIDER": "Provedor de SMS/Telefone",
"NOTIFICATIONS_DESC": "Configurações de SMTP e SMS",
"MESSAGETEXTS": "Textos de Mensagem",
"IDP": "Provedores de Identidade",

View File

@ -1019,6 +1019,8 @@
"LOCKOUT": "安全锁策略",
"COMPLEXITY": "密码复杂性",
"NOTIFICATIONS": "通知设置",
"SMTP_PROVIDER": "SMTP 提供商",
"SMS_PROVIDER": "短信/电话提供商",
"NOTIFICATIONS_DESC": "SMTP 和 SMS 设置",
"MESSAGETEXTS": "消息文本",
"IDP": "身份提供者",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -0,0 +1,62 @@
const notificationPath = `/settings?id=notifications`;
const smtpPath = `/settings?id=smtpprovider`;
const smsPath = `/settings?id=smsprovider`;
beforeEach(() => {
cy.context().as('ctx');
});
describe('instance notifications', () => {
describe('notification settings', () => {
it(`should show notification settings`, () => {
cy.visit(notificationPath);
cy.contains('Notification');
});
});
describe('smtp settings', () => {
it(`should show SMTP provider settings`, () => {
cy.visit(smtpPath);
cy.contains('SMTP Settings');
});
it(`should add SMTP provider settings`, () => {
cy.visit(smtpPath);
cy.get('[formcontrolname="senderAddress"]').clear().type('sender@example.com');
cy.get('[formcontrolname="senderName"]').clear().type('Zitadel');
cy.get('[formcontrolname="hostAndPort"]').clear().type('smtp.mailtrap.io:2525');
cy.get('[formcontrolname="user"]').clear().type('user@example.com');
cy.get('[data-e2e="save-smtp-settings-button"]').click();
cy.shouldConfirmSuccess();
cy.get('[formcontrolname="senderAddress"]').should('have.value', 'sender@example.com');
cy.get('[formcontrolname="senderName"]').should('have.value', 'Zitadel');
cy.get('[formcontrolname="hostAndPort"]').should('have.value', 'smtp.mailtrap.io:2525');
cy.get('[formcontrolname="user"]').should('have.value', 'user@example.com');
});
it(`should add SMTP provider password`, () => {
cy.visit(smtpPath);
cy.get('[data-e2e="add-smtp-password-button"]').click();
cy.get('[data-e2e="notification-setting-password"]').clear().type('dummy@example.com');
cy.get('[data-e2e="save-notification-setting-password-button"]').click();
cy.shouldConfirmSuccess();
});
});
describe('sms settings', () => {
it(`should show SMS provider settings`, () => {
cy.visit(smsPath);
cy.contains('SMS Settings');
});
it(`should add SMS provider`, () => {
cy.visit(smsPath);
cy.get('[data-e2e="new-twilio-button"]').click();
cy.get('[formcontrolname="sid"]').clear().type('test');
cy.get('[formcontrolname="token"]').clear().type('token');
cy.get('[formcontrolname="senderNumber"]').clear().type('2312123132');
cy.get('[data-e2e="save-sms-settings-button"]').click();
cy.shouldConfirmSuccess();
});
});
});