mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-08 15:15:12 +00:00
feat: enable iframe use (#4766)
* feat: enable iframe use * cleanup * fix mocks * fix linting * docs: add iframe usage to solution scenarios configurations * improve api * feat(console): security policy * description * remove unnecessary line * disable input button and urls when not enabled * add image to docs Co-authored-by: Max Peintner <[email protected]> Co-authored-by: Fabi <[email protected]>
This commit is contained in:
co-authored by
Max Peintner
Fabi
parent
33e973f015
commit
632639ae7f
@@ -0,0 +1,61 @@
|
||||
<h2>{{ 'SETTINGS.LIST.SECURITY' | translate }}</h2>
|
||||
|
||||
<div class="spinner-wr">
|
||||
<mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<div class="security-wrapper">
|
||||
<cnsl-info-section [type]="InfoSectionType.ALERT">{{ 'SETTING.SECURITY.DESCRIPTION' | translate }}</cnsl-info-section>
|
||||
|
||||
<mat-checkbox
|
||||
card-actions
|
||||
class="security-policy-toggle"
|
||||
color="primary"
|
||||
ngDefaultControl
|
||||
(change)="enabledChanged($event)"
|
||||
[(ngModel)]="enabled"
|
||||
[disabled]="(['iam.policy.write'] | hasRole | async) === false"
|
||||
>
|
||||
{{ 'SETTING.SECURITY.IFRAMEENABLED' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
<form class="security-allowed-originsform" (ngSubmit)="add(redInput)">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'SETTING.SECURITY.ALLOWEDORIGINS' | translate }}</cnsl-label>
|
||||
|
||||
<input #redInput cnslInput placeholder="ex. https://" [formControl]="originsControl" />
|
||||
</cnsl-form-field>
|
||||
<button
|
||||
matTooltip="{{ 'ACTIONS.ADD' | translate }}"
|
||||
type="submit"
|
||||
mat-icon-button
|
||||
[disabled]="!enabled || originsControl.invalid || (['iam.policy.write'] | hasRole | async) === false"
|
||||
>
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="security-allowed-uris-list">
|
||||
<div *ngFor="let uri of originsList" class="uri-line" [ngClass]="{ disabled: !enabled }">
|
||||
<span class="uri">{{ uri }}</span>
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<button matTooltip="{{ 'ACTIONS.DELETE' | translate }}" mat-icon-button (click)="remove(uri)" class="icon-button">
|
||||
<mat-icon class="icon">cancel</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="general-btn-container">
|
||||
<button
|
||||
class="save-button"
|
||||
(click)="savePolicy()"
|
||||
color="primary"
|
||||
type="submit"
|
||||
mat-raised-button
|
||||
[disabled]="(['iam.policy.write'] | hasRole | async) === false"
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -0,0 +1,97 @@
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
@mixin security-policy-theme($theme) {
|
||||
$foreground: map-get($theme, foreground);
|
||||
$background: map-get($theme, background);
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
$warn: map-get($theme, warn);
|
||||
$warn-color: map-get($warn, 500);
|
||||
$button-text-color: map-get($foreground, text);
|
||||
$button-disabled-text-color: map-get($foreground, disabled-button);
|
||||
$divider-color: map-get($foreground, dividers);
|
||||
$secondary-text: map-get($foreground, secondary-text);
|
||||
|
||||
.security-wrapper {
|
||||
max-width: 500px;
|
||||
|
||||
.security-policy-toggle {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.security-allowed-uris-list {
|
||||
width: 100%;
|
||||
|
||||
.uri-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0.5rem 0;
|
||||
padding: 0 0 0 0.75rem;
|
||||
border-radius: 4px;
|
||||
background: map-get($background, infosection);
|
||||
height: 30px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.uri {
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
font-size: 14px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
|
||||
.icon {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
&:not(:hover) {
|
||||
color: $secondary-text;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
|
||||
.icon-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.security-allowed-originsform {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
min-width: 320px;
|
||||
|
||||
.formfield {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-bottom: 14px;
|
||||
margin-right: -0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.spinner-wr {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.general-btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-top: 1rem;
|
||||
|
||||
.save-button {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SecurityPolicyComponent } from './security-policy.component';
|
||||
|
||||
describe('SecurityPolicyComponent', () => {
|
||||
let component: SecurityPolicyComponent;
|
||||
let fixture: ComponentFixture<SecurityPolicyComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [SecurityPolicyComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SecurityPolicyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,95 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { UntypedFormControl } from '@angular/forms';
|
||||
import { MatCheckboxChange } from '@angular/material/checkbox';
|
||||
import { SetDefaultLanguageResponse, SetSecurityPolicyRequest } from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
import { InfoSectionType } from '../../info-section/info-section.component';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-security-policy',
|
||||
templateUrl: './security-policy.component.html',
|
||||
styleUrls: ['./security-policy.component.scss'],
|
||||
})
|
||||
export class SecurityPolicyComponent implements OnInit {
|
||||
public originsList: string[] = [];
|
||||
public enabled: boolean = false;
|
||||
|
||||
public loading: boolean = false;
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
|
||||
@Input() public originsControl: UntypedFormControl = new UntypedFormControl({ value: [], disabled: true });
|
||||
|
||||
constructor(private service: AdminService, private toast: ToastService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
private fetchData(): void {
|
||||
this.service.getSecurityPolicy().then((securityPolicy) => {
|
||||
if (securityPolicy.policy) {
|
||||
this.enabled = securityPolicy.policy?.enableIframeEmbedding;
|
||||
this.originsList = securityPolicy.policy?.allowedOriginsList;
|
||||
if (securityPolicy.policy.enableIframeEmbedding) {
|
||||
this.originsControl.enable();
|
||||
} else {
|
||||
this.originsControl.disable();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private updateData(): Promise<SetDefaultLanguageResponse.AsObject> {
|
||||
const req = new SetSecurityPolicyRequest();
|
||||
req.setAllowedOriginsList(this.originsList);
|
||||
req.setEnableIframeEmbedding(this.enabled);
|
||||
return (this.service as AdminService).setSecurityPolicy(req);
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
const prom = this.updateData();
|
||||
this.loading = true;
|
||||
if (prom) {
|
||||
prom
|
||||
.then(() => {
|
||||
this.toast.showInfo('POLICY.SECURITY_POLICY.SAVED', true);
|
||||
this.loading = false;
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.loading = false;
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public add(input: any): void {
|
||||
if (this.originsControl.valid) {
|
||||
if (input.value !== '' && input.value !== ' ' && input.value !== '/') {
|
||||
this.originsList.push(input.value);
|
||||
}
|
||||
if (input) {
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public remove(redirect: any): void {
|
||||
const index = this.originsList.indexOf(redirect);
|
||||
|
||||
if (index >= 0) {
|
||||
this.originsList.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public enabledChanged(event: MatCheckboxChange) {
|
||||
if (event.checked) {
|
||||
this.originsControl.enable();
|
||||
} else {
|
||||
this.originsControl.disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
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 { SecurityPolicyComponent } from './security-policy.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [SecurityPolicyComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
CardModule,
|
||||
FormsModule,
|
||||
InfoSectionModule,
|
||||
MatCheckboxModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatButtonModule,
|
||||
FormFieldModule,
|
||||
InputModule,
|
||||
MatIconModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatSelectModule,
|
||||
HasRolePipeModule,
|
||||
MatTooltipModule,
|
||||
TranslateModule,
|
||||
],
|
||||
exports: [SecurityPolicyComponent],
|
||||
})
|
||||
export class SecurityPolicyModule {}
|
||||
@@ -35,7 +35,9 @@
|
||||
<ng-container *ngIf="currentSetting === 'secrets' && serviceType === PolicyComponentServiceType.ADMIN">
|
||||
<cnsl-secret-generator></cnsl-secret-generator>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentSetting === 'security' && serviceType === PolicyComponentServiceType.ADMIN">
|
||||
<cnsl-security-policy></cnsl-security-policy>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="currentSetting === 'branding'">
|
||||
<cnsl-private-labeling-policy [serviceType]="serviceType"></cnsl-private-labeling-policy>
|
||||
</ng-container>
|
||||
|
||||
@@ -18,6 +18,7 @@ import { PasswordLockoutPolicyModule } from '../policies/password-lockout-policy
|
||||
import { PrivacyPolicyModule } from '../policies/privacy-policy/privacy-policy.module';
|
||||
import { PrivateLabelingPolicyModule } from '../policies/private-labeling-policy/private-labeling-policy.module';
|
||||
import { SecretGeneratorModule } from '../policies/secret-generator/secret-generator.module';
|
||||
import { SecurityPolicyModule } from '../policies/security-policy/security-policy.module';
|
||||
import { SidenavModule } from '../sidenav/sidenav.module';
|
||||
import { SettingsListComponent } from './settings-list.component';
|
||||
|
||||
@@ -36,6 +37,7 @@ import { SettingsListComponent } from './settings-list.component';
|
||||
IdpSettingsModule,
|
||||
PrivacyPolicyModule,
|
||||
MessageTextsPolicyModule,
|
||||
SecurityPolicyModule,
|
||||
LoginTextsPolicyModule,
|
||||
DomainPolicyModule,
|
||||
TranslateModule,
|
||||
|
||||
@@ -25,6 +25,14 @@ export const SECRETS: SidenavSetting = {
|
||||
},
|
||||
};
|
||||
|
||||
export const SECURITY: SidenavSetting = {
|
||||
id: 'security',
|
||||
i18nKey: 'SETTINGS.LIST.SECURITY',
|
||||
requiredRoles: {
|
||||
[PolicyComponentServiceType.ADMIN]: ['iam.policy.read'],
|
||||
},
|
||||
};
|
||||
|
||||
export const LOGIN: SidenavSetting = {
|
||||
id: 'login',
|
||||
i18nKey: 'SETTINGS.LIST.LOGIN',
|
||||
|
||||
Reference in New Issue
Block a user