mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 21:37:24 +00:00
feat: add notification policy and password change message (#5065)
Implementation of new notification policy with functionality to send email when a password is changed
This commit is contained in:
parent
8b5894c0bb
commit
19621acfd3
@ -436,6 +436,8 @@ DefaultInstance:
|
||||
TOSLink: https://zitadel.com/docs/legal/terms-of-service
|
||||
PrivacyLink: https://zitadel.com/docs/legal/privacy-policy
|
||||
HelpLink: ""
|
||||
NotificationPolicy:
|
||||
PasswordChange: true
|
||||
LabelPolicy:
|
||||
PrimaryColor: "#5469d4"
|
||||
BackgroundColor: "#fafafa"
|
||||
@ -514,6 +516,14 @@ DefaultInstance:
|
||||
Greeting: Hallo {{.FirstName}} {{.LastName}},
|
||||
Text: Die Domain {{.Domain}} wurde von einer Organisation beansprucht. Dein derzeitiger User {{.Username}} ist nicht Teil dieser Organisation. Daher musst du beim nächsten Login eine neue Email hinterlegen. Für diesen Login haben wir dir einen temporären Usernamen ({{.TempUsername}}) erstellt.
|
||||
ButtonText: Login
|
||||
- MessageTextType: PasswordChange
|
||||
Language: de
|
||||
Title: ZITADEL - Passwort von Benutzer wurde geändert
|
||||
PreHeader: Passwort Änderung
|
||||
Subject: Passwort von Benutzer wurde geändert
|
||||
Greeting: Hallo {{.FirstName}} {{.LastName}},
|
||||
Text: Das Password vom Benutzer wurde geändert. Wenn diese Änderung von jemand anderem gemacht wurde, empfehlen wir die sofortige Zurücksetzung ihres Passworts.
|
||||
ButtonText: Login
|
||||
- MessageTextType: InitCode
|
||||
Language: en
|
||||
Title: Zitadel - Initialize User
|
||||
@ -554,6 +564,14 @@ DefaultInstance:
|
||||
Greeting: Hello {{.FirstName}} {{.LastName}},
|
||||
Text: The domain {{.Domain}} has been claimed by an organisation. Your current user {{.UserName}} is not part of this organisation. Therefore you'll have to change your email when you login. We have created a temporary username ({{.TempUsername}}) for this login.
|
||||
ButtonText: Login
|
||||
- MessageTextType: PasswordChange
|
||||
Language: en
|
||||
Title: ZITADEL - Password of user has changed
|
||||
PreHeader: Change password
|
||||
Subject: Password of user has changed
|
||||
Greeting: Hello {{.FirstName}} {{.LastName}},
|
||||
Text: The password of your user has changed. If this change was not done by you, please be advised to immediately reset your password.
|
||||
ButtonText: Login
|
||||
|
||||
InternalAuthZ:
|
||||
RolePermissionMappings:
|
||||
|
@ -3,31 +3,39 @@ import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
|
||||
import { MatLegacySelectChange as MatSelectChange } from '@angular/material/legacy-select';
|
||||
import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
|
||||
import {
|
||||
GetCustomPasswordResetMessageTextRequest as AdminGetCustomPasswordResetMessageTextRequest,
|
||||
GetDefaultInitMessageTextRequest as AdminGetDefaultInitMessageTextRequest,
|
||||
GetDefaultVerifyEmailMessageTextRequest as AdminGetDefaultVerifyEmailMessageTextRequest,
|
||||
GetDefaultVerifyPhoneMessageTextRequest as AdminGetDefaultVerifyPhoneMessageTextRequest,
|
||||
SetDefaultDomainClaimedMessageTextRequest,
|
||||
SetDefaultInitMessageTextRequest,
|
||||
SetDefaultPasswordChangeMessageTextRequest,
|
||||
SetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||
SetDefaultPasswordResetMessageTextRequest,
|
||||
SetDefaultVerifyEmailMessageTextRequest,
|
||||
SetDefaultVerifyPhoneMessageTextRequest,
|
||||
GetDefaultPasswordChangeMessageTextRequest as AdminGetDefaultPasswordChangeMessageTextRequest,
|
||||
GetDefaultInitMessageTextRequest as AdminGetDefaultInitMessageTextRequest,
|
||||
GetDefaultVerifyEmailMessageTextRequest as AdminGetDefaultVerifyEmailMessageTextRequest,
|
||||
GetDefaultVerifyPhoneMessageTextRequest as AdminGetDefaultVerifyPhoneMessageTextRequest,
|
||||
GetDefaultPasswordResetMessageTextRequest as AdminGetDefaultPasswordResetMessageTextRequest,
|
||||
GetDefaultDomainClaimedMessageTextRequest as AdminGetDefaultDomainClaimedMessageTextRequest,
|
||||
GetDefaultPasswordlessRegistrationMessageTextRequest as AdminGetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
GetCustomDomainClaimedMessageTextRequest,
|
||||
GetCustomInitMessageTextRequest,
|
||||
GetCustomPasswordChangeMessageTextRequest,
|
||||
GetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
GetCustomPasswordResetMessageTextRequest,
|
||||
GetCustomVerifyEmailMessageTextRequest,
|
||||
GetCustomVerifyPhoneMessageTextRequest,
|
||||
GetDefaultDomainClaimedMessageTextRequest,
|
||||
GetDefaultInitMessageTextRequest,
|
||||
GetDefaultPasswordChangeMessageTextRequest,
|
||||
GetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||
GetDefaultPasswordResetMessageTextRequest,
|
||||
GetDefaultVerifyEmailMessageTextRequest,
|
||||
GetDefaultVerifyPhoneMessageTextRequest,
|
||||
SetCustomDomainClaimedMessageTextRequest,
|
||||
SetCustomInitMessageTextRequest,
|
||||
SetCustomPasswordChangeMessageTextRequest,
|
||||
SetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
SetCustomPasswordResetMessageTextRequest,
|
||||
SetCustomVerifyEmailMessageTextRequest,
|
||||
@ -50,12 +58,30 @@ enum MESSAGETYPES {
|
||||
PASSWORDRESET = 'PR',
|
||||
DOMAINCLAIMED = 'DC',
|
||||
PASSWORDLESS = 'PL',
|
||||
PASSWORDCHANGE = 'PC',
|
||||
}
|
||||
|
||||
const REQUESTMAP = {
|
||||
[PolicyComponentServiceType.MGMT]: {
|
||||
[MESSAGETYPES.PASSWORDCHANGE]: {
|
||||
get: new GetCustomPasswordChangeMessageTextRequest(),
|
||||
set: new SetCustomPasswordChangeMessageTextRequest(),
|
||||
getDefault: new GetDefaultPasswordChangeMessageTextRequest(),
|
||||
setFcn: (map: Partial<MessageCustomText.AsObject>): SetCustomPasswordChangeMessageTextRequest => {
|
||||
const req = new SetCustomPasswordChangeMessageTextRequest();
|
||||
req.setButtonText(map.buttonText ?? '');
|
||||
req.setFooterText(map.footerText ?? '');
|
||||
req.setGreeting(map.greeting ?? '');
|
||||
req.setPreHeader(map.preHeader ?? '');
|
||||
req.setSubject(map.subject ?? '');
|
||||
req.setText(map.text ?? '');
|
||||
req.setTitle(map.title ?? '');
|
||||
|
||||
return req;
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.INIT]: {
|
||||
get: new GetDefaultInitMessageTextRequest(),
|
||||
get: new GetCustomInitMessageTextRequest(),
|
||||
set: new SetCustomInitMessageTextRequest(),
|
||||
getDefault: new GetDefaultInitMessageTextRequest(),
|
||||
setFcn: (map: Partial<MessageCustomText.AsObject>): SetCustomInitMessageTextRequest => {
|
||||
@ -164,6 +190,22 @@ const REQUESTMAP = {
|
||||
},
|
||||
},
|
||||
[PolicyComponentServiceType.ADMIN]: {
|
||||
[MESSAGETYPES.PASSWORDCHANGE]: {
|
||||
get: new AdminGetDefaultPasswordChangeMessageTextRequest(),
|
||||
set: new SetDefaultPasswordChangeMessageTextRequest(),
|
||||
setFcn: (map: Partial<MessageCustomText.AsObject>): SetDefaultPasswordChangeMessageTextRequest => {
|
||||
const req = new SetDefaultPasswordChangeMessageTextRequest();
|
||||
req.setButtonText(map.buttonText ?? '');
|
||||
req.setFooterText(map.footerText ?? '');
|
||||
req.setGreeting(map.greeting ?? '');
|
||||
req.setPreHeader(map.preHeader ?? '');
|
||||
req.setSubject(map.subject ?? '');
|
||||
req.setText(map.text ?? '');
|
||||
req.setTitle(map.title ?? '');
|
||||
|
||||
return req;
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.INIT]: {
|
||||
get: new AdminGetDefaultInitMessageTextRequest(),
|
||||
set: new SetDefaultInitMessageTextRequest(),
|
||||
@ -213,7 +255,7 @@ const REQUESTMAP = {
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.PASSWORDRESET]: {
|
||||
get: new AdminGetCustomPasswordResetMessageTextRequest(),
|
||||
get: new AdminGetDefaultPasswordResetMessageTextRequest(),
|
||||
set: new SetDefaultPasswordResetMessageTextRequest(),
|
||||
setFcn: (
|
||||
map: Partial<SetDefaultPasswordResetMessageTextRequest.AsObject>,
|
||||
@ -231,7 +273,7 @@ const REQUESTMAP = {
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.DOMAINCLAIMED]: {
|
||||
get: new GetDefaultDomainClaimedMessageTextRequest(),
|
||||
get: new AdminGetDefaultDomainClaimedMessageTextRequest(),
|
||||
set: new SetDefaultDomainClaimedMessageTextRequest(),
|
||||
setFcn: (
|
||||
map: Partial<SetDefaultDomainClaimedMessageTextRequest.AsObject>,
|
||||
@ -249,7 +291,7 @@ const REQUESTMAP = {
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.PASSWORDLESS]: {
|
||||
get: new GetDefaultPasswordlessRegistrationMessageTextRequest(),
|
||||
get: new AdminGetDefaultPasswordlessRegistrationMessageTextRequest(),
|
||||
set: new SetDefaultPasswordlessRegistrationMessageTextRequest(),
|
||||
setFcn: (
|
||||
map: Partial<SetDefaultPasswordlessRegistrationMessageTextRequest.AsObject>,
|
||||
@ -382,6 +424,20 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.loginnames', value: '{{.LoginNames}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.changedate', value: '{{.ChangeDate}}' },
|
||||
],
|
||||
[MESSAGETYPES.PASSWORDCHANGE]: [
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.preferredLoginName', value: '{{.PreferredLoginName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.username', value: '{{.UserName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.firstname', value: '{{.FirstName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastname', value: '{{.Lastname}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.nickName', value: '{{.NickName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.displayName', value: '{{.DisplayName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastEmail', value: '{{.LastEmail}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.verifiedEmail', value: '{{.VerifiedEmail}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastPhone', value: '{{.LastPhone}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.verifiedPhone', value: '{{.VerifiedPhone}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.loginnames', value: '{{.LoginNames}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.changedate', value: '{{.ChangeDate}}' },
|
||||
],
|
||||
};
|
||||
|
||||
public locale: string = 'en';
|
||||
@ -435,6 +491,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return this.stripDetails(this.service.getDefaultDomainClaimedMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return this.stripDetails(this.service.getDefaultPasswordlessRegistrationMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return this.stripDetails(this.service.getDefaultPasswordChangeMessageText(req));
|
||||
}
|
||||
}
|
||||
|
||||
@ -453,6 +511,9 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return this.stripDetails((this.service as ManagementService).getCustomDomainClaimedMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return this.stripDetails((this.service as ManagementService).getCustomPasswordlessRegistrationMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return this.stripDetails((this.service as ManagementService).getCustomPasswordChangeMessageText(req));
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
@ -470,6 +531,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return this.stripDetails((this.service as AdminService).getCustomDomainClaimedMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return this.stripDetails((this.service as AdminService).getCustomPasswordlessRegistrationMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return this.stripDetails((this.service as AdminService).getCustomPasswordChangeMessageText(req));
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
@ -535,6 +598,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return handler(
|
||||
(this.service as ManagementService).getCustomPasswordlessRegistrationMessageText(this.updateRequest),
|
||||
);
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return handler((this.service as ManagementService).getCustomPasswordChangeMessageText(this.updateRequest));
|
||||
}
|
||||
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||
switch (this.currentType) {
|
||||
@ -550,6 +615,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return handler((this.service as AdminService).setDefaultDomainClaimedMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return handler((this.service as AdminService).setDefaultPasswordlessRegistrationMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return handler((this.service as AdminService).setDefaultPasswordChangeMessageText(this.updateRequest));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -595,6 +662,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return handler(
|
||||
(this.service as ManagementService).resetCustomPasswordlessRegistrationMessageTextToDefault(this.locale),
|
||||
);
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return handler((this.service as ManagementService).resetCustomPasswordChangeMessageTextToDefault(this.locale));
|
||||
default:
|
||||
return Promise.reject();
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
<h2>{{ 'POLICY.NOTIFICATION.TITLE' | translate }}</h2>
|
||||
<p class="cnsl-secondary-text">{{ 'POLICY.NOTIFICATION.DESCRIPTION' | translate }}</p>
|
||||
|
||||
<div *ngIf="loading" class="spinner-wr">
|
||||
<mat-spinner diameter="30" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['policy.delete']">
|
||||
<button
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{ 'POLICY.RESET' | translate }}"
|
||||
color="warn"
|
||||
(click)="removePolicy()"
|
||||
mat-stroked-button
|
||||
>
|
||||
{{ 'POLICY.RESET' | translate }}
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<div class="notification-policy-card">
|
||||
<cnsl-card *ngIf="notificationData">
|
||||
<div class="notification-policy-content">
|
||||
<div class="row">
|
||||
<mat-checkbox
|
||||
class="slide-toggle"
|
||||
color="primary"
|
||||
name="hasUppercase"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="notificationData.passwordChange"
|
||||
[disabled]="(['policy.write'] | hasRole | async) === false"
|
||||
>
|
||||
{{ 'POLICY.NOTIFICATION.PASSWORDCHANGE' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button
|
||||
(click)="savePolicy()"
|
||||
[disabled]="(['policy.write'] | hasRole | async) === false"
|
||||
color="primary"
|
||||
type="submit"
|
||||
mat-raised-button
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
@ -0,0 +1,32 @@
|
||||
.spinner-wr {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.policy-applied-to {
|
||||
margin: -1rem 0 0 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.notification-policy-card {
|
||||
max-width: 400px;
|
||||
|
||||
.notification-policy-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
button {
|
||||
display: block;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { PasswordComplexityPolicyComponent } from './password-complexity-policy.component';
|
||||
|
||||
describe('PasswordComplexityPolicyComponent', () => {
|
||||
let component: PasswordComplexityPolicyComponent;
|
||||
let fixture: ComponentFixture<PasswordComplexityPolicyComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [PasswordComplexityPolicyComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PasswordComplexityPolicyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,177 @@
|
||||
import { Component, Injector, Input, OnInit, Type } from '@angular/core';
|
||||
import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
|
||||
import {
|
||||
AddNotificationPolicyRequest,
|
||||
GetNotificationPolicyResponse as AdminGetNotificationPolicyResponse,
|
||||
UpdateNotificationPolicyRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
AddCustomNotificationPolicyRequest,
|
||||
GetNotificationPolicyResponse as MgmtGetNotificationPolicyResponse,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { NotificationPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.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';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-notification-policy',
|
||||
templateUrl: './notification-policy.component.html',
|
||||
styleUrls: ['./notification-policy.component.scss'],
|
||||
})
|
||||
export class NotificationPolicyComponent implements OnInit {
|
||||
@Input() public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
public service!: ManagementService | AdminService;
|
||||
|
||||
public notificationData?: NotificationPolicy.AsObject = { isDefault: false, passwordChange: false };
|
||||
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
|
||||
public loading: boolean = false;
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
|
||||
public isDefault: boolean = false;
|
||||
private hasNotificationPolicy: boolean = false;
|
||||
constructor(private toast: ToastService, private injector: Injector, private dialog: MatDialog) {}
|
||||
|
||||
public ngOnInit(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
break;
|
||||
}
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.loading = true;
|
||||
|
||||
this.getData()
|
||||
.then((data) => {
|
||||
if (data.policy) {
|
||||
this.hasNotificationPolicy = true;
|
||||
this.notificationData = data.policy;
|
||||
this.isDefault = data.policy.isDefault;
|
||||
this.loading = false;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.loading = false;
|
||||
if (error && error.code === 5) {
|
||||
console.log(error);
|
||||
this.hasNotificationPolicy = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(): Promise<
|
||||
MgmtGetNotificationPolicyResponse.AsObject | AdminGetNotificationPolicyResponse.AsObject
|
||||
> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getNotificationPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getNotificationPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
public removePolicy(): void {
|
||||
if (this.service instanceof ManagementService) {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.RESET',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'SETTING.DIALOG.RESET.DEFAULTTITLE',
|
||||
descriptionKey: 'SETTING.DIALOG.RESET.DEFAULTDESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp) {
|
||||
(this.service as ManagementService)
|
||||
.resetNotificationPolicyToDefault()
|
||||
.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
this.isDefault = true;
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
if (this.notificationData) {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.notificationData as NotificationPolicy.AsObject).isDefault) {
|
||||
const req = new AddCustomNotificationPolicyRequest();
|
||||
req.setPasswordChange(this.notificationData.passwordChange);
|
||||
(this.service as ManagementService)
|
||||
.addCustomNotificationPolicy(req)
|
||||
.then(() => {
|
||||
this.isDefault = false;
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
const req = new UpdateNotificationPolicyRequest();
|
||||
req.setPasswordChange(this.notificationData.passwordChange);
|
||||
(this.service as ManagementService)
|
||||
.updateCustomNotificationPolicy(req)
|
||||
.then(() => {
|
||||
this.isDefault = false;
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
if (this.hasNotificationPolicy) {
|
||||
const req = new UpdateNotificationPolicyRequest();
|
||||
req.setPasswordChange(this.notificationData.passwordChange);
|
||||
(this.service as AdminService)
|
||||
.updateNotificationPolicy(req)
|
||||
.then(() => {
|
||||
this.isDefault = false;
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
const req = new AddNotificationPolicyRequest();
|
||||
req.setPasswordChange(this.notificationData.passwordChange);
|
||||
(this.service as AdminService)
|
||||
.addNotificationPolicy(req)
|
||||
.then(() => {
|
||||
this.isDefault = false;
|
||||
this.hasNotificationPolicy = true;
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
|
||||
import { MatLegacyCheckboxModule as MatCheckboxModule } from '@angular/material/legacy-checkbox';
|
||||
import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner';
|
||||
import { MatLegacyTooltipModule as MatTooltipModule } from '@angular/material/legacy-tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
|
||||
import { CardModule } from '../../card/card.module';
|
||||
import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
import { WarnDialogModule } from '../../warn-dialog/warn-dialog.module';
|
||||
import { NotificationPolicyComponent } from './notification-policy.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [NotificationPolicyComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
InputModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
MatDialogModule,
|
||||
MatTooltipModule,
|
||||
MatCheckboxModule,
|
||||
HasRolePipeModule,
|
||||
TranslateModule,
|
||||
WarnDialogModule,
|
||||
DetailLayoutModule,
|
||||
CardModule,
|
||||
MatProgressSpinnerModule,
|
||||
InfoSectionModule,
|
||||
],
|
||||
exports: [NotificationPolicyComponent],
|
||||
})
|
||||
export class NotificationPolicyModule {}
|
@ -1,20 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { PasswordComplexityPolicyComponent } from './password-complexity-policy.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: PasswordComplexityPolicyComponent,
|
||||
data: {
|
||||
animation: 'DetailPage',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class PasswordComplexityPolicyRoutingModule {}
|
@ -16,13 +16,11 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
|
||||
import { CardModule } from '../../card/card.module';
|
||||
import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
import { WarnDialogModule } from '../../warn-dialog/warn-dialog.module';
|
||||
import { PasswordComplexityPolicyRoutingModule } from './password-complexity-policy-routing.module';
|
||||
import { PasswordComplexityPolicyComponent } from './password-complexity-policy.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [PasswordComplexityPolicyComponent],
|
||||
imports: [
|
||||
PasswordComplexityPolicyRoutingModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
InputModule,
|
||||
|
@ -25,9 +25,14 @@
|
||||
<cnsl-idp-settings [serviceType]="serviceType"></cnsl-idp-settings>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="currentSetting === 'notifications' && serviceType === PolicyComponentServiceType.ADMIN">
|
||||
<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 === 'oidc' && serviceType === PolicyComponentServiceType.ADMIN">
|
||||
<cnsl-oidc-configuration></cnsl-oidc-configuration>
|
||||
</ng-container>
|
||||
|
@ -11,6 +11,7 @@ import { IdpSettingsModule } from '../policies/idp-settings/idp-settings.module'
|
||||
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 { OIDCConfigurationModule } from '../policies/oidc-configuration/oidc-configuration.module';
|
||||
import { PasswordComplexityPolicyModule } from '../policies/password-complexity-policy/password-complexity-policy.module';
|
||||
@ -34,6 +35,7 @@ import { SettingsListComponent } from './settings-list.component';
|
||||
PasswordLockoutPolicyModule,
|
||||
PrivateLabelingPolicyModule,
|
||||
GeneralSettingsModule,
|
||||
NotificationPolicyModule,
|
||||
IdpSettingsModule,
|
||||
PrivacyPolicyModule,
|
||||
MessageTextsPolicyModule,
|
||||
|
@ -92,6 +92,15 @@ export const NOTIFICATIONS: SidenavSetting = {
|
||||
},
|
||||
};
|
||||
|
||||
export const NOTIFICATION_POLICY: SidenavSetting = {
|
||||
id: 'notifications',
|
||||
i18nKey: 'SETTINGS.LIST.NOTIFICATIONS',
|
||||
groupI18nKey: 'SETTINGS.GROUPS.NOTIFICATIONS',
|
||||
requiredRoles: {
|
||||
[PolicyComponentServiceType.MGMT]: ['policy.read'],
|
||||
},
|
||||
};
|
||||
|
||||
export const MESSAGETEXTS: SidenavSetting = {
|
||||
id: 'messagetexts',
|
||||
i18nKey: 'SETTINGS.LIST.MESSAGETEXTS',
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
DOMAIN,
|
||||
IDP,
|
||||
LOCKOUT,
|
||||
NOTIFICATION_POLICY,
|
||||
LOGIN,
|
||||
LOGINTEXTS,
|
||||
MESSAGETEXTS,
|
||||
@ -30,6 +31,7 @@ export class OrgSettingsComponent {
|
||||
IDP,
|
||||
COMPLEXITY,
|
||||
LOCKOUT,
|
||||
NOTIFICATION_POLICY,
|
||||
DOMAIN,
|
||||
BRANDING,
|
||||
MESSAGETEXTS,
|
||||
|
@ -204,6 +204,18 @@ import {
|
||||
GetSecurityPolicyResponse,
|
||||
SetSecurityPolicyRequest,
|
||||
SetSecurityPolicyResponse,
|
||||
GetNotificationPolicyRequest,
|
||||
GetNotificationPolicyResponse,
|
||||
UpdateNotificationPolicyRequest,
|
||||
UpdateNotificationPolicyResponse,
|
||||
GetDefaultPasswordChangeMessageTextResponse,
|
||||
GetDefaultPasswordChangeMessageTextRequest,
|
||||
GetCustomPasswordChangeMessageTextResponse,
|
||||
SetDefaultPasswordChangeMessageTextRequest,
|
||||
SetDefaultPasswordChangeMessageTextResponse,
|
||||
GetCustomPasswordChangeMessageTextRequest,
|
||||
AddNotificationPolicyRequest,
|
||||
AddNotificationPolicyResponse,
|
||||
} from '../proto/generated/zitadel/admin_pb';
|
||||
import { SearchQuery } from '../proto/generated/zitadel/member_pb';
|
||||
import { ListQuery } from '../proto/generated/zitadel/object_pb';
|
||||
@ -346,6 +358,24 @@ export class AdminService {
|
||||
return this.grpcService.admin.setDefaultPasswordlessRegistrationMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultPasswordChangeMessageText(
|
||||
req: GetDefaultPasswordChangeMessageTextRequest,
|
||||
): Promise<GetDefaultPasswordChangeMessageTextResponse.AsObject> {
|
||||
return this.grpcService.admin.getDefaultPasswordChangeMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getCustomPasswordChangeMessageText(
|
||||
req: GetCustomPasswordChangeMessageTextRequest,
|
||||
): Promise<GetCustomPasswordChangeMessageTextResponse.AsObject> {
|
||||
return this.grpcService.admin.getCustomPasswordChangeMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public setDefaultPasswordChangeMessageText(
|
||||
req: SetDefaultPasswordChangeMessageTextRequest,
|
||||
): Promise<SetDefaultPasswordChangeMessageTextResponse.AsObject> {
|
||||
return this.grpcService.admin.setDefaultPasswordChangeMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public SetUpOrg(org: SetUpOrgRequest.Org, human: SetUpOrgRequest.Human): Promise<SetUpOrgResponse.AsObject> {
|
||||
const req = new SetUpOrgRequest();
|
||||
|
||||
@ -484,6 +514,21 @@ export class AdminService {
|
||||
return this.grpcService.admin.setDefaultLanguage(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
/* notification policy */
|
||||
|
||||
public getNotificationPolicy(): Promise<GetNotificationPolicyResponse.AsObject> {
|
||||
const req = new GetNotificationPolicyRequest();
|
||||
return this.grpcService.admin.getNotificationPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateNotificationPolicy(req: UpdateNotificationPolicyRequest): Promise<UpdateNotificationPolicyResponse.AsObject> {
|
||||
return this.grpcService.admin.updateNotificationPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public addNotificationPolicy(req: AddNotificationPolicyRequest): Promise<AddNotificationPolicyResponse.AsObject> {
|
||||
return this.grpcService.admin.addNotificationPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
/* security policy */
|
||||
|
||||
public getSecurityPolicy(): Promise<GetSecurityPolicyResponse.AsObject> {
|
||||
|
@ -24,6 +24,8 @@ import {
|
||||
AddCustomLockoutPolicyResponse,
|
||||
AddCustomLoginPolicyRequest,
|
||||
AddCustomLoginPolicyResponse,
|
||||
AddCustomNotificationPolicyRequest,
|
||||
AddCustomNotificationPolicyResponse,
|
||||
AddCustomPasswordAgePolicyRequest,
|
||||
AddCustomPasswordAgePolicyResponse,
|
||||
AddCustomPasswordComplexityPolicyRequest,
|
||||
@ -108,6 +110,8 @@ import {
|
||||
GetCustomInitMessageTextResponse,
|
||||
GetCustomLoginTextsRequest,
|
||||
GetCustomLoginTextsResponse,
|
||||
GetCustomPasswordChangeMessageTextRequest,
|
||||
GetCustomPasswordChangeMessageTextResponse,
|
||||
GetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
GetCustomPasswordlessRegistrationMessageTextResponse,
|
||||
GetCustomPasswordResetMessageTextRequest,
|
||||
@ -124,6 +128,8 @@ import {
|
||||
GetDefaultLabelPolicyResponse,
|
||||
GetDefaultLoginTextsRequest,
|
||||
GetDefaultLoginTextsResponse,
|
||||
GetDefaultPasswordChangeMessageTextRequest,
|
||||
GetDefaultPasswordChangeMessageTextResponse,
|
||||
GetDefaultPasswordComplexityPolicyRequest,
|
||||
GetDefaultPasswordComplexityPolicyResponse,
|
||||
GetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||
@ -156,6 +162,8 @@ import {
|
||||
GetLoginPolicyResponse,
|
||||
GetMyOrgRequest,
|
||||
GetMyOrgResponse,
|
||||
GetNotificationPolicyRequest,
|
||||
GetNotificationPolicyResponse,
|
||||
GetOIDCInformationRequest,
|
||||
GetOIDCInformationResponse,
|
||||
GetOrgByDomainGlobalRequest,
|
||||
@ -343,6 +351,8 @@ import {
|
||||
ResetCustomInitMessageTextToDefaultResponse,
|
||||
ResetCustomLoginTextsToDefaultRequest,
|
||||
ResetCustomLoginTextsToDefaultResponse,
|
||||
ResetCustomPasswordChangeMessageTextToDefaultRequest,
|
||||
ResetCustomPasswordChangeMessageTextToDefaultResponse,
|
||||
ResetCustomPasswordlessRegistrationMessageTextToDefaultRequest,
|
||||
ResetCustomPasswordlessRegistrationMessageTextToDefaultResponse,
|
||||
ResetCustomPasswordResetMessageTextToDefaultRequest,
|
||||
@ -357,6 +367,8 @@ import {
|
||||
ResetLockoutPolicyToDefaultResponse,
|
||||
ResetLoginPolicyToDefaultRequest,
|
||||
ResetLoginPolicyToDefaultResponse,
|
||||
ResetNotificationPolicyToDefaultRequest,
|
||||
ResetNotificationPolicyToDefaultResponse,
|
||||
ResetPasswordAgePolicyToDefaultRequest,
|
||||
ResetPasswordAgePolicyToDefaultResponse,
|
||||
ResetPasswordComplexityPolicyToDefaultRequest,
|
||||
@ -403,6 +415,8 @@ import {
|
||||
UpdateCustomLockoutPolicyResponse,
|
||||
UpdateCustomLoginPolicyRequest,
|
||||
UpdateCustomLoginPolicyResponse,
|
||||
UpdateCustomNotificationPolicyRequest,
|
||||
UpdateCustomNotificationPolicyResponse,
|
||||
UpdateCustomPasswordAgePolicyRequest,
|
||||
UpdateCustomPasswordAgePolicyResponse,
|
||||
UpdateCustomPasswordComplexityPolicyRequest,
|
||||
@ -631,6 +645,26 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.getDefaultPasswordlessRegistrationMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultPasswordChangeMessageText(
|
||||
req: GetDefaultPasswordChangeMessageTextRequest,
|
||||
): Promise<GetDefaultPasswordChangeMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt.getDefaultPasswordChangeMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getCustomPasswordChangeMessageText(
|
||||
req: GetCustomPasswordChangeMessageTextRequest,
|
||||
): Promise<GetCustomPasswordChangeMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt.getCustomPasswordChangeMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomPasswordChangeMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomPasswordChangeMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomPasswordChangeMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.mgmt.resetCustomPasswordChangeMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getCustomPasswordlessRegistrationMessageText(
|
||||
req: GetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
): Promise<GetCustomPasswordlessRegistrationMessageTextResponse.AsObject> {
|
||||
@ -1371,6 +1405,30 @@ export class ManagementService {
|
||||
}
|
||||
}
|
||||
|
||||
/* notification policy */
|
||||
|
||||
public getNotificationPolicy(): Promise<GetNotificationPolicyResponse.AsObject> {
|
||||
const req = new GetNotificationPolicyRequest();
|
||||
return this.grpcService.mgmt.getNotificationPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetNotificationPolicyToDefault(): Promise<ResetNotificationPolicyToDefaultResponse.AsObject> {
|
||||
const req = new ResetNotificationPolicyToDefaultRequest();
|
||||
return this.grpcService.mgmt.resetNotificationPolicyToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public addCustomNotificationPolicy(
|
||||
req: AddCustomNotificationPolicyRequest,
|
||||
): Promise<AddCustomNotificationPolicyResponse.AsObject> {
|
||||
return this.grpcService.mgmt.addCustomNotificationPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateCustomNotificationPolicy(
|
||||
req: UpdateCustomNotificationPolicyRequest,
|
||||
): Promise<UpdateCustomNotificationPolicyResponse.AsObject> {
|
||||
return this.grpcService.mgmt.updateCustomNotificationPolicy(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getUserByID(id: string): Promise<GetUserByIDResponse.AsObject> {
|
||||
const req = new GetUserByIDRequest();
|
||||
req.setId(id);
|
||||
|
@ -883,7 +883,7 @@
|
||||
"LOGIN": "Loginverhalten und Sicherheit",
|
||||
"LOCKOUT": "Sperrmechanismen",
|
||||
"COMPLEXITY": "Passwordkomplexität",
|
||||
"NOTIFICATIONS": "Benachrichtigungen",
|
||||
"NOTIFICATIONS": "Benachrichtigungseinstellungen",
|
||||
"NOTIFICATIONS_DESC": "SMTP und SMS Einstellungen",
|
||||
"MESSAGETEXTS": "Benachrichtigungstexte",
|
||||
"IDP": "Identity Provider",
|
||||
@ -1007,6 +1007,11 @@
|
||||
"NUMBERERROR": "Muss eine Ziffer beinhalten.",
|
||||
"PATTERNERROR": "Das Passwort erfüllt nicht die vorgeschriebene Richtlinie."
|
||||
},
|
||||
"NOTIFICATION": {
|
||||
"TITLE": "Notification",
|
||||
"DESCRIPTION": "Legt fest, bei welchen Änderungen Benachrichtigungen gesendet werden",
|
||||
"PASSWORDCHANGE": "Passwordänderung"
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE": "Branding",
|
||||
"DESCRIPTION": "Verleihen Sie dem Login Ihren benutzerdefinierten Style und passen Sie das Verhalten an.",
|
||||
@ -1146,7 +1151,8 @@
|
||||
"VP": "Telefonnummerverifikation",
|
||||
"PR": "Passwort Wiederherstellung",
|
||||
"DC": "Domainbeanspruchung",
|
||||
"PL": "Passwortlos"
|
||||
"PL": "Passwortlos",
|
||||
"PC": "Passwordwechsel"
|
||||
},
|
||||
"CHIPS": {
|
||||
"firstname": "Vorname",
|
||||
|
@ -883,7 +883,7 @@
|
||||
"LOGIN": "Login Behavior and Security",
|
||||
"LOCKOUT": "Lockout",
|
||||
"COMPLEXITY": "Password complexity",
|
||||
"NOTIFICATIONS": "Notification providers and SMTP",
|
||||
"NOTIFICATIONS": "Notification settings",
|
||||
"NOTIFICATIONS_DESC": "SMTP and SMS Settings",
|
||||
"MESSAGETEXTS": "Message Texts",
|
||||
"IDP": "Identity Providers",
|
||||
@ -1007,6 +1007,11 @@
|
||||
"NUMBERERROR": "Must include a digit.",
|
||||
"PATTERNERROR": "The password does not meet the required pattern."
|
||||
},
|
||||
"NOTIFICATION": {
|
||||
"TITLE": "Notification",
|
||||
"DESCRIPTION": "Determines on which changes, notifications will be sent.",
|
||||
"PASSWORDCHANGE": "Password change"
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE": "Branding",
|
||||
"DESCRIPTION": "Give the login your personalized style and modify its behavior.",
|
||||
@ -1146,7 +1151,8 @@
|
||||
"VP": "Verify Phone",
|
||||
"PR": "Password Reset",
|
||||
"DC": "Domain Claim",
|
||||
"PL": "Passwordless"
|
||||
"PL": "Passwordless",
|
||||
"PC": "Password Change"
|
||||
},
|
||||
"CHIPS": {
|
||||
"firstname": "Firstname",
|
||||
|
@ -883,7 +883,7 @@
|
||||
"LOGIN": "Comportement de connexion et sécurité",
|
||||
"LOCKOUT": "Verrouillage",
|
||||
"COMPLEXITY": "Complexité du mot de passe",
|
||||
"NOTIFICATIONS": "Fournisseurs de notifications et SMTP",
|
||||
"NOTIFICATIONS": "Paramètres de notification",
|
||||
"NOTIFICATIONS_DESC": "Paramètres SMTP et SMS",
|
||||
"MESSAGETEXTS": "Textes des messages",
|
||||
"IDP": "Fournisseurs d'identité",
|
||||
@ -1007,6 +1007,11 @@
|
||||
"NUMBERERROR": "Doit inclure un chiffre.",
|
||||
"PATTERNERROR": "Le mot de passe ne correspond pas au modèle requis."
|
||||
},
|
||||
"NOTIFICATION": {
|
||||
"TITLE": "Notifications",
|
||||
"DESCRIPTION": "Détermine sur quels changements, les notifications seront envoyées",
|
||||
"PASSWORDCHANGE": "Changement de mot de passe"
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE": "Image de marque",
|
||||
"DESCRIPTION": "Donnez au login votre style personnalisé et modifiez son comportement.",
|
||||
@ -1146,7 +1151,8 @@
|
||||
"VP": "Vérifier le téléphone",
|
||||
"PR": "Réinitialisation du mot de passe",
|
||||
"DC": "Réclamation de domaine",
|
||||
"PL": "Sans mot de passe"
|
||||
"PL": "Sans mot de passe",
|
||||
"PC": "Changement de mot de passe"
|
||||
},
|
||||
"CHIPS": {
|
||||
"firstname": "Prénom",
|
||||
|
@ -884,7 +884,7 @@
|
||||
"LOGIN": "Comportamento login e sicurezza",
|
||||
"LOCKOUT": "Meccanismi di bloccaggio",
|
||||
"COMPLEXITY": "Complessità della password",
|
||||
"NOTIFICATIONS": "Notifiche",
|
||||
"NOTIFICATIONS": "Impostazioni di notifica",
|
||||
"NOTIFICATIONS_DESC": "Impostazioni SMTP e SMS",
|
||||
"MESSAGETEXTS": "Testi di notifica",
|
||||
"IDP": "Identity Providers",
|
||||
@ -1008,6 +1008,11 @@
|
||||
"NUMBERERROR": "Deve includere una cifra.",
|
||||
"PATTERNERROR": "La password non corrisponde al modello richiesto."
|
||||
},
|
||||
"NOTIFICATION": {
|
||||
"TITLE": "Notifiche",
|
||||
"DESCRIPTION": "Determina su quali modifiche verranno inviate le notifiche",
|
||||
"PASSWORDCHANGE": "Cambiamento della password"
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE": "Branding",
|
||||
"DESCRIPTION": "Dai al login il tuo stile personalizzato e modifica il suo comportamento.",
|
||||
@ -1147,7 +1152,8 @@
|
||||
"VP": "Verificazione del telefono",
|
||||
"PR": "Ripristino della password",
|
||||
"DC": "Rivendicazione del dominio",
|
||||
"PL": "Autenticazione Passwordless"
|
||||
"PL": "Autenticazione Passwordless",
|
||||
"PC": "Cambiamento della password"
|
||||
},
|
||||
"CHIPS": {
|
||||
"firstname": "Nome",
|
||||
|
@ -883,7 +883,7 @@
|
||||
"LOGIN": "登录行为和安全",
|
||||
"LOCKOUT": "安全锁策略",
|
||||
"COMPLEXITY": "密码复杂性",
|
||||
"NOTIFICATIONS": "通知服务商",
|
||||
"NOTIFICATIONS": "通知设置",
|
||||
"NOTIFICATIONS_DESC": "SMTP 和 SMS 设置",
|
||||
"MESSAGETEXTS": "消息文本",
|
||||
"IDP": "身份提供者",
|
||||
@ -1007,6 +1007,11 @@
|
||||
"NUMBERERROR": "密码必须包含数字。",
|
||||
"PATTERNERROR": "密码不符合要求。"
|
||||
},
|
||||
"NOTIFICATION": {
|
||||
"TITLE": "通知",
|
||||
"DESCRIPTION": "确定将发送哪些更改、通知",
|
||||
"PASSWORDCHANGE": "更改密码"
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE": "品牌标识",
|
||||
"DESCRIPTION": "为登录提供您的个性化风格并修改其行为。",
|
||||
@ -1145,7 +1150,8 @@
|
||||
"VP": "验证手机号码",
|
||||
"PR": "重置密码",
|
||||
"DC": "域名声明",
|
||||
"PL": "无密码身份验证"
|
||||
"PL": "无密码身份验证",
|
||||
"PC": "修改密码"
|
||||
},
|
||||
"CHIPS": {
|
||||
"firstname": "名",
|
||||
|
@ -1072,6 +1072,44 @@ Variable {{.Lang}} can be set to have different links based on the language
|
||||
PUT: /policies/privacy
|
||||
|
||||
|
||||
### AddNotificationPolicy
|
||||
|
||||
> **rpc** AddNotificationPolicy([AddNotificationPolicyRequest](#addnotificationpolicyrequest))
|
||||
[AddNotificationPolicyResponse](#addnotificationpolicyresponse)
|
||||
|
||||
Add a default notification policy for ZITADEL
|
||||
it impacts all organisations without a customised policy
|
||||
|
||||
|
||||
|
||||
POST: /policies/notification
|
||||
|
||||
|
||||
### GetNotificationPolicy
|
||||
|
||||
> **rpc** GetNotificationPolicy([GetNotificationPolicyRequest](#getnotificationpolicyrequest))
|
||||
[GetNotificationPolicyResponse](#getnotificationpolicyresponse)
|
||||
|
||||
Returns the notification policy defined by the administrators of ZITADEL
|
||||
|
||||
|
||||
|
||||
GET: /policies/notification
|
||||
|
||||
|
||||
### UpdateNotificationPolicy
|
||||
|
||||
> **rpc** UpdateNotificationPolicy([UpdateNotificationPolicyRequest](#updatenotificationpolicyrequest))
|
||||
[UpdateNotificationPolicyResponse](#updatenotificationpolicyresponse)
|
||||
|
||||
Updates the default notification policy of ZITADEL
|
||||
it impacts all organisations without a customised policy
|
||||
|
||||
|
||||
|
||||
PUT: /policies/notification
|
||||
|
||||
|
||||
### GetDefaultInitMessageText
|
||||
|
||||
> **rpc** GetDefaultInitMessageText([GetDefaultInitMessageTextRequest](#getdefaultinitmessagetextrequest))
|
||||
@ -1104,7 +1142,7 @@ Returns the custom text for initial message (overwritten in eventstore)
|
||||
Sets the default custom text for initial message
|
||||
it impacts all organisations without customized initial message text
|
||||
The Following Variables can be used:
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
|
||||
|
||||
|
||||
@ -1156,7 +1194,7 @@ Returns the custom text for password reset message (overwritten in eventstore)
|
||||
Sets the default custom text for password reset message
|
||||
it impacts all organisations without customized password reset message text
|
||||
The Following Variables can be used:
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
|
||||
|
||||
|
||||
@ -1208,7 +1246,7 @@ Returns the custom text for verify email message (overwritten in eventstore)
|
||||
Sets the default custom text for verify email message
|
||||
it impacts all organisations without customized verify email message text
|
||||
The Following Variables can be used:
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
|
||||
|
||||
|
||||
@ -1260,7 +1298,7 @@ Returns the custom text for verify phone message
|
||||
Sets the default custom text for verify phone message
|
||||
it impacts all organisations without customized verify phone message text
|
||||
The Following Variables can be used:
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
|
||||
|
||||
|
||||
@ -1309,10 +1347,10 @@ Returns the custom text for domain claimed message (overwritten in eventstore)
|
||||
> **rpc** SetDefaultDomainClaimedMessageText([SetDefaultDomainClaimedMessageTextRequest](#setdefaultdomainclaimedmessagetextrequest))
|
||||
[SetDefaultDomainClaimedMessageTextResponse](#setdefaultdomainclaimedmessagetextresponse)
|
||||
|
||||
Sets the default custom text for domain claimed phone message
|
||||
Sets the default custom text for domain claimed message
|
||||
it impacts all organisations without customized domain claimed message text
|
||||
The Following Variables can be used:
|
||||
{{.Domain}} {{.TempUsername}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
{{.Domain}} {{.TempUsername}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
|
||||
|
||||
|
||||
@ -1364,7 +1402,7 @@ Returns the custom text for passwordless registration message (overwritten in ev
|
||||
Sets the default custom text for passwordless registration message
|
||||
it impacts all organisations without customized passwordless registration message text
|
||||
The Following Variables can be used:
|
||||
{{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
{{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
|
||||
|
||||
|
||||
@ -1384,6 +1422,58 @@ The default text from the translation file will trigger after
|
||||
DELETE: /text/message/passwordless_registration/{language}
|
||||
|
||||
|
||||
### GetDefaultPasswordChangeMessageText
|
||||
|
||||
> **rpc** GetDefaultPasswordChangeMessageText([GetDefaultPasswordChangeMessageTextRequest](#getdefaultpasswordchangemessagetextrequest))
|
||||
[GetDefaultPasswordChangeMessageTextResponse](#getdefaultpasswordchangemessagetextresponse)
|
||||
|
||||
Returns the default text for password change message (translation file)
|
||||
|
||||
|
||||
|
||||
GET: /text/default/message/password_change/{language}
|
||||
|
||||
|
||||
### GetCustomPasswordChangeMessageText
|
||||
|
||||
> **rpc** GetCustomPasswordChangeMessageText([GetCustomPasswordChangeMessageTextRequest](#getcustompasswordchangemessagetextrequest))
|
||||
[GetCustomPasswordChangeMessageTextResponse](#getcustompasswordchangemessagetextresponse)
|
||||
|
||||
Returns the custom text for password change message (overwritten in eventstore)
|
||||
|
||||
|
||||
|
||||
GET: /text/message/password_change/{language}
|
||||
|
||||
|
||||
### SetDefaultPasswordChangeMessageText
|
||||
|
||||
> **rpc** SetDefaultPasswordChangeMessageText([SetDefaultPasswordChangeMessageTextRequest](#setdefaultpasswordchangemessagetextrequest))
|
||||
[SetDefaultPasswordChangeMessageTextResponse](#setdefaultpasswordchangemessagetextresponse)
|
||||
|
||||
Sets the default custom text for password change message
|
||||
it impacts all organisations without customized password change message text
|
||||
The Following Variables can be used:
|
||||
{{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
|
||||
|
||||
|
||||
PUT: /text/message/password_change/{language}
|
||||
|
||||
|
||||
### ResetCustomPasswordChangeMessageTextToDefault
|
||||
|
||||
> **rpc** ResetCustomPasswordChangeMessageTextToDefault([ResetCustomPasswordChangeMessageTextToDefaultRequest](#resetcustompasswordchangemessagetexttodefaultrequest))
|
||||
[ResetCustomPasswordChangeMessageTextToDefaultResponse](#resetcustompasswordchangemessagetexttodefaultresponse)
|
||||
|
||||
Removes the custom password change message text of the system
|
||||
The default text from the translation file will trigger after
|
||||
|
||||
|
||||
|
||||
DELETE: /text/message/password_change/{language}
|
||||
|
||||
|
||||
### GetDefaultLoginTexts
|
||||
|
||||
> **rpc** GetDefaultLoginTexts([GetDefaultLoginTextsRequest](#getdefaultlogintextsrequest))
|
||||
@ -1793,6 +1883,28 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### AddNotificationPolicyRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| password_change | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### AddNotificationPolicyResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### AddOIDCIDPRequest
|
||||
|
||||
|
||||
@ -2210,6 +2322,28 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### GetCustomPasswordChangeMessageTextRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| language | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetCustomPasswordChangeMessageTextResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| custom_text | zitadel.text.v1.MessageCustomText | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetCustomPasswordResetMessageTextRequest
|
||||
|
||||
|
||||
@ -2398,6 +2532,28 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### GetDefaultPasswordChangeMessageTextRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| language | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetDefaultPasswordChangeMessageTextResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| custom_text | zitadel.text.v1.MessageCustomText | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetDefaultPasswordResetMessageTextRequest
|
||||
|
||||
|
||||
@ -2627,6 +2783,23 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### GetNotificationPolicyRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### GetNotificationPolicyResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| policy | zitadel.policy.v1.NotificationPolicy | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetOIDCSettingsRequest
|
||||
This is an empty request
|
||||
|
||||
@ -3839,6 +4012,28 @@ this is en empty request
|
||||
|
||||
|
||||
|
||||
### ResetCustomPasswordChangeMessageTextToDefaultRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| language | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### ResetCustomPasswordChangeMessageTextToDefaultResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ResetCustomPasswordResetMessageTextToDefaultRequest
|
||||
|
||||
|
||||
@ -4085,6 +4280,35 @@ this is en empty request
|
||||
|
||||
|
||||
|
||||
### SetDefaultPasswordChangeMessageTextRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| language | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| title | string | - | string.max_len: 200<br /> |
|
||||
| pre_header | string | - | string.max_len: 200<br /> |
|
||||
| subject | string | - | string.max_len: 200<br /> |
|
||||
| greeting | string | - | string.max_len: 200<br /> |
|
||||
| text | string | - | string.max_len: 800<br /> |
|
||||
| button_text | string | - | string.max_len: 200<br /> |
|
||||
| footer_text | string | - | string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### SetDefaultPasswordChangeMessageTextResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### SetDefaultPasswordResetMessageTextRequest
|
||||
|
||||
|
||||
@ -4581,6 +4805,28 @@ this is en empty request
|
||||
|
||||
|
||||
|
||||
### UpdateNotificationPolicyRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| password_change | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### UpdateNotificationPolicyResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### UpdateOIDCSettingsRequest
|
||||
|
||||
|
||||
|
@ -2231,7 +2231,7 @@ Variable {{.Lang}} can be set to have different links based on the language
|
||||
> **rpc** UpdateCustomPrivacyPolicy([UpdateCustomPrivacyPolicyRequest](#updatecustomprivacypolicyrequest))
|
||||
[UpdateCustomPrivacyPolicyResponse](#updatecustomprivacypolicyresponse)
|
||||
|
||||
Update the privacy complexity policy for the organisation
|
||||
Update the privacy policy for the organisation
|
||||
With this policy privacy relevant things can be configured (e.g. tos link)
|
||||
Variable {{.Lang}} can be set to have different links based on the language
|
||||
|
||||
@ -2253,6 +2253,71 @@ The default policy of the IAM will trigger after
|
||||
DELETE: /policies/privacy
|
||||
|
||||
|
||||
### GetNotificationPolicy
|
||||
|
||||
> **rpc** GetNotificationPolicy([GetNotificationPolicyRequest](#getnotificationpolicyrequest))
|
||||
[GetNotificationPolicyResponse](#getnotificationpolicyresponse)
|
||||
|
||||
Returns the notification policy of the organisation
|
||||
With this notification policy it can be configured how users should be notified
|
||||
|
||||
|
||||
|
||||
GET: /policies/notification
|
||||
|
||||
|
||||
### GetDefaultNotificationPolicy
|
||||
|
||||
> **rpc** GetDefaultNotificationPolicy([GetDefaultNotificationPolicyRequest](#getdefaultnotificationpolicyrequest))
|
||||
[GetDefaultNotificationPolicyResponse](#getdefaultnotificationpolicyresponse)
|
||||
|
||||
Returns the default notification policy of the IAM
|
||||
With this notification privacy it can be configured how users should be notified
|
||||
|
||||
|
||||
|
||||
GET: /policies/default/notification
|
||||
|
||||
|
||||
### AddCustomNotificationPolicy
|
||||
|
||||
> **rpc** AddCustomNotificationPolicy([AddCustomNotificationPolicyRequest](#addcustomnotificationpolicyrequest))
|
||||
[AddCustomNotificationPolicyResponse](#addcustomnotificationpolicyresponse)
|
||||
|
||||
Add a custom notification policy for the organisation
|
||||
With this notification privacy it can be configured how users should be notified
|
||||
|
||||
|
||||
|
||||
POST: /policies/notification
|
||||
|
||||
|
||||
### UpdateCustomNotificationPolicy
|
||||
|
||||
> **rpc** UpdateCustomNotificationPolicy([UpdateCustomNotificationPolicyRequest](#updatecustomnotificationpolicyrequest))
|
||||
[UpdateCustomNotificationPolicyResponse](#updatecustomnotificationpolicyresponse)
|
||||
|
||||
Update the notification policy for the organisation
|
||||
With this notification privacy it can be configured how users should be notified
|
||||
|
||||
|
||||
|
||||
PUT: /policies/notification
|
||||
|
||||
|
||||
### ResetNotificationPolicyToDefault
|
||||
|
||||
> **rpc** ResetNotificationPolicyToDefault([ResetNotificationPolicyToDefaultRequest](#resetnotificationpolicytodefaultrequest))
|
||||
[ResetNotificationPolicyToDefaultResponse](#resetnotificationpolicytodefaultresponse)
|
||||
|
||||
Removes the notification policy of the organisation
|
||||
The default policy of the IAM will trigger after
|
||||
|
||||
|
||||
|
||||
DELETE: /policies/notification
|
||||
|
||||
|
||||
### GetLabelPolicy
|
||||
|
||||
> **rpc** GetLabelPolicy([GetLabelPolicyRequest](#getlabelpolicyrequest))
|
||||
@ -2485,7 +2550,7 @@ Returns the default text for password reset message
|
||||
|
||||
Sets the custom text for password reset message
|
||||
The Following Variables can be used:
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
|
||||
|
||||
|
||||
@ -2536,7 +2601,7 @@ Returns the default text for verify email message
|
||||
|
||||
Sets the custom text for verify email message
|
||||
The Following Variables can be used:
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
|
||||
|
||||
|
||||
@ -2587,7 +2652,7 @@ Returns the custom text for verify email message
|
||||
|
||||
Sets the default custom text for verify email message
|
||||
The Following Variables can be used:
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
{{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
|
||||
|
||||
|
||||
@ -2638,7 +2703,7 @@ Returns the custom text for domain claimed message
|
||||
|
||||
Sets the custom text for domain claimed message
|
||||
The Following Variables can be used:
|
||||
{{.Domain}} {{.TempUsername}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
{{.Domain}} {{.TempUsername}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
|
||||
|
||||
|
||||
@ -2689,7 +2754,7 @@ Returns the custom text for passwordless link message
|
||||
|
||||
Sets the custom text for passwordless link message
|
||||
The Following Variables can be used:
|
||||
{{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
{{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
|
||||
|
||||
|
||||
@ -2709,6 +2774,57 @@ The default text of the IAM will trigger after
|
||||
DELETE: /text/message/passwordless_registration/{language}
|
||||
|
||||
|
||||
### GetCustomPasswordChangeMessageText
|
||||
|
||||
> **rpc** GetCustomPasswordChangeMessageText([GetCustomPasswordChangeMessageTextRequest](#getcustompasswordchangemessagetextrequest))
|
||||
[GetCustomPasswordChangeMessageTextResponse](#getcustompasswordchangemessagetextresponse)
|
||||
|
||||
Returns the custom text for password change message
|
||||
|
||||
|
||||
|
||||
GET: /text/message/password_change/{language}
|
||||
|
||||
|
||||
### GetDefaultPasswordChangeMessageText
|
||||
|
||||
> **rpc** GetDefaultPasswordChangeMessageText([GetDefaultPasswordChangeMessageTextRequest](#getdefaultpasswordchangemessagetextrequest))
|
||||
[GetDefaultPasswordChangeMessageTextResponse](#getdefaultpasswordchangemessagetextresponse)
|
||||
|
||||
Returns the custom text for password change link message
|
||||
|
||||
|
||||
|
||||
GET: /text/default/message/password_change/{language}
|
||||
|
||||
|
||||
### SetCustomPasswordChangeMessageCustomText
|
||||
|
||||
> **rpc** SetCustomPasswordChangeMessageCustomText([SetCustomPasswordChangeMessageTextRequest](#setcustompasswordchangemessagetextrequest))
|
||||
[SetCustomPasswordChangeMessageTextResponse](#setcustompasswordchangemessagetextresponse)
|
||||
|
||||
Sets the custom text for password change message
|
||||
The Following Variables can be used:
|
||||
{{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
|
||||
|
||||
|
||||
PUT: /text/message/password_change/{language}
|
||||
|
||||
|
||||
### ResetCustomPasswordChangeMessageTextToDefault
|
||||
|
||||
> **rpc** ResetCustomPasswordChangeMessageTextToDefault([ResetCustomPasswordChangeMessageTextToDefaultRequest](#resetcustompasswordchangemessagetexttodefaultrequest))
|
||||
[ResetCustomPasswordChangeMessageTextToDefaultResponse](#resetcustompasswordchangemessagetexttodefaultresponse)
|
||||
|
||||
Removes the custom password change message text of the organisation
|
||||
The default text of the IAM will trigger after
|
||||
|
||||
|
||||
|
||||
DELETE: /text/message/password_change/{language}
|
||||
|
||||
|
||||
### GetCustomLoginTexts
|
||||
|
||||
> **rpc** GetCustomLoginTexts([GetCustomLoginTextsRequest](#getcustomlogintextsrequest))
|
||||
@ -3226,6 +3342,28 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### AddCustomNotificationPolicyRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| password_change | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### AddCustomNotificationPolicyResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### AddCustomPasswordAgePolicyRequest
|
||||
|
||||
|
||||
@ -4446,6 +4584,28 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### GetCustomPasswordChangeMessageTextRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| language | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetCustomPasswordChangeMessageTextResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| custom_text | zitadel.text.v1.MessageCustomText | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetCustomPasswordResetMessageTextRequest
|
||||
|
||||
|
||||
@ -4651,6 +4811,23 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### GetDefaultNotificationPolicyRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### GetDefaultNotificationPolicyResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| policy | zitadel.policy.v1.NotificationPolicy | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetDefaultPasswordAgePolicyRequest
|
||||
This is an empty request
|
||||
|
||||
@ -4668,6 +4845,28 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### GetDefaultPasswordChangeMessageTextRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| language | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetDefaultPasswordChangeMessageTextResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| custom_text | zitadel.text.v1.MessageCustomText | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetDefaultPasswordComplexityPolicyRequest
|
||||
This is an empty request
|
||||
|
||||
@ -5034,6 +5233,23 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### GetNotificationPolicyRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### GetNotificationPolicyResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| policy | zitadel.policy.v1.NotificationPolicy | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetOIDCInformationRequest
|
||||
This is an empty request
|
||||
|
||||
@ -7442,6 +7658,28 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### ResetCustomPasswordChangeMessageTextToDefaultRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| language | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### ResetCustomPasswordChangeMessageTextToDefaultResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ResetCustomPasswordResetMessageTextToDefaultRequest
|
||||
|
||||
|
||||
@ -7581,6 +7819,23 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### ResetNotificationPolicyToDefaultRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### ResetNotificationPolicyToDefaultResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ResetPasswordAgePolicyToDefaultRequest
|
||||
This is an empty request
|
||||
|
||||
@ -7791,6 +8046,35 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### SetCustomPasswordChangeMessageTextRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| language | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| title | string | - | string.max_len: 200<br /> |
|
||||
| pre_header | string | - | string.max_len: 200<br /> |
|
||||
| subject | string | - | string.max_len: 200<br /> |
|
||||
| greeting | string | - | string.max_len: 200<br /> |
|
||||
| text | string | - | string.max_len: 800<br /> |
|
||||
| button_text | string | - | string.max_len: 200<br /> |
|
||||
| footer_text | string | - | string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### SetCustomPasswordChangeMessageTextResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### SetCustomPasswordResetMessageTextRequest
|
||||
|
||||
|
||||
@ -8234,6 +8518,28 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### UpdateCustomNotificationPolicyRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| password_change | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### UpdateCustomNotificationPolicyResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### UpdateCustomPasswordAgePolicyRequest
|
||||
|
||||
|
||||
|
@ -95,6 +95,19 @@ title: zitadel/policy.proto
|
||||
|
||||
|
||||
|
||||
### NotificationPolicy
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
| is_default | bool | - | |
|
||||
| password_change | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### OrgIAMPolicy
|
||||
deprecated: please use DomainPolicy instead
|
||||
|
||||
|
@ -15,7 +15,7 @@ To access instance settings, use the instance page at `{instanceDomain}/ui/conso
|
||||
When you configure your instance, you can set the following:
|
||||
|
||||
- **General**: Default Language for the UI
|
||||
- [**Notification providers and SMTP**](#notification-providers-and-smtp): Email Server settings, so initialization-, verification- and other mails are sent from your own domain. For SMS, Twilio is supported as notification provider.
|
||||
- [**Notification settings**](#notification-providers-and-smtp): Notification and Email Server settings, so initialization-, verification- and other mails are sent from your own domain. For SMS, Twilio is supported as notification provider.
|
||||
- [**Login Behaviour and Access**](#login-behaviour-and-access): Multifactor Authentication Options and Enforcement, Define whether Passwordless authentication methods are allowed or not, Set Login Lifetimes and advanced behavour for the login interface.
|
||||
- [**Identity Providers**](#identity-providers): Define IDPs which are available for all organizations
|
||||
- [**Password Complexity**](#password-complexity): Requirements for Passwords ex. Symbols, Numbers, min length and more.
|
||||
@ -48,9 +48,16 @@ Make sure you click the "Apply configuration" button after you finish your confi
|
||||
|
||||
Branding settings applied on you instance act as a default for all your organizations. If you need custom branding on a organization take a look at our guide under [organization settiong](./organizations#branding).
|
||||
|
||||
## Notification providers and SMTP
|
||||
## Notification settings
|
||||
|
||||
In the notification settings you can configure your SMTP Server settings and your SMS Provider. At the moment Twilio is available as SMS provider.
|
||||
In the notification settings you can configure when to notify users about certain events and you can customize your SMTP Server settings and your SMS Provider.
|
||||
At the moment Twilio is available as SMS provider.
|
||||
|
||||
### Notification
|
||||
|
||||
You can configure on which changes the users will be notified. The text of the message can be changed in the [Message texts](#message-texts)
|
||||
|
||||
<img src="/docs/img/guides/console/notification.png" alt="Notification" width="400px" />
|
||||
|
||||
### SMTP
|
||||
|
||||
@ -197,13 +204,14 @@ Example:
|
||||
|
||||
These are the texts for your notification mails. Available for change are:
|
||||
|
||||
| Message Text | Description |
|
||||
| -------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| Domain Claim | Enable self register possibility in the login ui |
|
||||
| Initialization | The mail after a user has been created. A code is part of the message which then must be verified on first login |
|
||||
| Passwordless | Possibility to login with an external identity (e.g Google, Microsoft, Apple, etc) |
|
||||
| Password Reset | Force a user to register and use a multifactor authentication |
|
||||
| Verify Email | Choose if passwordless login is allowed or not |
|
||||
| Message Text | Description |
|
||||
| --------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Domain Claim | The Mail after an organisation claimed a domain for itself. Users on other organisations with this domain will be notified |
|
||||
| Initialization | The mail after a user has been created. A code is part of the message which then must be verified on first login |
|
||||
| Passwordless | The Mail to register an additional passwordless device by a link |
|
||||
| Password Reset | The Mail to reset the password by a link |
|
||||
| Verify Email | The mail after the email has been changed. A code is part of the message which then must be verified on the next login |
|
||||
| Password Change | Notify the user, that the password has been changed. Can be configured in [Notification](#notification) |
|
||||
|
||||
You can set the locale of the translations on the right.
|
||||
|
||||
|
BIN
docs/static/img/guides/console/notification.png
vendored
Normal file
BIN
docs/static/img/guides/console/notification.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
@ -252,6 +252,54 @@ func (s *Server) ResetCustomDomainClaimedMessageTextToDefault(ctx context.Contex
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultPasswordChangeMessageText(ctx context.Context, req *admin_pb.GetDefaultPasswordChangeMessageTextRequest) (*admin_pb.GetDefaultPasswordChangeMessageTextResponse, error) {
|
||||
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.PasswordChangeMessageType, req.Language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetDefaultPasswordChangeMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetCustomPasswordChangeMessageText(ctx context.Context, req *admin_pb.GetCustomPasswordChangeMessageTextRequest) (*admin_pb.GetCustomPasswordChangeMessageTextResponse, error) {
|
||||
msg, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, authz.GetInstance(ctx).InstanceID(), domain.PasswordChangeMessageType, req.Language, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetCustomPasswordChangeMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SetDefaultPasswordChangeMessageText(ctx context.Context, req *admin_pb.SetDefaultPasswordChangeMessageTextRequest) (*admin_pb.SetDefaultPasswordChangeMessageTextResponse, error) {
|
||||
result, err := s.command.SetDefaultMessageText(ctx, authz.GetInstance(ctx).InstanceID(), SetPasswordChangeCustomTextToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.SetDefaultPasswordChangeMessageTextResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ResetCustomPasswordChangeMessageTextToDefault(ctx context.Context, req *admin_pb.ResetCustomPasswordChangeMessageTextToDefaultRequest) (*admin_pb.ResetCustomPasswordChangeMessageTextToDefaultResponse, error) {
|
||||
result, err := s.command.RemoveInstanceMessageTexts(ctx, domain.PasswordChangeMessageType, language.Make(req.Language))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.ResetCustomPasswordChangeMessageTextToDefaultResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultPasswordlessRegistrationMessageText(ctx context.Context, req *admin_pb.GetDefaultPasswordlessRegistrationMessageTextRequest) (*admin_pb.GetDefaultPasswordlessRegistrationMessageTextResponse, error) {
|
||||
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.PasswordlessRegistrationMessageType, req.Language)
|
||||
if err != nil {
|
||||
|
@ -83,6 +83,21 @@ func SetDomainClaimedCustomTextToDomain(msg *admin_pb.SetDefaultDomainClaimedMes
|
||||
}
|
||||
}
|
||||
|
||||
func SetPasswordChangeCustomTextToDomain(msg *admin_pb.SetDefaultPasswordChangeMessageTextRequest) *domain.CustomMessageText {
|
||||
langTag := language.Make(msg.Language)
|
||||
return &domain.CustomMessageText{
|
||||
MessageTextType: domain.PasswordChangeMessageType,
|
||||
Language: langTag,
|
||||
Title: msg.Title,
|
||||
PreHeader: msg.PreHeader,
|
||||
Subject: msg.Subject,
|
||||
Greeting: msg.Greeting,
|
||||
Text: msg.Text,
|
||||
ButtonText: msg.ButtonText,
|
||||
FooterText: msg.FooterText,
|
||||
}
|
||||
}
|
||||
|
||||
func SetPasswordlessRegistrationCustomTextToDomain(msg *admin_pb.SetDefaultPasswordlessRegistrationMessageTextRequest) *domain.CustomMessageText {
|
||||
langTag := language.Make(msg.Language)
|
||||
return &domain.CustomMessageText{
|
||||
|
46
internal/api/grpc/admin/notification_policy.go
Normal file
46
internal/api/grpc/admin/notification_policy.go
Normal file
@ -0,0 +1,46 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
policy_grpc "github.com/zitadel/zitadel/internal/api/grpc/policy"
|
||||
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||
)
|
||||
|
||||
func (s *Server) AddNotificationPolicy(ctx context.Context, req *admin_pb.AddNotificationPolicyRequest) (*admin_pb.AddNotificationPolicyResponse, error) {
|
||||
result, err := s.command.AddDefaultNotificationPolicy(ctx, authz.GetInstance(ctx).InstanceID(), req.GetPasswordChange())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.AddNotificationPolicyResponse{
|
||||
Details: object.AddToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetNotificationPolicy(ctx context.Context, _ *admin_pb.GetNotificationPolicyRequest) (*admin_pb.GetNotificationPolicyResponse, error) {
|
||||
policy, err := s.query.DefaultNotificationPolicy(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetNotificationPolicyResponse{Policy: policy_grpc.ModelNotificationPolicyToPb(policy)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateNotificationPolicy(ctx context.Context, req *admin_pb.UpdateNotificationPolicyRequest) (*admin_pb.UpdateNotificationPolicyResponse, error) {
|
||||
result, err := s.command.ChangeDefaultNotificationPolicy(ctx, authz.GetInstance(ctx).InstanceID(), req.GetPasswordChange())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.UpdateNotificationPolicyResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
@ -252,6 +252,54 @@ func (s *Server) ResetCustomDomainClaimedMessageTextToDefault(ctx context.Contex
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetCustomPasswordChangeMessageText(ctx context.Context, req *mgmt_pb.GetCustomPasswordChangeMessageTextRequest) (*mgmt_pb.GetCustomPasswordChangeMessageTextResponse, error) {
|
||||
msg, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, authz.GetCtxData(ctx).OrgID, domain.PasswordChangeMessageType, req.Language, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetCustomPasswordChangeMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultPasswordChangeMessageText(ctx context.Context, req *mgmt_pb.GetDefaultPasswordChangeMessageTextRequest) (*mgmt_pb.GetDefaultPasswordChangeMessageTextResponse, error) {
|
||||
msg, err := s.query.IAMMessageTextByTypeAndLanguage(ctx, domain.PasswordChangeMessageType, req.Language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetDefaultPasswordChangeMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SetCustomPasswordChangeMessageCustomText(ctx context.Context, req *mgmt_pb.SetCustomPasswordChangeMessageTextRequest) (*mgmt_pb.SetCustomPasswordChangeMessageTextResponse, error) {
|
||||
result, err := s.command.SetOrgMessageText(ctx, authz.GetCtxData(ctx).OrgID, SetPasswordChangeCustomTextToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.SetCustomPasswordChangeMessageTextResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ResetCustomPasswordChangeMessageTextToDefault(ctx context.Context, req *mgmt_pb.ResetCustomPasswordChangeMessageTextToDefaultRequest) (*mgmt_pb.ResetCustomPasswordChangeMessageTextToDefaultResponse, error) {
|
||||
result, err := s.command.RemoveOrgMessageTexts(ctx, authz.GetCtxData(ctx).OrgID, domain.PasswordChangeMessageType, language.Make(req.Language))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.ResetCustomPasswordChangeMessageTextToDefaultResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetCustomPasswordlessRegistrationMessageText(ctx context.Context, req *mgmt_pb.GetCustomPasswordlessRegistrationMessageTextRequest) (*mgmt_pb.GetCustomPasswordlessRegistrationMessageTextResponse, error) {
|
||||
msg, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, authz.GetCtxData(ctx).OrgID, domain.PasswordlessRegistrationMessageType, req.Language, false)
|
||||
if err != nil {
|
||||
|
@ -83,6 +83,21 @@ func SetDomainClaimedCustomTextToDomain(msg *mgmt_pb.SetCustomDomainClaimedMessa
|
||||
}
|
||||
}
|
||||
|
||||
func SetPasswordChangeCustomTextToDomain(msg *mgmt_pb.SetCustomPasswordChangeMessageTextRequest) *domain.CustomMessageText {
|
||||
langTag := language.Make(msg.Language)
|
||||
return &domain.CustomMessageText{
|
||||
MessageTextType: domain.PasswordChangeMessageType,
|
||||
Language: langTag,
|
||||
Title: msg.Title,
|
||||
PreHeader: msg.PreHeader,
|
||||
Subject: msg.Subject,
|
||||
Greeting: msg.Greeting,
|
||||
Text: msg.Text,
|
||||
ButtonText: msg.ButtonText,
|
||||
FooterText: msg.FooterText,
|
||||
}
|
||||
}
|
||||
|
||||
func SetPasswordlessRegistrationCustomTextToDomain(msg *mgmt_pb.SetCustomPasswordlessRegistrationMessageTextRequest) *domain.CustomMessageText {
|
||||
langTag := language.Make(msg.Language)
|
||||
return &domain.CustomMessageText{
|
||||
|
64
internal/api/grpc/management/policy_notification.go
Normal file
64
internal/api/grpc/management/policy_notification.go
Normal file
@ -0,0 +1,64 @@
|
||||
package management
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
policy_grpc "github.com/zitadel/zitadel/internal/api/grpc/policy"
|
||||
mgmt_pb "github.com/zitadel/zitadel/pkg/grpc/management"
|
||||
)
|
||||
|
||||
func (s *Server) GetNotificationPolicy(ctx context.Context, _ *mgmt_pb.GetNotificationPolicyRequest) (*mgmt_pb.GetNotificationPolicyResponse, error) {
|
||||
policy, err := s.query.NotificationPolicyByOrg(ctx, true, authz.GetCtxData(ctx).OrgID, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetNotificationPolicyResponse{Policy: policy_grpc.ModelNotificationPolicyToPb(policy)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultNotificationPolicy(ctx context.Context, _ *mgmt_pb.GetDefaultNotificationPolicyRequest) (*mgmt_pb.GetDefaultNotificationPolicyResponse, error) {
|
||||
policy, err := s.query.DefaultNotificationPolicy(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetDefaultNotificationPolicyResponse{Policy: policy_grpc.ModelNotificationPolicyToPb(policy)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) AddCustomNotificationPolicy(ctx context.Context, req *mgmt_pb.AddCustomNotificationPolicyRequest) (*mgmt_pb.AddCustomNotificationPolicyResponse, error) {
|
||||
result, err := s.command.AddNotificationPolicy(ctx, authz.GetCtxData(ctx).OrgID, req.GetPasswordChange())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.AddCustomNotificationPolicyResponse{
|
||||
Details: object.AddToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateCustomNotificationPolicy(ctx context.Context, req *mgmt_pb.UpdateCustomNotificationPolicyRequest) (*mgmt_pb.UpdateCustomNotificationPolicyResponse, error) {
|
||||
result, err := s.command.ChangeNotificationPolicy(ctx, authz.GetCtxData(ctx).OrgID, req.GetPasswordChange())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.UpdateCustomNotificationPolicyResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ResetNotificationPolicyToDefault(ctx context.Context, _ *mgmt_pb.ResetNotificationPolicyToDefaultRequest) (*mgmt_pb.ResetNotificationPolicyToDefaultResponse, error) {
|
||||
objectDetails, err := s.command.RemoveNotificationPolicy(ctx, authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.ResetNotificationPolicyToDefaultResponse{
|
||||
Details: object.DomainToChangeDetailsPb(objectDetails),
|
||||
}, nil
|
||||
}
|
20
internal/api/grpc/policy/notification_policy.go
Normal file
20
internal/api/grpc/policy/notification_policy.go
Normal file
@ -0,0 +1,20 @@
|
||||
package policy
|
||||
|
||||
import (
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
policy_pb "github.com/zitadel/zitadel/pkg/grpc/policy"
|
||||
)
|
||||
|
||||
func ModelNotificationPolicyToPb(policy *query.NotificationPolicy) *policy_pb.NotificationPolicy {
|
||||
return &policy_pb.NotificationPolicy{
|
||||
IsDefault: policy.IsDefault,
|
||||
PasswordChange: policy.PasswordChange,
|
||||
Details: object.ToViewDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.CreationDate,
|
||||
policy.ChangeDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}
|
||||
}
|
@ -52,6 +52,10 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func LoginHintLink(origin, username string) string {
|
||||
return origin + HandlerPrefix + "?login_hint=" + username
|
||||
}
|
||||
|
||||
func (i *spaHandler) Open(name string) (http.File, error) {
|
||||
ret, err := i.fileSystem.Open(name)
|
||||
if !os.IsNotExist(err) || path.Ext(name) != "" {
|
||||
|
@ -82,6 +82,9 @@ type InstanceSetup struct {
|
||||
SecondFactorCheckLifetime time.Duration
|
||||
MultiFactorCheckLifetime time.Duration
|
||||
}
|
||||
NotificationPolicy struct {
|
||||
PasswordChange bool
|
||||
}
|
||||
PrivacyPolicy struct {
|
||||
TOSLink string
|
||||
PrivacyLink string
|
||||
@ -236,6 +239,7 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str
|
||||
prepareAddMultiFactorToDefaultLoginPolicy(instanceAgg, domain.MultiFactorTypeU2FWithPIN),
|
||||
|
||||
prepareAddDefaultPrivacyPolicy(instanceAgg, setup.PrivacyPolicy.TOSLink, setup.PrivacyPolicy.PrivacyLink, setup.PrivacyPolicy.HelpLink),
|
||||
prepareAddDefaultNotificationPolicy(instanceAgg, setup.NotificationPolicy.PasswordChange),
|
||||
prepareAddDefaultLockoutPolicy(instanceAgg, setup.LockoutPolicy.MaxAttempts, setup.LockoutPolicy.ShouldShowLockoutFailure),
|
||||
|
||||
prepareAddDefaultLabelPolicy(
|
||||
|
92
internal/command/instance_policy_notification.go
Normal file
92
internal/command/instance_policy_notification.go
Normal file
@ -0,0 +1,92 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
func (c *Commands) AddDefaultNotificationPolicy(ctx context.Context, resourceOwner string, passwordChange bool) (*domain.ObjectDetails, error) {
|
||||
instanceAgg := instance.NewAggregate(resourceOwner)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddDefaultNotificationPolicy(instanceAgg, passwordChange))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pushedEventsToObjectDetails(pushedEvents), nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeDefaultNotificationPolicy(ctx context.Context, resourceOwner string, passwordChange bool) (*domain.ObjectDetails, error) {
|
||||
instanceAgg := instance.NewAggregate(resourceOwner)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareChangeDefaultNotificationPolicy(instanceAgg, passwordChange))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pushedEventsToObjectDetails(pushedEvents), nil
|
||||
}
|
||||
|
||||
func prepareAddDefaultNotificationPolicy(
|
||||
a *instance.Aggregate,
|
||||
passwordChange bool,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
writeModel := NewInstanceNotificationPolicyWriteModel(ctx)
|
||||
events, err := filter(ctx, writeModel.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writeModel.AppendEvents(events...)
|
||||
if err = writeModel.Reduce(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if writeModel.State == domain.PolicyStateActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-xpo1bj", "Errors.Instance.NotificationPolicy.AlreadyExists")
|
||||
}
|
||||
return []eventstore.Command{
|
||||
instance.NewNotificationPolicyAddedEvent(ctx, &a.Aggregate, passwordChange),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func prepareChangeDefaultNotificationPolicy(
|
||||
a *instance.Aggregate,
|
||||
passwordChange bool,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
writeModel := NewInstanceNotificationPolicyWriteModel(ctx)
|
||||
events, err := filter(ctx, writeModel.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writeModel.AppendEvents(events...)
|
||||
if err = writeModel.Reduce(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if writeModel.State == domain.PolicyStateUnspecified || writeModel.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-x891na", "Errors.IAM.NotificationPolicy.NotFound")
|
||||
}
|
||||
change, hasChanged := writeModel.NewChangedEvent(ctx, &a.Aggregate, passwordChange)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "INSTANCE-29x02n", "Errors.IAM.NotificationPolicy.NotChanged")
|
||||
}
|
||||
return []eventstore.Command{
|
||||
change,
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
72
internal/command/instance_policy_notification_model.go
Normal file
72
internal/command/instance_policy_notification_model.go
Normal file
@ -0,0 +1,72 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
"github.com/zitadel/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
type InstanceNotificationPolicyWriteModel struct {
|
||||
NotificationPolicyWriteModel
|
||||
}
|
||||
|
||||
func NewInstanceNotificationPolicyWriteModel(ctx context.Context) *InstanceNotificationPolicyWriteModel {
|
||||
return &InstanceNotificationPolicyWriteModel{
|
||||
NotificationPolicyWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: authz.GetInstance(ctx).InstanceID(),
|
||||
ResourceOwner: authz.GetInstance(ctx).InstanceID(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *InstanceNotificationPolicyWriteModel) AppendEvents(events ...eventstore.Event) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *instance.NotificationPolicyAddedEvent:
|
||||
wm.NotificationPolicyWriteModel.AppendEvents(&e.NotificationPolicyAddedEvent)
|
||||
case *instance.NotificationPolicyChangedEvent:
|
||||
wm.NotificationPolicyWriteModel.AppendEvents(&e.NotificationPolicyChangedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *InstanceNotificationPolicyWriteModel) Reduce() error {
|
||||
return wm.NotificationPolicyWriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *InstanceNotificationPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateTypes(instance.AggregateType).
|
||||
AggregateIDs(wm.NotificationPolicyWriteModel.AggregateID).
|
||||
EventTypes(
|
||||
instance.NotificationPolicyAddedEventType,
|
||||
instance.NotificationPolicyChangedEventType).
|
||||
Builder()
|
||||
}
|
||||
|
||||
func (wm *InstanceNotificationPolicyWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
passwordChange bool,
|
||||
) (*instance.NotificationPolicyChangedEvent, bool) {
|
||||
|
||||
changes := make([]policy.NotificationPolicyChanges, 0)
|
||||
if wm.PasswordChange != passwordChange {
|
||||
changes = append(changes, policy.ChangePasswordChange(passwordChange))
|
||||
}
|
||||
if len(changes) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
changedEvent, err := instance.NewNotificationPolicyChangedEvent(ctx, aggregate, changes)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return changedEvent, true
|
||||
}
|
260
internal/command/instance_policy_notification_test.go
Normal file
260
internal/command/instance_policy_notification_test.go
Normal file
@ -0,0 +1,260 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
"github.com/zitadel/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddDefaultNotificationPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
resourceOwner string
|
||||
passwordChange bool
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "notification policy already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewNotificationPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "INSTANCE",
|
||||
passwordChange: true,
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add policy,ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
instance.NewNotificationPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
true,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "INSTANCE",
|
||||
passwordChange: true,
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "INSTANCE",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add empty policy,ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
instance.NewNotificationPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
true,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "INSTANCE",
|
||||
passwordChange: true,
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "INSTANCE",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.AddDefaultNotificationPolicy(tt.args.ctx, tt.args.resourceOwner, tt.args.passwordChange)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_ChangeDefaultNotificationPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
resourceOwner string
|
||||
passwordChange bool
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "privacy policy not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "INSTANCE",
|
||||
passwordChange: true,
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewNotificationPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "INSTANCE",
|
||||
passwordChange: true,
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewNotificationPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newDefaultNotificationPolicyChangedEvent(context.Background(),
|
||||
true,
|
||||
)),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "INSTANCE",
|
||||
passwordChange: true,
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "INSTANCE",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.ChangeDefaultNotificationPolicy(tt.args.ctx, tt.args.resourceOwner, tt.args.passwordChange)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newDefaultNotificationPolicyChangedEvent(ctx context.Context, passwordChange bool) *instance.NotificationPolicyChangedEvent {
|
||||
event, _ := instance.NewNotificationPolicyChangedEvent(ctx,
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
[]policy.NotificationPolicyChanges{
|
||||
policy.ChangePasswordChange(passwordChange),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}
|
@ -49,5 +49,6 @@ func orgWriteModelToPrivacyPolicy(wm *OrgPrivacyPolicyWriteModel) *domain.Privac
|
||||
ObjectRoot: writeModelToObjectRoot(wm.PrivacyPolicyWriteModel.WriteModel),
|
||||
TOSLink: wm.TOSLink,
|
||||
PrivacyLink: wm.PrivacyLink,
|
||||
HelpLink: wm.HelpLink,
|
||||
}
|
||||
}
|
||||
|
139
internal/command/org_policy_notification.go
Normal file
139
internal/command/org_policy_notification.go
Normal file
@ -0,0 +1,139 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
func (c *Commands) AddNotificationPolicy(ctx context.Context, resourceOwner string, passwordChange bool) (*domain.ObjectDetails, error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-x801sk2i", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
orgAgg := org.NewAggregate(resourceOwner)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddNotificationPolicy(orgAgg, passwordChange))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pushedEventsToObjectDetails(pushedEvents), nil
|
||||
}
|
||||
|
||||
func prepareAddNotificationPolicy(
|
||||
a *org.Aggregate,
|
||||
passwordChange bool,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
writeModel := NewOrgNotificationPolicyWriteModel(a.Aggregate.ID)
|
||||
events, err := filter(ctx, writeModel.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writeModel.AppendEvents(events...)
|
||||
if err = writeModel.Reduce(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if writeModel.State == domain.PolicyStateActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "Org-xa08n2", "Errors.Org.NotificationPolicy.AlreadyExists")
|
||||
}
|
||||
return []eventstore.Command{
|
||||
org.NewNotificationPolicyAddedEvent(ctx, &a.Aggregate, passwordChange),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeNotificationPolicy(ctx context.Context, resourceOwner string, passwordChange bool) (*domain.ObjectDetails, error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-x091n1g", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
orgAgg := org.NewAggregate(resourceOwner)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareChangeNotificationPolicy(orgAgg, passwordChange))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pushedEventsToObjectDetails(pushedEvents), nil
|
||||
}
|
||||
|
||||
func prepareChangeNotificationPolicy(
|
||||
a *org.Aggregate,
|
||||
passwordChange bool,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
writeModel := NewOrgNotificationPolicyWriteModel(a.Aggregate.ID)
|
||||
events, err := filter(ctx, writeModel.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writeModel.AppendEvents(events...)
|
||||
if err = writeModel.Reduce(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if writeModel.State == domain.PolicyStateUnspecified || writeModel.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-x029n3", "Errors.Org.NotificationPolicy.NotFound")
|
||||
}
|
||||
change, hasChanged := writeModel.NewChangedEvent(ctx, &a.Aggregate, passwordChange)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-ioqnxz", "Errors.Org.NotificationPolicy.NotChanged")
|
||||
}
|
||||
return []eventstore.Command{
|
||||
change,
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveNotificationPolicy(ctx context.Context, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-x89ns2", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
orgAgg := org.NewAggregate(resourceOwner)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareRemoveNotificationPolicy(orgAgg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pushedEventsToObjectDetails(pushedEvents), nil
|
||||
}
|
||||
|
||||
func prepareRemoveNotificationPolicy(
|
||||
a *org.Aggregate,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
writeModel := NewOrgNotificationPolicyWriteModel(a.Aggregate.ID)
|
||||
events, err := filter(ctx, writeModel.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writeModel.AppendEvents(events...)
|
||||
if err = writeModel.Reduce(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if writeModel.State == domain.PolicyStateUnspecified || writeModel.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-x029n1s", "Errors.Org.NotificationPolicy.NotFound")
|
||||
}
|
||||
return []eventstore.Command{
|
||||
org.NewNotificationPolicyRemovedEvent(ctx, &a.Aggregate),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
73
internal/command/org_policy_notification_model.go
Normal file
73
internal/command/org_policy_notification_model.go
Normal file
@ -0,0 +1,73 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/org"
|
||||
"github.com/zitadel/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
type OrgNotificationPolicyWriteModel struct {
|
||||
NotificationPolicyWriteModel
|
||||
}
|
||||
|
||||
func NewOrgNotificationPolicyWriteModel(orgID string) *OrgNotificationPolicyWriteModel {
|
||||
return &OrgNotificationPolicyWriteModel{
|
||||
NotificationPolicyWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: orgID,
|
||||
ResourceOwner: orgID,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgNotificationPolicyWriteModel) AppendEvents(events ...eventstore.Event) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *org.NotificationPolicyAddedEvent:
|
||||
wm.NotificationPolicyWriteModel.AppendEvents(&e.NotificationPolicyAddedEvent)
|
||||
case *org.NotificationPolicyChangedEvent:
|
||||
wm.NotificationPolicyWriteModel.AppendEvents(&e.NotificationPolicyChangedEvent)
|
||||
case *org.NotificationPolicyRemovedEvent:
|
||||
wm.NotificationPolicyWriteModel.AppendEvents(&e.NotificationPolicyRemovedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgNotificationPolicyWriteModel) Reduce() error {
|
||||
return wm.NotificationPolicyWriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *OrgNotificationPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateIDs(wm.NotificationPolicyWriteModel.AggregateID).
|
||||
AggregateTypes(org.AggregateType).
|
||||
EventTypes(org.NotificationPolicyAddedEventType,
|
||||
org.NotificationPolicyChangedEventType,
|
||||
org.NotificationPolicyRemovedEventType).
|
||||
Builder()
|
||||
}
|
||||
|
||||
func (wm *OrgNotificationPolicyWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
passwordChange bool,
|
||||
) (*org.NotificationPolicyChangedEvent, bool) {
|
||||
|
||||
changes := make([]policy.NotificationPolicyChanges, 0)
|
||||
if wm.PasswordChange != passwordChange {
|
||||
changes = append(changes, policy.ChangePasswordChange(passwordChange))
|
||||
}
|
||||
if len(changes) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
changedEvent, err := org.NewNotificationPolicyChangedEvent(ctx, aggregate, changes)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return changedEvent, true
|
||||
}
|
391
internal/command/org_policy_notification_test.go
Normal file
391
internal/command/org_policy_notification_test.go
Normal file
@ -0,0 +1,391 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||
"github.com/zitadel/zitadel/internal/repository/org"
|
||||
"github.com/zitadel/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddNotificationPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
passwordChange bool
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "org id missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "",
|
||||
passwordChange: true,
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "policy already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewNotificationPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
passwordChange: true,
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add policy,ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
org.NewNotificationPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
passwordChange: true,
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add policy empty, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
org.NewNotificationPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1").Aggregate,
|
||||
false,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
passwordChange: false,
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.AddNotificationPolicy(tt.args.ctx, tt.args.orgID, tt.args.passwordChange)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_ChangeNotificationPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
passwordChange bool
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "org id missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
passwordChange: true,
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "policy not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
passwordChange: true,
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewNotificationPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
passwordChange: true,
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewNotificationPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newNotificationPolicyChangedEvent(context.Background(), "org1", false),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
passwordChange: false,
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.ChangeNotificationPolicy(tt.args.ctx, tt.args.orgID, tt.args.passwordChange)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_RemoveNotificationPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "org id missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "policy not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewNotificationPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
org.NewNotificationPolicyRemovedEvent(context.Background(),
|
||||
&org.NewAggregate("org1").Aggregate),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.RemoveNotificationPolicy(tt.args.ctx, tt.args.orgID)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newNotificationPolicyChangedEvent(ctx context.Context, orgID string, passwordChange bool) *org.NotificationPolicyChangedEvent {
|
||||
event, _ := org.NewNotificationPolicyChangedEvent(ctx,
|
||||
&org.NewAggregate(orgID).Aggregate,
|
||||
[]policy.NotificationPolicyChanges{
|
||||
policy.ChangePasswordChange(passwordChange),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}
|
31
internal/command/policy_notification_model.go
Normal file
31
internal/command/policy_notification_model.go
Normal file
@ -0,0 +1,31 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
type NotificationPolicyWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
PasswordChange bool
|
||||
State domain.PolicyState
|
||||
}
|
||||
|
||||
func (wm *NotificationPolicyWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *policy.NotificationPolicyAddedEvent:
|
||||
wm.PasswordChange = e.PasswordChange
|
||||
wm.State = domain.PolicyStateActive
|
||||
case *policy.NotificationPolicyChangedEvent:
|
||||
if e.PasswordChange != nil {
|
||||
wm.PasswordChange = *e.PasswordChange
|
||||
}
|
||||
case *policy.NotificationPolicyRemovedEvent:
|
||||
wm.State = domain.PolicyStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
@ -196,6 +196,23 @@ func (c *Commands) PasswordCodeSent(ctx context.Context, orgID, userID string) (
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Commands) PasswordChangeSent(ctx context.Context, orgID, userID string) (err error) {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-pqlm2n", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
||||
existingPassword, err := c.passwordWriteModel(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-x902b2v", "Errors.User.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||
_, err = c.eventstore.Push(ctx, user.NewHumanPasswordChangeSentEvent(ctx, userAgg))
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Commands) HumanCheckPassword(ctx context.Context, orgID, userID, password string, authRequest *domain.AuthRequest, lockoutPolicy *domain.LockoutPolicy) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
@ -13,6 +13,7 @@ const (
|
||||
VerifyPhoneMessageType = "VerifyPhone"
|
||||
DomainClaimedMessageType = "DomainClaimed"
|
||||
PasswordlessRegistrationMessageType = "PasswordlessRegistration"
|
||||
PasswordChangeMessageType = "PasswordChange"
|
||||
MessageTitle = "Title"
|
||||
MessagePreHeader = "PreHeader"
|
||||
MessageSubject = "Subject"
|
||||
@ -29,6 +30,7 @@ type MessageTexts struct {
|
||||
VerifyPhone CustomMessageText
|
||||
DomainClaimed CustomMessageText
|
||||
PasswordlessRegistration CustomMessageText
|
||||
PasswordChange CustomMessageText
|
||||
}
|
||||
|
||||
type CustomMessageText struct {
|
||||
@ -65,6 +67,8 @@ func (m *MessageTexts) GetMessageTextByType(msgType string) *CustomMessageText {
|
||||
return &m.DomainClaimed
|
||||
case PasswordlessRegistrationMessageType:
|
||||
return &m.PasswordlessRegistration
|
||||
case PasswordChangeMessageType:
|
||||
return &m.PasswordChange
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -75,5 +79,6 @@ func IsMessageTextType(textType string) bool {
|
||||
textType == VerifyEmailMessageType ||
|
||||
textType == VerifyPhoneMessageType ||
|
||||
textType == DomainClaimedMessageType ||
|
||||
textType == PasswordlessRegistrationMessageType
|
||||
textType == PasswordlessRegistrationMessageType ||
|
||||
textType == PasswordChangeMessageType
|
||||
}
|
||||
|
@ -137,6 +137,10 @@ func (p *notificationsProjection) reducers() []handler.AggregateReducer {
|
||||
Event: user.HumanPhoneCodeAddedType,
|
||||
Reduce: p.reducePhoneCodeAdded,
|
||||
},
|
||||
{
|
||||
Event: user.HumanPasswordChangedType,
|
||||
Reduce: p.reducePasswordChanged,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -463,6 +467,74 @@ func (p *notificationsProjection) reducePasswordlessCodeRequested(event eventsto
|
||||
return crdb.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
func (p *notificationsProjection) reducePasswordChanged(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, ok := event.(*user.HumanPasswordChangedEvent)
|
||||
if !ok {
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Yko2z8", "reduce.wrong.event.type %s", user.HumanPasswordChangedType)
|
||||
}
|
||||
ctx := setNotificationContext(event.Aggregate())
|
||||
alreadyHandled, err := p.checkIfAlreadyHandled(ctx, event, nil, user.HumanPasswordChangeSentType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if alreadyHandled {
|
||||
return crdb.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
notificationPolicy, err := p.queries.NotificationPolicyByOrg(ctx, true, e.Aggregate().ResourceOwner, false)
|
||||
if errors.IsNotFound(err) {
|
||||
return crdb.NewNoOpStatement(e), nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if notificationPolicy.PasswordChange {
|
||||
colors, err := p.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
template, err := p.queries.MailTemplateByOrg(ctx, e.Aggregate().ResourceOwner, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
notifyUser, err := p.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
translator, err := p.getTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.PasswordChangeMessageType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, origin, err := p.origin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = types.SendEmail(
|
||||
ctx,
|
||||
string(template.Template),
|
||||
translator,
|
||||
notifyUser,
|
||||
p.getSMTPConfig,
|
||||
p.getFileSystemProvider,
|
||||
p.getLogProvider,
|
||||
colors,
|
||||
p.assetsPrefix(ctx),
|
||||
).SendPasswordChange(notifyUser, origin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = p.commands.PasswordChangeSent(ctx, e.Aggregate().ResourceOwner, e.Aggregate().ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return crdb.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
func (p *notificationsProjection) reducePhoneCodeAdded(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, ok := event.(*user.HumanPhoneCodeAddedEvent)
|
||||
if !ok {
|
||||
|
@ -40,3 +40,10 @@ PasswordlessRegistration:
|
||||
Greeting: Hallo {{.FirstName}} {{.LastName}},
|
||||
Text: Wir haben eine Anfrage für das Hinzufügen eines Token für den passwortlosen Login erhalten. Du kannst den untenstehenden Button verwenden, um dein Token oder Gerät hinzuzufügen.
|
||||
ButtonText: Passwortlosen Login hinzufügen
|
||||
PasswordChange:
|
||||
Title: ZITADEL - Passwort von Benutzer wurde geändert
|
||||
PreHeader: Passwort Änderung
|
||||
Subject: Passwort von Benutzer wurde geändert
|
||||
Greeting: Hallo {{.FirstName}} {{.LastName}},
|
||||
Text: Das Password vom Benutzer wurde geändert, wenn diese Änderung von jemand anderem gemacht wurde, empfehlen wir die sofortige Zurücksetzung ihres Passworts.
|
||||
ButtonText: Login
|
@ -40,3 +40,10 @@ PasswordlessRegistration:
|
||||
Greeting: Hello {{.FirstName}} {{.LastName}},
|
||||
Text: We received a request to add a token for passwordless login. Please use the button below to add your token or device for passwordless login.
|
||||
ButtonText: Add Passwordless Login
|
||||
PasswordChange:
|
||||
Title: ZITADEL - Password of user has changed
|
||||
PreHeader: Change password
|
||||
Subject: Password of user has changed
|
||||
Greeting: Hello {{.FirstName}} {{.LastName}},
|
||||
Text: The password of your user has changed, if this change was not done by you, please be advised to immediately reset your password.
|
||||
ButtonText: Login
|
@ -40,3 +40,10 @@ PasswordlessRegistration:
|
||||
Greeting: Bonjour {{.FirstName}} {{.LastName}},
|
||||
Text: Nous avons reçu une demande d'ajout d'un jeton pour la connexion sans mot de passe. Veuillez utiliser le bouton ci-dessous pour ajouter votre jeton ou dispositif pour la connexion sans mot de passe.
|
||||
ButtonText: Ajouter une connexion sans mot de passe
|
||||
PasswordChange:
|
||||
Title: ZITADEL - Le mot de passe de l'utilisateur a changé
|
||||
PreHeader: Modifier le mot de passe
|
||||
Subject: Le mot de passe de l'utilisateur a changé
|
||||
Greeting: Bonjour {{.FirstName}} {{.LastName}},
|
||||
Text: Le mot de passe de votre utilisateur a changé, si ce changement n'a pas été fait par vous, nous vous conseillons de réinitialiser immédiatement votre mot de passe.
|
||||
ButtonText: Login
|
||||
|
@ -40,3 +40,10 @@ PasswordlessRegistration:
|
||||
Greeting: 'Ciao {{.FirstName}} {{.LastName}},'
|
||||
Text: Abbiamo ricevuto una richiesta per aggiungere l'autenticazione passwordless. Usa il pulsante qui sotto per aggiungere il tuo token o dispositivo per il login senza password.
|
||||
ButtonText: Attiva passwordless
|
||||
PasswordChange:
|
||||
Title: ZITADEL - La password dell'utente è stata modificata
|
||||
PreHeader: Modifica della password
|
||||
Subject: La password dell'utente è stata modificata
|
||||
Greeting: Ciao {{.FirstName}} {{.LastName}},
|
||||
Text: La password del vostro utente è cambiata; se questa modifica non è stata fatta da voi, vi consigliamo di reimpostare immediatamente la vostra password.
|
||||
ButtonText: Login
|
@ -40,3 +40,10 @@ PasswordlessRegistration:
|
||||
Greeting: 你好 {{.FirstName}} {{.LastName}},
|
||||
Text: 我们收到了为无密码登录添加令牌的请求。请使用下面的按钮添加您的令牌或设备以进行无密码登录。
|
||||
ButtonText: 添加无密码登录
|
||||
PasswordChange:
|
||||
Title: ZITADEL - 用户的密码已经改变
|
||||
PreHeader: 更改密码
|
||||
Subject: 用户的密码已经改变
|
||||
Greeting: 你好 {{.FirstName}} {{.LastName}},
|
||||
Text: 您的用户的密码已经改变,如果这个改变不是由您做的,请注意立即重新设置您的密码。
|
||||
ButtonText: 登录
|
||||
|
13
internal/notification/types/password_change.go
Normal file
13
internal/notification/types/password_change.go
Normal file
@ -0,0 +1,13 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/zitadel/zitadel/internal/api/ui/console"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
)
|
||||
|
||||
func (notify Notify) SendPasswordChange(user *query.NotifyUser, origin string) error {
|
||||
url := console.LoginHintLink(origin, user.PreferredLoginName)
|
||||
args := make(map[string]interface{})
|
||||
return notify(url, args, domain.PasswordChangeMessageType, true)
|
||||
}
|
@ -29,6 +29,7 @@ type MessageTexts struct {
|
||||
VerifyPhone MessageText
|
||||
DomainClaimed MessageText
|
||||
PasswordlessRegistration MessageText
|
||||
PasswordChange MessageText
|
||||
}
|
||||
|
||||
type MessageText struct {
|
||||
@ -330,6 +331,8 @@ func (m *MessageTexts) GetMessageTextByType(msgType string) *MessageText {
|
||||
return &m.DomainClaimed
|
||||
case domain.PasswordlessRegistrationMessageType:
|
||||
return &m.PasswordlessRegistration
|
||||
case domain.PasswordChangeMessageType:
|
||||
return &m.PasswordChange
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
166
internal/query/notification_policy.go
Normal file
166
internal/query/notification_policy.go
Normal file
@ -0,0 +1,166 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
errs "errors"
|
||||
"time"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
type NotificationPolicy struct {
|
||||
ID string
|
||||
Sequence uint64
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
ResourceOwner string
|
||||
State domain.PolicyState
|
||||
|
||||
PasswordChange bool
|
||||
|
||||
IsDefault bool
|
||||
}
|
||||
|
||||
var (
|
||||
notificationPolicyTable = table{
|
||||
name: projection.NotificationPolicyProjectionTable,
|
||||
instanceIDCol: projection.NotificationPolicyColumnInstanceID,
|
||||
}
|
||||
NotificationPolicyColID = Column{
|
||||
name: projection.NotificationPolicyColumnID,
|
||||
table: notificationPolicyTable,
|
||||
}
|
||||
NotificationPolicyColSequence = Column{
|
||||
name: projection.NotificationPolicyColumnSequence,
|
||||
table: notificationPolicyTable,
|
||||
}
|
||||
NotificationPolicyColCreationDate = Column{
|
||||
name: projection.NotificationPolicyColumnCreationDate,
|
||||
table: notificationPolicyTable,
|
||||
}
|
||||
NotificationPolicyColChangeDate = Column{
|
||||
name: projection.NotificationPolicyColumnChangeDate,
|
||||
table: notificationPolicyTable,
|
||||
}
|
||||
NotificationPolicyColResourceOwner = Column{
|
||||
name: projection.NotificationPolicyColumnResourceOwner,
|
||||
table: notificationPolicyTable,
|
||||
}
|
||||
NotificationPolicyColInstanceID = Column{
|
||||
name: projection.NotificationPolicyColumnInstanceID,
|
||||
table: notificationPolicyTable,
|
||||
}
|
||||
NotificationPolicyColPasswordChange = Column{
|
||||
name: projection.NotificationPolicyColumnPasswordChange,
|
||||
table: notificationPolicyTable,
|
||||
}
|
||||
NotificationPolicyColIsDefault = Column{
|
||||
name: projection.NotificationPolicyColumnIsDefault,
|
||||
table: notificationPolicyTable,
|
||||
}
|
||||
NotificationPolicyColState = Column{
|
||||
name: projection.NotificationPolicyColumnStateCol,
|
||||
table: notificationPolicyTable,
|
||||
}
|
||||
NotificationPolicyColOwnerRemoved = Column{
|
||||
name: projection.NotificationPolicyColumnOwnerRemoved,
|
||||
table: notificationPolicyTable,
|
||||
}
|
||||
)
|
||||
|
||||
func (q *Queries) NotificationPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (_ *NotificationPolicy, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
if shouldTriggerBulk {
|
||||
if err := projection.NotificationPolicyProjection.Trigger(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
eq := sq.Eq{NotificationPolicyColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()}
|
||||
if !withOwnerRemoved {
|
||||
eq[NotificationPolicyColOwnerRemoved.identifier()] = false
|
||||
}
|
||||
stmt, scan := prepareNotificationPolicyQuery()
|
||||
query, args, err := stmt.Where(
|
||||
sq.And{
|
||||
eq,
|
||||
sq.Or{
|
||||
sq.Eq{NotificationPolicyColID.identifier(): orgID},
|
||||
sq.Eq{NotificationPolicyColID.identifier(): authz.GetInstance(ctx).InstanceID()},
|
||||
},
|
||||
}).
|
||||
OrderBy(NotificationPolicyColIsDefault.identifier()).Limit(1).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Xuoapqm", "Errors.Query.SQLStatement")
|
||||
}
|
||||
|
||||
row := q.client.QueryRowContext(ctx, query, args...)
|
||||
return scan(row)
|
||||
}
|
||||
|
||||
func (q *Queries) DefaultNotificationPolicy(ctx context.Context, shouldTriggerBulk bool) (_ *NotificationPolicy, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
if shouldTriggerBulk {
|
||||
if err := projection.NotificationPolicyProjection.Trigger(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
stmt, scan := prepareNotificationPolicyQuery()
|
||||
query, args, err := stmt.Where(sq.Eq{
|
||||
NotificationPolicyColID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
NotificationPolicyColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}).
|
||||
OrderBy(NotificationPolicyColIsDefault.identifier()).
|
||||
Limit(1).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-xlqp209", "Errors.Query.SQLStatement")
|
||||
}
|
||||
|
||||
row := q.client.QueryRowContext(ctx, query, args...)
|
||||
return scan(row)
|
||||
}
|
||||
|
||||
func prepareNotificationPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*NotificationPolicy, error)) {
|
||||
return sq.Select(
|
||||
NotificationPolicyColID.identifier(),
|
||||
NotificationPolicyColSequence.identifier(),
|
||||
NotificationPolicyColCreationDate.identifier(),
|
||||
NotificationPolicyColChangeDate.identifier(),
|
||||
NotificationPolicyColResourceOwner.identifier(),
|
||||
NotificationPolicyColPasswordChange.identifier(),
|
||||
NotificationPolicyColIsDefault.identifier(),
|
||||
NotificationPolicyColState.identifier(),
|
||||
).
|
||||
From(notificationPolicyTable.identifier()).PlaceholderFormat(sq.Dollar),
|
||||
func(row *sql.Row) (*NotificationPolicy, error) {
|
||||
policy := new(NotificationPolicy)
|
||||
err := row.Scan(
|
||||
&policy.ID,
|
||||
&policy.Sequence,
|
||||
&policy.CreationDate,
|
||||
&policy.ChangeDate,
|
||||
&policy.ResourceOwner,
|
||||
&policy.PasswordChange,
|
||||
&policy.IsDefault,
|
||||
&policy.State,
|
||||
)
|
||||
if err != nil {
|
||||
if errs.Is(err, sql.ErrNoRows) {
|
||||
return nil, errors.ThrowNotFound(err, "QUERY-x0so2p", "Errors.NotificationPolicy.NotFound")
|
||||
}
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Zixoooq", "Errors.Internal")
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
}
|
116
internal/query/notification_policy_test.go
Normal file
116
internal/query/notification_policy_test.go
Normal file
@ -0,0 +1,116 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
errs "github.com/zitadel/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
var notificationPolicyStmt = regexp.QuoteMeta(`SELECT projections.notification_policies.id,` +
|
||||
` projections.notification_policies.sequence,` +
|
||||
` projections.notification_policies.creation_date,` +
|
||||
` projections.notification_policies.change_date,` +
|
||||
` projections.notification_policies.resource_owner,` +
|
||||
` projections.notification_policies.password_change,` +
|
||||
` projections.notification_policies.is_default,` +
|
||||
` projections.notification_policies.state` +
|
||||
` FROM projections.notification_policies`)
|
||||
|
||||
func Test_NotificationPolicyPrepares(t *testing.T) {
|
||||
type want struct {
|
||||
sqlExpectations sqlExpectation
|
||||
err checkErr
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
prepare interface{}
|
||||
want want
|
||||
object interface{}
|
||||
}{
|
||||
{
|
||||
name: "prepareNotificationPolicyQuery no result",
|
||||
prepare: prepareNotificationPolicyQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
notificationPolicyStmt,
|
||||
nil,
|
||||
nil,
|
||||
),
|
||||
err: func(err error) (error, bool) {
|
||||
if !errs.IsNotFound(err) {
|
||||
return fmt.Errorf("err should be NotFoundError got: %w", err), false
|
||||
}
|
||||
return nil, true
|
||||
},
|
||||
},
|
||||
object: (*NotificationPolicy)(nil),
|
||||
},
|
||||
{
|
||||
name: "prepareNotificationPolicyQuery found",
|
||||
prepare: prepareNotificationPolicyQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQuery(
|
||||
notificationPolicyStmt,
|
||||
[]string{
|
||||
"id",
|
||||
"sequence",
|
||||
"creation_date",
|
||||
"change_date",
|
||||
"resource_owner",
|
||||
"password_change",
|
||||
"is_default",
|
||||
"state",
|
||||
},
|
||||
[]driver.Value{
|
||||
"pol-id",
|
||||
uint64(20211109),
|
||||
testNow,
|
||||
testNow,
|
||||
"ro",
|
||||
true,
|
||||
true,
|
||||
domain.PolicyStateActive,
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &NotificationPolicy{
|
||||
ID: "pol-id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211109,
|
||||
ResourceOwner: "ro",
|
||||
State: domain.PolicyStateActive,
|
||||
PasswordChange: true,
|
||||
IsDefault: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareNotificationPolicyQuery sql err",
|
||||
prepare: prepareNotificationPolicyQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueryErr(
|
||||
notificationPolicyStmt,
|
||||
sql.ErrConnDone,
|
||||
),
|
||||
err: func(err error) (error, bool) {
|
||||
if !errors.Is(err, sql.ErrConnDone) {
|
||||
return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false
|
||||
}
|
||||
return nil, true
|
||||
},
|
||||
},
|
||||
object: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err)
|
||||
})
|
||||
}
|
||||
}
|
@ -273,7 +273,8 @@ func isMessageTemplate(template string) bool {
|
||||
template == domain.VerifyEmailMessageType ||
|
||||
template == domain.VerifyPhoneMessageType ||
|
||||
template == domain.DomainClaimedMessageType ||
|
||||
template == domain.PasswordlessRegistrationMessageType
|
||||
template == domain.PasswordlessRegistrationMessageType ||
|
||||
template == domain.PasswordChangeMessageType
|
||||
}
|
||||
func isTitle(key string) bool {
|
||||
return key == domain.MessageTitle
|
||||
|
187
internal/query/projection/notification_policy.go
Normal file
187
internal/query/projection/notification_policy.go
Normal file
@ -0,0 +1,187 @@
|
||||
package projection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/handler"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/handler/crdb"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
"github.com/zitadel/zitadel/internal/repository/org"
|
||||
"github.com/zitadel/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
const (
|
||||
NotificationPolicyProjectionTable = "projections.notification_policies"
|
||||
|
||||
NotificationPolicyColumnID = "id"
|
||||
NotificationPolicyColumnCreationDate = "creation_date"
|
||||
NotificationPolicyColumnChangeDate = "change_date"
|
||||
NotificationPolicyColumnResourceOwner = "resource_owner"
|
||||
NotificationPolicyColumnInstanceID = "instance_id"
|
||||
NotificationPolicyColumnSequence = "sequence"
|
||||
NotificationPolicyColumnStateCol = "state"
|
||||
NotificationPolicyColumnIsDefault = "is_default"
|
||||
NotificationPolicyColumnPasswordChange = "password_change"
|
||||
NotificationPolicyColumnOwnerRemoved = "owner_removed"
|
||||
)
|
||||
|
||||
type notificationPolicyProjection struct {
|
||||
crdb.StatementHandler
|
||||
}
|
||||
|
||||
func newNotificationPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *notificationPolicyProjection {
|
||||
p := new(notificationPolicyProjection)
|
||||
config.ProjectionName = NotificationPolicyProjectionTable
|
||||
config.Reducers = p.reducers()
|
||||
config.InitCheck = crdb.NewTableCheck(
|
||||
crdb.NewTable([]*crdb.Column{
|
||||
crdb.NewColumn(NotificationPolicyColumnID, crdb.ColumnTypeText),
|
||||
crdb.NewColumn(NotificationPolicyColumnCreationDate, crdb.ColumnTypeTimestamp),
|
||||
crdb.NewColumn(NotificationPolicyColumnChangeDate, crdb.ColumnTypeTimestamp),
|
||||
crdb.NewColumn(NotificationPolicyColumnResourceOwner, crdb.ColumnTypeText),
|
||||
crdb.NewColumn(NotificationPolicyColumnInstanceID, crdb.ColumnTypeText),
|
||||
crdb.NewColumn(NotificationPolicyColumnSequence, crdb.ColumnTypeInt64),
|
||||
crdb.NewColumn(NotificationPolicyColumnStateCol, crdb.ColumnTypeEnum),
|
||||
crdb.NewColumn(NotificationPolicyColumnIsDefault, crdb.ColumnTypeBool),
|
||||
crdb.NewColumn(NotificationPolicyColumnPasswordChange, crdb.ColumnTypeBool),
|
||||
crdb.NewColumn(NotificationPolicyColumnOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)),
|
||||
},
|
||||
crdb.NewPrimaryKey(NotificationPolicyColumnInstanceID, NotificationPolicyColumnID),
|
||||
),
|
||||
)
|
||||
p.StatementHandler = crdb.NewStatementHandler(ctx, config)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *notificationPolicyProjection) reducers() []handler.AggregateReducer {
|
||||
return []handler.AggregateReducer{
|
||||
{
|
||||
Aggregate: org.AggregateType,
|
||||
EventRedusers: []handler.EventReducer{
|
||||
{
|
||||
Event: org.NotificationPolicyAddedEventType,
|
||||
Reduce: p.reduceAdded,
|
||||
},
|
||||
{
|
||||
Event: org.NotificationPolicyChangedEventType,
|
||||
Reduce: p.reduceChanged,
|
||||
},
|
||||
{
|
||||
Event: org.NotificationPolicyRemovedEventType,
|
||||
Reduce: p.reduceRemoved,
|
||||
},
|
||||
{
|
||||
Event: org.OrgRemovedEventType,
|
||||
Reduce: p.reduceOwnerRemoved,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Aggregate: instance.AggregateType,
|
||||
EventRedusers: []handler.EventReducer{
|
||||
{
|
||||
Event: instance.InstanceRemovedEventType,
|
||||
Reduce: reduceInstanceRemovedHelper(NotificationPolicyColumnInstanceID),
|
||||
},
|
||||
{
|
||||
Event: instance.NotificationPolicyAddedEventType,
|
||||
Reduce: p.reduceAdded,
|
||||
},
|
||||
{
|
||||
Event: instance.NotificationPolicyChangedEventType,
|
||||
Reduce: p.reduceChanged,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *notificationPolicyProjection) reduceAdded(event eventstore.Event) (*handler.Statement, error) {
|
||||
var policyEvent policy.NotificationPolicyAddedEvent
|
||||
var isDefault bool
|
||||
switch e := event.(type) {
|
||||
case *org.NotificationPolicyAddedEvent:
|
||||
policyEvent = e.NotificationPolicyAddedEvent
|
||||
isDefault = false
|
||||
case *instance.NotificationPolicyAddedEvent:
|
||||
policyEvent = e.NotificationPolicyAddedEvent
|
||||
isDefault = true
|
||||
default:
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-x02s1m", "reduce.wrong.event.type %v", []eventstore.EventType{org.NotificationPolicyAddedEventType, instance.NotificationPolicyAddedEventType})
|
||||
}
|
||||
return crdb.NewCreateStatement(
|
||||
&policyEvent,
|
||||
[]handler.Column{
|
||||
handler.NewCol(NotificationPolicyColumnCreationDate, policyEvent.CreationDate()),
|
||||
handler.NewCol(NotificationPolicyColumnChangeDate, policyEvent.CreationDate()),
|
||||
handler.NewCol(NotificationPolicyColumnSequence, policyEvent.Sequence()),
|
||||
handler.NewCol(NotificationPolicyColumnID, policyEvent.Aggregate().ID),
|
||||
handler.NewCol(NotificationPolicyColumnStateCol, domain.PolicyStateActive),
|
||||
handler.NewCol(NotificationPolicyColumnPasswordChange, policyEvent.PasswordChange),
|
||||
handler.NewCol(NotificationPolicyColumnIsDefault, isDefault),
|
||||
handler.NewCol(NotificationPolicyColumnResourceOwner, policyEvent.Aggregate().ResourceOwner),
|
||||
handler.NewCol(NotificationPolicyColumnInstanceID, policyEvent.Aggregate().InstanceID),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (p *notificationPolicyProjection) reduceChanged(event eventstore.Event) (*handler.Statement, error) {
|
||||
var policyEvent policy.NotificationPolicyChangedEvent
|
||||
switch e := event.(type) {
|
||||
case *org.NotificationPolicyChangedEvent:
|
||||
policyEvent = e.NotificationPolicyChangedEvent
|
||||
case *instance.NotificationPolicyChangedEvent:
|
||||
policyEvent = e.NotificationPolicyChangedEvent
|
||||
default:
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-psom2h19", "reduce.wrong.event.type %v", []eventstore.EventType{org.NotificationPolicyChangedEventType, instance.NotificationPolicyChangedEventType})
|
||||
}
|
||||
cols := []handler.Column{
|
||||
handler.NewCol(NotificationPolicyColumnChangeDate, policyEvent.CreationDate()),
|
||||
handler.NewCol(NotificationPolicyColumnSequence, policyEvent.Sequence()),
|
||||
}
|
||||
if policyEvent.PasswordChange != nil {
|
||||
cols = append(cols, handler.NewCol(NotificationPolicyColumnPasswordChange, *policyEvent.PasswordChange))
|
||||
}
|
||||
return crdb.NewUpdateStatement(
|
||||
&policyEvent,
|
||||
cols,
|
||||
[]handler.Condition{
|
||||
handler.NewCond(NotificationPolicyColumnID, policyEvent.Aggregate().ID),
|
||||
handler.NewCond(NotificationPolicyColumnInstanceID, policyEvent.Aggregate().InstanceID),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (p *notificationPolicyProjection) reduceRemoved(event eventstore.Event) (*handler.Statement, error) {
|
||||
policyEvent, ok := event.(*org.NotificationPolicyRemovedEvent)
|
||||
if !ok {
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Po2iso2", "reduce.wrong.event.type %s", org.NotificationPolicyRemovedEventType)
|
||||
}
|
||||
return crdb.NewDeleteStatement(
|
||||
policyEvent,
|
||||
[]handler.Condition{
|
||||
handler.NewCond(NotificationPolicyColumnID, policyEvent.Aggregate().ID),
|
||||
handler.NewCond(NotificationPolicyColumnInstanceID, policyEvent.Aggregate().InstanceID),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (p *notificationPolicyProjection) reduceOwnerRemoved(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, ok := event.(*org.OrgRemovedEvent)
|
||||
if !ok {
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-poxi9a", "reduce.wrong.event.type %s", org.OrgRemovedEventType)
|
||||
}
|
||||
|
||||
return crdb.NewUpdateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(DomainPolicyChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(DomainPolicySequenceCol, e.Sequence()),
|
||||
handler.NewCol(DomainPolicyOwnerRemovedCol, true),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(DomainPolicyInstanceIDCol, e.Aggregate().InstanceID),
|
||||
handler.NewCond(DomainPolicyResourceOwnerCol, e.Aggregate().ID),
|
||||
},
|
||||
), nil
|
||||
}
|
258
internal/query/projection/notification_policy_test.go
Normal file
258
internal/query/projection/notification_policy_test.go
Normal file
@ -0,0 +1,258 @@
|
||||
package projection
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/handler"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
"github.com/zitadel/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
func TestNotificationPolicyProjection_reduces(t *testing.T) {
|
||||
type args struct {
|
||||
event func(t *testing.T) eventstore.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
reduce func(event eventstore.Event) (*handler.Statement, error)
|
||||
want wantReduce
|
||||
}{
|
||||
{
|
||||
name: "org reduceAdded",
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(org.NotificationPolicyAddedEventType),
|
||||
org.AggregateType,
|
||||
[]byte(`{
|
||||
"passwordChange": true
|
||||
}`),
|
||||
), org.NotificationPolicyAddedEventMapper),
|
||||
},
|
||||
reduce: (¬ificationPolicyProjection{}).reduceAdded,
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("org"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.notification_policies (creation_date, change_date, sequence, id, state, password_change, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"agg-id",
|
||||
domain.PolicyStateActive,
|
||||
true,
|
||||
false,
|
||||
"ro-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org reduceChanged",
|
||||
reduce: (¬ificationPolicyProjection{}).reduceChanged,
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(org.NotificationPolicyChangedEventType),
|
||||
org.AggregateType,
|
||||
[]byte(`{
|
||||
"passwordChange": true
|
||||
}`),
|
||||
), org.NotificationPolicyChangedEventMapper),
|
||||
},
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("org"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.notification_policies SET (change_date, sequence, password_change) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
true,
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org reduceRemoved",
|
||||
reduce: (¬ificationPolicyProjection{}).reduceRemoved,
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(org.NotificationPolicyRemovedEventType),
|
||||
org.AggregateType,
|
||||
nil,
|
||||
), org.NotificationPolicyRemovedEventMapper),
|
||||
},
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("org"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.notification_policies WHERE (id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: "instance reduceInstanceRemoved",
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(instance.InstanceRemovedEventType),
|
||||
instance.AggregateType,
|
||||
nil,
|
||||
), instance.InstanceRemovedEventMapper),
|
||||
},
|
||||
reduce: reduceInstanceRemovedHelper(NotificationPolicyColumnInstanceID),
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("instance"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.notification_policies WHERE (instance_id = $1)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "instance reduceAdded",
|
||||
reduce: (¬ificationPolicyProjection{}).reduceAdded,
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(instance.NotificationPolicyAddedEventType),
|
||||
instance.AggregateType,
|
||||
[]byte(`{
|
||||
"passwordChange": true
|
||||
}`),
|
||||
), instance.NotificationPolicyAddedEventMapper),
|
||||
},
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("instance"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.notification_policies (creation_date, change_date, sequence, id, state, password_change, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"agg-id",
|
||||
domain.PolicyStateActive,
|
||||
true,
|
||||
true,
|
||||
"ro-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "instance reduceChanged",
|
||||
reduce: (¬ificationPolicyProjection{}).reduceChanged,
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(instance.NotificationPolicyChangedEventType),
|
||||
instance.AggregateType,
|
||||
[]byte(`{
|
||||
"passwordChange": true
|
||||
}`),
|
||||
), instance.NotificationPolicyChangedEventMapper),
|
||||
},
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("instance"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.notification_policies SET (change_date, sequence, password_change) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
true,
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org.reduceOwnerRemoved",
|
||||
reduce: (¬ificationPolicyProjection{}).reduceOwnerRemoved,
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(org.OrgRemovedEventType),
|
||||
org.AggregateType,
|
||||
nil,
|
||||
), org.OrgRemovedEventMapper),
|
||||
},
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("org"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.notification_policies SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (resource_owner = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
true,
|
||||
"instance-id",
|
||||
"agg-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
event := baseEvent(t)
|
||||
got, err := tt.reduce(event)
|
||||
|
||||
if ok := errors.IsErrorInvalidArgument(err); !ok {
|
||||
t.Errorf("no wrong event mapping: %v, got: %v", err, got)
|
||||
}
|
||||
|
||||
event = tt.args.event(t)
|
||||
got, err = tt.reduce(event)
|
||||
assertReduce(t, got, err, NotificationPolicyProjectionTable, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
@ -60,6 +60,7 @@ var (
|
||||
DebugNotificationProviderProjection *debugNotificationProviderProjection
|
||||
KeyProjection *keyProjection
|
||||
SecurityPolicyProjection *securityPolicyProjection
|
||||
NotificationPolicyProjection *notificationPolicyProjection
|
||||
NotificationsProjection interface{}
|
||||
)
|
||||
|
||||
@ -133,6 +134,7 @@ func Create(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, c
|
||||
DebugNotificationProviderProjection = newDebugNotificationProviderProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["debug_notification_provider"]))
|
||||
KeyProjection = newKeyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["keys"]), keyEncryptionAlgorithm, certEncryptionAlgorithm)
|
||||
SecurityPolicyProjection = newSecurityPolicyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["security_policies"]))
|
||||
NotificationPolicyProjection = newNotificationPolicyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["notification_policies"]))
|
||||
newProjectionsList()
|
||||
return nil
|
||||
}
|
||||
@ -224,5 +226,6 @@ func newProjectionsList() {
|
||||
DebugNotificationProviderProjection,
|
||||
KeyProjection,
|
||||
SecurityPolicyProjection,
|
||||
NotificationPolicyProjection,
|
||||
}
|
||||
}
|
||||
|
@ -89,5 +89,7 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
|
||||
RegisterFilterEventMapper(AggregateType, InstanceDomainRemovedEventType, DomainRemovedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, InstanceAddedEventType, InstanceAddedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, InstanceChangedEventType, InstanceChangedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, InstanceRemovedEventType, InstanceRemovedEventMapper)
|
||||
RegisterFilterEventMapper(AggregateType, InstanceRemovedEventType, InstanceRemovedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, NotificationPolicyAddedEventType, NotificationPolicyAddedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, NotificationPolicyChangedEventType, NotificationPolicyChangedEventMapper)
|
||||
}
|
||||
|
73
internal/repository/instance/policy_notification.go
Normal file
73
internal/repository/instance/policy_notification.go
Normal file
@ -0,0 +1,73 @@
|
||||
package instance
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||
"github.com/zitadel/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
const (
|
||||
NotificationPolicyAddedEventType = instanceEventTypePrefix + policy.NotificationPolicyAddedEventType
|
||||
NotificationPolicyChangedEventType = instanceEventTypePrefix + policy.NotificationPolicyChangedEventType
|
||||
)
|
||||
|
||||
type NotificationPolicyAddedEvent struct {
|
||||
policy.NotificationPolicyAddedEvent
|
||||
}
|
||||
|
||||
func NewNotificationPolicyAddedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
passwordChange bool,
|
||||
) *NotificationPolicyAddedEvent {
|
||||
return &NotificationPolicyAddedEvent{
|
||||
NotificationPolicyAddedEvent: *policy.NewNotificationPolicyAddedEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
NotificationPolicyAddedEventType),
|
||||
passwordChange),
|
||||
}
|
||||
}
|
||||
|
||||
func NotificationPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
e, err := policy.NotificationPolicyAddedEventMapper(event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NotificationPolicyAddedEvent{NotificationPolicyAddedEvent: *e.(*policy.NotificationPolicyAddedEvent)}, nil
|
||||
}
|
||||
|
||||
type NotificationPolicyChangedEvent struct {
|
||||
policy.NotificationPolicyChangedEvent
|
||||
}
|
||||
|
||||
func NewNotificationPolicyChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
changes []policy.NotificationPolicyChanges,
|
||||
) (*NotificationPolicyChangedEvent, error) {
|
||||
changedEvent, err := policy.NewNotificationPolicyChangedEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
NotificationPolicyChangedEventType),
|
||||
changes,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &NotificationPolicyChangedEvent{NotificationPolicyChangedEvent: *changedEvent}, nil
|
||||
}
|
||||
|
||||
func NotificationPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
e, err := policy.NotificationPolicyChangedEventMapper(event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NotificationPolicyChangedEvent{NotificationPolicyChangedEvent: *e.(*policy.NotificationPolicyChangedEvent)}, nil
|
||||
}
|
@ -83,5 +83,8 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
|
||||
RegisterFilterEventMapper(AggregateType, FlowClearedEventType, FlowClearedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, MetadataSetType, MetadataSetEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, MetadataRemovedType, MetadataRemovedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, MetadataRemovedAllType, MetadataRemovedAllEventMapper)
|
||||
RegisterFilterEventMapper(AggregateType, MetadataRemovedAllType, MetadataRemovedAllEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, NotificationPolicyAddedEventType, NotificationPolicyAddedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, NotificationPolicyChangedEventType, NotificationPolicyChangedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, NotificationPolicyRemovedEventType, NotificationPolicyRemovedEventMapper)
|
||||
}
|
||||
|
102
internal/repository/org/policy_notification.go
Normal file
102
internal/repository/org/policy_notification.go
Normal file
@ -0,0 +1,102 @@
|
||||
package org
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||
"github.com/zitadel/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
var (
|
||||
NotificationPolicyAddedEventType = orgEventTypePrefix + policy.NotificationPolicyAddedEventType
|
||||
NotificationPolicyChangedEventType = orgEventTypePrefix + policy.NotificationPolicyChangedEventType
|
||||
NotificationPolicyRemovedEventType = orgEventTypePrefix + policy.NotificationPolicyRemovedEventType
|
||||
)
|
||||
|
||||
type NotificationPolicyAddedEvent struct {
|
||||
policy.NotificationPolicyAddedEvent
|
||||
}
|
||||
|
||||
func NewNotificationPolicyAddedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
passwordChange bool,
|
||||
) *NotificationPolicyAddedEvent {
|
||||
return &NotificationPolicyAddedEvent{
|
||||
NotificationPolicyAddedEvent: *policy.NewNotificationPolicyAddedEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
NotificationPolicyAddedEventType),
|
||||
passwordChange,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func NotificationPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
e, err := policy.NotificationPolicyAddedEventMapper(event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NotificationPolicyAddedEvent{NotificationPolicyAddedEvent: *e.(*policy.NotificationPolicyAddedEvent)}, nil
|
||||
}
|
||||
|
||||
type NotificationPolicyChangedEvent struct {
|
||||
policy.NotificationPolicyChangedEvent
|
||||
}
|
||||
|
||||
func NewNotificationPolicyChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
changes []policy.NotificationPolicyChanges,
|
||||
) (*NotificationPolicyChangedEvent, error) {
|
||||
changedEvent, err := policy.NewNotificationPolicyChangedEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
NotificationPolicyChangedEventType),
|
||||
changes,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &NotificationPolicyChangedEvent{NotificationPolicyChangedEvent: *changedEvent}, nil
|
||||
}
|
||||
|
||||
func NotificationPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
e, err := policy.NotificationPolicyChangedEventMapper(event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NotificationPolicyChangedEvent{NotificationPolicyChangedEvent: *e.(*policy.NotificationPolicyChangedEvent)}, nil
|
||||
}
|
||||
|
||||
type NotificationPolicyRemovedEvent struct {
|
||||
policy.NotificationPolicyRemovedEvent
|
||||
}
|
||||
|
||||
func NewNotificationPolicyRemovedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
) *NotificationPolicyRemovedEvent {
|
||||
return &NotificationPolicyRemovedEvent{
|
||||
NotificationPolicyRemovedEvent: *policy.NewNotificationPolicyRemovedEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
NotificationPolicyRemovedEventType),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func NotificationPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
e, err := policy.NotificationPolicyRemovedEventMapper(event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NotificationPolicyRemovedEvent{NotificationPolicyRemovedEvent: *e.(*policy.NotificationPolicyRemovedEvent)}, nil
|
||||
}
|
127
internal/repository/policy/policy_notification.go
Normal file
127
internal/repository/policy/policy_notification.go
Normal file
@ -0,0 +1,127 @@
|
||||
package policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||
)
|
||||
|
||||
const (
|
||||
NotificationPolicyAddedEventType = "policy.notification.added"
|
||||
NotificationPolicyChangedEventType = "policy.notification.changed"
|
||||
NotificationPolicyRemovedEventType = "policy.notification.removed"
|
||||
)
|
||||
|
||||
type NotificationPolicyAddedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
PasswordChange bool `json:"passwordChange,omitempty"`
|
||||
}
|
||||
|
||||
func (e *NotificationPolicyAddedEvent) Data() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *NotificationPolicyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewNotificationPolicyAddedEvent(
|
||||
base *eventstore.BaseEvent,
|
||||
passwordChange bool,
|
||||
) *NotificationPolicyAddedEvent {
|
||||
return &NotificationPolicyAddedEvent{
|
||||
BaseEvent: *base,
|
||||
PasswordChange: passwordChange,
|
||||
}
|
||||
}
|
||||
|
||||
func NotificationPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
e := &NotificationPolicyAddedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}
|
||||
|
||||
err := json.Unmarshal(event.Data, e)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "POLIC-0sp2nios", "unable to unmarshal policy")
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
type NotificationPolicyChangedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
PasswordChange *bool `json:"passwordChange,omitempty"`
|
||||
}
|
||||
|
||||
func (e *NotificationPolicyChangedEvent) Data() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *NotificationPolicyChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewNotificationPolicyChangedEvent(
|
||||
base *eventstore.BaseEvent,
|
||||
changes []NotificationPolicyChanges,
|
||||
) (*NotificationPolicyChangedEvent, error) {
|
||||
if len(changes) == 0 {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "POLICY-09sp2m", "Errors.NoChangesFound")
|
||||
}
|
||||
changeEvent := &NotificationPolicyChangedEvent{
|
||||
BaseEvent: *base,
|
||||
}
|
||||
for _, change := range changes {
|
||||
change(changeEvent)
|
||||
}
|
||||
return changeEvent, nil
|
||||
}
|
||||
|
||||
type NotificationPolicyChanges func(*NotificationPolicyChangedEvent)
|
||||
|
||||
func ChangePasswordChange(passwordChange bool) func(*NotificationPolicyChangedEvent) {
|
||||
return func(e *NotificationPolicyChangedEvent) {
|
||||
e.PasswordChange = &passwordChange
|
||||
}
|
||||
}
|
||||
|
||||
func NotificationPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
e := &NotificationPolicyChangedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}
|
||||
|
||||
err := json.Unmarshal(event.Data, e)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "POLIC-09s2oss", "unable to unmarshal policy")
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
type NotificationPolicyRemovedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
}
|
||||
|
||||
func (e *NotificationPolicyRemovedEvent) Data() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *NotificationPolicyRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewNotificationPolicyRemovedEvent(base *eventstore.BaseEvent) *NotificationPolicyRemovedEvent {
|
||||
return &NotificationPolicyRemovedEvent{
|
||||
BaseEvent: *base,
|
||||
}
|
||||
}
|
||||
|
||||
func NotificationPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
return &NotificationPolicyRemovedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}, nil
|
||||
}
|
@ -59,6 +59,7 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
|
||||
RegisterFilterEventMapper(AggregateType, HumanPasswordChangedType, HumanPasswordChangedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, HumanPasswordCodeAddedType, HumanPasswordCodeAddedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, HumanPasswordCodeSentType, HumanPasswordCodeSentEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, HumanPasswordChangeSentType, HumanPasswordChangeSentEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, HumanPasswordCheckSucceededType, HumanPasswordCheckSucceededEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, HumanPasswordCheckFailedType, HumanPasswordCheckFailedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, UserIDPLinkAddedType, UserIDPLinkAddedEventMapper).
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
const (
|
||||
passwordEventPrefix = humanEventPrefix + "password."
|
||||
HumanPasswordChangedType = passwordEventPrefix + "changed"
|
||||
HumanPasswordChangeSentType = passwordEventPrefix + "change.sent"
|
||||
HumanPasswordCodeAddedType = passwordEventPrefix + "code.added"
|
||||
HumanPasswordCodeSentType = passwordEventPrefix + "code.sent"
|
||||
HumanPasswordCheckSucceededType = passwordEventPrefix + "check.succeeded"
|
||||
@ -144,6 +145,34 @@ func HumanPasswordCodeSentEventMapper(event *repository.Event) (eventstore.Event
|
||||
}, nil
|
||||
}
|
||||
|
||||
type HumanPasswordChangeSentEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
}
|
||||
|
||||
func (e *HumanPasswordChangeSentEvent) Data() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *HumanPasswordChangeSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewHumanPasswordChangeSentEvent(ctx context.Context, aggregate *eventstore.Aggregate) *HumanPasswordChangeSentEvent {
|
||||
return &HumanPasswordChangeSentEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
HumanPasswordChangeSentType,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func HumanPasswordChangeSentEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
return &HumanPasswordChangeSentEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type HumanPasswordCheckSucceededEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
*AuthRequestInfo
|
||||
|
@ -225,6 +225,10 @@ Errors:
|
||||
Empty: Org IAM Policy ist leer
|
||||
NotExisting: Org IAM Policy existiert nicht
|
||||
AlreadyExists: Org IAM Policy existiert bereits
|
||||
NotificationPolicy:
|
||||
NotFound: Notification Policy konnte nicht gefunden werden
|
||||
NotChanged: Notification Policy wurde nicht verändert
|
||||
AlreadyExists: Notification Policy existiert bereits
|
||||
Project:
|
||||
ProjectIDMissing: Project ID fehlt
|
||||
AlreadyExists: Project existiert bereits auf der Organisation
|
||||
@ -351,6 +355,10 @@ Errors:
|
||||
AlreadyExists: Default Org IAM Policy existiert bereits
|
||||
Empty: Default Org IAM Policy leer
|
||||
NotChanged: Default Org IAM Policy wurde nicht verändert
|
||||
NotificationPolicy:
|
||||
NotFound: Default Notification Policy konnte nicht gefunden werden
|
||||
NotChanged: Default Notification Policy wurde nicht verändert
|
||||
AlreadyExists: Default Notification Policy existiert bereits
|
||||
Policy:
|
||||
AlreadyExists: Policy existiert bereits
|
||||
Label:
|
||||
@ -750,6 +758,10 @@ EventTypes:
|
||||
added: Passwortaussperrrichtlinie hinzugefügt
|
||||
changed: Passwortaussperrrichtlinie geändert
|
||||
removed: Passwortaussperrrichtlinie gelöscht
|
||||
notification:
|
||||
added: Notifikation Richtlinie hinzugefügt
|
||||
changed: Notifikation Richtlinie geändert
|
||||
removed: Notifikation Richtlinie entfernt
|
||||
flow:
|
||||
trigger_actions:
|
||||
set: Aktionen festgelegt
|
||||
@ -980,7 +992,7 @@ EventTypes:
|
||||
removed: Instanzmitglied gelöscht
|
||||
cascade:
|
||||
removed: Instanzmitglied kaskadierend gelöscht
|
||||
notification:
|
||||
notification:
|
||||
provider:
|
||||
debug:
|
||||
fileadded: Datei zu Debug Notification Provider hinzugefügt
|
||||
@ -1047,7 +1059,7 @@ EventTypes:
|
||||
changed: Datenschutzrichtlinie geändert
|
||||
security:
|
||||
set: Sicherheitsrichtlinie gesetzt
|
||||
|
||||
|
||||
removed: Instanz gelöscht
|
||||
secret:
|
||||
generator:
|
||||
|
@ -225,6 +225,10 @@ Errors:
|
||||
Empty: Org IAM Policy is empty
|
||||
NotExisting: Org IAM Policy doesn't exist
|
||||
AlreadyExists: Org IAM Policy already exists
|
||||
NotificationPolicy:
|
||||
NotFound: Notification Policy not found
|
||||
NotChanged: Notification Policy not changed
|
||||
AlreadyExists: Notification Policy already exists
|
||||
Project:
|
||||
ProjectIDMissing: Project Id missing
|
||||
AlreadyExists: Project already exists on organization
|
||||
@ -351,6 +355,10 @@ Errors:
|
||||
NotExisting: Org IAM Policy not existing
|
||||
AlreadyExists: Org IAM Policy already exists
|
||||
NotChanged: Org IAM Policy has not been changed
|
||||
NotificationPolicy:
|
||||
NotFound: Default Notification Policy not found
|
||||
NotChanged: Default Notification Policy not changed
|
||||
AlreadyExists: Default Notification Policy already exists
|
||||
Policy:
|
||||
AlreadyExists: Policy already exists
|
||||
Label:
|
||||
@ -750,6 +758,10 @@ EventTypes:
|
||||
added: Lockout policy added
|
||||
changed: Lockout policy changed
|
||||
removed: Lockout policy removed
|
||||
notification:
|
||||
added: Notification policy added
|
||||
changed: Notification policy changed
|
||||
removed: Notification policy removed
|
||||
flow:
|
||||
trigger_actions:
|
||||
set: Action set
|
||||
@ -1047,7 +1059,7 @@ EventTypes:
|
||||
changed: Privacy policy changed
|
||||
security:
|
||||
set: Security policy set
|
||||
|
||||
|
||||
removed: Instance removed
|
||||
secret:
|
||||
generator:
|
||||
|
@ -225,6 +225,10 @@ Errors:
|
||||
Empty: La politique IAM d'Org est vide
|
||||
NotExisting: La politique Org IAM n'existe pas
|
||||
AlreadyExists: La politique IAM d'Org existe déjà
|
||||
NotificationPolicy:
|
||||
NotFound: La politique notification n'a pas été trouvée
|
||||
NotChanged: La politique notification n'a pas été modifiée
|
||||
AlreadyExists: La politique notification existe déjà
|
||||
Project:
|
||||
ProjectIDMissing: Id de projet manquant
|
||||
AlreadyExists: Le projet existe déjà dans l'organisation
|
||||
@ -351,6 +355,10 @@ Errors:
|
||||
NotExisting: La politique IAM d'Org n'existe pas
|
||||
AlreadyExists: La politique IAM d'Org existe déjà
|
||||
NotChanged: La politique IAM d'Org n'a pas été modifiée
|
||||
NotificationPolicy:
|
||||
NotFound: La politique de notification par défaut n'a pas été trouvée
|
||||
NotChanged: La politique de notification par défaut n'a pas été modifiée
|
||||
AlreadyExists: La ppolitique de notification par défaut existe déjà
|
||||
Policy:
|
||||
AlreadyExists: La politique existe déjà
|
||||
Label:
|
||||
@ -725,6 +733,10 @@ EventTypes:
|
||||
added: Politique de confidentialité et CGU ajoutés
|
||||
changed: Politique de confidentialité et CGU modifiées
|
||||
removed: Politique de confidentialité et conditions d'utilisation supprimées
|
||||
notification:
|
||||
added: Politique de notification ajoutée
|
||||
changed: Politique de notification modifiée
|
||||
removed: Politique de notification supprimée
|
||||
flow:
|
||||
trigger_actions:
|
||||
set: Action set
|
||||
|
@ -225,6 +225,10 @@ Errors:
|
||||
Empty: Mancano le impostazioni Org IAM
|
||||
NotExisting: Impostazioni Org IAM non esistenti
|
||||
AlreadyExists: Impostazioni Org IAM già esistenti
|
||||
NotificationPolicy:
|
||||
NotFound: Impostazioni di notifica non trovate
|
||||
NotChanged: Impostazioni di notifica non è stato cambiato
|
||||
AlreadyExists: Impostazioni di notifica già esistente
|
||||
Project:
|
||||
ProjectIDMissing: ID del progetto mancante
|
||||
AlreadyExists: Il progetto è già stato creato nell'organizzazione
|
||||
@ -351,6 +355,10 @@ Errors:
|
||||
NotExisting: Impostazioni Org IAM non esistenti
|
||||
AlreadyExists: Impostazioni Org IAM già esistenti
|
||||
NotChanged: Impostazioni Org IAM non sono state cambiate
|
||||
NotificationPolicy:
|
||||
NotFound: Impostazioni di notifica predefinite non trovate
|
||||
NotChanged: Impostazioni di notifica predefinite non è stato cambiato
|
||||
AlreadyExists: Impostazioni di notifica predefinite già esistente
|
||||
Policy:
|
||||
AlreadyExists: Impostazioni già esistenti
|
||||
Label:
|
||||
@ -725,6 +733,10 @@ EventTypes:
|
||||
added: Informativa sulla privacy e termini e condizioni aggiunti
|
||||
changed: Informativa sulla privacy e termini e condizioni cambiati
|
||||
removed: Informativa sulla privacy e termini e condizioni rimossi
|
||||
notification:
|
||||
added: Impostazione di notifica creata
|
||||
changed: Impostazione di notifica cambiata
|
||||
removed: Impostazione di notifica rimossa
|
||||
flow:
|
||||
trigger_actions:
|
||||
set: azioni salvate
|
||||
|
@ -225,6 +225,10 @@ Errors:
|
||||
Empty: 组织 IAM 策略为空
|
||||
NotExisting: 组织 IAM 策略不存在
|
||||
AlreadyExists: 组织 IAM 策略已存在
|
||||
NotificationPolicy:
|
||||
NotFound: 未找到通知政策
|
||||
NotChanged: 通知政策没有改变
|
||||
AlreadyExists: 已经存在的通知政策
|
||||
Project:
|
||||
ProjectIDMissing: P缺少项目 ID
|
||||
AlreadyExists: 项目以存在于组织中
|
||||
@ -351,6 +355,10 @@ Errors:
|
||||
NotExisting: 组织 IAM 策略不存在
|
||||
AlreadyExists: 组织 IAM 策略已存在
|
||||
NotChanged: 组织 IAM 策略未更改
|
||||
NotificationPolicy:
|
||||
NotFound: 没有找到默认的通知政策
|
||||
NotChanged: 默认的通知政策没有改变
|
||||
AlreadyExists: 默认的通知政策已经存在
|
||||
Policy:
|
||||
AlreadyExists: 策略已存在
|
||||
Label:
|
||||
@ -715,6 +723,10 @@ EventTypes:
|
||||
added: 添加隐私政策和服务条款
|
||||
changed: 更改隐私政策和服务条款
|
||||
removed: 删除隐私政策和服务条款
|
||||
notification:
|
||||
added: 增加了通知政策
|
||||
changed: 通知政策改变
|
||||
removed: 删除了通知政策
|
||||
flow:
|
||||
trigger_actions:
|
||||
set: 设置动作
|
||||
|
@ -1945,6 +1945,91 @@ service AdminService {
|
||||
};
|
||||
}
|
||||
|
||||
//Add a default notification policy for ZITADEL
|
||||
// it impacts all organisations without a customised policy
|
||||
rpc AddNotificationPolicy(AddNotificationPolicyRequest) returns (AddNotificationPolicyResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/policies/notification"
|
||||
body: "*"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "iam.policy.write";
|
||||
};
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||
tags: "policy";
|
||||
tags: "notification policy";
|
||||
tags: "notification";
|
||||
responses: {
|
||||
key: "200";
|
||||
value: {
|
||||
description: "default notification policy";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//Returns the notification policy defined by the administrators of ZITADEL
|
||||
rpc GetNotificationPolicy(GetNotificationPolicyRequest) returns (GetNotificationPolicyResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/policies/notification";
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "iam.policy.read";
|
||||
};
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||
tags: "policy";
|
||||
tags: "notification policy";
|
||||
tags: "notification";
|
||||
responses: {
|
||||
key: "200";
|
||||
value: {
|
||||
description: "default notification policy";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
//Updates the default notification policy of ZITADEL
|
||||
// it impacts all organisations without a customised policy
|
||||
rpc UpdateNotificationPolicy(UpdateNotificationPolicyRequest) returns (UpdateNotificationPolicyResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/policies/notification";
|
||||
body: "*";
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "iam.policy.write";
|
||||
};
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||
tags: "policy";
|
||||
tags: "notification policy";
|
||||
tags: "notification";
|
||||
responses: {
|
||||
key: "200";
|
||||
value: {
|
||||
description: "default notification policy updated";
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
key: "400";
|
||||
value: {
|
||||
description: "invalid argument";
|
||||
schema: {
|
||||
json_schema: {
|
||||
ref: "#/definitions/rpcStatus";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
//Returns the default text for initial message (translation file)
|
||||
rpc GetDefaultInitMessageText(GetDefaultInitMessageTextRequest) returns (GetDefaultInitMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
@ -1967,10 +2052,11 @@ service AdminService {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//Sets the default custom text for initial message
|
||||
// it impacts all organisations without customized initial message text
|
||||
// The Following Variables can be used:
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
rpc SetDefaultInitMessageText(SetDefaultInitMessageTextRequest) returns (SetDefaultInitMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/text/message/init/{language}";
|
||||
@ -2019,7 +2105,7 @@ service AdminService {
|
||||
//Sets the default custom text for password reset message
|
||||
// it impacts all organisations without customized password reset message text
|
||||
// The Following Variables can be used:
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
rpc SetDefaultPasswordResetMessageText(SetDefaultPasswordResetMessageTextRequest) returns (SetDefaultPasswordResetMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/text/message/passwordreset/{language}";
|
||||
@ -2069,7 +2155,7 @@ service AdminService {
|
||||
//Sets the default custom text for verify email message
|
||||
// it impacts all organisations without customized verify email message text
|
||||
// The Following Variables can be used:
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
rpc SetDefaultVerifyEmailMessageText(SetDefaultVerifyEmailMessageTextRequest) returns (SetDefaultVerifyEmailMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/text/message/verifyemail/{language}";
|
||||
@ -2118,7 +2204,7 @@ service AdminService {
|
||||
//Sets the default custom text for verify phone message
|
||||
// it impacts all organisations without customized verify phone message text
|
||||
// The Following Variables can be used:
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
rpc SetDefaultVerifyPhoneMessageText(SetDefaultVerifyPhoneMessageTextRequest) returns (SetDefaultVerifyPhoneMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/text/message/verifyphone/{language}";
|
||||
@ -2164,10 +2250,10 @@ service AdminService {
|
||||
};
|
||||
}
|
||||
|
||||
//Sets the default custom text for domain claimed phone message
|
||||
//Sets the default custom text for domain claimed message
|
||||
// it impacts all organisations without customized domain claimed message text
|
||||
// The Following Variables can be used:
|
||||
// {{.Domain}} {{.TempUsername}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
// {{.Domain}} {{.TempUsername}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
rpc SetDefaultDomainClaimedMessageText(SetDefaultDomainClaimedMessageTextRequest) returns (SetDefaultDomainClaimedMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/text/message/domainclaimed/{language}";
|
||||
@ -2216,7 +2302,7 @@ service AdminService {
|
||||
//Sets the default custom text for passwordless registration message
|
||||
// it impacts all organisations without customized passwordless registration message text
|
||||
// The Following Variables can be used:
|
||||
// {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
// {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
rpc SetDefaultPasswordlessRegistrationMessageText(SetDefaultPasswordlessRegistrationMessageTextRequest) returns (SetDefaultPasswordlessRegistrationMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/text/message/passwordless_registration/{language}";
|
||||
@ -2240,6 +2326,57 @@ service AdminService {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//Returns the default text for password change message (translation file)
|
||||
rpc GetDefaultPasswordChangeMessageText(GetDefaultPasswordChangeMessageTextRequest) returns (GetDefaultPasswordChangeMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/text/default/message/password_change/{language}";
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "iam.policy.read";
|
||||
};
|
||||
}
|
||||
|
||||
//Returns the custom text for password change message (overwritten in eventstore)
|
||||
rpc GetCustomPasswordChangeMessageText(GetCustomPasswordChangeMessageTextRequest) returns (GetCustomPasswordChangeMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/text/message/password_change/{language}";
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "iam.policy.read";
|
||||
};
|
||||
}
|
||||
|
||||
//Sets the default custom text for password change message
|
||||
// it impacts all organisations without customized password change message text
|
||||
// The Following Variables can be used:
|
||||
// {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
rpc SetDefaultPasswordChangeMessageText(SetDefaultPasswordChangeMessageTextRequest) returns (SetDefaultPasswordChangeMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/text/message/password_change/{language}";
|
||||
body: "*";
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "iam.policy.write";
|
||||
};
|
||||
}
|
||||
|
||||
// Removes the custom password change message text of the system
|
||||
// The default text from the translation file will trigger after
|
||||
rpc ResetCustomPasswordChangeMessageTextToDefault(ResetCustomPasswordChangeMessageTextToDefaultRequest) returns (ResetCustomPasswordChangeMessageTextToDefaultResponse) {
|
||||
option (google.api.http) = {
|
||||
delete: "/text/message/password_change/{language}"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "iam.policy.delete"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//Returns the default custom texts for login ui (translation file)
|
||||
rpc GetDefaultLoginTexts(GetDefaultLoginTextsRequest) returns (GetDefaultLoginTextsResponse) {
|
||||
option (google.api.http) = {
|
||||
@ -4111,6 +4248,29 @@ message UpdatePrivacyPolicyResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
message AddNotificationPolicyRequest {
|
||||
bool password_change = 1;
|
||||
}
|
||||
|
||||
message AddNotificationPolicyResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
//This is an empty request
|
||||
message GetNotificationPolicyRequest {}
|
||||
|
||||
message GetNotificationPolicyResponse {
|
||||
zitadel.policy.v1.NotificationPolicy policy = 1;
|
||||
}
|
||||
|
||||
message UpdateNotificationPolicyRequest {
|
||||
bool password_change = 1;
|
||||
}
|
||||
|
||||
message UpdateNotificationPolicyResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
message GetDefaultInitMessageTextRequest {
|
||||
string language = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
}
|
||||
@ -4331,6 +4491,51 @@ message ResetCustomDomainClaimedMessageTextToDefaultResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
message GetDefaultPasswordChangeMessageTextRequest {
|
||||
string language = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
}
|
||||
|
||||
message GetDefaultPasswordChangeMessageTextResponse {
|
||||
zitadel.text.v1.MessageCustomText custom_text = 1;
|
||||
}
|
||||
|
||||
message GetCustomPasswordChangeMessageTextRequest {
|
||||
string language = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
}
|
||||
|
||||
message GetCustomPasswordChangeMessageTextResponse {
|
||||
zitadel.text.v1.MessageCustomText custom_text = 1;
|
||||
}
|
||||
|
||||
message SetDefaultPasswordChangeMessageTextRequest {
|
||||
string language = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"de\""
|
||||
}
|
||||
];
|
||||
string title = 2 [(validate.rules).string = {max_len: 200}];
|
||||
string pre_header = 3 [(validate.rules).string = {max_len: 200}];
|
||||
string subject = 4 [(validate.rules).string = {max_len: 200}];
|
||||
string greeting = 5 [(validate.rules).string = {max_len: 200}];
|
||||
string text = 6 [(validate.rules).string = {max_len: 800}];
|
||||
string button_text = 7 [(validate.rules).string = {max_len: 200}];
|
||||
string footer_text = 8 [(validate.rules).string = {max_len: 200}];
|
||||
}
|
||||
|
||||
message SetDefaultPasswordChangeMessageTextResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
message ResetCustomPasswordChangeMessageTextToDefaultRequest {
|
||||
string language = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
}
|
||||
|
||||
message ResetCustomPasswordChangeMessageTextToDefaultResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
|
||||
message GetDefaultPasswordlessRegistrationMessageTextRequest {
|
||||
string language = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
}
|
||||
|
@ -2248,7 +2248,7 @@ service ManagementService {
|
||||
};
|
||||
}
|
||||
|
||||
// Update the privacy complexity policy for the organisation
|
||||
// Update the privacy policy for the organisation
|
||||
// With this policy privacy relevant things can be configured (e.g. tos link)
|
||||
// Variable {{.Lang}} can be set to have different links based on the language
|
||||
rpc UpdateCustomPrivacyPolicy(UpdateCustomPrivacyPolicyRequest) returns (UpdateCustomPrivacyPolicyResponse) {
|
||||
@ -2274,6 +2274,68 @@ service ManagementService {
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the notification policy of the organisation
|
||||
// With this notification policy it can be configured how users should be notified
|
||||
rpc GetNotificationPolicy(GetNotificationPolicyRequest) returns (GetNotificationPolicyResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/policies/notification"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "policy.read"
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the default notification policy of the IAM
|
||||
// With this notification privacy it can be configured how users should be notified
|
||||
rpc GetDefaultNotificationPolicy(GetDefaultNotificationPolicyRequest) returns (GetDefaultNotificationPolicyResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/policies/default/notification"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "policy.read"
|
||||
};
|
||||
}
|
||||
|
||||
// Add a custom notification policy for the organisation
|
||||
// With this notification privacy it can be configured how users should be notified
|
||||
rpc AddCustomNotificationPolicy(AddCustomNotificationPolicyRequest) returns (AddCustomNotificationPolicyResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/policies/notification"
|
||||
body: "*"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "policy.write"
|
||||
};
|
||||
}
|
||||
|
||||
// Update the notification policy for the organisation
|
||||
// With this notification privacy it can be configured how users should be notified
|
||||
rpc UpdateCustomNotificationPolicy(UpdateCustomNotificationPolicyRequest) returns (UpdateCustomNotificationPolicyResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/policies/notification"
|
||||
body: "*"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "policy.write"
|
||||
};
|
||||
}
|
||||
|
||||
// Removes the notification policy of the organisation
|
||||
// The default policy of the IAM will trigger after
|
||||
rpc ResetNotificationPolicyToDefault(ResetNotificationPolicyToDefaultRequest) returns (ResetNotificationPolicyToDefaultResponse) {
|
||||
option (google.api.http) = {
|
||||
delete: "/policies/notification"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "policy.delete"
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the active label policy of the organisation
|
||||
// With this policy the private labeling can be configured (colors, etc.)
|
||||
rpc GetLabelPolicy(GetLabelPolicyRequest) returns (GetLabelPolicyResponse) {
|
||||
@ -2487,7 +2549,7 @@ service ManagementService {
|
||||
|
||||
// Sets the custom text for password reset message
|
||||
// The Following Variables can be used:
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
rpc SetCustomPasswordResetMessageText(SetCustomPasswordResetMessageTextRequest) returns (SetCustomPasswordResetMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/text/message/passwordreset/{language}";
|
||||
@ -2535,7 +2597,7 @@ service ManagementService {
|
||||
|
||||
// Sets the custom text for verify email message
|
||||
// The Following Variables can be used:
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
rpc SetCustomVerifyEmailMessageText(SetCustomVerifyEmailMessageTextRequest) returns (SetCustomVerifyEmailMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/text/message/verifyemail/{language}";
|
||||
@ -2583,7 +2645,7 @@ service ManagementService {
|
||||
|
||||
// Sets the default custom text for verify email message
|
||||
// The Following Variables can be used:
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
// {{.Code}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
rpc SetCustomVerifyPhoneMessageText(SetCustomVerifyPhoneMessageTextRequest) returns (SetCustomVerifyPhoneMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/text/message/verifyphone/{language}";
|
||||
@ -2631,7 +2693,7 @@ service ManagementService {
|
||||
|
||||
// Sets the custom text for domain claimed message
|
||||
// The Following Variables can be used:
|
||||
// {{.Domain}} {{.TempUsername}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
// {{.Domain}} {{.TempUsername}} {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
rpc SetCustomDomainClaimedMessageCustomText(SetCustomDomainClaimedMessageTextRequest) returns (SetCustomDomainClaimedMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/text/message/domainclaimed/{language}";
|
||||
@ -2679,7 +2741,7 @@ service ManagementService {
|
||||
|
||||
// Sets the custom text for passwordless link message
|
||||
// The Following Variables can be used:
|
||||
// {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}}
|
||||
// {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
rpc SetCustomPasswordlessRegistrationMessageCustomText(SetCustomPasswordlessRegistrationMessageTextRequest) returns (SetCustomPasswordlessRegistrationMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/text/message/passwordless_registration/{language}";
|
||||
@ -2703,6 +2765,54 @@ service ManagementService {
|
||||
};
|
||||
}
|
||||
|
||||
//Returns the custom text for password change message
|
||||
rpc GetCustomPasswordChangeMessageText(GetCustomPasswordChangeMessageTextRequest) returns (GetCustomPasswordChangeMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/text/message/password_change/{language}";
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "policy.read";
|
||||
};
|
||||
}
|
||||
|
||||
//Returns the custom text for password change link message
|
||||
rpc GetDefaultPasswordChangeMessageText(GetDefaultPasswordChangeMessageTextRequest) returns (GetDefaultPasswordChangeMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/text/default/message/password_change/{language}";
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "policy.read";
|
||||
};
|
||||
}
|
||||
|
||||
// Sets the custom text for password change message
|
||||
// The Following Variables can be used:
|
||||
// {{.UserName}} {{.FirstName}} {{.LastName}} {{.NickName}} {{.DisplayName}} {{.LastEmail}} {{.VerifiedEmail}} {{.LastPhone}} {{.VerifiedPhone}} {{.PreferredLoginName}} {{.LoginNames}} {{.ChangeDate}} {{.CreationDate}}
|
||||
rpc SetCustomPasswordChangeMessageCustomText(SetCustomPasswordChangeMessageTextRequest) returns (SetCustomPasswordChangeMessageTextResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/text/message/password_change/{language}";
|
||||
body: "*";
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "policy.write";
|
||||
};
|
||||
}
|
||||
|
||||
// Removes the custom password change message text of the organisation
|
||||
// The default text of the IAM will trigger after
|
||||
rpc ResetCustomPasswordChangeMessageTextToDefault(ResetCustomPasswordChangeMessageTextToDefaultRequest) returns (ResetCustomPasswordChangeMessageTextToDefaultResponse) {
|
||||
option (google.api.http) = {
|
||||
delete: "/text/message/password_change/{language}"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "policy.delete"
|
||||
};
|
||||
}
|
||||
|
||||
//Returns the custom texts for login ui
|
||||
rpc GetCustomLoginTexts(GetCustomLoginTextsRequest) returns (GetCustomLoginTextsResponse) {
|
||||
option (google.api.http) = {
|
||||
@ -4941,6 +5051,43 @@ message ResetPrivacyPolicyToDefaultResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
//This is an empty request
|
||||
message GetNotificationPolicyRequest {}
|
||||
|
||||
message GetNotificationPolicyResponse {
|
||||
zitadel.policy.v1.NotificationPolicy policy = 1;
|
||||
}
|
||||
|
||||
//This is an empty request
|
||||
message GetDefaultNotificationPolicyRequest {}
|
||||
|
||||
message GetDefaultNotificationPolicyResponse {
|
||||
zitadel.policy.v1.NotificationPolicy policy = 1;
|
||||
}
|
||||
|
||||
message AddCustomNotificationPolicyRequest {
|
||||
bool password_change = 1;
|
||||
}
|
||||
|
||||
message AddCustomNotificationPolicyResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
message UpdateCustomNotificationPolicyRequest {
|
||||
bool password_change = 1;
|
||||
}
|
||||
|
||||
message UpdateCustomNotificationPolicyResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
//This is an empty request
|
||||
message ResetNotificationPolicyToDefaultRequest {}
|
||||
|
||||
message ResetNotificationPolicyToDefaultResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
//This is an empty request
|
||||
message GetLabelPolicyRequest {}
|
||||
|
||||
@ -5393,6 +5540,51 @@ message ResetCustomPasswordlessRegistrationMessageTextToDefaultResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
|
||||
message GetCustomPasswordChangeMessageTextRequest {
|
||||
string language = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
}
|
||||
|
||||
message GetCustomPasswordChangeMessageTextResponse {
|
||||
zitadel.text.v1.MessageCustomText custom_text = 1;
|
||||
}
|
||||
|
||||
message GetDefaultPasswordChangeMessageTextRequest {
|
||||
string language = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
}
|
||||
|
||||
message GetDefaultPasswordChangeMessageTextResponse {
|
||||
zitadel.text.v1.MessageCustomText custom_text = 1;
|
||||
}
|
||||
|
||||
message SetCustomPasswordChangeMessageTextRequest {
|
||||
string language = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"de\""
|
||||
}
|
||||
];
|
||||
string title = 2 [(validate.rules).string = {max_len: 200}];
|
||||
string pre_header = 3 [(validate.rules).string = {max_len: 200}];
|
||||
string subject = 4 [(validate.rules).string = {max_len: 200}];
|
||||
string greeting = 5 [(validate.rules).string = {max_len: 200}];
|
||||
string text = 6 [(validate.rules).string = {max_len: 800}];
|
||||
string button_text = 7 [(validate.rules).string = {max_len: 200}];
|
||||
string footer_text = 8 [(validate.rules).string = {max_len: 200}];
|
||||
}
|
||||
|
||||
message SetCustomPasswordChangeMessageTextResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
message ResetCustomPasswordChangeMessageTextToDefaultRequest {
|
||||
string language = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
}
|
||||
|
||||
message ResetCustomPasswordChangeMessageTextToDefaultResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
message GetOrgIDPByIDRequest {
|
||||
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
}
|
||||
|
@ -285,3 +285,9 @@ message PrivacyPolicy {
|
||||
bool is_default = 4;
|
||||
string help_link = 5;
|
||||
}
|
||||
|
||||
message NotificationPolicy {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
bool is_default = 2;
|
||||
bool password_change = 3;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user