feat: SMTP Templates (#6932)

* feat: smtp templates poc

* feat: add isActive & ProviderType to SMTP backend

* feat: change providertype to uint32 and fix tests

* feat: minimal smtp provider component

* feat: woking on diiferent providers

* feat: keep working on providers

* feat: initial stepper for new provider

* fix: settings list and working on stepper

* feat: step 1 and 2 form inputs

* feat: starter for smtp test step

* fix: misspelled SMPT

* fix: remove tests for now

* feat: add tls toggle remove old google provider

* feat: working on add smtp and table

* fix: duplicated identifiers

* fix: settings list

* fix: add missing smtp config properties

* fix: add configID to smtp config table

* fix: working on listproviders

* feat: working in listSMTPConfigs

* fix: add count to listsmtpconfigs

* fix: getting empty results from listSMTPConfigs

* feat: table now shows real data

* fix: remaining styles for smtp-table

* fix: remove old notification-smtp-provider-component

* feat: delete smtp configuration

* feat: deactivate smtp config

* feat: replace isActive with state for smtp config

* feat: activate smtp config

* fix: remaining errors after main merge

* fix: list smtp providers panic and material mdc

* feat: refactor to only one provider component

* feat: current provider details view

* fix: refactor AddSMTPConfig and ChangeSMTPConfig

* fix: smtp config reduce issue

* fix: recover domain in NewIAMSMTPConfigWriteModel

* fix: add code needed by SetUpInstance

* fix: go tests and warn about passing context to InstanceAggregateFromWriteModel

* fix: i18n and add missing trans for fr, it, zh

* fix: add e2e tests

* docs: add smtp templates

* fix: remove provider_type, add description

* fix: remaining error from merge main

* fix: add @stebenz change for primary key

* fix: inactive placed after removed to prevent deleted configs to show as inactive

* fix: smtp provider id can be empty (migrated)

* feat: add mailchimp transactional template

* feat: add Brevo (Sendinblue) template

* feat: change brevo logo, add color to tls icon

* fix: queries use resourceowner, id must not be empty

* fix: deal with old smtp settings and tests

* fix: resourceOwner is the instanceID

* fix: remove aggregate_id, rename SMTPConfigByAggregateID with SMTPConfigActive

* fix: add tests for multiple configs with different IDs

* fix: conflict

* fix: remove notification-smtp-provider

* fix: add @peintnermax suggestions, rename module and fix e2e tests

* fix: remove material legacy modules

* fix: remove ctx as parameter for  InstanceAggregateFromWriteModel

* fix: add Id to SMTPConfigToPb

* fix:  change InstanceAggregateFromWriteModel to avoid linter errors

* fix import

* rm unused package-lock

* update yarn lock

---------

Co-authored-by: Elio Bischof <elio@zitadel.com>
Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
This commit is contained in:
Miguel Cabrerizo 2024-04-11 09:16:10 +02:00 committed by GitHub
parent e2f0cd034a
commit d229da6af7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
93 changed files with 6359 additions and 6132 deletions

View File

@ -159,6 +159,8 @@ export class AppComponent implements OnDestroy {
this.matIconRegistry.addSvgIcon('mdi_jwt', this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/jwt.svg'));
this.matIconRegistry.addSvgIcon('mdi_smtp', this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/mail.svg'));
this.matIconRegistry.addSvgIcon('mdi_symbol', this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/symbol.svg'));
this.matIconRegistry.addSvgIcon(

View File

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

View File

@ -10,7 +10,7 @@ import {
import { SMSProvider, TwilioConfig } from 'src/app/proto/generated/zitadel/settings_pb';
import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service';
import { PasswordDialogComponent } from '../password-dialog/password-dialog.component';
import { PasswordDialogSMSProviderComponent } from '../password-dialog-sms-provider/password-dialog-sms-provider.component';
enum SMSProviderType {
Twilio = 1,
@ -73,7 +73,7 @@ export class DialogAddSMSProviderComponent {
}
public changeToken(): void {
const dialogRef = this.dialog.open(PasswordDialogComponent, {
const dialogRef = this.dialog.open(PasswordDialogSMSProviderComponent, {
width: '400px',
data: {
i18nTitle: 'SETTING.SMS.TWILIO.SETTOKEN',

View File

@ -6,6 +6,7 @@ import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { TranslateModule } from '@ngx-translate/core';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
@ -16,10 +17,11 @@ import { InputModule } from '../../input/input.module';
import { WarnDialogModule } from '../../warn-dialog/warn-dialog.module';
import { DialogAddSMSProviderComponent } from './dialog-add-sms-provider/dialog-add-sms-provider.component';
import { NotificationSMSProviderComponent } from './notification-sms-provider.component';
import { PasswordDialogSMSProviderComponent } from './password-dialog-sms-provider/password-dialog-sms-provider.component';
import { MatDialogModule } from '@angular/material/dialog';
@NgModule({
declarations: [NotificationSMSProviderComponent, DialogAddSMSProviderComponent],
declarations: [NotificationSMSProviderComponent, DialogAddSMSProviderComponent, PasswordDialogSMSProviderComponent],
imports: [
CommonModule,
CardModule,
@ -34,9 +36,9 @@ import { MatDialogModule } from '@angular/material/dialog';
FormFieldModule,
WarnDialogModule,
MatSelectModule,
MatDialogModule,
MatProgressSpinnerModule,
MatSelectModule,
MatDialogModule,
TranslateModule,
],
exports: [NotificationSMSProviderComponent],

View File

@ -1,6 +1,6 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { PasswordDialogComponent } from './password-dialog.component';
import { PasswordDialogComponent } from './password-dialog-sms-provider.component';
describe('PasswordDialogComponent', () => {
let component: PasswordDialogComponent;

View File

@ -2,14 +2,14 @@ import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'cnsl-password-dialog',
templateUrl: './password-dialog.component.html',
styleUrls: ['./password-dialog.component.scss'],
selector: 'cnsl-password-dialog-sms-provider',
templateUrl: './password-dialog-sms-provider.component.html',
styleUrls: ['./password-dialog-sms-provider.component.scss'],
})
export class PasswordDialogComponent {
export class PasswordDialogSMSProviderComponent {
public password: string = '';
constructor(
public dialogRef: MatDialogRef<PasswordDialogComponent>,
public dialogRef: MatDialogRef<PasswordDialogSMSProviderComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {}

View File

@ -1,70 +1,37 @@
<h2>{{ 'DESCRIPTIONS.SETTINGS.SMTP_PROVIDER.TITLE' | translate }}</h2>
<p class="cnsl-secondary-text">{{ 'DESCRIPTIONS.SETTINGS.SMTP_PROVIDER.DESCRIPTION' | translate }}</p>
<h2>{{ 'SMTP.LIST.TITLE' | translate }}</h2>
<p class="cnsl-secondary-text">{{ 'SMTP.LIST.DESCRIPTION' | translate }}</p>
<div class="spinner-wr">
<mat-spinner diameter="30" *ngIf="smtpLoading" color="primary"></mat-spinner>
<div class="cnsl-snmp-table-wrapper">
<cnsl-smtp-table></cnsl-smtp-table>
</div>
<cnsl-info-section
*ngIf="!smtpLoading && !form.valid"
class="info-section-warn"
[fitWidth]="true"
[type]="InfoSectionType.ALERT"
>{{ 'SETTING.SMTP.REQUIREDWARN' | translate }}</cnsl-info-section
>
<h2>{{ 'SMTP.CREATE.TITLE' | translate }}</h2>
<p class="cnsl-secondary-text">{{ 'SMTP.CREATE.DESCRIPTION' | translate }}</p>
<form (ngSubmit)="savePolicy()" [formGroup]="form" autocomplete="off">
<cnsl-form-field class="smtp-form-field" label="Sender Address" required="true">
<cnsl-label>{{ 'SETTING.SMTP.SENDERADDRESS' | translate }}</cnsl-label>
<input cnslInput name="senderAddress" formControlName="senderAddress" required />
</cnsl-form-field>
<cnsl-form-field class="smtp-form-field" label="Sender Name" required="true">
<cnsl-label>{{ 'SETTING.SMTP.SENDERNAME' | translate }}</cnsl-label>
<input cnslInput name="senderName" formControlName="senderName" required />
</cnsl-form-field>
<cnsl-form-field class="smtp-form-field" label="Reply-To Address">
<cnsl-label>{{ 'SETTING.SMTP.REPLYTOADDRESS' | translate }}</cnsl-label>
<input cnslInput name="senderReplyToAddress" formControlName="replyToAddress" />
</cnsl-form-field>
<mat-checkbox class="smtp-checkbox" formControlName="tls">
{{ 'SETTING.SMTP.TLS' | translate }}
</mat-checkbox>
<cnsl-form-field class="smtp-form-field" label="Host And Port" required="true">
<cnsl-label>{{ 'SETTING.SMTP.HOSTANDPORT' | translate }}</cnsl-label>
<input cnslInput name="hostAndPort" formControlName="hostAndPort" placeholder="smtp.mailtrap.io:2525" required />
</cnsl-form-field>
<cnsl-form-field class="smtp-form-field" label="User" required="true">
<cnsl-label>{{ 'SETTING.SMTP.USER' | translate }}</cnsl-label>
<input id="smtp-user" cnslInput name="smtp-user" autocomplete="smtp-user" formControlName="user" required />
</cnsl-form-field>
<button
class="set-password-btn"
[disabled]="(['iam.write'] | hasRole | async) === false || !hasSMTPConfig"
(click)="setSMTPPassword()"
type="button"
mat-stroked-button
data-e2e="add-smtp-password-button"
>
{{ 'SETTING.SMTP.SETPASSWORD' | translate }}
</button>
<div class="general-btn-container">
<button
class="save-button"
[disabled]="form.disabled"
(click)="savePolicy()"
color="primary"
type="submit"
mat-raised-button
data-e2e="save-smtp-settings-button"
<div class="new-smtp-wrapper">
<div *ngFor="let provider of providers">
<a
class="item card"
[routerLink]="['/instance', 'smtpprovider', provider.routerLinkElement, 'create']"
*ngIf="provider.name !== 'generic'"
>
{{ 'ACTIONS.SAVE' | translate }}
</button>
<img class="smtp-logo" src="{{ provider.image }}" alt="{{ provider.name }}" />
<div class="text-container">
<span class="title">{{ provider.name | titlecase }} </span>
</div>
</a>
<a
class="item card"
[routerLink]="['/instance', 'smtpprovider', provider.routerLinkElement, 'create']"
*ngIf="provider.name === 'generic'"
>
<div class="smtp-icon">
<mat-icon class="icon" svgIcon="mdi_smtp" />
</div>
<div class="text-container">
<span class="title">Generic SMTP</span>
</div>
</a>
</div>
</form>
</div>

View File

@ -1,32 +1,113 @@
.spinner-wr {
margin: 0.5rem 0;
}
@use '@angular/material' as mat;
.smtp-form-field,
.info-section-warn {
max-width: 400px;
display: block;
}
@mixin smtp-settings-theme($theme) {
$primary: map-get($theme, primary);
$primary-color: mat.get-color-from-palette($primary, 500);
$is-dark-theme: map-get($theme, is-dark);
$background: map-get($theme, background);
$foreground: map-get($theme, foreground);
.info-section-warn {
margin-bottom: 0.5rem;
}
.smtp-checkbox {
max-width: 400px;
display: block;
margin: 1rem 0;
}
.set-password-btn {
margin-bottom: 1rem;
}
.general-btn-container {
display: flex;
justify-content: flex-start;
.save-button {
.cnsl-smtp-table-wrapper {
display: block;
}
.new-smtp-wrapper {
display: grid;
row-gap: 1.5rem;
column-gap: 1.5rem;
box-sizing: border-box;
width: 100%;
grid-template-columns: 1fr;
@media only screen and (min-width: 700px) {
grid-template-columns: 1fr 1fr;
}
@media only screen and (min-width: 1000px) {
grid-template-columns: 1fr 1fr 1fr;
}
@media only screen and (min-width: 1300px) {
grid-template-columns: 1fr 1fr 1fr 1fr;
}
.item {
position: relative;
z-index: 100;
display: flex;
text-decoration: none;
cursor: pointer;
padding-top: 1rem;
padding-right: 1rem;
padding-bottom: 1rem;
padding-left: 1rem;
border-radius: 0.5rem;
box-sizing: border-box;
transition: box-shadow 0.1s ease-in;
align-items: center;
color: map-get($foreground, text);
.coming-soon-label {
position: absolute;
top: 0;
right: 1rem;
transform: translateY(-50%);
width: fit-content;
}
&:hover {
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
}
.smtp-logo {
margin-right: 1rem;
height: 36px;
width: 36px;
&.apple {
margin-bottom: 4px;
}
&.dark {
display: if($is-dark-theme, block, none);
}
&.light {
display: if($is-dark-theme, none, block);
}
}
.smtp-icon {
margin-right: 1rem;
display: flex;
justify-content: center;
align-items: center;
height: 36px;
width: 36px;
.icon {
font-size: 2.25rem;
height: 2.25rem;
width: 2.25rem;
}
}
.text-container {
display: flex;
flex-direction: column;
.title {
}
}
&.coming-soon {
filter: grayscale(1);
cursor: not-allowed;
&:hover {
box-shadow: none;
}
}
}
}
}

View File

@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NotificationSMTPProviderComponent } from './notification-smtp-provider.component';
describe('NotificationSMTPProviderComponent', () => {
describe('IdpSettingsComponent', () => {
let component: NotificationSMTPProviderComponent;
let fixture: ComponentFixture<NotificationSMTPProviderComponent>;

View File

@ -1,22 +1,9 @@
import { Component, Input, OnInit } from '@angular/core';
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { take } from 'rxjs';
import {
AddSMTPConfigRequest,
AddSMTPConfigResponse,
UpdateSMTPConfigPasswordRequest,
UpdateSMTPConfigRequest,
UpdateSMTPConfigResponse,
} from 'src/app/proto/generated/zitadel/admin_pb';
import { Component, Injector, Input, OnInit, Type } from '@angular/core';
import { AdminService } from 'src/app/services/admin.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ToastService } from 'src/app/services/toast.service';
import { requiredValidator } from '../../form-field/validators/validators';
import { ManagementService } from 'src/app/services/mgmt.service';
import { InfoSectionType } from '../../info-section/info-section.component';
import { PasswordDialogComponent } from '../notification-sms-provider/password-dialog/password-dialog.component';
import { PolicyComponentServiceType } from '../policy-component-types.enum';
import { SMTPKnownProviders } from '../../smtp-provider/known-smtp-providers-settings';
@Component({
selector: 'cnsl-notification-smtp-provider',
@ -25,151 +12,21 @@ import { PolicyComponentServiceType } from '../policy-component-types.enum';
})
export class NotificationSMTPProviderComponent implements OnInit {
@Input() public serviceType!: PolicyComponentServiceType;
public service!: ManagementService | AdminService;
public smtpLoading: boolean = false;
public PolicyComponentServiceType: any = PolicyComponentServiceType;
public providers = SMTPKnownProviders;
public form!: UntypedFormGroup;
public InfoSectionType: any = InfoSectionType;
public hasSMTPConfig: boolean = false;
// show available providers
constructor(
private service: AdminService,
private dialog: MatDialog,
private toast: ToastService,
private fb: UntypedFormBuilder,
private authService: GrpcAuthService,
) {
this.form = this.fb.group({
senderAddress: [{ disabled: true, value: '' }, [requiredValidator]],
senderName: [{ disabled: true, value: '' }, [requiredValidator]],
replyToAddress: [{ disabled: true, value: '' }],
tls: [{ disabled: true, value: true }, [requiredValidator]],
hostAndPort: [{ disabled: true, value: '' }, [requiredValidator]],
user: [{ disabled: true, value: '' }, [requiredValidator]],
});
}
constructor(private injector: Injector) {}
ngOnInit(): void {
this.fetchData();
this.authService
.isAllowed(['iam.write'])
.pipe(take(1))
.subscribe((allowed) => {
if (allowed) {
this.form.enable();
}
});
}
private fetchData(): void {
this.smtpLoading = true;
this.service
.getSMTPConfig()
.then((smtpConfig) => {
this.smtpLoading = false;
if (smtpConfig.smtpConfig) {
this.hasSMTPConfig = true;
this.form.patchValue(smtpConfig.smtpConfig);
this.form.patchValue({ ['hostAndPort']: smtpConfig.smtpConfig.host });
}
})
.catch((error) => {
this.smtpLoading = false;
if (error && error.code === 5) {
console.log(error);
this.hasSMTPConfig = false;
}
});
}
private updateData(): Promise<UpdateSMTPConfigResponse.AsObject | AddSMTPConfigResponse> {
if (this.hasSMTPConfig) {
const req = new UpdateSMTPConfigRequest();
req.setHost(this.hostAndPort?.value ?? '');
req.setSenderAddress(this.senderAddress?.value ?? '');
req.setSenderName(this.senderName?.value ?? '');
req.setReplyToAddress(this.replyToAddress?.value ?? '');
req.setTls(this.tls?.value ?? false);
req.setUser(this.user?.value ?? '');
return this.service.updateSMTPConfig(req);
} else {
const req = new AddSMTPConfigRequest();
req.setHost(this.hostAndPort?.value ?? '');
req.setSenderAddress(this.senderAddress?.value ?? '');
req.setSenderName(this.senderName?.value ?? '');
req.setReplyToAddress(this.replyToAddress?.value ?? '');
req.setTls(this.tls?.value ?? false);
req.setUser(this.user?.value ?? '');
return this.service.addSMTPConfig(req);
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;
}
}
public savePolicy(): void {
this.updateData()
.then(() => {
this.toast.showInfo('SETTING.SMTP.SAVED', true);
setTimeout(() => {
this.fetchData();
}, 2000);
})
.catch((error: unknown) => {
this.toast.showError(error);
});
}
public setSMTPPassword(): void {
const dialogRef = this.dialog.open(PasswordDialogComponent, {
width: '400px',
data: {
i18nTitle: 'SETTING.SMTP.SETPASSWORD',
i18nLabel: 'SETTING.SMTP.PASSWORD',
},
});
dialogRef.afterClosed().subscribe((password: string) => {
if (password) {
const passwordReq = new UpdateSMTPConfigPasswordRequest();
passwordReq.setPassword(password);
this.service
.updateSMTPConfigPassword(passwordReq)
.then(() => {
this.toast.showInfo('SETTING.SMTP.PASSWORDSET', true);
})
.catch((error) => {
this.toast.showError(error);
});
}
});
}
public get senderAddress(): AbstractControl | null {
return this.form.get('senderAddress');
}
public get senderName(): AbstractControl | null {
return this.form.get('senderName');
}
public get replyToAddress(): AbstractControl | null {
return this.form.get('replyToAddress');
}
public get tls(): AbstractControl | null {
return this.form.get('tls');
}
public get user(): AbstractControl | null {
return this.form.get('user');
}
public get hostAndPort(): AbstractControl | null {
return this.form.get('hostAndPort');
}
}

View File

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

View File

@ -14,7 +14,6 @@ import { LoginTextsPolicyModule } from '../policies/login-texts/login-texts.modu
import { MessageTextsPolicyModule } from '../policies/message-texts/message-texts.module';
import { NotificationPolicyModule } from '../policies/notification-policy/notification-policy.module';
import { NotificationSMSProviderModule } from '../policies/notification-sms-provider/notification-sms-provider.module';
import { NotificationSMTPProviderModule } from '../policies/notification-smtp-provider/notification-smtp-provider.module';
import { OIDCConfigurationModule } from '../policies/oidc-configuration/oidc-configuration.module';
import { PasswordComplexityPolicyModule } from '../policies/password-complexity-policy/password-complexity-policy.module';
import { PasswordLockoutPolicyModule } from '../policies/password-lockout-policy/password-lockout-policy.module';
@ -28,7 +27,9 @@ import FailedEventsModule from '../failed-events/failed-events.module';
import IamViewsModule from '../iam-views/iam-views.module';
import EventsModule from '../events/events.module';
import { OrgTableModule } from '../org-table/org-table.module';
import { NotificationSMTPProviderModule } from '../policies/notification-smtp-provider/notification-smtp-provider.module';
import { FeaturesComponent } from 'src/app/components/features/features.component';
import OrgListModule from 'src/app/pages/org-list/org-list.module';
@NgModule({
declarations: [SettingsListComponent],
@ -44,12 +45,14 @@ import { FeaturesComponent } from 'src/app/components/features/features.componen
LanguageSettingsModule,
NotificationPolicyModule,
IdpSettingsModule,
NotificationSMTPProviderModule,
PrivacyPolicyModule,
MessageTextsPolicyModule,
SecurityPolicyModule,
DomainsModule,
LoginTextsPolicyModule,
OrgTableModule,
OrgListModule,
DomainPolicyModule,
TranslateModule,
HasRolePipeModule,

View File

@ -0,0 +1,174 @@
export interface AmazonRegionsEndpoints {
'US East (Ohio)': string;
'US East (N. Virginia)': string;
'US West (N. California)': string;
'US West (Oregon)': string;
'Asia Pacific (Mumbai)': string;
'Asia Pacific (Osaka)': string;
'Asia Pacific (Seoul)': string;
'Asia Pacific (Singapore)': string;
'Asia Pacific (Sydney)': string;
'Asia Pacific (Tokyo)': string;
'Canada (Central)': string;
'Europe (Frankfurt)': string;
'Europe (London)': string;
'Europe (Paris)': string;
'Europe (Stockholm)': string;
'South America (São Paulo)': string;
}
const amazonEndpoints = {
'US East (Ohio)': 'email-smtp.us-east-2.amazonaws.com',
'US East (N. Virginia)': 'email-smtp.us-east-1.amazonaws.com',
'US West (N. California)': 'email-smtp.us-west-1.amazonaws.com',
'US West (Oregon)': 'email-smtp.us-west-2.amazonaws.com',
'Asia Pacific (Mumbai)': 'email-smtp.ap-south-1.amazonaws.com',
'Asia Pacific (Osaka)': 'email-smtp.ap-northeast-3.amazonaws.com',
'Asia Pacific (Seoul)': 'email-smtp.ap-northeast-2.amazonaws.com',
'Asia Pacific (Singapore)': 'email-smtp.ap-southeast-1.amazonaws.com',
'Asia Pacific (Sydney)': 'email-smtp.ap-southeast-2.amazonaws.com',
'Asia Pacific (Tokyo)': 'email-smtp.ap-northeast-1.amazonaws.com',
'Canada (Central)': 'email-smtp.ca-central-1.amazonaws.com',
'Europe (Frankfurt)': 'email-smtp.eu-central-1.amazonaws.com',
'Europe (Ireland)': 'email-smtp.eu-west-1.amazonaws.com',
'Europe (London)': 'email-smtp.eu-west-2.amazonaws.com',
'Europe (Paris)': 'email-smtp.eu-west-3.amazonaws.com',
'Europe (Stockholm)': 'email-smtp.eu-north-1.amazonaws.com',
'South America (São Paulo)': 'email-smtp.sa-east-1.amazonaws.com',
};
export interface ProviderDefaultSettings {
name: string;
regions?: AmazonRegionsEndpoints;
multiHostsLabel?: string;
requiredTls: boolean;
host?: string;
unencryptedPort?: number;
encryptedPort?: number;
user: {
value: string;
placeholder: string;
};
password: {
value: string;
placeholder: string;
};
image?: string;
routerLinkElement: string;
}
export const AmazonSESDefaultSettings: ProviderDefaultSettings = {
name: 'amazon SES',
regions: amazonEndpoints,
multiHostsLabel: 'Choose your region',
requiredTls: true,
encryptedPort: 587,
user: { value: '', placeholder: 'your Amazon SES credentials for this region' },
password: { value: '', placeholder: 'your Amazon SES credentials for this region' },
image: './assets/images/smtp/aws-ses.svg',
routerLinkElement: 'aws-ses',
};
export const GoogleDefaultSettings: ProviderDefaultSettings = {
name: 'google',
requiredTls: true,
host: 'smtp.gmail.com',
unencryptedPort: 587,
encryptedPort: 587,
user: { value: '', placeholder: 'your complete Google Workspace email address' },
password: { value: '', placeholder: 'your complete Google Workspace password' },
image: './assets/images/smtp/google.png',
routerLinkElement: 'google',
};
export const MailgunDefaultSettings: ProviderDefaultSettings = {
name: 'mailgun',
requiredTls: false,
host: 'smtp.mailgun.org',
unencryptedPort: 587,
encryptedPort: 465,
user: { value: '', placeholder: 'postmaster@YOURDOMAIN' },
password: { value: '', placeholder: 'Your mailgun smtp password' },
image: './assets/images/smtp/mailgun.svg',
routerLinkElement: 'mailgun',
};
export const MailjetDefaultSettings: ProviderDefaultSettings = {
name: 'mailjet',
requiredTls: false,
host: 'in-v3.mailjet.com',
unencryptedPort: 587,
encryptedPort: 465,
user: { value: '', placeholder: 'Your Mailjet API key' },
password: { value: '', placeholder: 'Your Mailjet Secret key' },
image: './assets/images/smtp/mailjet.svg',
routerLinkElement: 'mailjet',
};
export const PostmarkDefaultSettings: ProviderDefaultSettings = {
name: 'postmark',
requiredTls: false,
host: 'smtp.postmarkapp.com',
unencryptedPort: 587,
encryptedPort: 587,
user: { value: '', placeholder: 'Your Server API token' },
password: { value: '', placeholder: 'Your Server API token' },
image: './assets/images/smtp/postmark.png',
routerLinkElement: 'postmark',
};
export const SendgridDefaultSettings: ProviderDefaultSettings = {
name: 'sendgrid',
requiredTls: false,
host: 'smtp.sendgrid.net',
unencryptedPort: 587,
encryptedPort: 465,
user: { value: 'apikey', placeholder: '' },
password: { value: '', placeholder: ' Your SendGrid API Key' },
image: './assets/images/smtp/sendgrid.png',
routerLinkElement: 'sendgrid',
};
export const MailchimpDefaultSettings: ProviderDefaultSettings = {
name: 'mailchimp',
requiredTls: false,
host: 'smtp.mandrillapp.com',
unencryptedPort: 587,
encryptedPort: 465,
user: { value: '', placeholder: 'Your Mailchimp primary contact email' },
password: { value: '', placeholder: 'Your Mailchimp Transactional API key' },
image: './assets/images/smtp/mailchimp.svg',
routerLinkElement: 'mailchimp',
};
export const BrevoDefaultSettings: ProviderDefaultSettings = {
name: 'brevo',
requiredTls: false,
host: 'smtp-relay.sendinblue.com',
unencryptedPort: 587,
encryptedPort: 465,
user: { value: '', placeholder: 'Your SMTP login email address' },
password: { value: '', placeholder: 'Your SMTP key' },
image: './assets/images/smtp/brevo.svg',
routerLinkElement: 'brevo',
};
export const GenericDefaultSettings: ProviderDefaultSettings = {
name: 'generic',
requiredTls: false,
user: { value: '', placeholder: 'your SMTP user' },
password: { value: '', placeholder: 'your SMTP password' },
routerLinkElement: 'generic',
};
export const SMTPKnownProviders = [
AmazonSESDefaultSettings,
BrevoDefaultSettings,
// GoogleDefaultSettings,
MailgunDefaultSettings,
MailchimpDefaultSettings,
MailjetDefaultSettings,
PostmarkDefaultSettings,
SendgridDefaultSettings,
GenericDefaultSettings,
];

View File

@ -0,0 +1,39 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { SMTPProviderComponent } from './smtp-provider.component';
const types = [
{ path: 'aws-ses', component: SMTPProviderComponent },
{ path: 'generic', component: SMTPProviderComponent },
{ path: 'google', component: SMTPProviderComponent },
{ path: 'mailgun', component: SMTPProviderComponent },
{ path: 'postmark', component: SMTPProviderComponent },
{ path: 'sendgrid', component: SMTPProviderComponent },
{ path: 'mailjet', component: SMTPProviderComponent },
{ path: 'mailchimp', component: SMTPProviderComponent },
{ path: 'brevo', component: SMTPProviderComponent },
];
const routes: Routes = types.map((value) => {
return {
path: value.path,
children: [
{
path: 'create',
component: value.component,
},
],
};
});
routes.push({
path: ':id',
component: SMTPProviderComponent,
});
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class SMTPProvidersRoutingModule {}

View File

@ -0,0 +1,144 @@
<cnsl-create-layout
title="{{
id ? ('SMTP.DETAIL.TITLE' | translate) : ('SMTP.CREATE.STEPS.TITLE' | translate: { value: providerDefaultSetting.name })
}}"
[createSteps]="2"
[currentCreateStep]="currentCreateStep"
(closed)="close()"
>
<div class="wizard-header">
<img
class="smtp-logo"
src="{{ providerDefaultSetting.image }}"
alt="{{ providerDefaultSetting.name }}"
*ngIf="providerDefaultSetting.name !== 'generic'"
/>
<div class="smtp-icon" *ngIf="providerDefaultSetting.name === 'generic'">
<mat-icon class="icon" svgIcon="mdi_smtp" alt="providerDefaultSetting.name" />
</div>
<h2>
{{
!id
? ('SMTP.CREATE.STEPS.CREATE_DESC_TITLE' | translate: { value: providerDefaultSetting.name | titlecase })
: ('SMTP.CREATE.STEPS.CURRENT_DESC_TITLE' | translate)
}}
</h2>
</div>
<mat-progress-bar class="progress-bar" color="primary" *ngIf="smtpLoading" mode="indeterminate"></mat-progress-bar>
<mat-horizontal-stepper
class="stepper {{ 'last-edited-step-' + stepper.selectedIndex }}"
linear
#stepper
labelPosition="bottom"
(selectionChange)="changeStep($event)"
>
<mat-step [editable]="true">
<ng-template matStepLabel>{{ 'SMTP.CREATE.STEPS.PROVIDER_SETTINGS' | translate }}</ng-template>
<form [formGroup]="firstFormGroup" autocomplete="off">
<mat-checkbox class="smtp-checkbox" formControlName="tls" (change)="toggleTLS($event)" data-e2e="tls-checkbox">
{{ 'SETTING.SMTP.TLS' | translate }}
</mat-checkbox>
<cnsl-form-field class="smtp-form-field" *ngIf="providerDefaultSetting.regions">
<cnsl-label>{{ providerDefaultSetting.multiHostsLabel }}</cnsl-label>
<mat-select formControlName="region">
<mat-option *ngFor="let region of providerDefaultSetting.regions | keyvalue" [value]="region.value">
{{ region.key }}
</mat-option>
</mat-select>
</cnsl-form-field>
<cnsl-form-field class="smtp-form-field" label="Description">
<cnsl-label>{{ 'SETTING.SMTP.DESCRIPTION' | translate }}</cnsl-label>
<input cnslInput name="description" formControlName="description" placeholder="description" />
</cnsl-form-field>
<cnsl-form-field class="smtp-form-field" label="Host And Port">
<cnsl-label>{{ 'SETTING.SMTP.HOSTANDPORT' | translate }}</cnsl-label>
<input cnslInput name="hostAndPort" formControlName="hostAndPort" placeholder="host:port" required />
</cnsl-form-field>
<cnsl-form-field class="smtp-form-field" label="User">
<cnsl-label>{{ 'SETTING.SMTP.USER' | translate }}</cnsl-label>
<input
id="smtp-user"
cnslInput
name="smtp-user"
autocomplete="smtp-user"
formControlName="user"
placeholder="{{ providerDefaultSetting.user.placeholder }}"
required
/>
</cnsl-form-field>
<cnsl-form-field class="smtp-form-field" label="Password">
<cnsl-label>{{ 'SETTING.SMTP.PASSWORD' | translate }}</cnsl-label>
<input
id="smtp-password"
cnslInput
name="smtp-password"
autocomplete="off webauthn"
formControlName="password"
placeholder="{{ hasSMTPConfig ? '****************' : providerDefaultSetting.password.placeholder }}"
type="password"
required="{{ !hasSMTPConfig }}"
/>
</cnsl-form-field>
</form>
<div class="smtp-create-actions">
<button
mat-raised-button
[disabled]="firstFormGroup.invalid"
color="primary"
matStepperNext
data-e2e="continue-button"
>
{{ 'ACTIONS.CONTINUE' | translate }}
</button>
</div>
</mat-step>
<mat-step [editable]="true">
<form [formGroup]="secondFormGroup">
<ng-template matStepLabel>{{ 'SMTP.CREATE.STEPS.SENDER_SETTINGS' | translate }}</ng-template>
<cnsl-form-field class="smtp-form-field" label="Sender Address">
<cnsl-label>{{ 'SETTING.SMTP.SENDERADDRESS' | translate }}</cnsl-label>
<input cnslInput name="senderAddress" formControlName="senderAddress" placeholder="sender@example.com" required />
</cnsl-form-field>
<cnsl-form-field class="smtp-form-field" label="Sender Name">
<cnsl-label>{{ 'SETTING.SMTP.SENDERNAME' | translate }}</cnsl-label>
<input cnslInput name="senderName" formControlName="senderName" placeholder="Zitadel" required />
</cnsl-form-field>
<cnsl-form-field class="smtp-form-field" label="Reply-To Address">
<cnsl-label>{{ 'SETTING.SMTP.REPLYTOADDRESS' | translate }}</cnsl-label>
<input cnslInput name="senderReplyToAddress" formControlName="replyToAddress" placeholder="replyto@example.com" />
</cnsl-form-field>
</form>
<div class="smtp-create-actions">
<button mat-stroked-button matStepperPrevious class="bck-button">{{ 'ACTIONS.BACK' | translate }}</button>
<button
mat-raised-button
class="create-button"
color="primary"
data-e2e="create-button"
(click)="savePolicy()"
[disabled]="
firstFormGroup.invalid || secondFormGroup.invalid || (['iam.policy.write'] | hasRole | async) === false
"
>
{{ !hasSMTPConfig ? ('ACTIONS.CREATE' | translate) : ('ACTIONS.SAVE' | translate) }}
</button>
</div>
</mat-step>
<ng-template matStepperIcon="edit">
<mat-icon>check</mat-icon>
</ng-template>
</mat-horizontal-stepper>
</cnsl-create-layout>

View File

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

View File

@ -0,0 +1,278 @@
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
import { Location } from '@angular/common';
import { Component } from '@angular/core';
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { Subject } from 'rxjs';
import { StepperSelectionEvent } from '@angular/cdk/stepper';
import { Options } from 'src/app/proto/generated/zitadel/idp_pb';
import { requiredValidator } from '../form-field/validators/validators';
import { PolicyComponentServiceType } from '../policies/policy-component-types.enum';
import {
AddSMTPConfigRequest,
AddSMTPConfigResponse,
UpdateSMTPConfigRequest,
UpdateSMTPConfigResponse,
} from 'src/app/proto/generated/zitadel/admin_pb';
import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service';
import { ActivatedRoute, Router } from '@angular/router';
import { MatCheckboxChange } from '@angular/material/checkbox';
import {
AmazonSESDefaultSettings,
BrevoDefaultSettings,
GenericDefaultSettings,
GoogleDefaultSettings,
MailchimpDefaultSettings,
MailgunDefaultSettings,
MailjetDefaultSettings,
PostmarkDefaultSettings,
ProviderDefaultSettings,
SendgridDefaultSettings,
} from './known-smtp-providers-settings';
@Component({
selector: 'cnsl-smtp-provider',
templateUrl: './smtp-provider.component.html',
styleUrls: ['./smtp-provider.scss'],
})
export class SMTPProviderComponent {
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
public id: string = '';
public providerDefaultSetting: ProviderDefaultSettings = GenericDefaultSettings;
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
public smtpLoading: boolean = false;
public hasSMTPConfig: boolean = false;
public updateClientSecret: boolean = false;
// stepper
public currentCreateStep: number = 1;
public requestRedirectValuesSubject$: Subject<void> = new Subject();
public firstFormGroup!: UntypedFormGroup;
public secondFormGroup!: UntypedFormGroup;
constructor(
private service: AdminService,
private _location: Location,
private fb: UntypedFormBuilder,
private toast: ToastService,
private router: Router,
private route: ActivatedRoute,
) {
this.route.parent?.url.subscribe((urlPath) => {
const providerName = urlPath[urlPath.length - 1].path;
switch (providerName) {
case 'aws-ses':
this.providerDefaultSetting = AmazonSESDefaultSettings;
break;
case 'google':
this.providerDefaultSetting = GoogleDefaultSettings;
break;
case 'mailgun':
this.providerDefaultSetting = MailgunDefaultSettings;
break;
case 'mailjet':
this.providerDefaultSetting = MailjetDefaultSettings;
break;
case 'postmark':
this.providerDefaultSetting = PostmarkDefaultSettings;
break;
case 'sendgrid':
this.providerDefaultSetting = SendgridDefaultSettings;
break;
case 'mailchimp':
this.providerDefaultSetting = MailchimpDefaultSettings;
break;
case 'brevo':
this.providerDefaultSetting = BrevoDefaultSettings;
break;
}
this.firstFormGroup = this.fb.group({
description: [this.providerDefaultSetting.name],
tls: [{ value: this.providerDefaultSetting.requiredTls, disabled: this.providerDefaultSetting.requiredTls }],
region: [''],
hostAndPort: [
this.providerDefaultSetting?.host
? `${this.providerDefaultSetting?.host}:${this.providerDefaultSetting?.unencryptedPort}`
: '',
],
user: [this.providerDefaultSetting?.user.value || ''],
password: [this.providerDefaultSetting?.password.value || ''],
});
this.secondFormGroup = this.fb.group({
senderAddress: ['', [requiredValidator]],
senderName: ['', [requiredValidator]],
replyToAddress: [''],
});
this.region?.valueChanges.subscribe((region: string) => {
this.hostAndPort?.setValue(
`${region}:${
this.tls ? this.providerDefaultSetting?.encryptedPort : this.providerDefaultSetting?.unencryptedPort
}`,
);
});
if (!this.router.url.endsWith('/create')) {
this.id = this.route.snapshot.paramMap.get('id') || '';
if (this.id) {
this.fetchData(this.id);
}
}
});
}
public changeStep(event: StepperSelectionEvent): void {
this.currentCreateStep = event.selectedIndex + 1;
if (event.selectedIndex >= 2) {
this.requestRedirectValuesSubject$.next();
}
}
public close(): void {
this._location.back();
}
public toggleTLS(event: MatCheckboxChange) {
if (this.providerDefaultSetting.host) {
this.hostAndPort?.setValue(
`${this.providerDefaultSetting?.host}:${
event.checked ? this.providerDefaultSetting?.encryptedPort : this.providerDefaultSetting?.unencryptedPort
}`,
);
}
}
private fetchData(id: string): void {
this.smtpLoading = true;
this.service
.getSMTPConfigById(id)
.then((data) => {
this.smtpLoading = false;
if (data.smtpConfig) {
this.hasSMTPConfig = true;
this.firstFormGroup.patchValue({
['description']: data.smtpConfig.description,
['tls']: data.smtpConfig.tls,
['hostAndPort']: data.smtpConfig.host,
['user']: data.smtpConfig.user,
});
this.secondFormGroup.patchValue({
['senderAddress']: data.smtpConfig.senderAddress,
['senderName']: data.smtpConfig.senderName,
['replyToAddress']: data.smtpConfig.replyToAddress,
});
}
})
.catch((error) => {
this.smtpLoading = false;
if (error && error.code === 5) {
this.hasSMTPConfig = false;
}
});
}
private updateData(): Promise<UpdateSMTPConfigResponse.AsObject | AddSMTPConfigResponse> {
if (this.hasSMTPConfig) {
const req = new UpdateSMTPConfigRequest();
req.setId(this.id);
req.setDescription(this.description?.value || '');
req.setTls(this.tls?.value ?? false);
if (this.hostAndPort && this.hostAndPort.value) {
req.setHost(this.hostAndPort.value);
}
if (this.user && this.user.value) {
req.setUser(this.user.value);
}
if (this.password && this.password.value) {
req.setPassword(this.password.value);
}
if (this.senderAddress && this.senderAddress.value) {
req.setSenderAddress(this.senderAddress.value);
}
if (this.senderName && this.senderName.value) {
req.setSenderName(this.senderName.value);
}
if (this.replyToAddress && this.replyToAddress.value) {
req.setReplyToAddress(this.replyToAddress.value);
}
return this.service.updateSMTPConfig(req);
} else {
const req = new AddSMTPConfigRequest();
req.setDescription(this.description?.value ?? '');
req.setHost(this.hostAndPort?.value ?? '');
req.setSenderAddress(this.senderAddress?.value ?? '');
req.setSenderName(this.senderName?.value ?? '');
req.setReplyToAddress(this.replyToAddress?.value ?? '');
req.setTls(this.tls?.value ?? false);
req.setUser(this.user?.value ?? '');
req.setPassword(this.password?.value ?? '');
return this.service.addSMTPConfig(req);
}
}
public savePolicy(): void {
this.updateData()
.then(() => {
this.toast.showInfo('SETTING.SMTP.SAVED', true);
setTimeout(() => {
this.close();
}, 2000);
})
.catch((error: unknown) => {
if (`${error}`.includes('No changes')) {
this.toast.showInfo('SETTING.SMTP.NOCHANGES', true);
setTimeout(() => {
this.close();
}, 2000);
} else {
this.toast.showError(error);
}
});
}
public get description(): AbstractControl | null {
return this.firstFormGroup.get('description');
}
public get tls(): AbstractControl | null {
return this.firstFormGroup.get('tls');
}
public get region(): AbstractControl | null {
return this.firstFormGroup.get('region');
}
public get hostAndPort(): AbstractControl | null {
return this.firstFormGroup.get('hostAndPort');
}
public get user(): AbstractControl | null {
return this.firstFormGroup.get('user');
}
public get password(): AbstractControl | null {
return this.firstFormGroup.get('password');
}
public get senderAddress(): AbstractControl | null {
return this.secondFormGroup.get('senderAddress');
}
public get senderName(): AbstractControl | null {
return this.secondFormGroup.get('senderName');
}
public get replyToAddress(): AbstractControl | null {
return this.secondFormGroup.get('replyToAddress');
}
}

View File

@ -0,0 +1,51 @@
import { CommonModule, TitleCasePipe } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatIconModule } from '@angular/material/icon';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { MatStepperModule } from '@angular/material/stepper';
import { CardModule } from '../card/card.module';
import { CreateLayoutModule } from '../create-layout/create-layout.module';
import { InfoSectionModule } from '../info-section/info-section.module';
import { ProviderOptionsModule } from '../provider-options/provider-options.module';
import { StringListModule } from '../string-list/string-list.module';
import { SMTPProvidersRoutingModule } from './smtp-provider-routing.module';
import { SMTPProviderComponent } from './smtp-provider.component';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { MatButtonModule } from '@angular/material/button';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatSelectModule } from '@angular/material/select';
import { MatChipsModule } from '@angular/material/chips';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@NgModule({
declarations: [SMTPProviderComponent],
imports: [
SMTPProvidersRoutingModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
CreateLayoutModule,
StringListModule,
InfoSectionModule,
InputModule,
MatButtonModule,
MatProgressBarModule,
MatSelectModule,
MatIconModule,
MatChipsModule,
CardModule,
MatCheckboxModule,
MatTooltipModule,
MatStepperModule,
TranslateModule,
ProviderOptionsModule,
HasRolePipeModule,
MatProgressSpinnerModule,
],
})
export default class SMTPProviderModule {}

View File

@ -0,0 +1,71 @@
@use '@angular/material' as mat;
@mixin smtp-provider-theme($theme) {
$is-dark-theme: map-get($theme, is-dark);
$background: map-get($theme, background);
$foreground: map-get($theme, foreground);
.stepper {
background: inherit !important;
margin: 0 -1.5rem;
}
.smtp-create-actions {
margin-top: 2rem;
display: flex;
align-items: center;
justify-content: space-between;
.bck-button {
margin-right: 1rem;
}
.create-button {
padding: 0.5rem 4rem;
}
}
.smtp-form-field,
.info-section-warn {
max-width: 400px;
display: block;
}
.wizard-header {
display: flex;
align-items: center;
.smtp-logo {
margin-right: 1rem;
height: 48px;
width: 48px;
flex-shrink: 0;
}
.smtp-icon {
margin-right: 1rem;
display: flex;
justify-content: center;
align-items: center;
height: 36px;
width: 36px;
.icon {
font-size: 2.25rem;
height: 2.25rem;
width: 2.25rem;
}
}
}
.row {
display: flex;
justify-content: space-between;
.left,
.right {
margin-bottom: 0.5rem;
font-size: 14px;
}
}
}

View File

@ -0,0 +1,129 @@
<cnsl-refresh-table
[loading]="loading$ | async"
(refreshed)="refreshPage()"
[dataSize]="dataSource.data.length"
[emitRefreshOnPreviousRoutes]="[
'/instance/smtpprovider/aws-ses/create',
'/instance/smtpprovider/generic/create',
'/instance/smtpprovider/google/create',
'/instance/smtpprovider/mailgun/create',
'/instance/smtpprovider/mailjet/create',
'/instance/smtpprovider/postmark/create',
'/instance/smtpprovider/sendgrid/create',
'/instance/smtpprovider/mailchimp/create',
'/instance/smtpprovider/brevo/create'
]"
[timestamp]="configsResult?.details?.viewTimestamp"
[selection]="selection"
[hideRefresh]="true"
>
<div class="table-wrapper">
<table class="table" mat-table [dataSource]="dataSource">
<ng-container matColumnDef="activated">
<th class="availability" mat-header-cell *matHeaderCellDef>
<span>{{ 'SMTP.LIST.ACTIVATED' | translate }}</span>
</th>
<td class="pointer availability" mat-cell *matCellDef="let config">
<i
matTooltip="{{ 'SMTP.LIST.ACTIVATED' | translate }}"
*ngIf="isActive(config.state)"
class="smtp-available las la-check-circle"
data-e2e="active-provider"
></i>
</td>
</ng-container>
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef>{{ 'SETTING.SMTP.DESCRIPTION' | translate }}</th>
<td class="pointer" mat-cell *matCellDef="let config">
<span>{{ config?.description }}</span>
</td>
</ng-container>
<ng-container matColumnDef="tls">
<th class="availability" mat-header-cell *matHeaderCellDef>
<span>TLS</span>
</th>
<td class="pointer availability" mat-cell *matCellDef="let config">
<i *ngIf="config.tls" class="las la-lock"></i>
<i *ngIf="!config.tls" class="las la-unlock"></i>
</td>
</ng-container>
<ng-container matColumnDef="host">
<th mat-header-cell *matHeaderCellDef>{{ 'SETTING.SMTP.HOSTANDPORT' | translate }}</th>
<td class="pointer" mat-cell *matCellDef="let config">
<span>{{ config?.host }}</span>
</td>
</ng-container>
<ng-container matColumnDef="senderAddress">
<th mat-header-cell *matHeaderCellDef>{{ 'SETTING.SMTP.SENDERADDRESS' | translate }}</th>
<td class="pointer" mat-cell *matCellDef="let config">
<span>{{ config?.senderAddress }}</span>
</td>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
<th class="smtp-table-actions" mat-header-cell *matHeaderCellDef></th>
<td class="smtp-table-actions" mat-cell *matCellDef="let config">
<cnsl-table-actions>
<button
actions
*ngIf="!isActive(config.state)"
[disabled]="(['iam.write'] | hasRole | async) === false"
mat-icon-button
matTooltip="{{ 'SMTP.LIST.ACTIVATE' | translate }}"
data-e2e="activate-provider-button"
(click)="activateSMTPConfig(config.id); $event.stopPropagation()"
>
<i class="las la-check-circle"></i>
</button>
<button
actions
*ngIf="isActive(config.state)"
[disabled]="(['iam.write'] | hasRole | async) === false"
mat-icon-button
matTooltip="{{ 'SMTP.LIST.DEACTIVATE' | translate }}"
data-e2e="deactivate-provider-button"
(click)="deactivateSMTPConfig(config.id); $event.stopPropagation()"
>
<i class="las la-times-circle"></i>
</button>
<button
actions
[disabled]="(['iam.write'] | hasRole | async) === false"
mat-icon-button
color="warn"
matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"
data-e2e="delete-provider-button"
(click)="deleteSMTPConfig(config.id, config.senderName); $event.stopPropagation()"
>
<i class="las la-trash"></i>
</button>
</cnsl-table-actions>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" (click)="navigateToProvider(row)" mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</div>
<div *ngIf="(loading$ | async) === false && !dataSource?.data?.length" class="no-content-row">
<i class="las la-exclamation"></i>
<span>{{ 'SMTP.LIST.EMPTY' | translate }}</span>
</div>
<cnsl-paginator
#paginator
class="paginator"
[timestamp]="configsResult?.details?.viewTimestamp"
[length]="configsResult?.details?.totalResult || 0"
[pageSize]="10"
[pageSizeOptions]="[10, 20, 50, 100]"
(page)="changePage($event)"
></cnsl-paginator>
</cnsl-refresh-table>

View File

@ -0,0 +1,81 @@
@mixin smtp-table-theme($theme) {
$primary: map-get($theme, primary);
$warn: map-get($theme, warn);
$background: map-get($theme, background);
$accent: map-get($theme, accent);
$primary-color: map-get($primary, 500);
$warn-color: map-get($warn, 500);
$accent-color: map-get($accent, 500);
$foreground: map-get($theme, foreground);
$is-dark-theme: map-get($theme, is-dark);
$back: map-get($background, background);
.smtp-table-provider-type {
display: flex;
align-items: center;
.smtp-logo {
margin-right: 1rem;
height: 28px;
width: 28px;
flex-shrink: 0;
&.dark {
display: if($is-dark-theme, block, none);
}
&.light {
display: if($is-dark-theme, none, block);
}
}
.smtp-icon {
font-size: 1.75rem;
height: 1.75rem;
width: 1.75rem;
margin-right: 1rem;
flex-shrink: 0;
}
}
}
.smtp-margin-right {
margin-right: 0.5rem;
}
td {
outline: none;
}
tr {
outline: none;
&.disabled,
&.disabled * {
opacity: 0.8;
cursor: default;
}
}
.availability {
width: 40px;
}
.smtp-available {
color: var(--success);
}
.smtp-not-available {
color: var(--warn);
}
.smtp-table-actions {
width: 100px;
}
.la-unlock {
color: orangered;
}
.la-lock {
color: darkorange;
}

View File

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

View File

@ -0,0 +1,194 @@
import { SelectionModel } from '@angular/cdk/collections';
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core';
import { Router, RouterLink } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { SMTPConfigState } from 'src/app/proto/generated/zitadel/settings_pb';
import { ListQuery } from 'src/app/proto/generated/zitadel/object_pb';
import { LoginPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service';
import { PageEvent, PaginatorComponent } from '../paginator/paginator.component';
import { PolicyComponentServiceType } from '../policies/policy-component-types.enum';
import { ListSMTPConfigsRequest, ListSMTPConfigsResponse } from 'src/app/proto/generated/zitadel/admin_pb';
import { SMTPConfig } from 'src/app/proto/generated/zitadel/settings_pb';
import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
import { MatTableDataSource } from '@angular/material/table';
import { MatDialog } from '@angular/material/dialog';
@Component({
selector: 'cnsl-smtp-table',
templateUrl: './smtp-table.component.html',
styleUrls: ['./smtp-table.component.scss'],
})
export class SMTPTableComponent implements OnInit {
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
public dataSource: MatTableDataSource<SMTPConfig.AsObject> = new MatTableDataSource<SMTPConfig.AsObject>();
public selection: SelectionModel<SMTPConfig.AsObject> = new SelectionModel<SMTPConfig.AsObject>(true, []);
public configsResult?: ListSMTPConfigsResponse.AsObject;
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
public PolicyComponentServiceType: any = PolicyComponentServiceType;
public displayedColumns: string[] = ['activated', 'description', 'tls', 'host', 'senderAddress', 'actions'];
@Output() public changedSelection: EventEmitter<Array<SMTPConfig.AsObject>> = new EventEmitter();
public loginPolicy!: LoginPolicy.AsObject;
constructor(
private adminService: AdminService,
public translate: TranslateService,
private toast: ToastService,
private dialog: MatDialog,
private router: Router,
) {
this.selection.changed.subscribe(() => {
this.changedSelection.emit(this.selection.selected);
});
}
ngOnInit(): void {
this.getData(10, 0);
}
public isActive(state: number) {
return state === SMTPConfigState.SMTP_CONFIG_ACTIVE;
}
public isAllSelected(): boolean {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
public masterToggle(): void {
this.isAllSelected() ? this.selection.clear() : this.dataSource.data.forEach((row) => this.selection.select(row));
}
public changePage(event: PageEvent): void {
this.getData(event.pageSize, event.pageIndex * event.pageSize);
}
public activateSMTPConfig(id: string): void {
const dialogRef = this.dialog.open(WarnDialogComponent, {
data: {
confirmKey: 'ACTIONS.CONTINUE',
cancelKey: 'ACTIONS.CANCEL',
titleKey: 'SMTP.LIST.DIALOG.ACTIVATE_WARN_TITLE',
descriptionKey: 'SMTP.LIST.DIALOG.ACTIVATE_WARN_DESCRIPTION',
},
width: '400px',
});
dialogRef.afterClosed().subscribe((resp) => {
if (resp) {
this.adminService
.activateSMTPConfig(id)
.then(() => {
this.toast.showInfo('SMTP.LIST.DIALOG.ACTIVATED', true);
this.refreshPage();
})
.catch((error) => {
this.toast.showError(error);
});
}
});
}
public deactivateSMTPConfig(id: string): void {
const dialogRef = this.dialog.open(WarnDialogComponent, {
data: {
confirmKey: 'ACTIONS.CONTINUE',
cancelKey: 'ACTIONS.CANCEL',
titleKey: 'SMTP.LIST.DIALOG.DEACTIVATE_WARN_TITLE',
descriptionKey: 'SMTP.LIST.DIALOG.DEACTIVATE_WARN_DESCRIPTION',
},
width: '400px',
});
dialogRef.afterClosed().subscribe((resp) => {
if (resp) {
this.adminService
.deactivateSMTPConfig(id)
.then(() => {
this.toast.showInfo('SMTP.LIST.DIALOG.DEACTIVATED', true);
this.refreshPage();
})
.catch((error) => {
this.toast.showError(error);
});
}
});
}
public deleteSMTPConfig(id: string, senderName: string): void {
const dialogRef = this.dialog.open(WarnDialogComponent, {
data: {
confirmKey: 'ACTIONS.DELETE',
cancelKey: 'ACTIONS.CANCEL',
titleKey: 'SMTP.LIST.DIALOG.DELETE_TITLE',
descriptionKey: 'SMTP.LIST.DIALOG.DELETE_DESCRIPTION',
confirmationKey: 'SMTP.LIST.DIALOG.SENDER',
confirmation: senderName,
},
width: '400px',
});
dialogRef.afterClosed().subscribe((resp) => {
if (resp) {
this.adminService
.removeSMTPConfig(id)
.then(() => {
this.toast.showInfo('SMTP.LIST.DIALOG.DELETED', true);
this.refreshPage();
})
.catch((error) => {
this.toast.showError(error);
});
}
});
}
private async getData(limit: number, offset: number): Promise<void> {
this.loadingSubject.next(true);
const req = new ListSMTPConfigsRequest();
const lq = new ListQuery();
lq.setOffset(offset);
lq.setLimit(limit);
req.setQuery(lq);
this.adminService
.listSMTPConfigs()
.then((resp) => {
this.configsResult = resp;
if (resp.resultList) {
this.dataSource.data = resp.resultList;
}
this.loadingSubject.next(false);
})
.catch((error) => {
this.toast.showError(error);
this.loadingSubject.next(false);
});
}
public refreshPage(): void {
this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize);
}
public get createRouterLink(): RouterLink | any {
return ['/instance', 'idp', 'create'];
}
public routerLinkForRow(row: SMTPConfig.AsObject): any {
return ['/instance', 'smtpprovider', row.id];
}
public get displayedColumnsWithActions(): string[] {
return ['actions', ...this.displayedColumns];
}
public navigateToProvider(row: SMTPConfig.AsObject) {
this.router.navigate(this.routerLinkForRow(row));
}
}

View File

@ -0,0 +1,46 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatIconModule } from '@angular/material/icon';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
import { TruncatePipeModule } from 'src/app/pipes/truncate-pipe/truncate-pipe.module';
import { PaginatorModule } from '../paginator/paginator.module';
import { TableActionsModule } from '../table-actions/table-actions.module';
import { SMTPTableComponent } from './smtp-table.component';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatTableModule } from '@angular/material/table';
@NgModule({
declarations: [SMTPTableComponent],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
MatButtonModule,
TableActionsModule,
MatCheckboxModule,
MatIconModule,
MatTooltipModule,
TranslateModule,
LocalizedDatePipeModule,
TimestampToDatePipeModule,
MatTableModule,
PaginatorModule,
RouterModule,
RefreshTableModule,
HasRoleModule,
HasRolePipeModule,
TruncatePipeModule,
],
exports: [SMTPTableComponent],
})
export class SMTPTableModule {}

View File

@ -32,6 +32,15 @@ const routes: Routes = [
serviceType: PolicyComponentServiceType.ADMIN,
},
},
{
path: 'smtpprovider',
canActivate: [AuthGuard, RoleGuard],
loadChildren: () => import('src/app/modules/smtp-provider/smtp-provider.module'),
data: {
roles: ['iam.idp.read'],
serviceType: PolicyComponentServiceType.ADMIN,
},
},
];
@NgModule({

View File

@ -6,6 +6,8 @@ import {
ActivateLabelPolicyResponse,
ActivateSMSProviderRequest,
ActivateSMSProviderResponse,
ActivateSMTPConfigRequest,
ActivateSMTPConfigResponse,
AddAppleProviderRequest,
AddAppleProviderResponse,
AddAzureADProviderRequest,
@ -52,6 +54,8 @@ import {
DeactivateIDPResponse,
DeactivateSMSProviderRequest,
DeactivateSMSProviderResponse,
DeactivateSMTPConfigRequest,
DeactivateSMTPConfigResponse,
DeleteProviderRequest,
DeleteProviderResponse,
GetAllowedLanguagesRequest,
@ -135,6 +139,8 @@ import {
GetSecurityPolicyResponse,
GetSMSProviderRequest,
GetSMSProviderResponse,
GetSMTPConfigByIdRequest,
GetSMTPConfigByIdResponse,
GetSMTPConfigRequest,
GetSMTPConfigResponse,
GetSupportedLanguagesRequest,
@ -167,6 +173,8 @@ import {
ListSecretGeneratorsResponse,
ListSMSProvidersRequest,
ListSMSProvidersResponse,
ListSMTPConfigsRequest,
ListSMTPConfigsResponse,
ListViewsRequest,
ListViewsResponse,
ReactivateIDPRequest,
@ -193,6 +201,8 @@ import {
RemoveSecondFactorFromLoginPolicyResponse,
RemoveSMSProviderRequest,
RemoveSMSProviderResponse,
RemoveSMTPConfigRequest,
RemoveSMTPConfigResponse,
ResetCustomDomainPolicyToDefaultRequest,
ResetCustomDomainPolicyToDefaultResponse,
ResetCustomLoginTextsToDefaultRequest,
@ -894,6 +904,17 @@ export class AdminService {
return this.grpcService.admin.getSMTPConfig(req, null).then((resp) => resp.toObject());
}
public getSMTPConfigById(id: string): Promise<GetSMTPConfigByIdResponse.AsObject> {
const req = new GetSMTPConfigByIdRequest();
req.setId(id);
return this.grpcService.admin.getSMTPConfigById(req, null).then((resp) => resp.toObject());
}
public listSMTPConfigs(): Promise<ListSMTPConfigsResponse.AsObject> {
const req = new ListSMTPConfigsRequest();
return this.grpcService.admin.listSMTPConfigs(req, null).then((resp) => resp.toObject());
}
public addSMTPConfig(req: AddSMTPConfigRequest): Promise<AddSMTPConfigResponse.AsObject> {
return this.grpcService.admin.addSMTPConfig(req, null).then((resp) => resp.toObject());
}
@ -906,6 +927,24 @@ export class AdminService {
return this.grpcService.admin.updateSMTPConfigPassword(req, null).then((resp) => resp.toObject());
}
public activateSMTPConfig(id: string): Promise<ActivateSMTPConfigResponse.AsObject> {
const req = new ActivateSMTPConfigRequest();
req.setId(id);
return this.grpcService.admin.activateSMTPConfig(req, null).then((resp) => resp.toObject());
}
public deactivateSMTPConfig(id: string): Promise<DeactivateSMTPConfigResponse.AsObject> {
const req = new DeactivateSMTPConfigRequest();
req.setId(id);
return this.grpcService.admin.deactivateSMTPConfig(req, null).then((resp) => resp.toObject());
}
public removeSMTPConfig(id: string): Promise<RemoveSMTPConfigResponse.AsObject> {
const req = new RemoveSMTPConfigRequest();
req.setId(id);
return this.grpcService.admin.removeSMTPConfig(req, null).then((resp) => resp.toObject());
}
/* sms */
public listSMSProviders(): Promise<ListSMSProvidersResponse.AsObject> {

View File

@ -1374,6 +1374,8 @@
}
},
"SMTP": {
"TITLE": "SMTP настройки",
"DESCRIPTION": "Описание",
"SENDERADDRESS": "Имейл адрес на изпращача",
"SENDERNAME": "Име на изпращача",
"REPLYTOADDRESS": "Reply-to адрес",
@ -1384,6 +1386,7 @@
"PASSWORDSET": "Паролата за SMTP бе зададена успешно.",
"TLS": "Сигурност на транспортния слой (TLS)",
"SAVED": "Запазено успешно!",
"NOCHANGES": "Без промени!",
"REQUIREDWARN": "За да изпращате известия от вашия домейн, трябва да въведете своите SMTP данни."
},
"SMS": {
@ -2208,6 +2211,48 @@
"1": "Позволен"
}
},
"SMTP": {
"LIST": {
"TITLE": "SMTP доставчик",
"DESCRIPTION": "Това са SMTP доставчиците за вашето копие на Zitadel. Активирайте този, който искате да използвате, за да изпращате известия до вашите потребители.",
"EMPTY": "Няма наличен SMTP доставчик",
"ACTIVATED": "Активиран",
"ACTIVATE": "Активирайте доставчика",
"DEACTIVATE": "Деактивирайте доставчика",
"TYPE": "Тип",
"DIALOG": {
"ACTIVATED": "SMTP конфигурацията е активирана",
"ACTIVATE_WARN_TITLE": "Активирайте SMTP конфигурацията",
"ACTIVATE_WARN_DESCRIPTION": "На път сте да активирате SMTP конфигурация. Първо ще деактивираме текущия активен доставчик и след това ще активираме тази конфигурация. Сигурен ли си?",
"DEACTIVATE_WARN_TITLE": "Деактивирайте SMTP конфигурацията",
"DEACTIVATE_WARN_DESCRIPTION": "На път сте да деактивирате SMTP конфигурация. Сигурен ли си?",
"DEACTIVATED": "SMTP конфигурацията е деактивирана",
"DELETE_TITLE": "Изтриване на SMTP конфигурация",
"DELETE_DESCRIPTION": "На път сте да изтриете конфигурация. Потвърдете това действие, като въведете името на подателя",
"DELETED": "SMTP конфигурацията е изтрита",
"SENDER": "Въведете {{ value }}, за да изтриете тази SMTP конфигурация."
}
},
"CREATE": {
"TITLE": "Добавете SMTP доставчик",
"DESCRIPTION": "Изберете един или повече от следните доставчици.",
"STEPS": {
"TITLE": "Добавете {{ value }} SMTP доставчик",
"CREATE_DESC_TITLE": "Въведете своите {{ value }} SMTP настройки стъпка по стъпка",
"CURRENT_DESC_TITLE": "Това са вашите SMTP настройки",
"PROVIDER_SETTINGS": "Настройки на SMTP доставчик",
"SENDER_SETTINGS": "Настройки на изпращача",
"TEST_SETTINGS": "Тествайте настройките на SMTP"
}
},
"DETAIL": {
"TITLE": "Настройки на SMTP доставчик"
},
"EMPTY": "Няма наличен SMTP доставчик",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "Приложения",
"COMPLIANCE": "Съответствие с OIDC",

View File

@ -1381,6 +1381,8 @@
}
},
"SMTP": {
"TITLE": "Nastavení SMTP",
"DESCRIPTION": "Popis",
"SENDERADDRESS": "E-mailová adresa odesílatele",
"SENDERNAME": "Jméno odesílatele",
"REPLYTOADDRESS": "Adresa pro odpovědi",
@ -1391,6 +1393,7 @@
"PASSWORDSET": "SMTP heslo bylo úspěšně nastaveno.",
"TLS": "Zabezpečení transportní vrstvy (TLS)",
"SAVED": "Úspěšně uloženo!",
"NOCHANGES": "Žádné změny!",
"REQUIREDWARN": "Pro odesílání oznámení z vaší domény musíte zadat vaše SMTP údaje."
},
"SMS": {
@ -2227,6 +2230,48 @@
"1": "Povoleno"
}
},
"SMTP": {
"LIST": {
"TITLE": "Poskytovatel SMTP",
"DESCRIPTION": "Toto jsou poskytovatelé SMTP pro vaši instanci Zitadel. Aktivujte ten, který chcete používat k odesílání upozornění svým uživatelům.",
"EMPTY": "Není k dispozici žádný poskytovatel SMTP",
"ACTIVATED": "Aktivováno",
"ACTIVATE": "Aktivujte poskytovatele",
"DEACTIVATE": "Deaktivovat poskytovatele",
"TYPE": "Typ",
"DIALOG": {
"ACTIVATED": "Konfigurace SMTP byla aktivována",
"ACTIVATE_WARN_TITLE": "Aktivujte konfiguraci SMTP",
"ACTIVATE_WARN_DESCRIPTION": "Chystáte se aktivovat konfiguraci SMTP. Nejprve deaktivujeme aktuálního aktivního poskytovatele a poté aktivujeme tuto konfiguraci. Jsi si jistá?",
"DEACTIVATE_WARN_TITLE": "Deaktivujte konfiguraci SMTP",
"DEACTIVATE_WARN_DESCRIPTION": "Chystáte se deaktivovat konfiguraci SMTP. Jsi si jistá?",
"DEACTIVATED": "Konfigurace SMTP byla deaktivována",
"DELETE_TITLE": "Smazat konfiguraci SMTP",
"DELETE_DESCRIPTION": "Chystáte se smazat konfiguraci. Potvrďte tuto akci zadáním jména odesílatele",
"DELETED": "Konfigurace SMTP byla smazána",
"SENDER": "Chcete-li smazat tuto konfiguraci SMTP, zadejte {{ value }}."
}
},
"CREATE": {
"TITLE": "Přidat poskytovatele SMTP",
"DESCRIPTION": "Vyberte jednoho nebo více z následujících poskytovatelů.",
"STEPS": {
"TITLE": "Přidejte {{ value }} poskytovatele SMTP",
"CREATE_DESC_TITLE": "Krok za krokem zadejte nastavení SMTP {{ value }}",
"CURRENT_DESC_TITLE": "Toto jsou vaše nastavení SMTP",
"PROVIDER_SETTINGS": "Nastavení poskytovatele SMTP",
"SENDER_SETTINGS": "Nastavení odesílatele",
"TEST_SETTINGS": "Otestujte nastavení SMTP"
}
},
"DETAIL": {
"TITLE": "Nastavení poskytovatele SMTP"
},
"EMPTY": "Není k dispozici žádný poskytovatel SMTP",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "Aplikace",
"COMPLIANCE": "OIDC Kompatibilita",

View File

@ -1380,6 +1380,8 @@
}
},
"SMTP": {
"TITLE": "SMTP Einstellungen",
"DESCRIPTION": "Beschreibung",
"SENDERADDRESS": "Sender Email-Adresse",
"SENDERNAME": "Sender Name",
"REPLYTOADDRESS": "Reply-to-Adresse",
@ -1390,6 +1392,7 @@
"PASSWORDSET": "SMTP Passwort erfolgreich gesetzt.",
"TLS": "Transport Layer Security (TLS)",
"SAVED": "Erfolgreich gespeichert.",
"NOCHANGES": "Keine Änderungen!",
"REQUIREDWARN": "Damit Mails von Ihrer Domain verschickt werden können, müssen Sie Ihre SMTP Einstellungen konfigurieren."
},
"SMS": {
@ -2217,6 +2220,48 @@
"1": "Erlaubt"
}
},
"SMTP": {
"LIST": {
"TITLE": "SMTP-Anbieter",
"DESCRIPTION": "Dies sind die SMTP-Anbieter für Ihre Zitadel-Instanz. Aktivieren Sie diejenige, die Sie zum Senden von Benachrichtigungen an Ihre Benutzer verwenden möchten.",
"EMPTY": "Kein SMTP-Anbieter verfügbar",
"ACTIVATED": "Aktiviert",
"ACTIVATE": "Anbieter aktivieren",
"DEACTIVATE": "Anbieter deaktivieren",
"TYPE": "Typ",
"DIALOG": {
"ACTIVATED": "Die SMTP-Konfiguration wurde aktiviert",
"ACTIVATE_WARN_TITLE": "Aktivieren Sie die SMTP-Konfiguration",
"ACTIVATE_WARN_DESCRIPTION": "Sie sind dabei, eine SMTP-Konfiguration zu aktivieren. Zuerst deaktivieren wir den aktuell aktiven Anbieter und aktivieren dann diese Konfiguration. Bist du sicher?",
"DEACTIVATE_WARN_TITLE": "Deaktivieren Sie die SMTP-Konfiguration",
"DEACTIVATE_WARN_DESCRIPTION": "Sie sind dabei, eine SMTP-Konfiguration zu deaktivieren. Bist du sicher?",
"DEACTIVATED": "Die SMTP-Konfiguration wurde deaktiviert",
"DELETE_TITLE": "SMTP-Konfiguration löschen",
"DELETE_DESCRIPTION": "Sie sind dabei, eine Konfiguration zu löschen. Bestätigen Sie diese Aktion, indem Sie den Absendernamen eingeben",
"DELETED": "SMTP-Konfiguration wurde gelöscht",
"SENDER": "Geben Sie {{ value }} ein, um diese SMTP-Konfiguration zu löschen."
}
},
"CREATE": {
"TITLE": "SMTP-Anbieter hinzufügen",
"DESCRIPTION": "Wählen Sie einen oder mehrere der folgenden Anbieter aus.",
"STEPS": {
"TITLE": "Fügen Sie {{ value }} SMTP-Anbieter hinzu",
"CREATE_DESC_TITLE": "Geben Sie Schritt für Schritt Ihre {{ value }} SMTP-Einstellungen ein",
"CURRENT_DESC_TITLE": "Dies sind Ihre SMTP-Einstellungen",
"PROVIDER_SETTINGS": "SMTP-Anbietereinstellungen",
"SENDER_SETTINGS": "Absendereinstellungen",
"TEST_SETTINGS": "Testen Sie die SMTP-Einstellungen"
}
},
"DETAIL": {
"TITLE": "SMTP-Anbietereinstellungen"
},
"EMPTY": "Kein SMTP-Anbieter verfügbar",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "Apps",
"COMPLIANCE": "OIDC Einhaltung",

View File

@ -1381,6 +1381,8 @@
}
},
"SMTP": {
"TITLE": "SMTP Provider",
"DESCRIPTION": "Description",
"SENDERADDRESS": "Sender Email Address",
"SENDERNAME": "Sender Name",
"REPLYTOADDRESS": "Reply-to Address",
@ -1391,6 +1393,7 @@
"PASSWORDSET": "SMTP Password was set successfully.",
"TLS": "Transport Layer Security (TLS)",
"SAVED": "Saved successfully!",
"NOCHANGES": "No changes!",
"REQUIREDWARN": "To send notifications from your domain, you have to enter your SMTP data."
},
"SMS": {
@ -2236,6 +2239,48 @@
"1": "Allowed"
}
},
"SMTP": {
"LIST": {
"TITLE": "SMTP Provider",
"DESCRIPTION": "These are the SMTP providers for your Zitadel instance. Activate the one you want to use to send notifications to your users.",
"EMPTY": "No SMTP Provider available",
"ACTIVATED": "Activated",
"ACTIVATE": "Activate provider",
"DEACTIVATE": "Deactivate provider",
"TYPE": "Type",
"DIALOG": {
"ACTIVATED": "SMTP config has been activated",
"ACTIVATE_WARN_TITLE": "Activate SMTP config",
"ACTIVATE_WARN_DESCRIPTION": "You are about to activate an SMTP configuration. First we'll deactivate the current active provider and then activate this configuration. Are you sure?",
"DEACTIVATE_WARN_TITLE": "Deactivate SMTP config",
"DEACTIVATE_WARN_DESCRIPTION": "You are about to deactivate an SMTP configuration. Are you sure?",
"DEACTIVATED": "SMTP config has been deactivated",
"DELETE_TITLE": "Delete SMTP config",
"DELETE_DESCRIPTION": "You are about to delete a configuration. Confirm this action typing the sender name",
"DELETED": "SMTP config has been deleted",
"SENDER": "Type {{value}}, to delete this SMTP configuration."
}
},
"CREATE": {
"TITLE": "Add SMTP provider",
"DESCRIPTION": "Select one ore more of the following providers.",
"STEPS": {
"TITLE": "Add {{ value }} SMTP Provider",
"CREATE_DESC_TITLE": "Enter your {{ value }} SMTP settings step by step",
"CURRENT_DESC_TITLE": "These are your SMTP settings",
"PROVIDER_SETTINGS": "SMTP Provider Settings",
"SENDER_SETTINGS": "Sender Settings",
"TEST_SETTINGS": "Test SMTP Settings"
}
},
"DETAIL": {
"TITLE": "SMTP Provider Settings"
},
"EMPTY": "No SMTP provider available",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "Applications",
"COMPLIANCE": "OIDC Compliance",

View File

@ -1382,6 +1382,8 @@
}
},
"SMTP": {
"TITLE": "Ajustes SMTP",
"DESCRIPTION": "Descripción",
"SENDERADDRESS": "Dirección email del emisor",
"SENDERNAME": "Nombre del emisor",
"REPLYTOADDRESS": "Dirección Reply-To",
@ -1392,6 +1394,7 @@
"PASSWORDSET": "La contraseña SMTP se estableció con éxito.",
"TLS": "Transport Layer Security (TLS)",
"SAVED": "¡Se guardó con éxito!",
"NOCHANGES": "¡Sin cambios!",
"REQUIREDWARN": "Para enviar notificaciones para tu dominio, debes introducir tus datos SMTP."
},
"SMS": {
@ -2215,6 +2218,48 @@
"1": "Permitido"
}
},
"SMTP": {
"LIST": {
"TITLE": "Proveedor SMTP",
"DESCRIPTION": "Estos son los proveedores SMTP para tu instancia de Zitadel. Activa el que quieras utilizar para enviar notificaciones a tus usuarios.",
"EMPTY": "No hay ningún proveedor SMTP disponible",
"ACTIVATED": "Activado",
"ACTIVATE": "Activar proveedor",
"DEACTIVATE": "Desactivar proveedor",
"TYPE": "Tipo",
"DIALOG": {
"ACTIVATED": "Tu configuración SMTP ha sido activada",
"ACTIVATE_WARN_TITLE": "Activar configuración SMTP",
"ACTIVATE_WARN_DESCRIPTION": "Estás a punto de activar una configuración SMTP. Primero desactivaremos la configuración de tu proveedor actual y después activaremos esta configuración. ¿Estás seguro?",
"DEACTIVATE_WARN_TITLE": "Desactivar configuración SMTP",
"DEACTIVATE_WARN_DESCRIPTION": "Estás a punto de desactivar una configuración SMTP. ¿Está seguro?",
"DEACTIVATED": "La configuración SMTP ha sido desactivada",
"DELETE_TITLE": "Eliminar configuración SMTP",
"DELETE_DESCRIPTION": "Estás a punto de eliminar una configuración. Confirma esta acción escribiendo el nombre del remitente",
"DELETED": "La configuración SMTP ha sido eliminada",
"SENDER": "Escribe {{ value }} para eliminar esta configuración SMTP."
}
},
"CREATE": {
"TITLE": "Agrega un proveedor SMTP",
"DESCRIPTION": "Selecciona uno o más de los siguientes proveedores.",
"STEPS": {
"TITLE": "Agrega un proveedor SMTP {{ value }} ",
"CREATE_DESC_TITLE": "Introduce tu configuración SMTP de {{ value }} paso a paso",
"CURRENT_DESC_TITLE": "Estas son tus configuraciones SMTP",
"PROVIDER_SETTINGS": "Configuración del proveedor SMTP",
"SENDER_SETTINGS": "Configuración del remitente",
"TEST_SETTINGS": "Probar la configuración SMTP"
}
},
"DETAIL": {
"TITLE": "Configuración del proveedor SMTP"
},
"EMPTY": "No hay ningún proveedor SMTP disponible",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "Aplicaciones",
"COMPLIANCE": "Cumplimiento OIDC",

View File

@ -1380,6 +1380,8 @@
}
},
"SMTP": {
"TITLE": "Paramètres SMTP",
"DESCRIPTION": "Description",
"SENDERADDRESS": "Adresse e-mail de l'expéditeur",
"SENDERNAME": "Nom de l'expéditeur",
"REPLYTOADDRESS": "Adresse Reply-to",
@ -1390,6 +1392,7 @@
"PASSWORDSET": "Le mot de passe SMTP a été défini avec succès.",
"TLS": "Sécurité de la couche de transport (TLS)",
"SAVED": "Enregistré avec succès!",
"NOCHANGES": "Aucun changement!",
"REQUIREDWARN": "Pour envoyer des notifications depuis votre domaine, vous devez entrer vos données SMTP."
},
"SMS": {
@ -2218,6 +2221,48 @@
"1": "Autorisée"
}
},
"SMTP": {
"LIST": {
"TITLE": "Fournisseur SMTP",
"DESCRIPTION": "Ce sont les fournisseurs SMTP de votre instance Zitadel. Activez celui que vous souhaitez utiliser pour envoyer des notifications à vos utilisateurs.",
"EMPTY": "Aucun fournisseur SMTP disponible",
"ACTIVATED": "Activé",
"ACTIVATE": "Activer le fournisseur",
"DEACTIVATE": "Désactiver le fournisseur",
"TYPE": "Taper",
"DIALOG": {
"ACTIVATED": "La configuration SMTP a été activée",
"ACTIVATE_WARN_TITLE": "Activer la configuration SMTP",
"ACTIVATE_WARN_DESCRIPTION": "Vous êtes sur le point d'activer une configuration SMTP. Nous allons dabord désactiver le fournisseur actif actuel, puis activer cette configuration. Es-tu sûr?",
"DEACTIVATE_WARN_TITLE": "Désactiver la configuration SMTP",
"DEACTIVATE_WARN_DESCRIPTION": "Vous êtes sur le point de désactiver une configuration SMTP. Es-tu sûr?",
"DEACTIVATED": "La configuration SMTP a été désactivée",
"DELETE_TITLE": "Supprimer la configuration SMTP",
"DELETE_DESCRIPTION": "Vous êtes sur le point de supprimer une configuration. Confirmez cette action en tapant le nom de l'expéditeur",
"DELETED": "La configuration SMTP a été supprimée",
"SENDER": "Tapez {{ value }} pour supprimer cette configuration SMTP."
}
},
"CREATE": {
"TITLE": "Ajouter un fournisseur SMTP",
"DESCRIPTION": "Sélectionnez un ou plusieurs des fournisseurs suivants.",
"STEPS": {
"TITLE": "Ajouter {{ value }} fournisseur SMTP",
"CREATE_DESC_TITLE": "Entrez vos paramètres SMTP {{ value }} étape par étape",
"CURRENT_DESC_TITLE": "Ce sont vos paramètres SMTP",
"PROVIDER_SETTINGS": "Paramètres du fournisseur SMTP",
"SENDER_SETTINGS": "Paramètres de l'expéditeur",
"TEST_SETTINGS": "Tester les paramètres SMTP"
}
},
"DETAIL": {
"TITLE": "Paramètres du fournisseur SMTP"
},
"EMPTY": "Aucun fournisseur SMTP disponible",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "Applications",
"COMPLIANCE": "Conformité à l'OIDC",

View File

@ -1380,6 +1380,8 @@
}
},
"SMTP": {
"TITLE": "Impostazioni SMTP",
"DESCRIPTION": "Descrizione",
"SENDERADDRESS": "Indirizzo email del mittente",
"SENDERNAME": "Nome del mittente",
"REPLYTOADDRESS": "Indirizzo Reply-to",
@ -1390,6 +1392,7 @@
"PASSWORDSET": "SMTP Password impostata con successo.",
"TLS": "Transport Layer Security (TLS)",
"SAVED": "Salvato con successo!",
"NOCHANGES": "Nessun cambiamento!",
"REQUIREDWARN": "Per inviare notifiche dal tuo dominio, devi inserire i tuoi dati SMTP."
},
"SMS": {
@ -2218,6 +2221,48 @@
"1": "Consentito"
}
},
"SMTP": {
"LIST": {
"TITLE": "Fornitore SMTP",
"DESCRIPTION": "Questi sono i provider SMTP per la tua istanza Zitadel. Attiva quello che desideri utilizzare per inviare notifiche ai tuoi utenti.",
"EMPTY": "Nessun provider SMTP disponibile",
"ACTIVATED": "Attivato",
"ACTIVATE": "Attiva fornitore",
"DEACTIVATE": "Disattiva fornitore",
"TYPE": "Tipo",
"DIALOG": {
"ACTIVATED": "La configurazione SMTP è stata attivata",
"ACTIVATE_WARN_TITLE": "Attiva la configurazione SMTP",
"ACTIVATE_WARN_DESCRIPTION": "Stai per attivare una configurazione SMTP. Per prima cosa disattiveremo il provider attualmente attivo e poi attiveremo questa configurazione. Sei sicuro?",
"DEACTIVATE_WARN_TITLE": "Disattiva la configurazione SMTP",
"DEACTIVATE_WARN_DESCRIPTION": "Stai per disattivare una configurazione SMTP. Sei sicuro?",
"DEACTIVATED": "La configurazione SMTP è stata disattivata",
"DELETE_TITLE": "Elimina configurazione SMTP",
"DELETE_DESCRIPTION": "Stai per eliminare una configurazione. Conferma questa azione digitando il nome del mittente",
"DELETED": "La configurazione SMTP è stata eliminata",
"SENDER": "Digita {{ value }} per eliminare questa configurazione SMTP."
}
},
"CREATE": {
"TITLE": "Aggiungi provider SMTP",
"DESCRIPTION": "Seleziona uno o più dei seguenti fornitori.",
"STEPS": {
"TITLE": "Aggiungi {{ value }} provider SMTP",
"CREATE_DESC_TITLE": "Inserisci le tue impostazioni SMTP {{ value }} passo dopo passo",
"CURRENT_DESC_TITLE": "Queste sono le tue impostazioni SMTP",
"PROVIDER_SETTINGS": "Impostazioni del provider SMTP",
"SENDER_SETTINGS": "Impostazioni mittente",
"TEST_SETTINGS": "Testare le impostazioni SMTP"
}
},
"DETAIL": {
"TITLE": "Impostazioni del provider SMTP"
},
"EMPTY": "Nessun provider SMTP disponibile",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "Applicazioni",
"COMPLIANCE": "Conformità con OIDC",

View File

@ -1381,6 +1381,8 @@
}
},
"SMTP": {
"TITLE": "SMTP設定",
"DESCRIPTION": "説明",
"SENDERADDRESS": "送信者のメールアドレス",
"SENDERNAME": "送信者名",
"REPLYTOADDRESS": "返信先アドレス",
@ -1391,6 +1393,7 @@
"PASSWORDSET": "SMTPパスワードは正常に設定されました。",
"TLS": "Transport Layer Security (TLS)",
"SAVED": "正常に保存されました!",
"NOCHANGES": "変更はありません!",
"REQUIREDWARN": "ドメインから通知を送信するには、SMTP情報を入力する必要があります。"
},
"SMS": {
@ -2209,6 +2212,48 @@
"1": "有効"
}
},
"SMTP": {
"LIST": {
"TITLE": "SMTPプロバイダー",
"DESCRIPTION": "これらは、Zitadel インスタンスの SMTP プロバイダーです。ユーザーに通知を送信するために使用するものをアクティブにします。",
"EMPTY": "使用可能な SMTP プロバイダーがありません",
"ACTIVATED": "アクティブ化された",
"ACTIVATE": "プロバイダーをアクティブ化する",
"DEACTIVATE": "プロバイダーを非アクティブ化する",
"TYPE": "タイプ",
"DIALOG": {
"ACTIVATED": "SMTP設定が有効化されました",
"ACTIVATE_WARN_TITLE": "SMTP設定を有効にする",
"ACTIVATE_WARN_DESCRIPTION": "SMTP 構成をアクティブにしようとしています。まず、現在アクティブなプロバイダーを非アクティブ化してから、この構成をアクティブ化します。本気ですか?",
"DEACTIVATE_WARN_TITLE": "SMTP設定を無効にする",
"DEACTIVATE_WARN_DESCRIPTION": "SMTP 構成を非アクティブ化しようとしています。本気ですか?",
"DEACTIVATED": "SMTP設定が無効化されました",
"DELETE_TITLE": "SMTP設定を削除する",
"DELETE_DESCRIPTION": "構成を削除しようとしています。送信者名を入力してこのアクションを確認します",
"DELETED": "SMTP設定が削除されました",
"SENDER": "この SMTP 構成を削除するには、「{{ value }}」と入力します。"
}
},
"CREATE": {
"TITLE": "SMTPプロバイダーの追加",
"DESCRIPTION": "次のプロバイダーから 1 つ以上を選択します。",
"STEPS": {
"TITLE": "{{ value }} SMTP プロバイダーを追加",
"CREATE_DESC_TITLE": "{{ value }} SMTP 設定をステップごとに入力します",
"CURRENT_DESC_TITLE": "これらは SMTP 設定です",
"PROVIDER_SETTINGS": "SMTPプロバイダーの設定",
"SENDER_SETTINGS": "送信者の設定",
"TEST_SETTINGS": "SMTP設定をテストする"
}
},
"DETAIL": {
"TITLE": "SMTPプロバイダーの設定"
},
"EMPTY": "使用可能な SMTP プロバイダーがありません",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "アプリケーション",
"COMPLIANCE": "OIDCコンプライアンス",

View File

@ -1382,6 +1382,8 @@
}
},
"SMTP": {
"TITLE": "SMTP подесувања",
"DESCRIPTION": "Опис",
"SENDERADDRESS": "Адреса на испраќачот",
"SENDERNAME": "Име на испраќачот",
"REPLYTOADDRESS": "Reply-to адреса",
@ -1392,6 +1394,7 @@
"PASSWORDSET": "SMTP лозинката е успешно поставена.",
"TLS": "Transport Layer Security (TLS)",
"SAVED": "Успешно зачувано!",
"NOCHANGES": "Нема промени!",
"REQUIREDWARN": "За да испраќате известувања од вашиот домен, мора да ги внесете вашите SMTP податоци."
},
"SMS": {
@ -2215,6 +2218,48 @@
"1": "Дозволено"
}
},
"SMTP": {
"LIST": {
"TITLE": "SMTP провајдер",
"DESCRIPTION": "Ова се давателите на SMTP за вашиот пример Zitadel. Активирајте го оној што сакате да го користите за испраќање известувања до вашите корисници.",
"EMPTY": "Нема достапен SMTP провајдер",
"ACTIVATED": "Активиран",
"ACTIVATE": "Активирајте го провајдерот",
"DEACTIVATE": "Деактивирајте го провајдерот",
"TYPE": "Тип",
"DIALOG": {
"ACTIVATED": "SMTP конфигурацијата е активирана",
"ACTIVATE_WARN_TITLE": "Активирајте SMTP конфигурација",
"ACTIVATE_WARN_DESCRIPTION": "Ќе активирате SMTP конфигурација. Прво ќе го деактивираме тековниот активен провајдер, а потоа ќе ја активираме оваа конфигурација. Дали си сигурен?",
"DEACTIVATE_WARN_TITLE": "Деактивирајте ја конфигурацијата SMTP",
"DEACTIVATE_WARN_DESCRIPTION": "Ќе деактивирате SMTP конфигурација. Дали си сигурен?",
"DEACTIVATED": "SMTP конфигурацијата е деактивирана",
"DELETE_TITLE": "Избришете ја конфигурацијата SMTP",
"DELETE_DESCRIPTION": "Ќе избришете конфигурација. Потврдете го ова дејство со внесување на името на испраќачот",
"DELETED": "SMTP конфигурацијата е избришана",
"SENDER": "Внесете {{ value }}, за да ја избришете оваа SMTP конфигурација."
}
},
"CREATE": {
"TITLE": "Додадете SMTP провајдер",
"DESCRIPTION": "Изберете еден или повеќе од следните провајдери.",
"STEPS": {
"TITLE": "Додадете {{ value }} SMTP провајдер",
"CREATE_DESC_TITLE": "Внесете ги вашите поставки за {{ value }} SMTP чекор по чекор",
"CURRENT_DESC_TITLE": "Ова се вашите поставки за SMTP",
"PROVIDER_SETTINGS": "Поставки на провајдерот SMTP",
"SENDER_SETTINGS": "Поставки на испраќачот",
"TEST_SETTINGS": "Тестирајте ги поставките за SMTP"
}
},
"DETAIL": {
"TITLE": "Поставки на провајдерот SMTP"
},
"EMPTY": "Нема достапен SMTP провајдер",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "Апликации",
"COMPLIANCE": "OIDC Соодветност",

View File

@ -1381,6 +1381,8 @@
}
},
"SMTP": {
"TITLE": "SMTP Instellingen",
"DESCRIPTION": "Beschrijving",
"SENDERADDRESS": "Afzender Email Adres",
"SENDERNAME": "Afzender Naam",
"REPLYTOADDRESS": "Antwoord-naar Adres",
@ -1391,6 +1393,7 @@
"PASSWORDSET": "SMTP Wachtwoord is succesvol ingesteld.",
"TLS": "Transport Layer Security (TLS)",
"SAVED": "Succesvol opgeslagen!",
"NOCHANGES": "Geen veranderingen!",
"REQUIREDWARN": "Om notificaties te kunnen versturen vanaf uw domein, moet u uw SMTP-gegevens invoeren."
},
"SMS": {
@ -2236,6 +2239,48 @@
"1": "Toegestaan"
}
},
"SMTP": {
"LIST": {
"TITLE": "SMTP-provider",
"DESCRIPTION": "Dit zijn de SMTP-providers voor uw Zitadel-instantie. Activeer degene die u wilt gebruiken om meldingen naar uw gebruikers te sturen.",
"EMPTY": "Geen SMTP-provider beschikbaar",
"ACTIVATED": "Geactiveerd",
"ACTIVATE": "Aanbieder activeren",
"DEACTIVATE": "Aanbieder deactiveren",
"TYPE": "Type",
"DIALOG": {
"ACTIVATED": "SMTP-configuratie is geactiveerd",
"ACTIVATE_WARN_TITLE": "Activeer SMTP-configuratie",
"ACTIVATE_WARN_DESCRIPTION": "U staat op het punt een SMTP-configuratie te activeren. Eerst deactiveren we de huidige actieve provider en activeren vervolgens deze configuratie. Weet je het zeker?",
"DEACTIVATE_WARN_TITLE": "Deactiveer SMTP-configuratie",
"DEACTIVATE_WARN_DESCRIPTION": "U staat op het punt een SMTP-configuratie te deactiveren. Weet je het zeker?",
"DEACTIVATED": "SMTP-configuratie is gedeactiveerd",
"DELETE_TITLE": "SMTP-configuratie verwijderen",
"DELETE_DESCRIPTION": "U staat op het punt een configuratie te verwijderen. Bevestig deze actie door de naam van de afzender te typen",
"DELETED": "SMTP-configuratie is verwijderd",
"SENDER": "Typ {{ value }} om deze SMTP-configuratie te verwijderen."
}
},
"CREATE": {
"TITLE": "SMTP-provider toevoegen",
"DESCRIPTION": "Selecteer één of meer van de volgende aanbieders.",
"STEPS": {
"TITLE": "Voeg {{ value }} SMTP-provider toe",
"CREATE_DESC_TITLE": "Voer stap voor stap uw {{ value }} SMTP-instellingen in",
"CURRENT_DESC_TITLE": "Dit zijn uw SMTP-instellingen",
"PROVIDER_SETTINGS": "SMTP-providerinstellingen",
"SENDER_SETTINGS": "Afzenderinstellingen",
"TEST_SETTINGS": "SMTP-instellingen testen"
}
},
"DETAIL": {
"TITLE": "SMTP-providerinstellingen"
},
"EMPTY": "Geen SMTP-provider beschikbaar",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "Toepassingen",
"COMPLIANCE": "OIDC Conformiteit",

View File

@ -1380,6 +1380,8 @@
}
},
"SMTP": {
"TITLE": "Ustawienia SMTP",
"DESCRIPTION": "Opis",
"SENDERADDRESS": "Adres e-mail nadawcy",
"SENDERNAME": "Nazwa nadawcy",
"REPLYTOADDRESS": "Adres Reply-to",
@ -1390,6 +1392,7 @@
"PASSWORDSET": "Hasło SMTP zostało pomyślnie ustawione.",
"TLS": "Bezpieczeństwo warstwy transportu (TLS)",
"SAVED": "Pomyślnie zapisano!",
"NOCHANGES": "Bez zmian!",
"REQUIREDWARN": "Aby wysyłać powiadomienia z twojej domeny, musisz podać dane SMTP."
},
"SMS": {
@ -2218,6 +2221,48 @@
"1": "Dozwolone"
}
},
"SMTP": {
"LIST": {
"TITLE": "Dostawca SMTP",
"DESCRIPTION": "To są dostawcy SMTP dla Twojej instancji Zitadel. Aktywuj ten, którego chcesz używać do wysyłania powiadomień do użytkowników.",
"EMPTY": "Brak dostępnego dostawcy SMTP",
"ACTIVATED": "Aktywowany",
"ACTIVATE": "Aktywuj dostawcę",
"DEACTIVATE": "Dezaktywuj dostawcę",
"TYPE": "Typ",
"DIALOG": {
"ACTIVATED": "Konfiguracja SMTP została aktywowana",
"ACTIVATE_WARN_TITLE": "Aktywuj konfigurację SMTP",
"ACTIVATE_WARN_DESCRIPTION": "Zamierzasz aktywować konfigurację SMTP. Najpierw dezaktywujemy bieżącego aktywnego dostawcę, a następnie aktywujemy tę konfigurację. Jesteś pewny?",
"DEACTIVATE_WARN_TITLE": "Dezaktywuj konfigurację SMTP",
"DEACTIVATE_WARN_DESCRIPTION": "Zamierzasz dezaktywować konfigurację SMTP. Jesteś pewny?",
"DEACTIVATED": "Konfiguracja SMTP została dezaktywowana",
"DELETE_TITLE": "Usuń konfigurację SMTP",
"DELETE_DESCRIPTION": "Zamierzasz usunąć konfigurację. Potwierdź tę czynność, wpisując nazwę nadawcy",
"DELETED": "Konfiguracja SMTP została usunięta",
"SENDER": "Wpisz {{ value }}, aby usunąć tę konfigurację SMTP."
}
},
"CREATE": {
"TITLE": "Dodaj dostawcę SMTP",
"DESCRIPTION": "Wybierz jednego lub więcej z poniższych dostawców.",
"STEPS": {
"TITLE": "Dodaj {{ value }} dostawcę SMTP",
"CREATE_DESC_TITLE": "Wprowadź krok po kroku ustawienia SMTP {{ value }}",
"CURRENT_DESC_TITLE": "To są Twoje ustawienia SMTP",
"PROVIDER_SETTINGS": "Ustawienia dostawcy SMTP",
"SENDER_SETTINGS": "Ustawienia nadawcy",
"TEST_SETTINGS": "Przetestuj ustawienia SMTP"
}
},
"DETAIL": {
"TITLE": "Ustawienia dostawcy SMTP"
},
"EMPTY": "Brak dostępnego dostawcy SMTP",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "Aplikacje",
"COMPLIANCE": "Zgodność OIDC",

View File

@ -1382,6 +1382,8 @@
}
},
"SMTP": {
"TITLE": "Configurações SMTP",
"DESCRIPTION": "Descrição",
"SENDERADDRESS": "Endereço de e-mail do remetente",
"SENDERNAME": "Nome do remetente",
"REPLYTOADDRESS": "Endereço Reply-To",
@ -1392,6 +1394,7 @@
"PASSWORDSET": "Senha do SMTP definida com sucesso.",
"TLS": "Transport Layer Security (TLS)",
"SAVED": "Salvo com sucesso!",
"NOCHANGES": "Sem alterações!",
"REQUIREDWARN": "Para enviar notificações do seu domínio, você precisa inserir seus dados SMTP."
},
"SMS": {
@ -2213,6 +2216,48 @@
"1": "Permitido"
}
},
"SMTP": {
"LIST": {
"TITLE": "Provedor SMTP",
"DESCRIPTION": "Estes são os provedores SMTP para sua instância Zitadel. Ative aquele que deseja usar para enviar notificações aos seus usuários.",
"EMPTY": "Nenhum provedor SMTP disponível",
"ACTIVATED": "Ativado",
"ACTIVATE": "Ativar provedor",
"DEACTIVATE": "Desativar provedor",
"TYPE": "Tipo",
"DIALOG": {
"ACTIVATED": "A configuração SMTP foi ativada",
"ACTIVATE_WARN_TITLE": "Ativar configuração SMTP",
"ACTIVATE_WARN_DESCRIPTION": "Você está prestes a ativar uma configuração SMTP. Primeiro, desativaremos o provedor ativo atual e depois ativaremos esta configuração. Tem certeza?",
"DEACTIVATE_WARN_TITLE": "Desativar configuração SMTP",
"DEACTIVATE_WARN_DESCRIPTION": "Você está prestes a desativar uma configuração SMTP. Tem certeza?",
"DEACTIVATED": "A configuração SMTP foi desativada",
"DELETE_TITLE": "Excluir configuração SMTP",
"DELETE_DESCRIPTION": "Você está prestes a excluir uma configuração. Confirme esta ação digitando o nome do remetente",
"DELETED": "A configuração SMTP foi excluída",
"SENDER": "Digite {{ value }} para excluir esta configuração SMTP."
}
},
"CREATE": {
"TITLE": "Adicionar provedor SMTP",
"DESCRIPTION": "Selecione um ou mais dos seguintes fornecedores.",
"STEPS": {
"TITLE": "Adicionar {{ value }} provedor SMTP",
"CREATE_DESC_TITLE": "Insira suas configurações SMTP de {{ value }} passo a passo",
"CURRENT_DESC_TITLE": "Estas são suas configurações de SMTP",
"PROVIDER_SETTINGS": "Configurações do provedor SMTP",
"SENDER_SETTINGS": "Configurações do remetente",
"TEST_SETTINGS": "Testar configurações de SMTP"
}
},
"DETAIL": {
"TITLE": "Configurações do provedor SMTP"
},
"EMPTY": "Nenhum provedor SMTP disponível",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "Aplicações",
"COMPLIANCE": "Conformidade OIDC",

View File

@ -1425,6 +1425,7 @@
},
"SMTP": {
"TITLE": "Настройки SMTP",
"DESCRIPTION": "Описание",
"SENDERADDRESS": "Адрес электронной почты отправителя",
"SENDERNAME": "Имя отправителя",
"HOSTANDPORT": "Хост и порт",
@ -1434,6 +1435,7 @@
"PASSWORDSET": "Пароль SMTP был успешно установлен.",
"TLS": "Безопасность транспортного уровня (TLS)",
"SAVED": "Успешно сохранено!",
"NOCHANGES": "Без изменений!",
"REQUIREDWARN": "Для того, чтобы отправлять уведомления с вашего домена, вам необходимо ввести свои данные SMTP."
},
"SMS": {
@ -2331,6 +2333,48 @@
"1": "Разрешён"
}
},
"SMTP": {
"LIST": {
"TITLE": "SMTP-провайдер",
"DESCRIPTION": "Это поставщики SMTP для вашего экземпляра Zitadel. Активируйте тот, который вы хотите использовать для отправки уведомлений вашим пользователям.",
"EMPTY": "SMTP-провайдер не доступен",
"ACTIVATED": "Активировано",
"ACTIVATE": "Активировать провайдера",
"DEACTIVATE": "Деактивировать провайдера",
"TYPE": "Тип",
"DIALOG": {
"ACTIVATED": "Конфигурация SMTP активирована",
"ACTIVATE_WARN_TITLE": "Активируйте конфигурацию SMTP",
"ACTIVATE_WARN_DESCRIPTION": "Вы собираетесь активировать конфигурацию SMTP. Сначала мы деактивируем текущего активного провайдера, а затем активируем эту конфигурацию. Вы уверены?",
"DEACTIVATE_WARN_TITLE": "Деактивировать конфигурацию SMTP",
"DEACTIVATE_WARN_DESCRIPTION": "Вы собираетесь деактивировать конфигурацию SMTP. Вы уверены?",
"DEACTIVATED": "Конфигурация SMTP деактивирована",
"DELETE_TITLE": "Удалить конфигурацию SMTP",
"DELETE_DESCRIPTION": "Вы собираетесь удалить конфигурацию. Подтвердите это действие, введя имя отправителя.",
"DELETED": "Конфигурация SMTP удалена.",
"SENDER": "Введите {{ value }}, чтобы удалить эту конфигурацию SMTP."
}
},
"CREATE": {
"TITLE": "Добавить SMTP-провайдера",
"DESCRIPTION": "Выберите одного или нескольких из следующих поставщиков.",
"STEPS": {
"TITLE": "Добавить {{ value }} SMTP-провайдера",
"CREATE_DESC_TITLE": "Введите {{ value }} настройки SMTP шаг за шагом.",
"CURRENT_DESC_TITLE": "Это ваши настройки SMTP",
"PROVIDER_SETTINGS": "Настройки SMTP-провайдера",
"SENDER_SETTINGS": "Настройки отправителя",
"TEST_SETTINGS": "Проверка настроек SMTP"
}
},
"DETAIL": {
"TITLE": "Настройки SMTP-провайдера"
},
"EMPTY": "Нет доступного поставщика SMTP",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "Приложения",
"COMPLIANCE": "Соответствие OIDC",

View File

@ -1380,6 +1380,8 @@
}
},
"SMTP": {
"TITLE": "SMTP 设置",
"DESCRIPTION": "描述",
"SENDERADDRESS": "发件人地址",
"SENDERNAME": "发件人名称",
"REPLYTOADDRESS": "Reply-to 地址",
@ -1390,6 +1392,7 @@
"PASSWORDSET": "SMTP 密码设置成功。",
"TLS": "使用安全传输层 (TLS)",
"SAVED": "保存成功!",
"NOCHANGES": "没有变化!",
"REQUIREDWARN": "要从您的域发送通知,您必须输入您的 SMTP 数据。"
},
"SMS": {
@ -2217,6 +2220,48 @@
"1": "允许"
}
},
"SMTP": {
"LIST": {
"TITLE": "SMTP 提供商",
"DESCRIPTION": "这些是您的 Zitadel 实例的 SMTP 提供商。激活您想要用来向用户发送通知的通知。",
"EMPTY": "没有可用的 SMTP 提供商",
"ACTIVATED": "活性",
"ACTIVATE": "激活提供商",
"DEACTIVATE": "停用提供商",
"TYPE": "类型",
"DIALOG": {
"ACTIVATED": "SMTP 配置已激活",
"ACTIVATE_WARN_TITLE": "激活 SMTP 配置",
"ACTIVATE_WARN_DESCRIPTION": "您即将激活 SMTP 配置。首先,我们将停用当前活动的提供程序,然后激活此配置。你确定吗?",
"DEACTIVATE_WARN_TITLE": "停用 SMTP 配置",
"DEACTIVATE_WARN_DESCRIPTION": "您即将停用 SMTP 配置。你确定吗?",
"DEACTIVATED": "SMTP 配置已停用",
"DELETE_TITLE": "删除 SMTP 配置",
"DELETE_DESCRIPTION": "您将要删除一个配置。输入发件人姓名确认此操作",
"DELETED": "SMTP 配置已被删除",
"SENDER": "输入 {{ value }},删除此 SMTP 配置。"
}
},
"CREATE": {
"TITLE": "添加 SMTP 提供商",
"DESCRIPTION": "选择以下一个或多个提供商。",
"STEPS": {
"TITLE": "添加 {{ value }} SMTP 提供商",
"CREATE_DESC_TITLE": "逐步输入您的 {{ value }} SMTP 设置",
"CURRENT_DESC_TITLE": "这些是您的 SMTP 设置",
"PROVIDER_SETTINGS": "SMTP 提供商设置",
"SENDER_SETTINGS": "发件人设置",
"TEST_SETTINGS": "测试 SMTP 设置"
}
},
"DETAIL": {
"TITLE": "SMTP 提供商设置"
},
"EMPTY": "没有可用的 SMTP 提供商",
"STEPS": {
"SENDGRID": {}
}
},
"APP": {
"LIST": "应用",
"COMPLIANCE": "OIDC 兼容性",

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256px" height="299px" viewBox="0 0 256 299" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<path d="M60.556,47.602 L0,144.01 L60.556,240.434 L61.612,239.681 L60.837,47.8 L60.556,47.602" fill="#876929"></path>
<path d="M128.187,223.105 L60.556,240.434 L60.556,47.602 L128.187,64.927 L128.187,223.105" fill="#D9A741"></path>
<path d="M255.979,71.868 L223.379,77.259 L148.538,0 L111.331,16.292 L116.239,25.445 L89.906,35.971 L89.906,279.399 L128.186,298.552 L128.823,298.053 L128.234,47.818 L209.376,170.786 L255.979,71.868" fill="#876929"></path>
<path d="M148.538,0 L248.217,49.837 L208.8,121.357 L148.538,0" fill="#D9A741"></path>
<path d="M255.975,71.868 L256,234.596 L128.187,298.552 L128.17,20.683 L208.8,166.974 L255.975,71.868" fill="#D9A741"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 927 B

View File

@ -0,0 +1,3 @@
<svg width="1000" height="295" viewBox="0 0 1000 295" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M820.28 182.338C820.28 137.755 848.604 106.4 888.892 106.4C929.18 106.4 957.956 137.737 957.956 182.338C957.956 226.939 929.187 256.5 888.892 256.5C848.598 256.5 820.28 225.588 820.28 182.338ZM778.224 182.338C778.224 248.12 824.262 294.479 888.886 294.479C953.51 294.479 1000 248.12 1000 182.338C1000 116.556 953.962 68.4399 888.886 68.4399C823.81 68.4399 778.224 115.686 778.224 182.338ZM563.521 71.0853L650.292 291.821H691.025L777.791 71.0853H733.966L671.104 241.498H670.214L607.352 71.0853H563.521ZM394.856 174.383C397.508 133.76 424.515 106.4 461.261 106.4C493.128 106.4 517.037 126.712 520.58 157.179H447.089C420.973 157.179 406.801 160.269 396.191 174.402H394.856V174.39V174.383ZM352.805 181.006C352.805 246.788 399.289 294.46 463.468 294.46C506.854 294.46 544.916 272.391 561.295 237.502L525.885 219.835C513.494 242.792 489.585 256.482 463.468 256.482C432.028 256.482 403.704 232.637 403.704 209.679C403.704 197.766 411.673 192.457 423.18 192.457H563.502V180.544C563.502 114.317 521.007 68.4029 459.925 68.4029C398.844 68.4029 352.799 115.649 352.799 180.988M232.399 291.796H272.242V156.285C272.242 127.149 290.382 106.394 315.627 106.394C326.256 106.394 337.311 109.927 342.635 114.774C346.623 104.174 352.818 93.5923 362.111 82.9924C351.482 74.1684 333.342 68.4153 315.627 68.4153C266.937 68.4153 232.399 104.618 232.399 156.267V291.809V291.796ZM39.843 145.698V37.9598H105.358C127.486 37.9598 142.103 50.7611 142.103 70.185C142.103 92.2542 123.072 109.033 84.1191 121.834C57.5571 130.214 45.6116 137.281 41.1785 145.679L39.843 145.692V145.698ZM39.843 253.861V208.835C39.843 188.967 56.6668 169.543 80.1311 162.032C100.943 154.966 118.193 147.899 132.81 140.407C152.286 151.895 164.232 171.744 164.232 192.5C164.232 227.814 130.584 253.861 84.9909 253.861H39.843ZM0 291.821H88.5337C155.829 291.821 206.282 249.884 206.282 194.257C206.282 163.79 190.794 136.43 163.341 118.763C177.513 104.63 184.153 88.2955 184.153 68.4276C184.153 27.3784 154.493 0 109.791 0H0V291.821Z" fill="#0B996E"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1 @@
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><rect fill="#ffe01b" height="512" rx="15%" width="512"/><path d="m418 306-6-17s25-38-37-51c0 0 4-47-18-69 48-47 37-118-72-72-56-107-272 144-182 184-9 12-9 72 53 78 42 90 144 96 203 69s93-113 59-122zm-263 40c-51-5-56-75-12-82s55 86 12 82zm-15-95c-14 0-31 19-31 19-68-33 123-252 164-167 0 0-100 48-133 148zm200 85c0-4-21 6-59-7 3-21 48 18 123-33l6 21c28-5 0 90-90 89-73-1-96-76-56-117 8-8-29-24-22-59 3-15 16-37 49-31s40-24 62-13 9 53 12 59 35 7 41 24-41 54-114 44c-17-2-27 20-16 34 22 32 112 11 127-20-38 29-116 40-122 9 22 10 59 4 59 0zm-131-158c22-27 51-43 51-43l-6 15s21-16 44-16l-8 8c26 1 37 11 37 11s-61-18-118 25zm135 39c13-1 9 29 9 29h-8s-14-28-1-29zm-59 33c-9 1-19 6-18 2 4-16 41-12 40 2s-9-6-22-4zm21 12c1 2-7 0-13 1s-12 4-12 2 23-11 25-3zm20 3c3-6 15 0 12 6s-15 0-12-6zm25 2c-6 0-6-13 0-13s6 14 0 14zm-180 53c3 3-6 9-13 4s8-29-10-35-13 17-18 14 7-35 28-22-6 33 6 39 5-2 7 0z" fill="#1e1e1e"/></svg>

After

Width:  |  Height:  |  Size: 969 B

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
<style type="text/css">
.st0{fill:#C12126;}
</style>
<path class="st0" d="M493,305.7c-88.9,0-161,72.1-161,161c0,88.9,72.1,161,161,161c88.9,0,161-72.1,161-161
C654,377.8,582,305.7,493,305.7z M242,466.7c0-138.7,112.4-251,251-251c138.7,0,251.1,112.4,251.1,251c0,9.2-0.5,18.2-1.4,27.1
c-1.9,24.5,16.1,43.2,40.4,43.2c41.3,0,45.7-53.2,45.7-70.3c0-185.4-150.3-335.6-335.6-335.6S157.4,281.4,157.4,466.7
c0,185.4,150.3,335.6,335.6,335.6c98.4,0,187-42.4,248.4-109.9l69,57.9c-77.9,87.1-191.3,142-317.4,142
c-235.1,0-425.7-190.6-425.7-425.7S257.9,41,493,41c235.1,0,425.7,190.6,425.7,425.7c0,94.5-45,171.2-135.4,171.2
c-39.8,0-64-18.2-77.2-38.6C661.9,670.5,583,717.8,493,717.8C354.4,717.8,242,605.4,242,466.7z M493,393.1c40.7,0,73.7,33,73.7,73.7
c0,40.7-33,73.7-73.7,73.7c-40.7,0-73.7-33-73.7-73.7S452.3,393.1,493,393.1z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="256px" height="255px" viewBox="0 0 256 255" version="1.1" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid">
<title>Mailjet</title>
<g>
<polygon fill="#9585F4" points="1.42108547e-14 97.9914749 93.4083546 140.330776 112.177323 121.670929 64.3819267 99.9556692 212.56948 43.2122762 155.607843 190.745098 134.001705 143.386189 115.123615 162.155158 116.105712 164.337596 157.462916 255.017903 256 -7.10542736e-15"></polygon>
</g>
</svg>

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm320-280L160-640v400h640v-400L480-440Zm0-80 320-200H160l320 200ZM160-640v-80 480-400Z"/></svg>

After

Width:  |  Height:  |  Size: 309 B

View File

@ -72,6 +72,9 @@
@import 'src/app/modules/info-overlay/info-overlay.component.scss';
@import 'src/app/modules/create-layout/create-layout.component.scss';
@import 'src/app/modules/domains/domain-verification/domain-verification.component.scss';
@import 'src/app/modules/smtp-table/smtp-table.component.scss';
@import 'src/app/modules/smtp-provider/smtp-provider.scss';
@import 'src/app/modules/policies/notification-smtp-provider/notification-smtp-provider.component.scss';
@import './styles/codemirror.scss';
@import 'src/app/components/copy-row/copy-row.component.scss';
@import 'src/app/modules/providers/provider-next/provider-next.component.scss';
@ -85,6 +88,8 @@
@include header-theme($theme);
@include app-type-radio-theme($theme);
@include idp-table-theme($theme);
@include smtp-table-theme($theme);
@include smtp-provider-theme($theme);
@include events-theme($theme);
@include projects-theme($theme);
@include grants-theme($theme);
@ -153,4 +158,5 @@
@include domain-verification-theme($theme);
@include copy-row-theme($theme);
@include provider-next-theme($theme);
@include smtp-settings-theme($theme);
}

View File

@ -37,12 +37,12 @@ We recommend setting your Branding and SMTP settings initially as it will comfor
In the Branding settings, you can upload you Logo for the login interface, set your own colors for buttons, background, links, and choose between multiple behavours. You don't need to be an expert as those settings can all be set without any knowledge of CSS.
| Setting | Description |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Logo | Upload your logo for the light and the dark design. This is used mainly in the login interface. |
| Icon | Upload your icon for the light and the dark design. Icons are used for smaller components. For example in console on the top left as the home button. |
| Colors | You can set four different colors to design your login page and email. (Background-, Primary-, Warn- and Font Color) |
| Font | Upload your custom font |
| Setting | Description |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Logo | Upload your logo for the light and the dark design. This is used mainly in the login interface. |
| Icon | Upload your icon for the light and the dark design. Icons are used for smaller components. For example in console on the top left as the home button. |
| Colors | You can set four different colors to design your login page and email. (Background-, Primary-, Warn- and Font Color) |
| Font | Upload your custom font |
| Advanced Behavior | **Hide Loginname suffix**: If enabled, your loginname suffix (Domain) will not be shown in the login page. **Disable Watermark**: If you disable the watermark you will not see the "Powered by ZITADEL" in the login page |
Make sure you click the "Apply configuration" button after you finish your configuration. This will ensure your design is visible for your customers.
@ -66,7 +66,14 @@ You can configure on which changes the users will be notified. The text of the m
### SMTP
On each instance we configure our default SMTP provider. To make sure, that you only send some E-Mails from domains you own. You need to add a custom domain on your instance.
On each instance we configure our default SMTP provider. To make sure, that you only send some E-Mails from domains you own, you need to add a custom domain on your instance.
You can configure many SMTP providers using templates for popular providers. The templates will add known settings like host, port or default user and will suggest values for user and/or password.
:::important
You have to activate your SMTP provider so Zitadel can use it to send your emails. Only one provider can be active.
:::
Go to the ZITADEL [customer portal](https://zitadel.cloud) to configure a custom domain.
To configure your custom SMTP please fill the following fields:
@ -90,16 +97,16 @@ No default provider is configured to send some SMS to your users. If you like to
The Login Policy defines how the login process should look like and which authentication options a user has to authenticate.
| Setting | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Username Password allowed | Possibility to login with username and password. If this is disabled only login with external identity providers will be allowed |
| Register allowed | Enable self register possibility in the login ui, this enables username password registration as well as registration with configured external identity providers |
| External IDP allowed | Possibility to login with an external identity (e.g Google, Microsoft, Apple, etc), If you like to allow external Identity providers add them to the providers list |
| Hide password reset | Disable the self-service option for users to reset their password. |
| Domain discovery allowed | If this setting is enabled, the user does't not mandatory have to exist when entering the username. It is required to have verified domains on the organization. Example: ZITADEL is registered as organization with the domain zitadel.com and Entra ID as identity provider. A user enters john@zitadel.com in the login but the user doesn't exist. The domain can be mapped to the organization and therefore the user can be redirected to the Entra ID.
| Ignore unknown usernames | This setting can be enabled, if no error message should be shown if the user doesn't exist. Example: A user enters the login name john@zitadel.com, the user doesn't exist, but will be redirected to the password screen. After entering a password, the user will get an error that either username or password are wrong. |
| Disable login with email address | By default users can additionally [login with the email attribute](/docs/guides/solution-scenarios/configurations#use-an-email-address-as-username) of their user. Check this option to disable. |
| Disable login with phone number | By default users can additionally [login with the phonenumber attribute](/docs/guides/solution-scenarios/configurations#use-a-phone-number-as-username) of their user. Check this option to disable. |
| Setting | Description |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Username Password allowed | Possibility to login with username and password. If this is disabled only login with external identity providers will be allowed |
| Register allowed | Enable self register possibility in the login ui, this enables username password registration as well as registration with configured external identity providers |
| External IDP allowed | Possibility to login with an external identity (e.g Google, Microsoft, Apple, etc), If you like to allow external Identity providers add them to the providers list |
| Hide password reset | Disable the self-service option for users to reset their password. |
| Domain discovery allowed | If this setting is enabled, the user does't not mandatory have to exist when entering the username. It is required to have verified domains on the organization. Example: ZITADEL is registered as organization with the domain zitadel.com and Entra ID as identity provider. A user enters john@zitadel.com in the login but the user doesn't exist. The domain can be mapped to the organization and therefore the user can be redirected to the Entra ID. |
| Ignore unknown usernames | This setting can be enabled, if no error message should be shown if the user doesn't exist. Example: A user enters the login name john@zitadel.com, the user doesn't exist, but will be redirected to the password screen. After entering a password, the user will get an error that either username or password are wrong. |
| Disable login with email address | By default users can additionally [login with the email attribute](/docs/guides/solution-scenarios/configurations#use-an-email-address-as-username) of their user. Check this option to disable. |
| Disable login with phone number | By default users can additionally [login with the phonenumber attribute](/docs/guides/solution-scenarios/configurations#use-a-phone-number-as-username) of their user. Check this option to disable. |
<img
src="/docs/img/guides/console/loginpolicy.png"
@ -115,6 +122,7 @@ The auth request contains a client-id and a redirect uri, that must match the co
If there is no [auth request](https://zitadel.com/docs/apis/openidoauth/authrequest), users will be redirected to the Default Redirect URI, which is by default https://<custom_domain>/ui/console/
Reasons why ZITADEL doesn't have a redirect URI:
- The login has not been called with an OIDC authorize request
- The user landed on the login through an email link, e.g. Password Reset, Initialize User

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -17,27 +17,118 @@ describe('instance notifications', () => {
describe('smtp settings', () => {
it(`should show SMTP provider settings`, () => {
cy.visit(smtpPath);
cy.contains('SMTP Settings');
cy.contains('SMTP Provider');
});
it(`should add SMTP provider settings`, () => {
it(`should add Mailgun SMTP provider settings`, () => {
let rowSelector = `a:contains('Mailgun')`;
cy.visit(smtpPath);
cy.get('[formcontrolname="senderAddress"]').clear().type('sender@example.com');
cy.get('[formcontrolname="senderName"]').clear().type('Zitadel');
cy.get('[formcontrolname="hostAndPort"]').clear().type('smtp.mailtrap.io:2525');
cy.get(rowSelector).click();
cy.get('[formcontrolname="hostAndPort"]').should('have.value', 'smtp.mailgun.org:587');
cy.get('[formcontrolname="user"]').clear().type('user@example.com');
cy.get('[data-e2e="save-smtp-settings-button"]').click();
cy.get('[formcontrolname="password"]').clear().type('password');
cy.get('[data-e2e="continue-button"]').click();
cy.get('[formcontrolname="senderAddress"]').clear().type('sender1@example.com');
cy.get('[formcontrolname="senderName"]').clear().type('Test1');
cy.get('[formcontrolname="replyToAddress"]').clear().type('replyto1@example.com');
cy.get('[data-e2e="create-button"]').click();
cy.shouldConfirmSuccess();
cy.get('[formcontrolname="senderAddress"]').should('have.value', 'sender@example.com');
cy.get('[formcontrolname="senderName"]').should('have.value', 'Zitadel');
cy.get('[formcontrolname="hostAndPort"]').should('have.value', 'smtp.mailtrap.io:2525');
cy.get('[formcontrolname="user"]').should('have.value', 'user@example.com');
cy.get('tr').contains('mailgun');
cy.get('tr').contains('smtp.mailgun.org:587');
cy.get('tr').contains('sender1@example.com');
});
it(`should add SMTP provider password`, () => {
it(`should change Mailgun SMTP provider settings`, () => {
let rowSelector = `tr:contains('mailgun')`;
cy.visit(smtpPath);
cy.get('[data-e2e="add-smtp-password-button"]').click();
cy.get('[data-e2e="notification-setting-password"]').clear().type('dummy@example.com');
cy.get('[data-e2e="save-notification-setting-password-button"]').click();
cy.get(rowSelector).click();
cy.get('[formcontrolname="hostAndPort"]').should('have.value', 'smtp.mailgun.org:587');
cy.get('[formcontrolname="user"]').should('have.value', 'user@example.com');
cy.get('[formcontrolname="user"]').clear().type('change@example.com');
cy.get('[data-e2e="continue-button"]').click();
cy.get('[formcontrolname="senderAddress"]').should('have.value', 'sender1@example.com');
cy.get('[formcontrolname="senderName"]').should('have.value', 'Test1');
cy.get('[formcontrolname="replyToAddress"]').should('have.value', 'replyto1@example.com');
cy.get('[formcontrolname="senderAddress"]').clear().type('senderchange1@example.com');
cy.get('[formcontrolname="senderName"]').clear().type('Change1');
cy.get('[data-e2e="create-button"]').click();
cy.shouldConfirmSuccess();
rowSelector = `tr:contains('mailgun')`;
cy.get(rowSelector).contains('mailgun');
cy.get(rowSelector).contains('smtp.mailgun.org:587');
cy.get(rowSelector).contains('senderchange1@example.com');
});
it(`should activate Mailgun SMTP provider settings`, () => {
let rowSelector = `tr:contains('smtp.mailgun.org:587')`;
cy.visit(smtpPath);
cy.get(rowSelector).find('[data-e2e="activate-provider-button"]').click({ force: true });
cy.get('[data-e2e="confirm-dialog-button"]').click();
cy.shouldConfirmSuccess();
rowSelector = `tr:contains('smtp.mailgun.org:587')`;
cy.get(rowSelector).find('[data-e2e="active-provider"]');
cy.get(rowSelector).contains('mailgun');
cy.get(rowSelector).contains('smtp.mailgun.org:587');
cy.get(rowSelector).contains('senderchange1@example.com');
});
it(`should add Mailjet SMTP provider settings`, () => {
let rowSelector = `a:contains('Mailjet')`;
cy.visit(smtpPath);
cy.get(rowSelector).click();
cy.get('[formcontrolname="hostAndPort"]').should('have.value', 'in-v3.mailjet.com:587');
cy.get('[formcontrolname="user"]').clear().type('user@example.com');
cy.get('[formcontrolname="password"]').clear().type('password');
cy.get('[data-e2e="continue-button"]').click();
cy.get('[formcontrolname="senderAddress"]').clear().type('sender2@example.com');
cy.get('[formcontrolname="senderName"]').clear().type('Test2');
cy.get('[formcontrolname="replyToAddress"]').clear().type('replyto2@example.com');
cy.get('[data-e2e="create-button"]').click();
cy.shouldConfirmSuccess();
rowSelector = `tr:contains('mailjet')`;
cy.get(rowSelector).contains('mailjet');
cy.get(rowSelector).contains('in-v3.mailjet.com:587');
cy.get(rowSelector).contains('sender2@example.com');
});
it(`should activate Mailjet SMTP provider settings an disable Mailgun`, () => {
let rowSelector = `tr:contains('in-v3.mailjet.com:587')`;
cy.visit(smtpPath);
cy.get(rowSelector).find('[data-e2e="activate-provider-button"]').click({ force: true });
cy.get('[data-e2e="confirm-dialog-button"]').click();
cy.shouldConfirmSuccess();
cy.get(rowSelector).find('[data-e2e="active-provider"]');
cy.get(rowSelector).contains('mailjet');
cy.get(rowSelector).contains('in-v3.mailjet.com:587');
cy.get(rowSelector).contains('sender2@example.com');
rowSelector = `tr:contains('mailgun')`;
cy.get(rowSelector).find('[data-e2e="active-provider"]').should('not.exist');
});
it(`should deactivate Mailjet SMTP provider`, () => {
let rowSelector = `tr:contains('mailjet')`;
cy.visit(smtpPath);
cy.get(rowSelector).find('[data-e2e="deactivate-provider-button"]').click({ force: true });
cy.get('[data-e2e="confirm-dialog-button"]').click();
cy.shouldConfirmSuccess();
rowSelector = `tr:contains('mailjet')`;
cy.get(rowSelector).find('[data-e2e="active-provider"]').should('not.exist');
rowSelector = `tr:contains('mailgun')`;
cy.get(rowSelector).find('[data-e2e="active-provider"]').should('not.exist');
});
it(`should delete Mailjet SMTP provider`, () => {
let rowSelector = `tr:contains('mailjet')`;
cy.visit(smtpPath);
cy.get(rowSelector).find('[data-e2e="delete-provider-button"]').click({ force: true });
cy.get('[data-e2e="confirm-dialog-input"]').focus().type('Test2');
cy.get('[data-e2e="confirm-dialog-button"]').click();
cy.shouldConfirmSuccess();
rowSelector = `tr:contains('mailjet')`;
cy.get(rowSelector).should('not.exist');
});
it(`should delete Mailgun SMTP provider`, () => {
let rowSelector = `tr:contains('mailgun')`;
cy.visit(smtpPath);
cy.get(rowSelector).find('[data-e2e="delete-provider-button"]').click({ force: true });
cy.get('[data-e2e="confirm-dialog-input"]').focus().type('Change1');
cy.get('[data-e2e="confirm-dialog-button"]').click();
cy.shouldConfirmSuccess();
rowSelector = `tr:contains('mailgun')`;
cy.get(rowSelector).should('not.exist');
});
});

5366
e2e/package-lock.json generated

File diff suppressed because it is too large Load Diff

1729
e2e/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,6 @@ package admin
import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/grpc/object"
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
)
@ -46,68 +45,6 @@ func (s *Server) UpdateSecretGenerator(ctx context.Context, req *admin_pb.Update
}, nil
}
func (s *Server) GetSMTPConfig(ctx context.Context, req *admin_pb.GetSMTPConfigRequest) (*admin_pb.GetSMTPConfigResponse, error) {
smtp, err := s.query.SMTPConfigByAggregateID(ctx, authz.GetInstance(ctx).InstanceID())
if err != nil {
return nil, err
}
return &admin_pb.GetSMTPConfigResponse{
SmtpConfig: SMTPConfigToPb(smtp),
}, nil
}
func (s *Server) AddSMTPConfig(ctx context.Context, req *admin_pb.AddSMTPConfigRequest) (*admin_pb.AddSMTPConfigResponse, error) {
details, err := s.command.AddSMTPConfig(ctx, AddSMTPToConfig(req))
if err != nil {
return nil, err
}
return &admin_pb.AddSMTPConfigResponse{
Details: object.ChangeToDetailsPb(
details.Sequence,
details.EventDate,
details.ResourceOwner),
}, nil
}
func (s *Server) UpdateSMTPConfig(ctx context.Context, req *admin_pb.UpdateSMTPConfigRequest) (*admin_pb.UpdateSMTPConfigResponse, error) {
details, err := s.command.ChangeSMTPConfig(ctx, UpdateSMTPToConfig(req))
if err != nil {
return nil, err
}
return &admin_pb.UpdateSMTPConfigResponse{
Details: object.ChangeToDetailsPb(
details.Sequence,
details.EventDate,
details.ResourceOwner),
}, nil
}
func (s *Server) RemoveSMTPConfig(ctx context.Context, _ *admin_pb.RemoveSMTPConfigRequest) (*admin_pb.RemoveSMTPConfigResponse, error) {
details, err := s.command.RemoveSMTPConfig(ctx)
if err != nil {
return nil, err
}
return &admin_pb.RemoveSMTPConfigResponse{
Details: object.ChangeToDetailsPb(
details.Sequence,
details.EventDate,
details.ResourceOwner),
}, nil
}
func (s *Server) UpdateSMTPConfigPassword(ctx context.Context, req *admin_pb.UpdateSMTPConfigPasswordRequest) (*admin_pb.UpdateSMTPConfigPasswordResponse, error) {
details, err := s.command.ChangeSMTPConfigPassword(ctx, req.Password)
if err != nil {
return nil, err
}
return &admin_pb.UpdateSMTPConfigPasswordResponse{
Details: object.ChangeToDetailsPb(
details.Sequence,
details.EventDate,
details.ResourceOwner),
}, nil
}
func (s *Server) GetSecurityPolicy(ctx context.Context, req *admin_pb.GetSecurityPolicyRequest) (*admin_pb.GetSecurityPolicyResponse, error) {
policy, err := s.query.SecurityPolicy(ctx)
if err != nil {

View File

@ -133,6 +133,7 @@ func SecretGeneratorTypeToDomain(generatorType settings_pb.SecretGeneratorType)
func AddSMTPToConfig(req *admin_pb.AddSMTPConfigRequest) *smtp.Config {
return &smtp.Config{
Description: req.Description,
Tls: req.Tls,
From: req.SenderAddress,
FromName: req.SenderName,
@ -147,26 +148,30 @@ func AddSMTPToConfig(req *admin_pb.AddSMTPConfigRequest) *smtp.Config {
func UpdateSMTPToConfig(req *admin_pb.UpdateSMTPConfigRequest) *smtp.Config {
return &smtp.Config{
Description: req.Description,
Tls: req.Tls,
From: req.SenderAddress,
FromName: req.SenderName,
ReplyToAddress: req.ReplyToAddress,
SMTP: smtp.SMTP{
Host: req.Host,
User: req.User,
Host: req.Host,
User: req.User,
Password: req.Password,
},
}
}
func SMTPConfigToPb(smtp *query.SMTPConfig) *settings_pb.SMTPConfig {
mapped := &settings_pb.SMTPConfig{
Description: smtp.Description,
Tls: smtp.TLS,
SenderAddress: smtp.SenderAddress,
SenderName: smtp.SenderName,
ReplyToAddress: smtp.ReplyToAddress,
Host: smtp.Host,
User: smtp.User,
Details: obj_grpc.ToViewDetailsPb(smtp.Sequence, smtp.CreationDate, smtp.ChangeDate, smtp.AggregateID),
Details: obj_grpc.ToViewDetailsPb(smtp.Sequence, smtp.CreationDate, smtp.ChangeDate, smtp.ResourceOwner),
Id: smtp.ID,
}
return mapped
}

View File

@ -0,0 +1,130 @@
package admin
import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/grpc/object"
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
)
func (s *Server) GetSMTPConfig(ctx context.Context, req *admin_pb.GetSMTPConfigRequest) (*admin_pb.GetSMTPConfigResponse, error) {
smtp, err := s.query.SMTPConfigActive(ctx, authz.GetInstance(ctx).InstanceID())
if err != nil {
return nil, err
}
return &admin_pb.GetSMTPConfigResponse{
SmtpConfig: SMTPConfigToPb(smtp),
}, nil
}
func (s *Server) GetSMTPConfigById(ctx context.Context, req *admin_pb.GetSMTPConfigByIdRequest) (*admin_pb.GetSMTPConfigByIdResponse, error) {
instanceID := authz.GetInstance(ctx).InstanceID()
resourceOwner := instanceID // Will be replaced when orgs have smtp configs
smtp, err := s.query.SMTPConfigByID(ctx, instanceID, resourceOwner, req.Id)
if err != nil {
return nil, err
}
return &admin_pb.GetSMTPConfigByIdResponse{
SmtpConfig: SMTPConfigToPb(smtp),
}, nil
}
func (s *Server) AddSMTPConfig(ctx context.Context, req *admin_pb.AddSMTPConfigRequest) (*admin_pb.AddSMTPConfigResponse, error) {
id, details, err := s.command.AddSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), AddSMTPToConfig(req))
if err != nil {
return nil, err
}
return &admin_pb.AddSMTPConfigResponse{
Details: object.ChangeToDetailsPb(
details.Sequence,
details.EventDate,
details.ResourceOwner),
Id: id,
}, nil
}
func (s *Server) UpdateSMTPConfig(ctx context.Context, req *admin_pb.UpdateSMTPConfigRequest) (*admin_pb.UpdateSMTPConfigResponse, error) {
details, err := s.command.ChangeSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id, UpdateSMTPToConfig(req))
if err != nil {
return nil, err
}
return &admin_pb.UpdateSMTPConfigResponse{
Details: object.ChangeToDetailsPb(
details.Sequence,
details.EventDate,
details.ResourceOwner),
}, nil
}
func (s *Server) RemoveSMTPConfig(ctx context.Context, req *admin_pb.RemoveSMTPConfigRequest) (*admin_pb.RemoveSMTPConfigResponse, error) {
details, err := s.command.RemoveSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id)
if err != nil {
return nil, err
}
return &admin_pb.RemoveSMTPConfigResponse{
Details: object.ChangeToDetailsPb(
details.Sequence,
details.EventDate,
details.ResourceOwner),
}, nil
}
func (s *Server) UpdateSMTPConfigPassword(ctx context.Context, req *admin_pb.UpdateSMTPConfigPasswordRequest) (*admin_pb.UpdateSMTPConfigPasswordResponse, error) {
details, err := s.command.ChangeSMTPConfigPassword(ctx, authz.GetInstance(ctx).InstanceID(), req.Id, req.Password)
if err != nil {
return nil, err
}
return &admin_pb.UpdateSMTPConfigPasswordResponse{
Details: object.ChangeToDetailsPb(
details.Sequence,
details.EventDate,
details.ResourceOwner),
}, nil
}
func (s *Server) ListSMTPConfigs(ctx context.Context, req *admin_pb.ListSMTPConfigsRequest) (*admin_pb.ListSMTPConfigsResponse, error) {
queries, err := listSMTPConfigsToModel(req)
if err != nil {
return nil, err
}
result, err := s.query.SearchSMTPConfigs(ctx, queries)
if err != nil {
return nil, err
}
return &admin_pb.ListSMTPConfigsResponse{
Details: object.ToListDetails(result.Count, result.Sequence, result.LastRun),
Result: SMTPConfigsToPb(result.Configs),
}, nil
}
func (s *Server) ActivateSMTPConfig(ctx context.Context, req *admin_pb.ActivateSMTPConfigRequest) (*admin_pb.ActivateSMTPConfigResponse, error) {
// Get the ID of current SMTP active provider if any
currentActiveProviderID := ""
smtp, err := s.query.SMTPConfigActive(ctx, authz.GetInstance(ctx).InstanceID())
if err == nil {
currentActiveProviderID = smtp.ID
}
result, err := s.command.ActivateSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id, currentActiveProviderID)
if err != nil {
return nil, err
}
return &admin_pb.ActivateSMTPConfigResponse{
Details: object.DomainToAddDetailsPb(result),
}, nil
}
func (s *Server) DeactivateSMTPConfig(ctx context.Context, req *admin_pb.DeactivateSMTPConfigRequest) (*admin_pb.DeactivateSMTPConfigResponse, error) {
result, err := s.command.DeactivateSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id)
if err != nil {
return nil, err
}
return &admin_pb.DeactivateSMTPConfigResponse{
Details: object.DomainToAddDetailsPb(result),
}, nil
}

View File

@ -0,0 +1,41 @@
package admin
import (
"github.com/zitadel/zitadel/internal/api/grpc/object"
"github.com/zitadel/zitadel/internal/query"
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
settings_pb "github.com/zitadel/zitadel/pkg/grpc/settings"
)
func listSMTPConfigsToModel(req *admin_pb.ListSMTPConfigsRequest) (*query.SMTPConfigsSearchQueries, error) {
offset, limit, asc := object.ListQueryToModel(req.Query)
return &query.SMTPConfigsSearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
},
}, nil
}
func SMTPConfigToProviderPb(config *query.SMTPConfig) *settings_pb.SMTPConfig {
return &settings_pb.SMTPConfig{
Details: object.ToViewDetailsPb(config.Sequence, config.CreationDate, config.ChangeDate, config.ResourceOwner),
Id: config.ID,
Description: config.Description,
Tls: config.TLS,
Host: config.Host,
User: config.User,
State: settings_pb.SMTPConfigState(config.State),
SenderAddress: config.SenderAddress,
SenderName: config.SenderName,
}
}
func SMTPConfigsToPb(configs []*query.SMTPConfig) []*settings_pb.SMTPConfig {
c := make([]*settings_pb.SMTPConfig, len(configs))
for i, config := range configs {
c[i] = SMTPConfigToProviderPb(config)
}
return c
}

View File

@ -391,6 +391,7 @@ func setupSMTPSettings(commands *Commands, validations *[]preparation.Validation
*validations = append(*validations,
commands.prepareAddSMTPConfig(
instanceAgg,
smtpConfig.Description,
smtpConfig.From,
smtpConfig.FromName,
smtpConfig.ReplyToAddress,

View File

@ -82,5 +82,11 @@ func (wm *InstanceWriteModel) Query() *eventstore.SearchQueryBuilder {
}
func InstanceAggregateFromWriteModel(wm *eventstore.WriteModel) *eventstore.Aggregate {
return eventstore.AggregateFromWriteModel(wm, instance.AggregateType, instance.AggregateVersion)
return &eventstore.Aggregate{
ID: wm.AggregateID,
Type: instance.AggregateType,
ResourceOwner: wm.ResourceOwner,
InstanceID: wm.InstanceID,
Version: instance.AggregateVersion,
}
}

View File

@ -9,16 +9,18 @@ import (
"github.com/zitadel/zitadel/internal/repository/instance"
)
type InstanceSMTPConfigWriteModel struct {
type IAMSMTPConfigWriteModel struct {
eventstore.WriteModel
SenderAddress string
SenderName string
ReplyToAddress string
ID string
Description string
TLS bool
Host string
User string
Password *crypto.CryptoValue
SenderAddress string
SenderName string
ReplyToAddress string
State domain.SMTPConfigState
domain string
@ -26,17 +28,19 @@ type InstanceSMTPConfigWriteModel struct {
smtpSenderAddressMatchesInstanceDomain bool
}
func NewInstanceSMTPConfigWriteModel(instanceID, domain string) *InstanceSMTPConfigWriteModel {
return &InstanceSMTPConfigWriteModel{
func NewIAMSMTPConfigWriteModel(instanceID, id, domain string) *IAMSMTPConfigWriteModel {
return &IAMSMTPConfigWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: instanceID,
ResourceOwner: instanceID,
InstanceID: instanceID,
},
ID: id,
domain: domain,
}
}
func (wm *InstanceSMTPConfigWriteModel) AppendEvents(events ...eventstore.Event) {
func (wm *IAMSMTPConfigWriteModel) AppendEvents(events ...eventstore.Event) {
for _, event := range events {
switch e := event.(type) {
case *instance.DomainAddedEvent:
@ -56,46 +60,34 @@ func (wm *InstanceSMTPConfigWriteModel) AppendEvents(events ...eventstore.Event)
}
}
func (wm *InstanceSMTPConfigWriteModel) Reduce() error {
func (wm *IAMSMTPConfigWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *instance.SMTPConfigAddedEvent:
wm.TLS = e.TLS
wm.SenderAddress = e.SenderAddress
wm.SenderName = e.SenderName
wm.ReplyToAddress = e.ReplyToAddress
wm.Host = e.Host
wm.User = e.User
wm.Password = e.Password
wm.State = domain.SMTPConfigStateActive
if wm.ID != e.ID {
continue
}
wm.reduceSMTPConfigAddedEvent(e)
case *instance.SMTPConfigChangedEvent:
if e.TLS != nil {
wm.TLS = *e.TLS
}
if e.FromAddress != nil {
wm.SenderAddress = *e.FromAddress
}
if e.FromName != nil {
wm.SenderName = *e.FromName
}
if e.ReplyToAddress != nil {
wm.ReplyToAddress = *e.ReplyToAddress
}
if e.Host != nil {
wm.Host = *e.Host
}
if e.User != nil {
wm.User = *e.User
if wm.ID != e.ID {
continue
}
wm.reduceSMTPConfigChangedEvent(e)
case *instance.SMTPConfigRemovedEvent:
wm.State = domain.SMTPConfigStateRemoved
wm.TLS = false
wm.SenderName = ""
wm.SenderAddress = ""
wm.ReplyToAddress = ""
wm.Host = ""
wm.User = ""
wm.Password = nil
if wm.ID != e.ID {
continue
}
wm.reduceSMTPConfigRemovedEvent(e)
case *instance.SMTPConfigActivatedEvent:
if wm.ID != e.ID {
continue
}
wm.State = domain.SMTPConfigStateActive
case *instance.SMTPConfigDeactivatedEvent:
if wm.ID != e.ID {
continue
}
wm.State = domain.SMTPConfigStateInactive
case *instance.DomainAddedEvent:
wm.domainState = domain.InstanceDomainStateActive
case *instance.DomainRemovedEvent:
@ -111,7 +103,13 @@ func (wm *InstanceSMTPConfigWriteModel) Reduce() error {
return wm.WriteModel.Reduce()
}
func (wm *InstanceSMTPConfigWriteModel) Query() *eventstore.SearchQueryBuilder {
func (wm *IAMSMTPConfigWriteModel) Query() *eventstore.SearchQueryBuilder {
// If ID equals ResourceOwner we're dealing with the old and unique smtp settings
// Let's set the empty ID for the query
if wm.ID == wm.ResourceOwner {
wm.ID = ""
}
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
@ -122,6 +120,9 @@ func (wm *InstanceSMTPConfigWriteModel) Query() *eventstore.SearchQueryBuilder {
instance.SMTPConfigRemovedEventType,
instance.SMTPConfigChangedEventType,
instance.SMTPConfigPasswordChangedEventType,
instance.SMTPConfigActivatedEventType,
instance.SMTPConfigDeactivatedEventType,
instance.SMTPConfigRemovedEventType,
instance.InstanceDomainAddedEventType,
instance.InstanceDomainRemovedEventType,
instance.DomainPolicyAddedEventType,
@ -129,10 +130,16 @@ func (wm *InstanceSMTPConfigWriteModel) Query() *eventstore.SearchQueryBuilder {
Builder()
}
func (wm *InstanceSMTPConfigWriteModel) NewChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, tls bool, fromAddress, fromName, replyToAddress, smtpHost, smtpUser string) (*instance.SMTPConfigChangedEvent, bool, error) {
func (wm *IAMSMTPConfigWriteModel) NewChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, id, description string, tls bool, fromAddress, fromName, replyToAddress, smtpHost, smtpUser string, smtpPassword *crypto.CryptoValue) (*instance.SMTPConfigChangedEvent, bool, error) {
changes := make([]instance.SMTPConfigChanges, 0)
var err error
if wm.ID != id {
changes = append(changes, instance.ChangeSMTPConfigID(id))
}
if wm.Description != description {
changes = append(changes, instance.ChangeSMTPConfigDescription(description))
}
if wm.TLS != tls {
changes = append(changes, instance.ChangeSMTPConfigTLS(tls))
}
@ -151,13 +158,87 @@ func (wm *InstanceSMTPConfigWriteModel) NewChangedEvent(ctx context.Context, agg
if wm.User != smtpUser {
changes = append(changes, instance.ChangeSMTPConfigSMTPUser(smtpUser))
}
if smtpPassword != nil {
changes = append(changes, instance.ChangeSMTPConfigSMTPPassword(smtpPassword))
}
if len(changes) == 0 {
return nil, false, nil
}
changeEvent, err := instance.NewSMTPConfigChangeEvent(ctx, aggregate, changes)
changeEvent, err := instance.NewSMTPConfigChangeEvent(ctx, aggregate, id, changes)
if err != nil {
return nil, false, err
}
return changeEvent, true, nil
}
func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigAddedEvent(e *instance.SMTPConfigAddedEvent) {
wm.Description = e.Description
wm.TLS = e.TLS
wm.Host = e.Host
wm.User = e.User
wm.Password = e.Password
wm.SenderAddress = e.SenderAddress
wm.SenderName = e.SenderName
wm.ReplyToAddress = e.ReplyToAddress
wm.State = domain.SMTPConfigStateInactive
// If ID has empty value we're dealing with the old and unique smtp settings
// These would be the default values for ID and State
if e.ID == "" {
wm.Description = "generic"
wm.ID = e.Aggregate().ResourceOwner
wm.State = domain.SMTPConfigStateActive
}
}
func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigChangedEvent(e *instance.SMTPConfigChangedEvent) {
if e.Description != nil {
wm.Description = *e.Description
}
if e.TLS != nil {
wm.TLS = *e.TLS
}
if e.Host != nil {
wm.Host = *e.Host
}
if e.User != nil {
wm.User = *e.User
}
if e.Password != nil {
wm.Password = e.Password
}
if e.FromAddress != nil {
wm.SenderAddress = *e.FromAddress
}
if e.FromName != nil {
wm.SenderName = *e.FromName
}
if e.ReplyToAddress != nil {
wm.ReplyToAddress = *e.ReplyToAddress
}
// If ID has empty value we're dealing with the old and unique smtp settings
// These would be the default values for ID and State
if e.ID == "" {
wm.Description = "generic"
wm.ID = e.Aggregate().ResourceOwner
wm.State = domain.SMTPConfigStateActive
}
}
func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigRemovedEvent(e *instance.SMTPConfigRemovedEvent) {
wm.Description = ""
wm.TLS = false
wm.SenderName = ""
wm.SenderAddress = ""
wm.ReplyToAddress = ""
wm.Host = ""
wm.User = ""
wm.Password = nil
wm.State = domain.SMTPConfigStateRemoved
// If ID has empty value we're dealing with the old and unique smtp settings
// These would be the default values for ID and State
if e.ID == "" {
wm.ID = e.Aggregate().ResourceOwner
}
}

View File

@ -15,51 +15,153 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func (c *Commands) AddSMTPConfig(ctx context.Context, config *smtp.Config) (*domain.ObjectDetails, error) {
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
validation := c.prepareAddSMTPConfig(instanceAgg, config.From, config.FromName, config.ReplyToAddress, config.SMTP.Host, config.SMTP.User, []byte(config.SMTP.Password), config.Tls)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation)
func (c *Commands) AddSMTPConfig(ctx context.Context, instanceID string, config *smtp.Config) (string, *domain.ObjectDetails, error) {
id, err := c.idGenerator.Next()
if err != nil {
return nil, err
return "", nil, err
}
events, err := c.eventstore.Push(ctx, cmds...)
from := strings.TrimSpace(config.From)
if from == "" {
return "", nil, zerrors.ThrowInvalidArgument(nil, "INST-ASv2d", "Errors.Invalid.Argument")
}
fromSplitted := strings.Split(from, "@")
senderDomain := fromSplitted[len(fromSplitted)-1]
description := strings.TrimSpace(config.Description)
replyTo := strings.TrimSpace(config.ReplyToAddress)
hostAndPort := strings.TrimSpace(config.SMTP.Host)
if _, _, err := net.SplitHostPort(hostAndPort); err != nil {
return "", nil, zerrors.ThrowInvalidArgument(nil, "INST-9JdRe", "Errors.Invalid.Argument")
}
var smtpPassword *crypto.CryptoValue
if config.SMTP.Password != "" {
smtpPassword, err = crypto.Encrypt([]byte(config.SMTP.Password), c.smtpEncryption)
if err != nil {
return "", nil, err
}
}
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, senderDomain)
if err != nil {
return nil, err
return "", nil, err
}
return &domain.ObjectDetails{
Sequence: events[len(events)-1].Sequence(),
EventDate: events[len(events)-1].CreatedAt(),
ResourceOwner: events[len(events)-1].Aggregate().InstanceID,
}, nil
err = checkSenderAddress(smtpConfigWriteModel)
if err != nil {
return "", nil, err
}
iamAgg := InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel)
pushedEvents, err := c.eventstore.Push(ctx, instance.NewSMTPConfigAddedEvent(
ctx,
iamAgg,
id,
description,
config.Tls,
config.From,
config.FromName,
replyTo,
hostAndPort,
config.SMTP.User,
smtpPassword,
))
if err != nil {
return "", nil, err
}
err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...)
if err != nil {
return "", nil, err
}
return id, writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
}
func (c *Commands) ChangeSMTPConfig(ctx context.Context, config *smtp.Config) (*domain.ObjectDetails, error) {
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
validation := c.prepareChangeSMTPConfig(instanceAgg, config.From, config.FromName, config.ReplyToAddress, config.SMTP.Host, config.SMTP.User, config.Tls)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation)
func (c *Commands) ChangeSMTPConfig(ctx context.Context, instanceID string, id string, config *smtp.Config) (*domain.ObjectDetails, error) {
if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "SMTP-x8vo9", "Errors.IDMissing")
}
from := strings.TrimSpace(config.From)
if from == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "INST-HSv2d", "Errors.Invalid.Argument")
}
fromSplitted := strings.Split(from, "@")
senderDomain := fromSplitted[len(fromSplitted)-1]
description := strings.TrimSpace(config.Description)
replyTo := strings.TrimSpace(config.ReplyToAddress)
hostAndPort := strings.TrimSpace(config.SMTP.Host)
if _, _, err := net.SplitHostPort(hostAndPort); err != nil {
return nil, zerrors.ThrowInvalidArgument(nil, "INST-Kv875", "Errors.Invalid.Argument")
}
var smtpPassword *crypto.CryptoValue
var err error
if config.SMTP.Password != "" {
smtpPassword, err = crypto.Encrypt([]byte(config.SMTP.Password), c.smtpEncryption)
if err != nil {
return nil, err
}
}
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, senderDomain)
if err != nil {
return nil, err
}
events, err := c.eventstore.Push(ctx, cmds...)
if !smtpConfigWriteModel.State.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-7j8gv", "Errors.SMTPConfig.NotFound")
}
err = checkSenderAddress(smtpConfigWriteModel)
if err != nil {
return nil, err
}
return &domain.ObjectDetails{
Sequence: events[len(events)-1].Sequence(),
EventDate: events[len(events)-1].CreatedAt(),
ResourceOwner: events[len(events)-1].Aggregate().InstanceID,
}, nil
iamAgg := InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel)
changedEvent, hasChanged, err := smtpConfigWriteModel.NewChangedEvent(
ctx,
iamAgg,
id,
description,
config.Tls,
from,
config.FromName,
replyTo,
hostAndPort,
config.SMTP.User,
smtpPassword,
)
if err != nil {
return nil, err
}
if !hasChanged {
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-lh3op", "Errors.NoChangesFound")
}
pushedEvents, err := c.eventstore.Push(ctx, changedEvent)
if err != nil {
return nil, err
}
err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
}
func (c *Commands) ChangeSMTPConfigPassword(ctx context.Context, password string) (*domain.ObjectDetails, error) {
func (c *Commands) ChangeSMTPConfigPassword(ctx context.Context, instanceID, id string, password string) (*domain.ObjectDetails, error) {
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
smtpConfigWriteModel, err := getSMTPConfigWriteModel(ctx, c.eventstore.Filter, "")
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, "")
if err != nil {
return nil, err
}
if smtpConfigWriteModel.State != domain.SMTPConfigStateActive {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-3n9ls", "Errors.SMTPConfig.NotFound")
}
var smtpPassword *crypto.CryptoValue
if password != "" {
smtpPassword, err = crypto.Encrypt([]byte(password), c.smtpEncryption)
@ -67,39 +169,144 @@ func (c *Commands) ChangeSMTPConfigPassword(ctx context.Context, password string
return nil, err
}
}
events, err := c.eventstore.Push(ctx, instance.NewSMTPConfigPasswordChangedEvent(
pushedEvents, err := c.eventstore.Push(ctx, instance.NewSMTPConfigPasswordChangedEvent(
ctx,
&instanceAgg.Aggregate,
id,
smtpPassword))
if err != nil {
return nil, err
}
return &domain.ObjectDetails{
Sequence: events[len(events)-1].Sequence(),
EventDate: events[len(events)-1].CreatedAt(),
ResourceOwner: events[len(events)-1].Aggregate().InstanceID,
}, nil
}
func (c *Commands) RemoveSMTPConfig(ctx context.Context) (*domain.ObjectDetails, error) {
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
validation := c.prepareRemoveSMTPConfig(instanceAgg)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation)
err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...)
if err != nil {
return nil, err
}
events, err := c.eventstore.Push(ctx, cmds...)
return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
}
func (c *Commands) ActivateSMTPConfig(ctx context.Context, instanceID, id, activatedId string) (*domain.ObjectDetails, error) {
if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "SMTP-nm56k", "Errors.IDMissing")
}
if len(activatedId) > 0 {
_, err := c.DeactivateSMTPConfig(ctx, instanceID, activatedId)
if err != nil {
return nil, err
}
}
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, "")
if err != nil {
return nil, err
}
return &domain.ObjectDetails{
Sequence: events[len(events)-1].Sequence(),
EventDate: events[len(events)-1].CreatedAt(),
ResourceOwner: events[len(events)-1].Aggregate().InstanceID,
}, nil
if !smtpConfigWriteModel.State.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-kg8yr", "Errors.SMTPConfig.NotFound")
}
if smtpConfigWriteModel.State == domain.SMTPConfigStateActive {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-ed3lr", "Errors.SMTPConfig.AlreadyActive")
}
iamAgg := InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel)
pushedEvents, err := c.eventstore.Push(ctx, instance.NewSMTPConfigActivatedEvent(
ctx,
iamAgg,
id))
if err != nil {
return nil, err
}
err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
}
func (c *Commands) prepareAddSMTPConfig(a *instance.Aggregate, from, name, replyTo, hostAndPort, user string, password []byte, tls bool) preparation.Validation {
func (c *Commands) DeactivateSMTPConfig(ctx context.Context, instanceID, id string) (*domain.ObjectDetails, error) {
if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "SMTP-98ikl", "Errors.IDMissing")
}
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, "")
if err != nil {
return nil, err
}
if !smtpConfigWriteModel.State.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-k39PJ", "Errors.SMTPConfig.NotFound")
}
if smtpConfigWriteModel.State == domain.SMTPConfigStateInactive {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-km8g3", "Errors.SMTPConfig.AlreadyDeactivated")
}
iamAgg := InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel)
pushedEvents, err := c.eventstore.Push(ctx, instance.NewSMTPConfigDeactivatedEvent(
ctx,
iamAgg,
id))
if err != nil {
return nil, err
}
err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
}
func (c *Commands) RemoveSMTPConfig(ctx context.Context, instanceID, id string) (*domain.ObjectDetails, error) {
if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "SMTP-7f5cv", "Errors.IDMissing")
}
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, "")
if err != nil {
return nil, err
}
if !smtpConfigWriteModel.State.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-kg8rt", "Errors.SMTPConfig.NotFound")
}
iamAgg := InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel)
pushedEvents, err := c.eventstore.Push(ctx, instance.NewSMTPConfigRemovedEvent(
ctx,
iamAgg,
id))
if err != nil {
return nil, err
}
err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
}
func checkSenderAddress(writeModel *IAMSMTPConfigWriteModel) error {
if !writeModel.smtpSenderAddressMatchesInstanceDomain {
return nil
}
if !writeModel.domainState.Exists() {
return zerrors.ThrowInvalidArgument(nil, "INST-83nl8", "Errors.SMTPConfig.SenderAdressNotCustomDomain")
}
return nil
}
func (c *Commands) getSMTPConfig(ctx context.Context, instanceID, id, domain string) (writeModel *IAMSMTPConfigWriteModel, err error) {
writeModel = NewIAMSMTPConfigWriteModel(instanceID, id, domain)
err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
// TODO: SetUpInstance still uses this and would be removed as soon as deprecated PrepareCommands is removed
func (c *Commands) prepareAddSMTPConfig(a *instance.Aggregate, description, from, name, replyTo, hostAndPort, user string, password []byte, tls bool) preparation.Validation {
return func() (preparation.CreateCommands, error) {
if from = strings.TrimSpace(from); from == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "INST-mruNY", "Errors.Invalid.Argument")
@ -112,9 +319,14 @@ func (c *Commands) prepareAddSMTPConfig(a *instance.Aggregate, from, name, reply
return nil, zerrors.ThrowInvalidArgument(nil, "INST-9JdRe", "Errors.Invalid.Argument")
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
id, err := c.idGenerator.Next()
if err != nil {
return nil, zerrors.ThrowInternal(nil, "INST-9JdRe", "Errors.Invalid.Argument")
}
fromSplitted := strings.Split(from, "@")
senderDomain := fromSplitted[len(fromSplitted)-1]
writeModel, err := getSMTPConfigWriteModel(ctx, filter, senderDomain)
writeModel, err := getSMTPConfigWriteModel(ctx, filter, id, senderDomain)
if err != nil {
return nil, err
}
@ -136,6 +348,8 @@ func (c *Commands) prepareAddSMTPConfig(a *instance.Aggregate, from, name, reply
instance.NewSMTPConfigAddedEvent(
ctx,
&a.Aggregate,
id,
description,
tls,
from,
name,
@ -149,83 +363,8 @@ func (c *Commands) prepareAddSMTPConfig(a *instance.Aggregate, from, name, reply
}
}
func (c *Commands) prepareChangeSMTPConfig(a *instance.Aggregate, from, name, replyTo, hostAndPort, user string, tls bool) preparation.Validation {
return func() (preparation.CreateCommands, error) {
if from = strings.TrimSpace(from); from == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "INST-ASv2d", "Errors.Invalid.Argument")
}
replyTo = strings.TrimSpace(replyTo)
hostAndPort = strings.TrimSpace(hostAndPort)
if _, _, err := net.SplitHostPort(hostAndPort); err != nil {
return nil, zerrors.ThrowInvalidArgument(nil, "INST-Kv875", "Errors.Invalid.Argument")
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
fromSplitted := strings.Split(from, "@")
senderDomain := fromSplitted[len(fromSplitted)-1]
writeModel, err := getSMTPConfigWriteModel(ctx, filter, senderDomain)
if err != nil {
return nil, err
}
if writeModel.State != domain.SMTPConfigStateActive {
return nil, zerrors.ThrowNotFound(nil, "INST-Svq1a", "Errors.SMTPConfig.NotFound")
}
err = checkSenderAddress(writeModel)
if err != nil {
return nil, err
}
changedEvent, hasChanged, err := writeModel.NewChangedEvent(
ctx,
&a.Aggregate,
tls,
from,
name,
replyTo,
hostAndPort,
user,
)
if err != nil {
return nil, err
}
if !hasChanged {
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-m0o3f", "Errors.NoChangesFound")
}
return []eventstore.Command{
changedEvent,
}, nil
}, nil
}
}
func (c *Commands) prepareRemoveSMTPConfig(a *instance.Aggregate) preparation.Validation {
return func() (preparation.CreateCommands, error) {
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
writeModel, err := getSMTPConfigWriteModel(ctx, filter, "")
if err != nil {
return nil, err
}
if writeModel.State != domain.SMTPConfigStateActive {
return nil, zerrors.ThrowNotFound(nil, "INST-Sfefg", "Errors.SMTPConfig.NotFound")
}
return []eventstore.Command{
instance.NewSMTPConfigRemovedEvent(ctx, &a.Aggregate),
}, nil
}, nil
}
}
func checkSenderAddress(writeModel *InstanceSMTPConfigWriteModel) error {
if !writeModel.smtpSenderAddressMatchesInstanceDomain {
return nil
}
if !writeModel.domainState.Exists() {
return zerrors.ThrowInvalidArgument(nil, "INST-83nl8", "Errors.SMTPConfig.SenderAdressNotCustomDomain")
}
return nil
}
func getSMTPConfigWriteModel(ctx context.Context, filter preparation.FilterToQueryReducer, domain string) (_ *InstanceSMTPConfigWriteModel, err error) {
writeModel := NewInstanceSMTPConfigWriteModel(authz.GetInstance(ctx).InstanceID(), domain)
func getSMTPConfigWriteModel(ctx context.Context, filter preparation.FilterToQueryReducer, id, domain string) (_ *IAMSMTPConfigWriteModel, err error) {
writeModel := NewIAMSMTPConfigWriteModel(authz.GetInstance(ctx).InstanceID(), id, domain)
events, err := filter(ctx, writeModel.Query())
if err != nil {
return nil, err

View File

@ -11,6 +11,8 @@ import (
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/id"
id_mock "github.com/zitadel/zitadel/internal/id/mock"
"github.com/zitadel/zitadel/internal/notification/channels/smtp"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/zerrors"
@ -18,12 +20,14 @@ import (
func TestCommandSide_AddSMTPConfig(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
alg crypto.EncryptionAlgorithm
eventstore *eventstore.Eventstore
idGenerator id.Generator
alg crypto.EncryptionAlgorithm
}
type args struct {
ctx context.Context
smtp *smtp.Config
ctx context.Context
instanceID string
smtp *smtp.Config
}
type res struct {
want *domain.ObjectDetails
@ -51,13 +55,13 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) {
),
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"),
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{
Tls: true,
From: "from@domain.ch",
FromName: "name",
From: "from@domain.ch",
SMTP: smtp.SMTP{
Host: "host:587",
User: "user",
@ -69,58 +73,6 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) {
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "smtp config, error already exists",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
instance.NewDomainAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"domain.ch",
false,
),
),
eventFromEventPusher(
instance.NewDomainPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
true, true, false,
),
),
eventFromEventPusher(
instance.NewSMTPConfigAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
true,
"from@domain.ch",
"name",
"",
"host:587",
"user",
&crypto.CryptoValue{},
),
),
),
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{
Tls: true,
From: "from@domain.ch",
FromName: "name",
ReplyToAddress: "",
SMTP: smtp.SMTP{
Host: "host:587",
User: "user",
Password: "password",
},
},
},
res: res{
err: zerrors.IsErrorAlreadyExists,
},
},
{
name: "add smtp config, ok",
fields: fields{
@ -145,6 +97,8 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) {
instance.NewSMTPConfigAddedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"configid",
"test",
true,
"from@domain.ch",
"name",
@ -160,14 +114,16 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) {
),
),
),
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"),
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{
Tls: true,
From: "from@domain.ch",
FromName: "name",
Description: "test",
Tls: true,
From: "from@domain.ch",
FromName: "name",
SMTP: smtp.SMTP{
Host: "host:587",
User: "user",
@ -205,6 +161,8 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) {
instance.NewSMTPConfigAddedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"configid",
"test",
true,
"from@domain.ch",
"name",
@ -220,11 +178,13 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) {
),
),
),
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"),
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{
Description: "test",
Tls: true,
From: "from@domain.ch",
FromName: "name",
@ -245,14 +205,17 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) {
{
name: "smtp config, port is missing",
fields: fields{
eventstore: eventstoreExpect(t),
eventstore: eventstoreExpect(t),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"),
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{
Tls: true,
From: "from@domain.ch",
FromName: "name",
Description: "test",
Tls: true,
From: "from@domain.ch",
FromName: "name",
SMTP: smtp.SMTP{
Host: "host",
User: "user",
@ -267,14 +230,17 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) {
{
name: "smtp config, host is empty",
fields: fields{
eventstore: eventstoreExpect(t),
eventstore: eventstoreExpect(t),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"),
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{
Tls: true,
From: "from@domain.ch",
FromName: "name",
Description: "test",
Tls: true,
From: "from@domain.ch",
FromName: "name",
SMTP: smtp.SMTP{
Host: " ",
User: "user",
@ -310,6 +276,8 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) {
instance.NewSMTPConfigAddedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"configid",
"test",
true,
"from@domain.ch",
"name",
@ -325,14 +293,16 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) {
),
),
),
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{
Tls: true,
From: "from@domain.ch",
FromName: "name",
Description: "test",
Tls: true,
From: "from@domain.ch",
FromName: "name",
SMTP: smtp.SMTP{
Host: "[2001:db8::1]:2525",
User: "user",
@ -351,9 +321,10 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
idGenerator: tt.fields.idGenerator,
smtpEncryption: tt.fields.alg,
}
got, err := r.AddSMTPConfig(tt.args.ctx, tt.args.smtp)
_, got, err := r.AddSMTPConfig(tt.args.ctx, tt.args.instanceID, tt.args.smtp)
if tt.res.err == nil {
assert.NoError(t, err)
}
@ -372,8 +343,10 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
smtp *smtp.Config
ctx context.Context
instanceID string
id string
smtp *smtp.Config
}
type res struct {
want *domain.ObjectDetails
@ -385,6 +358,21 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
args args
res res
}{
{
name: "id empty, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{},
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "empty config, invalid argument error",
fields: fields{
@ -395,6 +383,7 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{},
id: "configID",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
@ -411,14 +400,17 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{
Tls: true,
From: "from@domain.ch",
FromName: "name",
Description: "test",
Tls: true,
From: "from@domain.ch",
FromName: "name",
SMTP: smtp.SMTP{
Host: "host:587",
User: "user",
},
},
instanceID: "INSTANCE",
id: "ID",
},
res: res{
err: zerrors.IsNotFound,
@ -447,6 +439,8 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
instance.NewSMTPConfigAddedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
"test",
true,
"from@domain.ch",
"name",
@ -460,11 +454,14 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
instanceID: "INSTANCE",
id: "ID",
smtp: &smtp.Config{
Tls: true,
From: "from@wrongdomain.ch",
FromName: "name",
Description: "test",
Tls: true,
From: "from@wrongdomain.ch",
FromName: "name",
SMTP: smtp.SMTP{
Host: "host:587",
User: "user",
@ -498,6 +495,8 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
instance.NewSMTPConfigAddedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
"test",
true,
"from@domain.ch",
"name",
@ -513,14 +512,17 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{
Tls: true,
From: "from@domain.ch",
FromName: "name",
Description: "test",
Tls: true,
From: "from@domain.ch",
FromName: "name",
SMTP: smtp.SMTP{
Host: "host:587",
User: "user",
},
},
instanceID: "INSTANCE",
id: "ID",
},
res: res{
err: zerrors.IsPreconditionFailed,
@ -549,6 +551,8 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
instance.NewSMTPConfigAddedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
"",
true,
"from@domain.ch",
"name",
@ -562,6 +566,8 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
expectPush(
newSMTPConfigChangedEvent(
context.Background(),
"ID",
"test",
false,
"from2@domain.ch",
"name2",
@ -575,6 +581,7 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{
Description: "test",
Tls: false,
From: "from2@domain.ch",
FromName: "name2",
@ -584,6 +591,8 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
User: "user2",
},
},
id: "ID",
instanceID: "INSTANCE",
},
res: res{
want: &domain.ObjectDetails{
@ -599,15 +608,18 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{
Tls: true,
From: "from@domain.ch",
FromName: "name",
Description: "test",
Tls: true,
From: "from@domain.ch",
FromName: "name",
SMTP: smtp.SMTP{
Host: "host",
User: "user",
Password: "password",
},
},
instanceID: "INSTANCE",
id: "ID",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
@ -621,15 +633,18 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{
Tls: true,
From: "from@domain.ch",
FromName: "name",
Description: "test",
Tls: true,
From: "from@domain.ch",
FromName: "name",
SMTP: smtp.SMTP{
Host: " ",
User: "user",
Password: "password",
},
},
instanceID: "INSTANCE",
id: "ID",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
@ -658,6 +673,8 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
instance.NewSMTPConfigAddedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
"",
true,
"from@domain.ch",
"name",
@ -671,6 +688,8 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
expectPush(
newSMTPConfigChangedEvent(
context.Background(),
"ID",
"test",
false,
"from2@domain.ch",
"name2",
@ -684,6 +703,7 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
smtp: &smtp.Config{
Description: "test",
Tls: false,
From: "from2@domain.ch",
FromName: "name2",
@ -693,6 +713,8 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
User: "user2",
},
},
instanceID: "INSTANCE",
id: "ID",
},
res: res{
want: &domain.ObjectDetails{
@ -706,7 +728,7 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.ChangeSMTPConfig(tt.args.ctx, tt.args.smtp)
got, err := r.ChangeSMTPConfig(tt.args.ctx, tt.args.instanceID, tt.args.id, tt.args.smtp)
if tt.res.err == nil {
assert.NoError(t, err)
}
@ -726,8 +748,10 @@ func TestCommandSide_ChangeSMTPConfigPassword(t *testing.T) {
alg crypto.EncryptionAlgorithm
}
type args struct {
ctx context.Context
password string
ctx context.Context
instanceID string
id string
password string
}
type res struct {
want *domain.ObjectDetails
@ -750,6 +774,7 @@ func TestCommandSide_ChangeSMTPConfigPassword(t *testing.T) {
args: args{
ctx: context.Background(),
password: "",
id: "ID",
},
res: res{
err: zerrors.IsNotFound,
@ -765,6 +790,8 @@ func TestCommandSide_ChangeSMTPConfigPassword(t *testing.T) {
instance.NewSMTPConfigAddedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
"test",
true,
"from",
"name",
@ -774,11 +801,19 @@ func TestCommandSide_ChangeSMTPConfigPassword(t *testing.T) {
&crypto.CryptoValue{},
),
),
eventFromEventPusher(
instance.NewSMTPConfigActivatedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
),
),
),
expectPush(
instance.NewSMTPConfigPasswordChangedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
@ -791,8 +826,10 @@ func TestCommandSide_ChangeSMTPConfigPassword(t *testing.T) {
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
password: "password",
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
password: "password",
id: "ID",
instanceID: "INSTANCE",
},
res: res{
want: &domain.ObjectDetails{
@ -807,7 +844,246 @@ func TestCommandSide_ChangeSMTPConfigPassword(t *testing.T) {
eventstore: tt.fields.eventstore,
smtpEncryption: tt.fields.alg,
}
got, err := r.ChangeSMTPConfigPassword(tt.args.ctx, tt.args.password)
got, err := r.ChangeSMTPConfigPassword(tt.args.ctx, tt.args.instanceID, tt.args.id, tt.args.password)
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_ActivateSMTPConfig(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
alg crypto.EncryptionAlgorithm
}
type args struct {
ctx context.Context
instanceID string
id string
activatedId string
}
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "id empty, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "smtp not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
instanceID: "INSTANCE",
id: "id",
},
res: res{
err: zerrors.IsNotFound,
},
},
{
name: "activate smtp config, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
instance.NewSMTPConfigAddedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
"test",
true,
"from",
"name",
"",
"host:587",
"user",
&crypto.CryptoValue{},
),
),
),
expectPush(
instance.NewSMTPConfigActivatedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
),
),
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
id: "ID",
instanceID: "INSTANCE",
activatedId: "",
},
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,
smtpEncryption: tt.fields.alg,
}
got, err := r.ActivateSMTPConfig(tt.args.ctx, tt.args.instanceID, tt.args.id, tt.args.activatedId)
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_DeactivateSMTPConfig(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
alg crypto.EncryptionAlgorithm
}
type args struct {
ctx context.Context
instanceID string
id string
activatedId string
}
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "id empty, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "smtp not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
instanceID: "INSTANCE",
id: "id",
},
res: res{
err: zerrors.IsNotFound,
},
},
{
name: "deactivate smtp config, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
instance.NewSMTPConfigAddedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
"test",
true,
"from",
"name",
"",
"host:587",
"user",
&crypto.CryptoValue{},
),
),
eventFromEventPusher(
instance.NewSMTPConfigActivatedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
),
),
),
expectPush(
instance.NewSMTPConfigDeactivatedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
),
),
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
id: "ID",
instanceID: "INSTANCE",
activatedId: "",
},
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,
smtpEncryption: tt.fields.alg,
}
got, err := r.DeactivateSMTPConfig(tt.args.ctx, tt.args.instanceID, tt.args.id)
if tt.res.err == nil {
assert.NoError(t, err)
}
@ -827,7 +1103,9 @@ func TestCommandSide_RemoveSMTPConfig(t *testing.T) {
alg crypto.EncryptionAlgorithm
}
type args struct {
ctx context.Context
ctx context.Context
instanceID string
id string
}
type res struct {
want *domain.ObjectDetails
@ -849,6 +1127,7 @@ func TestCommandSide_RemoveSMTPConfig(t *testing.T) {
},
args: args{
ctx: context.Background(),
id: "ID",
},
res: res{
err: zerrors.IsNotFound,
@ -864,6 +1143,8 @@ func TestCommandSide_RemoveSMTPConfig(t *testing.T) {
instance.NewSMTPConfigAddedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
"test",
true,
"from",
"name",
@ -878,12 +1159,15 @@ func TestCommandSide_RemoveSMTPConfig(t *testing.T) {
instance.NewSMTPConfigRemovedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"ID",
),
),
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
id: "ID",
instanceID: "INSTANCE",
},
res: res{
want: &domain.ObjectDetails{
@ -898,7 +1182,7 @@ func TestCommandSide_RemoveSMTPConfig(t *testing.T) {
eventstore: tt.fields.eventstore,
smtpEncryption: tt.fields.alg,
}
got, err := r.RemoveSMTPConfig(tt.args.ctx)
got, err := r.RemoveSMTPConfig(tt.args.ctx, tt.args.instanceID, tt.args.id)
if tt.res.err == nil {
assert.NoError(t, err)
}
@ -912,8 +1196,9 @@ func TestCommandSide_RemoveSMTPConfig(t *testing.T) {
}
}
func newSMTPConfigChangedEvent(ctx context.Context, tls bool, fromAddress, fromName, replyTo, host, user string) *instance.SMTPConfigChangedEvent {
func newSMTPConfigChangedEvent(ctx context.Context, id, description string, tls bool, fromAddress, fromName, replyTo, host, user string) *instance.SMTPConfigChangedEvent {
changes := []instance.SMTPConfigChanges{
instance.ChangeSMTPConfigDescription(description),
instance.ChangeSMTPConfigTLS(tls),
instance.ChangeSMTPConfigFromAddress(fromAddress),
instance.ChangeSMTPConfigFromName(fromName),
@ -923,6 +1208,7 @@ func newSMTPConfigChangedEvent(ctx context.Context, tls bool, fromAddress, fromN
}
event, _ := instance.NewSMTPConfigChangeEvent(ctx,
&instance.NewAggregate("INSTANCE").Aggregate,
id,
changes,
)
return event

View File

@ -6,4 +6,9 @@ const (
SMTPConfigStateUnspecified SMTPConfigState = iota
SMTPConfigStateActive
SMTPConfigStateRemoved
SMTPConfigStateInactive
)
func (s SMTPConfigState) Exists() bool {
return s != SMTPConfigStateUnspecified && s != SMTPConfigStateRemoved
}

View File

@ -91,9 +91,9 @@ func (smtpConfig SMTP) connectToSMTP(tlsRequired bool) (client *smtp.Client, err
}
if !tlsRequired {
client, err = smtpConfig.getSMPTClient()
client, err = smtpConfig.getSMTPClient()
} else {
client, err = smtpConfig.getSMPTClientWithTls(host)
client, err = smtpConfig.getSMTPClientWithTls(host)
}
if err != nil {
return nil, err
@ -106,7 +106,7 @@ func (smtpConfig SMTP) connectToSMTP(tlsRequired bool) (client *smtp.Client, err
return client, nil
}
func (smtpConfig SMTP) getSMPTClient() (*smtp.Client, error) {
func (smtpConfig SMTP) getSMTPClient() (*smtp.Client, error) {
client, err := smtp.Dial(smtpConfig.Host)
if err != nil {
return nil, zerrors.ThrowInternal(err, "EMAIL-skwos", "could not make smtp dial")
@ -114,12 +114,12 @@ func (smtpConfig SMTP) getSMPTClient() (*smtp.Client, error) {
return client, nil
}
func (smtpConfig SMTP) getSMPTClientWithTls(host string) (*smtp.Client, error) {
func (smtpConfig SMTP) getSMTPClientWithTls(host string) (*smtp.Client, error) {
conn, err := tls.Dial("tcp", smtpConfig.Host, &tls.Config{})
if errors.As(err, &tls.RecordHeaderError{}) {
logging.Log("MAIN-xKIzT").OnError(err).Warn("could not connect using normal tls. trying starttls instead...")
return smtpConfig.getSMPTClientWithStartTls(host)
return smtpConfig.getSMTPClientWithStartTls(host)
}
if err != nil {
@ -133,8 +133,8 @@ func (smtpConfig SMTP) getSMPTClientWithTls(host string) (*smtp.Client, error) {
return client, err
}
func (smtpConfig SMTP) getSMPTClientWithStartTls(host string) (*smtp.Client, error) {
client, err := smtpConfig.getSMPTClient()
func (smtpConfig SMTP) getSMTPClientWithStartTls(host string) (*smtp.Client, error) {
client, err := smtpConfig.getSMTPClient()
if err != nil {
return nil, err
}

View File

@ -1,6 +1,7 @@
package smtp
type Config struct {
Description string
SMTP SMTP
Tls bool
From string

View File

@ -10,7 +10,7 @@ import (
// GetSMTPConfig reads the iam SMTP provider config
func (n *NotificationQueries) GetSMTPConfig(ctx context.Context) (*smtp.Config, error) {
config, err := n.SMTPConfigByAggregateID(ctx, authz.GetInstance(ctx).InstanceID())
config, err := n.SMTPConfigActive(ctx, authz.GetInstance(ctx).InstanceID())
if err != nil {
return nil, err
}
@ -19,6 +19,7 @@ func (n *NotificationQueries) GetSMTPConfig(ctx context.Context) (*smtp.Config,
return nil, err
}
return &smtp.Config{
Description: config.Description,
From: config.SenderAddress,
FromName: config.SenderName,
ReplyToAddress: config.ReplyToAddress,

View File

@ -181,19 +181,19 @@ func (mr *MockQueriesMockRecorder) SMSProviderConfig(arg0 any, arg1 ...any) *gom
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMSProviderConfig", reflect.TypeOf((*MockQueries)(nil).SMSProviderConfig), varargs...)
}
// SMTPConfigByAggregateID mocks base method.
func (m *MockQueries) SMTPConfigByAggregateID(arg0 context.Context, arg1 string) (*query.SMTPConfig, error) {
// SMTPConfigActive mocks base method.
func (m *MockQueries) SMTPConfigActive(arg0 context.Context, arg1 string) (*query.SMTPConfig, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SMTPConfigByAggregateID", arg0, arg1)
ret := m.ctrl.Call(m, "SMTPConfigActive", arg0, arg1)
ret0, _ := ret[0].(*query.SMTPConfig)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// SMTPConfigByAggregateID indicates an expected call of SMTPConfigByAggregateID.
func (mr *MockQueriesMockRecorder) SMTPConfigByAggregateID(arg0, arg1 any) *gomock.Call {
// SMTPConfigActive indicates an expected call of SMTPConfigActive.
func (mr *MockQueriesMockRecorder) SMTPConfigActive(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMTPConfigByAggregateID", reflect.TypeOf((*MockQueries)(nil).SMTPConfigByAggregateID), arg0, arg1)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMTPConfigActive", reflect.TypeOf((*MockQueries)(nil).SMTPConfigActive), arg0, arg1)
}
// SearchInstanceDomains mocks base method.

View File

@ -22,7 +22,7 @@ type Queries interface {
SearchMilestones(ctx context.Context, instanceIDs []string, queries *query.MilestonesSearchQueries) (*query.Milestones, error)
NotificationProviderByIDAndType(ctx context.Context, aggID string, providerType domain.NotificationProviderType) (*query.DebugNotificationProvider, error)
SMSProviderConfig(ctx context.Context, queries ...query.SearchQuery) (*query.SMSConfig, error)
SMTPConfigByAggregateID(ctx context.Context, aggregateID string) (*query.SMTPConfig, error)
SMTPConfigActive(ctx context.Context, resourceOwner string) (*query.SMTPConfig, error)
GetDefaultLanguage(ctx context.Context) language.Tag
GetInstanceRestrictions(ctx context.Context) (restrictions query.Restrictions, err error)
}

View File

@ -3,6 +3,7 @@ package projection
import (
"context"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
old_handler "github.com/zitadel/zitadel/internal/eventstore/handler"
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
@ -11,14 +12,13 @@ import (
)
const (
SMTPConfigProjectionTable = "projections.smtp_configs1"
SMTPConfigColumnAggregateID = "aggregate_id"
SMTPConfigProjectionTable = "projections.smtp_configs2"
SMTPConfigColumnInstanceID = "instance_id"
SMTPConfigColumnResourceOwner = "resource_owner"
SMTPConfigColumnID = "id"
SMTPConfigColumnCreationDate = "creation_date"
SMTPConfigColumnChangeDate = "change_date"
SMTPConfigColumnSequence = "sequence"
SMTPConfigColumnResourceOwner = "resource_owner"
SMTPConfigColumnInstanceID = "instance_id"
SMTPConfigColumnTLS = "tls"
SMTPConfigColumnSenderAddress = "sender_address"
SMTPConfigColumnSenderName = "sender_name"
@ -26,6 +26,8 @@ const (
SMTPConfigColumnSMTPHost = "host"
SMTPConfigColumnSMTPUser = "username"
SMTPConfigColumnSMTPPassword = "password"
SMTPConfigColumnState = "state"
SMTPConfigColumnDescription = "description"
)
type smtpConfigProjection struct{}
@ -41,7 +43,7 @@ func (*smtpConfigProjection) Name() string {
func (*smtpConfigProjection) Init() *old_handler.Check {
return handler.NewTableCheck(
handler.NewTable([]*handler.InitColumn{
handler.NewColumn(SMTPConfigColumnAggregateID, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnID, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnCreationDate, handler.ColumnTypeTimestamp),
handler.NewColumn(SMTPConfigColumnChangeDate, handler.ColumnTypeTimestamp),
handler.NewColumn(SMTPConfigColumnSequence, handler.ColumnTypeInt64),
@ -54,8 +56,10 @@ func (*smtpConfigProjection) Init() *old_handler.Check {
handler.NewColumn(SMTPConfigColumnSMTPHost, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnSMTPUser, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnSMTPPassword, handler.ColumnTypeJSONB, handler.Nullable()),
handler.NewColumn(SMTPConfigColumnState, handler.ColumnTypeEnum),
handler.NewColumn(SMTPConfigColumnDescription, handler.ColumnTypeText),
},
handler.NewPrimaryKey(SMTPConfigColumnInstanceID, SMTPConfigColumnAggregateID),
handler.NewPrimaryKey(SMTPConfigColumnInstanceID, SMTPConfigColumnResourceOwner, SMTPConfigColumnID),
),
)
}
@ -77,6 +81,14 @@ func (p *smtpConfigProjection) Reducers() []handler.AggregateReducer {
Event: instance.SMTPConfigPasswordChangedEventType,
Reduce: p.reduceSMTPConfigPasswordChanged,
},
{
Event: instance.SMTPConfigActivatedEventType,
Reduce: p.reduceSMTPConfigActivated,
},
{
Event: instance.SMTPConfigDeactivatedEventType,
Reduce: p.reduceSMTPConfigDeactivated,
},
{
Event: instance.SMTPConfigRemovedEventType,
Reduce: p.reduceSMTPConfigRemoved,
@ -95,15 +107,26 @@ func (p *smtpConfigProjection) reduceSMTPConfigAdded(event eventstore.Event) (*h
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-sk99F", "reduce.wrong.event.type %s", instance.SMTPConfigAddedEventType)
}
// Deal with old and unique SMTP settings (empty ID)
id := e.ID
description := e.Description
state := domain.SMTPConfigStateInactive
if e.ID == "" {
id = e.Aggregate().ResourceOwner
description = "generic"
state = domain.SMTPConfigStateActive
}
return handler.NewCreateStatement(
e,
[]handler.Column{
handler.NewCol(SMTPConfigColumnAggregateID, e.Aggregate().ID),
handler.NewCol(SMTPConfigColumnCreationDate, e.CreationDate()),
handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
handler.NewCol(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCol(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCol(SMTPConfigColumnSequence, e.Sequence()),
handler.NewCol(SMTPConfigColumnID, id),
handler.NewCol(SMTPConfigColumnTLS, e.TLS),
handler.NewCol(SMTPConfigColumnSenderAddress, e.SenderAddress),
handler.NewCol(SMTPConfigColumnSenderName, e.SenderName),
@ -111,6 +134,8 @@ func (p *smtpConfigProjection) reduceSMTPConfigAdded(event eventstore.Event) (*h
handler.NewCol(SMTPConfigColumnSMTPHost, e.Host),
handler.NewCol(SMTPConfigColumnSMTPUser, e.User),
handler.NewCol(SMTPConfigColumnSMTPPassword, e.Password),
handler.NewCol(SMTPConfigColumnState, state),
handler.NewCol(SMTPConfigColumnDescription, description),
},
), nil
}
@ -124,6 +149,13 @@ func (p *smtpConfigProjection) reduceSMTPConfigChanged(event eventstore.Event) (
columns := make([]handler.Column, 0, 8)
columns = append(columns, handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
handler.NewCol(SMTPConfigColumnSequence, e.Sequence()))
// Deal with old and unique SMTP settings (empty ID)
id := e.ID
if e.ID == "" {
id = e.Aggregate().ResourceOwner
}
if e.TLS != nil {
columns = append(columns, handler.NewCol(SMTPConfigColumnTLS, *e.TLS))
}
@ -142,11 +174,18 @@ func (p *smtpConfigProjection) reduceSMTPConfigChanged(event eventstore.Event) (
if e.User != nil {
columns = append(columns, handler.NewCol(SMTPConfigColumnSMTPUser, *e.User))
}
if e.Password != nil {
columns = append(columns, handler.NewCol(SMTPConfigColumnSMTPPassword, *e.Password))
}
if e.Description != nil {
columns = append(columns, handler.NewCol(SMTPConfigColumnDescription, *e.Description))
}
return handler.NewUpdateStatement(
e,
columns,
[]handler.Condition{
handler.NewCond(SMTPConfigColumnAggregateID, e.Aggregate().ID),
handler.NewCond(SMTPConfigColumnID, id),
handler.NewCond(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
},
), nil
@ -158,6 +197,12 @@ func (p *smtpConfigProjection) reduceSMTPConfigPasswordChanged(event eventstore.
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-fk02f", "reduce.wrong.event.type %s", instance.SMTPConfigChangedEventType)
}
// Deal with old and unique SMTP settings (empty ID)
id := e.ID
if e.ID == "" {
id = e.Aggregate().ResourceOwner
}
return handler.NewUpdateStatement(
e,
[]handler.Column{
@ -166,7 +211,62 @@ func (p *smtpConfigProjection) reduceSMTPConfigPasswordChanged(event eventstore.
handler.NewCol(SMTPConfigColumnSMTPPassword, e.Password),
},
[]handler.Condition{
handler.NewCond(SMTPConfigColumnAggregateID, e.Aggregate().ID),
handler.NewCond(SMTPConfigColumnID, id),
handler.NewCond(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
},
), nil
}
func (p *smtpConfigProjection) reduceSMTPConfigActivated(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMTPConfigActivatedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-fq92r", "reduce.wrong.event.type %s", instance.SMTPConfigActivatedEventType)
}
// Deal with old and unique SMTP settings (empty ID)
id := e.ID
if e.ID == "" {
id = e.Aggregate().ResourceOwner
}
return handler.NewUpdateStatement(
e,
[]handler.Column{
handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
handler.NewCol(SMTPConfigColumnSequence, e.Sequence()),
handler.NewCol(SMTPConfigColumnState, domain.SMTPConfigStateActive),
},
[]handler.Condition{
handler.NewCond(SMTPConfigColumnID, id),
handler.NewCond(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
},
), nil
}
func (p *smtpConfigProjection) reduceSMTPConfigDeactivated(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMTPConfigDeactivatedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-hv89j", "reduce.wrong.event.type %s", instance.SMTPConfigDeactivatedEventType)
}
// Deal with old and unique SMTP settings (empty ID)
id := e.ID
if e.ID == "" {
id = e.Aggregate().ResourceOwner
}
return handler.NewUpdateStatement(
e,
[]handler.Column{
handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
handler.NewCol(SMTPConfigColumnSequence, e.Sequence()),
handler.NewCol(SMTPConfigColumnState, domain.SMTPConfigStateInactive),
},
[]handler.Condition{
handler.NewCond(SMTPConfigColumnID, id),
handler.NewCond(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
},
), nil
@ -177,10 +277,18 @@ func (p *smtpConfigProjection) reduceSMTPConfigRemoved(event eventstore.Event) (
if err != nil {
return nil, err
}
// Deal with old and unique SMTP settings (empty ID)
id := e.ID
if e.ID == "" {
id = e.Aggregate().ResourceOwner
}
return handler.NewDeleteStatement(
e,
[]handler.Condition{
handler.NewCond(SMTPConfigColumnAggregateID, e.Aggregate().ID),
handler.NewCond(SMTPConfigColumnID, id),
handler.NewCond(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
},
), nil

View File

@ -3,6 +3,7 @@ package projection
import (
"testing"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
"github.com/zitadel/zitadel/internal/repository/instance"
@ -27,12 +28,16 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
instance.SMTPConfigChangedEventType,
instance.AggregateType,
[]byte(`{
"description": "test",
"tls": true,
"senderAddress": "sender",
"senderName": "name",
"replyToAddress": "reply-to",
"host": "host",
"user": "user"
"user": "user",
"id": "44444",
"resource_owner": "ro-id",
"instance_id": "instance-id"
}`,
),
), instance.SMTPConfigChangedEventMapper),
@ -44,7 +49,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs1 SET (change_date, sequence, tls, sender_address, sender_name, reply_to_address, host, username) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (aggregate_id = $9) AND (instance_id = $10)",
expectedStmt: "UPDATE projections.smtp_configs2 SET (change_date, sequence, tls, sender_address, sender_name, reply_to_address, host, username, description) = ($1, $2, $3, $4, $5, $6, $7, $8, $9) WHERE (id = $10) AND (resource_owner = $11) AND (instance_id = $12)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
@ -54,7 +59,9 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
"reply-to",
"host",
"user",
"agg-id",
"test",
"44444",
"ro-id",
"instance-id",
},
},
@ -71,6 +78,8 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
instance.AggregateType,
[]byte(`{
"tls": true,
"id": "id",
"description": "test",
"senderAddress": "sender",
"senderName": "name",
"replyToAddress": "reply-to",
@ -91,14 +100,14 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.smtp_configs1 (aggregate_id, creation_date, change_date, resource_owner, instance_id, sequence, tls, sender_address, sender_name, reply_to_address, host, username, password) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)",
expectedStmt: "INSERT INTO projections.smtp_configs2 (creation_date, change_date, resource_owner, instance_id, sequence, id, tls, sender_address, sender_name, reply_to_address, host, username, password, state, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)",
expectedArgs: []interface{}{
"agg-id",
anyArg{},
anyArg{},
"ro-id",
"instance-id",
uint64(15),
"id",
true,
"sender",
"name",
@ -106,6 +115,72 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
"host",
"user",
anyArg{},
domain.SMTPConfigState(3),
"test",
},
},
},
},
},
},
{
name: "reduceSMTPConfigActivated",
args: args{
event: getEvent(testEvent(
instance.SMTPConfigActivatedEventType,
instance.AggregateType,
[]byte(`{
"id": "config-id"
}`),
), instance.SMTPConfigActivatedEventMapper),
},
reduce: (&smtpConfigProjection{}).reduceSMTPConfigActivated,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs2 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (resource_owner = $5) AND (instance_id = $6)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
domain.SMTPConfigStateActive,
"config-id",
"ro-id",
"instance-id",
},
},
},
},
},
},
{
name: "reduceSMTPConfigDeactivated",
args: args{
event: getEvent(testEvent(
instance.SMTPConfigDeactivatedEventType,
instance.AggregateType,
[]byte(`{
"id": "config-id"
}`),
), instance.SMTPConfigDeactivatedEventMapper),
},
reduce: (&smtpConfigProjection{}).reduceSMTPConfigDeactivated,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs2 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (resource_owner = $5) AND (instance_id = $6)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
domain.SMTPConfigStateInactive,
"config-id",
"ro-id",
"instance-id",
},
},
},
@ -120,6 +195,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
instance.SMTPConfigPasswordChangedEventType,
instance.AggregateType,
[]byte(`{
"id": "config-id",
"password": {
"cryptoType": 0,
"algorithm": "RSA-265",
@ -135,12 +211,13 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs1 SET (change_date, sequence, password) = ($1, $2, $3) WHERE (aggregate_id = $4) AND (instance_id = $5)",
expectedStmt: "UPDATE projections.smtp_configs2 SET (change_date, sequence, password) = ($1, $2, $3) WHERE (id = $4) AND (resource_owner = $5) AND (instance_id = $6)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
anyArg{},
"agg-id",
"config-id",
"ro-id",
"instance-id",
},
},
@ -154,7 +231,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
event: getEvent(testEvent(
instance.SMTPConfigRemovedEventType,
instance.AggregateType,
[]byte(`{}`),
[]byte(`{ "id": "config-id"}`),
), instance.SMTPConfigRemovedEventMapper),
},
reduce: (&smtpConfigProjection{}).reduceSMTPConfigRemoved,
@ -164,9 +241,10 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.smtp_configs1 WHERE (aggregate_id = $1) AND (instance_id = $2)",
expectedStmt: "DELETE FROM projections.smtp_configs2 WHERE (id = $1) AND (resource_owner = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
"agg-id",
"config-id",
"ro-id",
"instance-id",
},
},
@ -191,7 +269,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.smtp_configs1 WHERE (instance_id = $1)",
expectedStmt: "DELETE FROM projections.smtp_configs2 WHERE (instance_id = $1)",
expectedArgs: []interface{}{
"agg-id",
},

View File

@ -11,20 +11,27 @@ import (
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/call"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/query/projection"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
"github.com/zitadel/zitadel/internal/zerrors"
)
type SMTPConfigsSearchQueries struct {
SearchRequest
Queries []SearchQuery
}
type SMTPConfigs struct {
SearchResponse
Configs []*SMTPConfig
}
var (
smtpConfigsTable = table{
name: projection.SMTPConfigProjectionTable,
instanceIDCol: projection.SMTPConfigColumnInstanceID,
}
SMTPConfigColumnAggregateID = Column{
name: projection.SMTPConfigColumnAggregateID,
table: smtpConfigsTable,
}
SMTPConfigColumnCreationDate = Column{
name: projection.SMTPConfigColumnCreationDate,
table: smtpConfigsTable,
@ -73,20 +80,25 @@ var (
name: projection.SMTPConfigColumnSMTPPassword,
table: smtpConfigsTable,
}
SMTPConfigColumnID = Column{
name: projection.SMTPConfigColumnID,
table: smtpConfigsTable,
}
SMTPConfigColumnState = Column{
name: projection.SMTPConfigColumnState,
table: smtpConfigsTable,
}
SMTPConfigColumnDescription = Column{
name: projection.SMTPConfigColumnDescription,
table: smtpConfigsTable,
}
)
type SMTPConfigs struct {
SearchResponse
SMTPConfigs []*SMTPConfig
}
type SMTPConfig struct {
AggregateID string
CreationDate time.Time
ChangeDate time.Time
ResourceOwner string
Sequence uint64
CreationDate time.Time
ChangeDate time.Time
ResourceOwner string
Sequence uint64
TLS bool
SenderAddress string
SenderName string
@ -94,19 +106,44 @@ type SMTPConfig struct {
Host string
User string
Password *crypto.CryptoValue
ID string
State domain.SMTPConfigState
Description string
}
func (q *Queries) SMTPConfigByAggregateID(ctx context.Context, aggregateID string) (config *SMTPConfig, err error) {
func (q *Queries) SMTPConfigActive(ctx context.Context, resourceOwner string) (config *SMTPConfig, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
stmt, scan := prepareSMTPConfigQuery(ctx, q.client)
query, args, err := stmt.Where(sq.Eq{
SMTPConfigColumnAggregateID.identifier(): aggregateID,
SMTPConfigColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
SMTPConfigColumnResourceOwner.identifier(): resourceOwner,
SMTPConfigColumnInstanceID.identifier(): resourceOwner,
SMTPConfigColumnState.identifier(): domain.SMTPConfigStateActive,
}).ToSql()
if err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-3m9sl", "Errors.Query.SQLStatment")
return nil, zerrors.ThrowInternal(err, "QUERY-3m9sl", "Errors.Query.SQLStatement")
}
err = q.client.QueryRowContext(ctx, func(row *sql.Row) error {
config, err = scan(row)
return err
}, query, args...)
return config, err
}
func (q *Queries) SMTPConfigByID(ctx context.Context, instanceID, resourceOwner, id string) (config *SMTPConfig, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
stmt, scan := prepareSMTPConfigQuery(ctx, q.client)
query, args, err := stmt.Where(sq.Eq{
SMTPConfigColumnResourceOwner.identifier(): resourceOwner,
SMTPConfigColumnInstanceID.identifier(): instanceID,
SMTPConfigColumnID.identifier(): id,
}).ToSql()
if err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-8f8gw", "Errors.Query.SQLStatement")
}
err = q.client.QueryRowContext(ctx, func(row *sql.Row) error {
@ -120,7 +157,6 @@ func prepareSMTPConfigQuery(ctx context.Context, db prepareDatabase) (sq.SelectB
password := new(crypto.CryptoValue)
return sq.Select(
SMTPConfigColumnAggregateID.identifier(),
SMTPConfigColumnCreationDate.identifier(),
SMTPConfigColumnChangeDate.identifier(),
SMTPConfigColumnResourceOwner.identifier(),
@ -131,13 +167,15 @@ func prepareSMTPConfigQuery(ctx context.Context, db prepareDatabase) (sq.SelectB
SMTPConfigColumnReplyToAddress.identifier(),
SMTPConfigColumnSMTPHost.identifier(),
SMTPConfigColumnSMTPUser.identifier(),
SMTPConfigColumnSMTPPassword.identifier()).
SMTPConfigColumnSMTPPassword.identifier(),
SMTPConfigColumnID.identifier(),
SMTPConfigColumnState.identifier(),
SMTPConfigColumnDescription.identifier()).
From(smtpConfigsTable.identifier() + db.Timetravel(call.Took(ctx))).
PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*SMTPConfig, error) {
config := new(SMTPConfig)
err := row.Scan(
&config.AggregateID,
&config.CreationDate,
&config.ChangeDate,
&config.ResourceOwner,
@ -149,6 +187,9 @@ func prepareSMTPConfigQuery(ctx context.Context, db prepareDatabase) (sq.SelectB
&config.Host,
&config.User,
&password,
&config.ID,
&config.State,
&config.Description,
)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
@ -160,3 +201,79 @@ func prepareSMTPConfigQuery(ctx context.Context, db prepareDatabase) (sq.SelectB
return config, nil
}
}
func prepareSMTPConfigsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*SMTPConfigs, error)) {
return sq.Select(
SMTPConfigColumnCreationDate.identifier(),
SMTPConfigColumnChangeDate.identifier(),
SMTPConfigColumnResourceOwner.identifier(),
SMTPConfigColumnSequence.identifier(),
SMTPConfigColumnTLS.identifier(),
SMTPConfigColumnSenderAddress.identifier(),
SMTPConfigColumnSenderName.identifier(),
SMTPConfigColumnReplyToAddress.identifier(),
SMTPConfigColumnSMTPHost.identifier(),
SMTPConfigColumnSMTPUser.identifier(),
SMTPConfigColumnSMTPPassword.identifier(),
SMTPConfigColumnID.identifier(),
SMTPConfigColumnState.identifier(),
SMTPConfigColumnDescription.identifier(),
countColumn.identifier()).
From(smtpConfigsTable.identifier() + db.Timetravel(call.Took(ctx))).
PlaceholderFormat(sq.Dollar),
func(rows *sql.Rows) (*SMTPConfigs, error) {
configs := &SMTPConfigs{Configs: []*SMTPConfig{}}
for rows.Next() {
config := new(SMTPConfig)
err := rows.Scan(
&config.CreationDate,
&config.ChangeDate,
&config.ResourceOwner,
&config.Sequence,
&config.TLS,
&config.SenderAddress,
&config.SenderName,
&config.ReplyToAddress,
&config.Host,
&config.User,
&config.Password,
&config.ID,
&config.State,
&config.Description,
&configs.Count,
)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, zerrors.ThrowNotFound(err, "QUERY-fwofw", "Errors.SMTPConfig.NotFound")
}
return nil, zerrors.ThrowInternal(err, "QUERY-9k87F", "Errors.Internal")
}
configs.Configs = append(configs.Configs, config)
}
return configs, nil
}
}
func (q *Queries) SearchSMTPConfigs(ctx context.Context, queries *SMTPConfigsSearchQueries) (configs *SMTPConfigs, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
query, scan := prepareSMTPConfigsQuery(ctx, q.client)
stmt, args, err := queries.toQuery(query).
Where(sq.Eq{
SMTPConfigColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
}).ToSql()
if err != nil {
return nil, zerrors.ThrowInvalidArgument(err, "QUERY-sZ7Cx", "Errors.Query.InvalidRequest")
}
err = q.client.QueryContext(ctx, func(rows *sql.Rows) error {
configs, err = scan(rows)
return err
}, stmt, args...)
if err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-tOpKN", "Errors.Internal")
}
configs.State, err = q.latestState(ctx, smsConfigsTable)
return configs, err
}

View File

@ -9,26 +9,28 @@ import (
"testing"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/zerrors"
)
var (
prepareSMTPConfigStmt = `SELECT projections.smtp_configs1.aggregate_id,` +
` projections.smtp_configs1.creation_date,` +
` projections.smtp_configs1.change_date,` +
` projections.smtp_configs1.resource_owner,` +
` projections.smtp_configs1.sequence,` +
` projections.smtp_configs1.tls,` +
` projections.smtp_configs1.sender_address,` +
` projections.smtp_configs1.sender_name,` +
` projections.smtp_configs1.reply_to_address,` +
` projections.smtp_configs1.host,` +
` projections.smtp_configs1.username,` +
` projections.smtp_configs1.password` +
` FROM projections.smtp_configs1` +
prepareSMTPConfigStmt = `SELECT projections.smtp_configs2.creation_date,` +
` projections.smtp_configs2.change_date,` +
` projections.smtp_configs2.resource_owner,` +
` projections.smtp_configs2.sequence,` +
` projections.smtp_configs2.tls,` +
` projections.smtp_configs2.sender_address,` +
` projections.smtp_configs2.sender_name,` +
` projections.smtp_configs2.reply_to_address,` +
` projections.smtp_configs2.host,` +
` projections.smtp_configs2.username,` +
` projections.smtp_configs2.password,` +
` projections.smtp_configs2.id,` +
` projections.smtp_configs2.state,` +
` projections.smtp_configs2.description` +
` FROM projections.smtp_configs2` +
` AS OF SYSTEM TIME '-1 ms'`
prepareSMTPConfigCols = []string{
"aggregate_id",
"creation_date",
"change_date",
"resource_owner",
@ -40,6 +42,9 @@ var (
"smtp_host",
"smtp_user",
"smtp_password",
"id",
"state",
"description",
}
)
@ -80,7 +85,6 @@ func Test_SMTPConfigsPrepares(t *testing.T) {
regexp.QuoteMeta(prepareSMTPConfigStmt),
prepareSMTPConfigCols,
[]driver.Value{
"agg-id",
testNow,
testNow,
"ro",
@ -92,11 +96,13 @@ func Test_SMTPConfigsPrepares(t *testing.T) {
"host",
"user",
&crypto.CryptoValue{},
"2232323",
domain.SMTPConfigStateActive,
"test",
},
),
},
object: &SMTPConfig{
AggregateID: "agg-id",
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "ro",
@ -108,6 +114,93 @@ func Test_SMTPConfigsPrepares(t *testing.T) {
Host: "host",
User: "user",
Password: &crypto.CryptoValue{},
ID: "2232323",
State: domain.SMTPConfigStateActive,
Description: "test",
},
},
{
name: "prepareSMTPConfigQuery another config found",
prepare: prepareSMTPConfigQuery,
want: want{
sqlExpectations: mockQuery(
regexp.QuoteMeta(prepareSMTPConfigStmt),
prepareSMTPConfigCols,
[]driver.Value{
testNow,
testNow,
"ro",
uint64(20211109),
true,
"sender2",
"name2",
"reply-to2",
"host2",
"user2",
&crypto.CryptoValue{},
"44442323",
domain.SMTPConfigStateInactive,
"test2",
},
),
},
object: &SMTPConfig{
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "ro",
Sequence: 20211109,
TLS: true,
SenderAddress: "sender2",
SenderName: "name2",
ReplyToAddress: "reply-to2",
Host: "host2",
User: "user2",
Password: &crypto.CryptoValue{},
ID: "44442323",
State: domain.SMTPConfigStateInactive,
Description: "test2",
},
},
{
name: "prepareSMTPConfigQuery yet another config found",
prepare: prepareSMTPConfigQuery,
want: want{
sqlExpectations: mockQuery(
regexp.QuoteMeta(prepareSMTPConfigStmt),
prepareSMTPConfigCols,
[]driver.Value{
testNow,
testNow,
"ro",
uint64(20211109),
true,
"sender3",
"name3",
"reply-to3",
"host3",
"user3",
&crypto.CryptoValue{},
"23234444",
domain.SMTPConfigStateInactive,
"test3",
},
),
},
object: &SMTPConfig{
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "ro",
Sequence: 20211109,
TLS: true,
SenderAddress: "sender3",
SenderName: "name3",
ReplyToAddress: "reply-to3",
Host: "host3",
User: "user3",
Password: &crypto.CryptoValue{},
ID: "23234444",
State: domain.SMTPConfigStateInactive,
Description: "test3",
},
},
{

View File

@ -14,6 +14,8 @@ func init() {
eventstore.RegisterFilterEventMapper(AggregateType, SecretGeneratorRemovedEventType, SecretGeneratorRemovedEventMapper)
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigAddedEventType, SMTPConfigAddedEventMapper)
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigChangedEventType, SMTPConfigChangedEventMapper)
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigActivatedEventType, SMTPConfigActivatedEventMapper)
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigDeactivatedEventType, SMTPConfigDeactivatedEventMapper)
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigPasswordChangedEventType, SMTPConfigPasswordChangedEventMapper)
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigRemovedEventType, SMTPConfigRemovedEventMapper)
eventstore.RegisterFilterEventMapper(AggregateType, SMSConfigTwilioAddedEventType, SMSConfigTwilioAddedEventMapper)

View File

@ -4,6 +4,7 @@ import (
"context"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/zerrors"
)
@ -14,23 +15,29 @@ const (
SMTPConfigChangedEventType = instanceEventTypePrefix + smtpConfigPrefix + "changed"
SMTPConfigPasswordChangedEventType = instanceEventTypePrefix + smtpConfigPrefix + "password.changed"
SMTPConfigRemovedEventType = instanceEventTypePrefix + smtpConfigPrefix + "removed"
SMTPConfigActivatedEventType = instanceEventTypePrefix + smtpConfigPrefix + "activated"
SMTPConfigDeactivatedEventType = instanceEventTypePrefix + smtpConfigPrefix + "deactivated"
)
type SMTPConfigAddedEvent struct {
eventstore.BaseEvent `json:"-"`
SenderAddress string `json:"senderAddress,omitempty"`
SenderName string `json:"senderName,omitempty"`
ReplyToAddress string `json:"replyToAddress,omitempty"`
TLS bool `json:"tls,omitempty"`
Host string `json:"host,omitempty"`
User string `json:"user,omitempty"`
Password *crypto.CryptoValue `json:"password,omitempty"`
ID string `json:"id,omitempty"`
Description string `json:"description,omitempty"`
SenderAddress string `json:"senderAddress,omitempty"`
SenderName string `json:"senderName,omitempty"`
ReplyToAddress string `json:"replyToAddress,omitempty"`
TLS bool `json:"tls,omitempty"`
Host string `json:"host,omitempty"`
User string `json:"user,omitempty"`
Password *crypto.CryptoValue `json:"password,omitempty"`
State domain.SMTPConfigState `json:"state,omitempty"`
}
func NewSMTPConfigAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
id, description string,
tls bool,
senderAddress,
senderName,
@ -45,6 +52,8 @@ func NewSMTPConfigAddedEvent(
aggregate,
SMTPConfigAddedEventType,
),
ID: id,
Description: description,
TLS: tls,
SenderAddress: senderAddress,
SenderName: senderName,
@ -77,13 +86,15 @@ func SMTPConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error
type SMTPConfigChangedEvent struct {
eventstore.BaseEvent `json:"-"`
FromAddress *string `json:"senderAddress,omitempty"`
FromName *string `json:"senderName,omitempty"`
ReplyToAddress *string `json:"replyToAddress,omitempty"`
TLS *bool `json:"tls,omitempty"`
Host *string `json:"host,omitempty"`
User *string `json:"user,omitempty"`
ID string `json:"id,omitempty"`
Description *string `json:"description,omitempty"`
FromAddress *string `json:"senderAddress,omitempty"`
FromName *string `json:"senderName,omitempty"`
ReplyToAddress *string `json:"replyToAddress,omitempty"`
TLS *bool `json:"tls,omitempty"`
Host *string `json:"host,omitempty"`
User *string `json:"user,omitempty"`
Password *crypto.CryptoValue `json:"password,omitempty"`
}
func (e *SMTPConfigChangedEvent) Payload() interface{} {
@ -97,6 +108,7 @@ func (e *SMTPConfigChangedEvent) UniqueConstraints() []*eventstore.UniqueConstra
func NewSMTPConfigChangeEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
id string,
changes []SMTPConfigChanges,
) (*SMTPConfigChangedEvent, error) {
if len(changes) == 0 {
@ -108,6 +120,7 @@ func NewSMTPConfigChangeEvent(
aggregate,
SMTPConfigChangedEventType,
),
ID: id,
}
for _, change := range changes {
change(changeEvent)
@ -117,6 +130,18 @@ func NewSMTPConfigChangeEvent(
type SMTPConfigChanges func(event *SMTPConfigChangedEvent)
func ChangeSMTPConfigID(id string) func(event *SMTPConfigChangedEvent) {
return func(e *SMTPConfigChangedEvent) {
e.ID = id
}
}
func ChangeSMTPConfigDescription(description string) func(event *SMTPConfigChangedEvent) {
return func(e *SMTPConfigChangedEvent) {
e.Description = &description
}
}
func ChangeSMTPConfigTLS(tls bool) func(event *SMTPConfigChangedEvent) {
return func(e *SMTPConfigChangedEvent) {
e.TLS = &tls
@ -153,6 +178,12 @@ func ChangeSMTPConfigSMTPUser(smtpUser string) func(event *SMTPConfigChangedEven
}
}
func ChangeSMTPConfigSMTPPassword(password *crypto.CryptoValue) func(event *SMTPConfigChangedEvent) {
return func(e *SMTPConfigChangedEvent) {
e.Password = password
}
}
func SMTPConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) {
e := &SMTPConfigChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
@ -168,13 +199,14 @@ func SMTPConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, err
type SMTPConfigPasswordChangedEvent struct {
eventstore.BaseEvent `json:"-"`
Password *crypto.CryptoValue `json:"password,omitempty"`
ID string `json:"id,omitempty"`
Password *crypto.CryptoValue `json:"password,omitempty"`
}
func NewSMTPConfigPasswordChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
id string,
password *crypto.CryptoValue,
) *SMTPConfigPasswordChangedEvent {
return &SMTPConfigPasswordChangedEvent{
@ -196,24 +228,106 @@ func (e *SMTPConfigPasswordChangedEvent) UniqueConstraints() []*eventstore.Uniqu
}
func SMTPConfigPasswordChangedEventMapper(event eventstore.Event) (eventstore.Event, error) {
smtpConfigPasswordChagned := &SMTPConfigPasswordChangedEvent{
smtpConfigPasswordChanged := &SMTPConfigPasswordChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(smtpConfigPasswordChagned)
err := event.Unmarshal(smtpConfigPasswordChanged)
if err != nil {
return nil, zerrors.ThrowInternal(err, "IAM-99iNF", "unable to unmarshal smtp config password changed")
}
return smtpConfigPasswordChagned, nil
return smtpConfigPasswordChanged, nil
}
type SMTPConfigActivatedEvent struct {
eventstore.BaseEvent `json:"-"`
ID string `json:"id,omitempty"`
}
func NewSMTPConfigActivatedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
id string,
) *SMTPConfigActivatedEvent {
return &SMTPConfigActivatedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
SMTPConfigActivatedEventType,
),
ID: id,
}
}
func (e *SMTPConfigActivatedEvent) Payload() interface{} {
return e
}
func (e *SMTPConfigActivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func SMTPConfigActivatedEventMapper(event eventstore.Event) (eventstore.Event, error) {
smtpConfigActivated := &SMTPConfigActivatedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(smtpConfigActivated)
if err != nil {
return nil, zerrors.ThrowInternal(err, "IAM-KPr5t", "unable to unmarshal smtp config removed")
}
return smtpConfigActivated, nil
}
type SMTPConfigDeactivatedEvent struct {
eventstore.BaseEvent `json:"-"`
ID string `json:"id,omitempty"`
}
func NewSMTPConfigDeactivatedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
id string,
) *SMTPConfigDeactivatedEvent {
return &SMTPConfigDeactivatedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
SMTPConfigDeactivatedEventType,
),
ID: id,
}
}
func (e *SMTPConfigDeactivatedEvent) Payload() interface{} {
return e
}
func (e *SMTPConfigDeactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func SMTPConfigDeactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) {
smtpConfigDeactivated := &SMTPConfigDeactivatedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(smtpConfigDeactivated)
if err != nil {
return nil, zerrors.ThrowInternal(err, "IAM-KPr5t", "unable to unmarshal smtp config removed")
}
return smtpConfigDeactivated, nil
}
type SMTPConfigRemovedEvent struct {
eventstore.BaseEvent `json:"-"`
ID string `json:"id,omitempty"`
}
func NewSMTPConfigRemovedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
id string,
) *SMTPConfigRemovedEvent {
return &SMTPConfigRemovedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
@ -221,6 +335,7 @@ func NewSMTPConfigRemovedEvent(
aggregate,
SMTPConfigRemovedEventType,
),
ID: id,
}
}

View File

@ -56,6 +56,7 @@ Errors:
SMTPConfig:
NotFound: SMTP конфигурацията не е намерена
AlreadyExists: SMTP конфигурация вече съществува
AlreadyDeactivated: SMTP конфигурацията вече е деактивирана
SenderAdressNotCustomDomain: >-
Адресът на изпращача трябва да бъде конфигуриран като персонализиран
домейн в екземпляра.
@ -1151,6 +1152,9 @@ EventTypes:
config:
added: Добавена е SMTP конфигурация
changed: SMTP конфигурацията е променена
activated: SMTP конфигурацията е активирана
deactivated: SMTP конфигурацията е деактивирана
removed: Премахната SMTP конфигурация
password:
changed: Тайната на конфигурацията на SMTP е променена
sms:
@ -1305,6 +1309,8 @@ EventTypes:
config:
added: Добавена е SMTP конфигурация
changed: SMTP конфигурацията е променена
activated: SMTP конфигурацията е активирана
deactivated: SMTP конфигурацията е деактивирана
password:
changed: Паролата на SMTP конфигурацията е променена
removed: Премахната SMTP конфигурация

View File

@ -56,6 +56,7 @@ Errors:
SMTPConfig:
NotFound: Konfigurace SMTP nebyla nalezena
AlreadyExists: Konfigurace SMTP již existuje
AlreadyDeactivated: Konfigurace SMTP je již deaktivována
SenderAdressNotCustomDomain: Adresa odesílatele musí být nakonfigurována jako vlastní doména na instanci.
Notification:
NoDomain: Pro zprávu nebyla nalezena žádná doména
@ -1126,6 +1127,9 @@ EventTypes:
config:
added: Konfigurace SMTP přidána
changed: Konfigurace SMTP změněna
activated: Konfigurace SMTP aktivována
deactivated: Konfigurace SMTP deaktivována
removed: Konfigurace SMTP odstraněna
password:
changed: Tajemství konfigurace SMTP změněno
sms:
@ -1271,6 +1275,8 @@ EventTypes:
config:
added: Konfigurace SMTP přidána
changed: Konfigurace SMTP změněna
activated: Konfigurace SMTP aktivována
deactivated: Konfigurace SMTP deaktivována
password:
changed: Heslo konfigurace SMTP změněno
removed: Konfigurace SMTP odstraněna

View File

@ -56,6 +56,7 @@ Errors:
SMTPConfig:
NotFound: SMTP Konfiguration nicht gefunden
AlreadyExists: SMTP Konfiguration existiert bereits
AlreadyDeactivated: SMTP-Konfiguration bereits deaktiviert
SenderAdressNotCustomDomain: Die Sender Adresse muss als Custom Domain auf der Instanz registriert sein.
Notification:
NoDomain: Keine Domäne für Nachricht gefunden
@ -1128,6 +1129,9 @@ EventTypes:
config:
added: SMTP Konfiguration hinzugefügt
changed: SMTP Konfiguration geändert
activated: SMTP Konfiguration aktiviert
deactivated: SMTP Konfiguration deaktiviert
removed: SMTP Konfiguration entfernt
password:
changed: SMTP Konfigurations Passwort geändert
sms:
@ -1273,6 +1277,8 @@ EventTypes:
config:
added: SMTP Konfiguration hinzugefügt
changed: SMTP Konfiguration geändert
activated: SMTP Konfiguration aktiviert
deactivated: SMTP Konfiguration deaktiviert
password:
changed: Passwort von SMTP Konfiguration geändert
removed: SMTP Konfiguration gelöscht

View File

@ -56,6 +56,7 @@ Errors:
SMTPConfig:
NotFound: SMTP configuration not found
AlreadyExists: SMTP configuration already exists
AlreadyDeactivated: SMTP configuration already deactivated
SenderAdressNotCustomDomain: The sender address must be configured as custom domain on the instance.
Notification:
NoDomain: No Domain found for message
@ -787,7 +788,7 @@ EventTypes:
code:
added: Phone number code generated
sent: Phone number code sent
removed: Phone number removed
profile:
changed: User profile changed
address:
@ -1128,6 +1129,9 @@ EventTypes:
config:
added: SMTP configuration added
changed: SMTP configuration changed
activated: SMTP configuration activated
deactivated: SMTP configuration deactivated
removed: SMTP configuration removed
password:
changed: SMTP configuration secret changed
sms:
@ -1273,6 +1277,8 @@ EventTypes:
config:
added: SMTP configuration added
changed: SMTP configuration changed
activated: SMTP configuration activated
deactivated: SMTP configuration deactivated
password:
changed: Password of SMTP configuration changed
removed: SMTP configuration removed

View File

@ -56,6 +56,7 @@ Errors:
SMTPConfig:
NotFound: configuración SMTP no encontrada
AlreadyExists: la configuración SMTP ya existe
AlreadyDeactivated: la configuración SMTP ya está desactivada
SenderAdressNotCustomDomain: La dirección del remitente debe configurarse como un dominio personalizado en la instancia.
Notification:
NoDomain: No se encontró el dominio para el mensaje
@ -1128,6 +1129,9 @@ EventTypes:
config:
added: Configuración SMTP añadida
changed: Configuración SMTP modificada
activated: Configuración SMTP activada
deactivated: Configuración SMTP desactivada
removed: Configuración SMTP eliminada
password:
changed: Configuración de secreto SMTP modificada
sms:
@ -1273,6 +1277,8 @@ EventTypes:
config:
added: Configuración SMTP añadida
changed: Configuración SMTP modificada
activated: Configuración SMTP activada
deactivated: Configuración SMTP desactivada
password:
changed: Contraseña de configuración SMTP modificada
removed: Configuración SMTP eliminada

View File

@ -56,6 +56,7 @@ Errors:
SMTPConfig:
NotFound: Configuration SMTP non trouvée
AlreadyExists: La configuration SMTP existe déjà
AlreadyDeactivated: Configuration SMTP déjà désactivée
SenderAdressNotCustomDomain: L'adresse de l'expéditeur doit être configurée comme un domaine personnalisé sur l'instance.
Notification:
NoDomain: Aucun domaine trouvé pour le message
@ -816,6 +817,12 @@ EventTypes:
set: Ensemble de métadonnées de l'utilisateur
removed: Métadonnées de l'utilisateur supprimées
removed.all: Suppression de toutes les métadonnées utilisateur
domain:
claimed: Domaine revendiqué
claimed.sent: Notification de domaine revendiqué envoyée
pat:
added: Personal Access Token added
removed: Personal Access Token removed
org:
added: Organisation ajoutée
changed: Organisation modifiée
@ -862,6 +869,10 @@ EventTypes:
config:
added: Configuration IDP SAML ajoutée
changed: Modification de la configuration IDP SAML
jwt:
config:
added: Configuration JWT IDP ajoutée
changed: La configuration du fournisseur d'identité JWT a été modifiée
customtext:
set: Jeu de texte personnalisé
removed: Texte personnalisé supprimé
@ -875,6 +886,8 @@ EventTypes:
idpprovider:
added: Fournisseur d'Idp ajouté à la politique de connexion
removed: Idp Provider supprimé de la politique de connexion
cascade:
removed: Cascade de fournisseurs d'identité supprimée de la stratégie de connexion
secondfactor:
added: Second factor ajouté à la politique de connexion
removed: Second facteur supprimé de la politique de connexion
@ -920,6 +933,14 @@ 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
domain:
added: Politique de domaine ajoutée
changed: Politique de domaine modifiée
removed: Politique de domaine supprimée
lockout:
added: Politique de verrouillage ajoutée
changed: La politique de verrouillage a été modifiée
removed: Politique de verrouillage supprimée
notification:
added: Politique de notification ajoutée
changed: Politique de notification modifiée
@ -930,6 +951,20 @@ EventTypes:
cascade:
removed: Cascade d'actions supprimée
removed: Actions supprimées
cleared: Flux effacé
mail:
template:
added: Modèle de courrier électronique ajouté
changed: Modèle d'e-mail modifié
removed: Modèle d'e-mail supprimé
text:
added: Texte de l'e-mail ajouté
changed: Le texte de l'e-mail a été modifié
removed: Texte de l'e-mail supprimé
metadata:
removed: Metadata removed
removed.all: All metadata removed
set: Metadata set
project:
added: Projet ajouté
changed: Projet modifié
@ -1036,6 +1071,10 @@ EventTypes:
saml:
config:
added: Ajout de la configuration SAML IDP
changed: La configuration SAML IDP a été modifiée
jwt:
config:
added: Configuration JWT du fournisseur d'identité ajoutée
changed: Modification de la configuration de SAML IDP
customtext:
set: Le texte a été mis en place
@ -1085,6 +1124,9 @@ EventTypes:
config:
added: Ajout de la configuration SMTP
changed: Modification de la configuration SMTP
activated: Configuration SMTP activée
deactivated: Configuration SMTP désactivée
removed: Configuration SMTP supprimée
password:
changed: Modification du secret de la configuration SMTP
sms:
@ -1099,6 +1141,8 @@ EventTypes:
deactivated: Fournisseur de SMS Twilio désactivé
key_pair:
added: Paire de clés ajoutée
certificate:
added: Certificat ajouté
action:
added: Action ajoutée
changed: Action modifiée
@ -1111,7 +1155,134 @@ EventTypes:
deactivated: Schéma utilisateur désactivé
reactivated: Schéma utilisateur réactivé
deleted: Schéma utilisateur supprimé
instance:
added: Instance ajoutée
changed: Instance modifiée
customtext:
removed: Texte personnalisé supprimé
set: Ensemble de texte personnalisé
template:
removed: Modèle de texte personnalisé supprimé
default:
language:
set: Langue par défaut définie
org:
set: Ensemble d'organisation par défaut
domain:
added: Domaine ajouté
primary:
set: Ensemble de domaines principal
removed: Domaine supprimé
iam:
console:
set: Ensemble d'applications Console ZITADEL
project:
set: ZITADEL project set
mail:
template:
added: Modèle de courrier électronique ajouté
changed: Modèle d'e-mail modifié
text:
added: Texte de l'e-mail ajouté
changed: Le texte de l'e-mail a été modifié
member:
added: Membre de l'instance ajouté
changed: Membre de l'instance modifié
removed: Membre de l'instance supprimé
cascade:
removed: Cascade de membres de l'instance supprimée
notification:
provider:
debug:
fileadded: Fournisseur de notification de débogage de fichiers ajouté
filechanged: Le fournisseur de notification de débogage de fichier a été modifié
fileremoved: Fournisseur de notification de débogage de fichier supprimé
logadded: Fournisseur de notification de débogage de journal ajouté
logchanged: Le fournisseur de notification de débogage du journal a été modifié
logremoved: Fournisseur de notification de débogage du journal supprimé
oidc:
settings:
added: Paramètres OIDC ajoutés
changed: Paramètres OIDC modifiés
policy:
domain:
added: Politique de domaine ajoutée
changed: Politique de domaine modifiée
label:
activated: Politique d'étiquetage activée
added: Politique d'étiquetage ajoutée
assets:
removed: L'élément de la stratégie d'étiquette a été supprimé
changed: Politique d'étiquetage modifiée
font:
added: Police ajoutée à la stratégie d'étiquette
removed: Police supprimée de la stratégie relative aux étiquettes
icon:
added: Icône ajoutée à la politique d'étiquetage
removed: Icône supprimée des règles relatives aux étiquettes
dark:
added: Icône ajoutée à la politique d'étiquette sombre
removed: Icône supprimée de la politique relative aux étiquettes sombres
logo:
added: Logo ajouté à la politique d'étiquetage
removed: Logo supprimé de la politique relative aux étiquettes
dark:
added: Logo ajouté à la politique relative aux étiquettes sombres
removed: Logo supprimé de la politique relative aux étiquettes sombres
lockout:
added: Politique de verrouillage ajoutée
changed: La politique de verrouillage a été modifiée
login:
added: Politique de connexion ajoutée
changed: Politique de connexion modifiée
idpprovider:
added: Fournisseur d'identité ajouté à la politique de connexion
cascade:
removed: Cascade de fournisseurs d'identité supprimée de la stratégie de connexion
removed: Fournisseur d'identité supprimé de la stratégie de connexion
multifactor:
added: Multifactor ajouté à la politique de connexion
removed: Multifactor supprimé de la politique de connexion
secondfactor:
added: Deuxième facteur ajouté à la politique de connexion
removed: Deuxième facteur supprimé de la politique de connexion
password:
age:
added: Politique d'âge du mot de passe ajoutée
changed: La politique relative à l'âge du mot de passe a été modifiée
complexity:
added: Politique de complexité des mots de passe ajoutée
changed: Politique de complexité des mots de passe supprimée
privacy:
added: Politique de confidentialité ajoutée
changed: Politique de confidentialité modifiée
security:
set: Ensemble de règles de sécurité
removed: Instance removed
secret:
generator:
added: Générateur de secrets ajouté
changed: Le générateur de secrets a changé
removed: Générateur de secrets supprimé
sms:
configtwilio:
activated: Configuration SMS Twilio activée
added: Configuration SMS Twilio ajoutée
changed: La configuration des SMS Twilio a été modifiée
deactivated: Configuration SMS Twilio désactivée
removed: Configuration SMS Twilio supprimée
token:
changed: Jeton de configuration SMS Twilio modifié
smtp:
config:
added: Configuration SMTP ajoutée
changed: Configuration SMTP modifiée
activated: Configuration SMTP activée
deactivated: Configuration SMTP désactivée
password:
changed: Mot de passe de configuration SMTP modifié
removed: Configuration SMTP supprimée
Application:
OIDC:
UnsupportedVersion: Votre version de l'OIDC n'est pas prise en charge

View File

@ -56,6 +56,7 @@ Errors:
SMTPConfig:
NotFound: Configurazione SMTP non trovata
AlreadyExists: La configurazione SMTP esiste già
AlreadyDeactivated: Configurazione SMTP già disattivata
SenderAdressNotCustomDomain: L'indirizzo del mittente deve essere configurato come dominio personalizzato sull'istanza.
Notification:
NoDomain: Nessun dominio trovato per il messaggio
@ -218,7 +219,6 @@ Errors:
EmptyString: I caratteri non numerici e alfabetici non validi sono stati sostituiti con spazi vuoti e il dominio risultante è una stringa vuota
IDP:
InvalidSearchQuery: Parametro di ricerca non valido
InvalidCharacter: Per un dominio sono ammessi solo caratteri alfanumerici, . e -
ClientIDMissing: ClientID mancante
TeamIDMissing: TeamID mancante
KeyIDMissing: ID chiave mancante
@ -616,6 +616,7 @@ EventTypes:
username:
reserved: Nome utente riservato
released: Nome utente rilasciato
changed: Nome utente cambiato
email:
reserved: Indirizzo e-mail riservato
released: Indirizzo e-mail rilasciato
@ -786,6 +787,7 @@ EventTypes:
code:
added: Codice del numero di telefono generato
sent: Codice del numero di telefono inviato
removed: Codice del numero di telefono rimosso
profile:
changed: Profilo utente cambiato
address:
@ -799,7 +801,9 @@ EventTypes:
succeeded: Controllo OTP riuscito
failed: Controllo OTP fallito
init:
skipped: Inizializzazione saltata
skipped: Inizializzazione OTP saltata
init:
skipped: Inizializzazione saltata
signed:
out: L'utente è uscito
grant:
@ -817,6 +821,12 @@ EventTypes:
set: Set di metadati utente
removed: Metadati utente rimossi
removed.all: Tutti i metadati utente rimossi
domain:
claimed: Dominio rivendicato
claimed.sent: Notifica di rivendicazione del dominio inviata
pat:
added: Aggiunto token di accesso personale
removed: Token di accesso personale rimosso
org:
added: Organizzazione aggiunta
changed: Organizzazione cambiata
@ -863,6 +873,10 @@ EventTypes:
config:
added: Aggiunta la configurazione IDP SAML
changed: Configurazione IDP SAML modificata
jwt:
config:
added: Aggiunta la configurazione IDP JWT
changed: La configurazione dell'IDP JWT è stata modificata
customtext:
set: Testo personalizzato salvato
removed: Testo personalizzato rimosso
@ -876,6 +890,8 @@ EventTypes:
idpprovider:
added: IDP aggiunto alle impostazioni di accesso
removed: IDP rimosso dalle impostazioni di accesso
cascade:
removed: Cascata di provider di identità rimossa dalla policy di accesso
secondfactor:
added: Secondo fattore aggiunto alle impostazioni di accesso
removed: Secondo fattore rimosso dalle impostazioni di accesso
@ -921,6 +937,14 @@ 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
domain:
added: Aggiunta politica di dominio
changed: La politica del dominio è cambiata
removed: Politica del dominio rimossa
lockout:
added: Lockout policy added
changed: Lockout policy changed
removed: Lockout policy removed
notification:
added: Impostazione di notifica creata
changed: Impostazione di notifica cambiata
@ -931,6 +955,20 @@ EventTypes:
cascade:
removed: Azioni a cascata rimosse
removed: Azioni rimosse
cleared: Il flusso è stato eliminato
mail:
template:
added: Aggiunto modello di posta elettronica
changed: Il modello di posta elettronica è stato modificato
removed: Modello di posta elettronica rimosso
text:
added: Aggiunto il testo dell'e-mail
changed: Il testo dell'e-mail è stato modificato
removed: Testo dell'e-mail rimosso
metadata:
removed: Metadati rimossi
removed.all: Tutti i metadati rimossi
set: Insieme di metadati
project:
added: Progetto aggiunto
changed: Progetto cambiato
@ -1037,7 +1075,11 @@ EventTypes:
saml:
config:
added: Aggiunta la configurazione IDP SAML
changed: Configurazione IDP SAML modificata
changed: La configurazione dell'IDP SAML è stata modificata
jwt:
config:
added: Aggiunta la configurazione JWT al provider di identità
changed: Configurazione JWT dal provider di identità rimossa
customtext:
set: Il testo è stato impostato
removed: Il testo è stato rimosso
@ -1086,6 +1128,9 @@ EventTypes:
config:
added: SMTP configuration added
changed: SMTP configuration changed
activated: Configurazione SMTP attivata
deactivated: Configurazione SMTP disattivata
removed: Configurazione SMTP rimossa
password:
changed: SMTP configuration secret changed
sms:
@ -1100,6 +1145,8 @@ EventTypes:
deactivated: Provider SMS Twilio disattivato
key_pair:
added: Keypair aggiunto
certificate:
added: Certificato aggiunto
action:
added: Azione aggiunta
changed: Azione cambiata
@ -1112,6 +1159,134 @@ EventTypes:
deactivated: Schema utente disattivato
reactivated: Schema utente riattivato
deleted: Schema utente eliminato
instance:
added: Istanza aggiunta
changed: L'istanza è cambiata
customtext:
removed: Testo personalizzato rimosso
set: Set di testo personalizzato
template:
removed: Modello di testo personalizzato rimosso
default:
language:
set: Lingua predefinita impostata
org:
set: Insieme di organizzazioni predefinito
domain:
added: Dominio aggiunto
primary:
set: Insieme di domini primari
removed: Dominio rimosso
iam:
console:
set: Set di applicazioni per console ZITADEL
project:
set: Set progetto ZITADEL
mail:
template:
added: Aggiunto modello di posta elettronica
changed: Il modello di posta elettronica è stato modificato
text:
added: Aggiunto il testo dell'e-mail
changed: Il testo dell'e-mail è stato modificato
member:
added: Membro dell'istanza aggiunto
changed: Il membro dell'istanza è cambiato
removed: Membro dell'istanza rimosso
cascade:
removed: Cascata di membri dell'istanza rimossa
notification:
provider:
debug:
fileadded: Aggiunto provider di notifiche di debug dei file
filechanged: Il provider delle notifiche di debug dei file è stato modificato
fileremoved: Provider di notifiche di debug del file rimosso
logadded: Aggiunto provider di notifiche di debug del registro
logchanged: Il provider delle notifiche di debug del registro è stato modificato
logremoved: Provider di notifiche di debug del registro rimosso
oidc:
settings:
added: Aggiunte impostazioni OIDC
changed: Le impostazioni OIDC sono state modificate
policy:
domain:
added: Aggiunta politica di dominio
changed: Domain policy changed
label:
activated: Criterio etichetta attivato
added: Aggiunta la politica sull'etichetta
assets:
removed: Risorsa dalla norma sull'etichetta rimossa
changed: La politica sull'etichetta è cambiata
font:
added: Carattere aggiunto ai criteri di etichetta
removed: Carattere rimosso dai criteri di etichetta
icon:
added: Icona aggiunta al criterio dell'etichetta
removed: Icona rimossa dal criterio di etichetta
dark:
added: Icona aggiunta al criterio dell'etichetta oscura
removed: Icona rimossa dal criterio dell'etichetta oscura
logo:
added: Logo aggiunto alla politica sull'etichetta
removed: Logo rimosso dalla politica sull'etichetta
dark:
added: Logo aggiunto alla politica delle etichette scure
removed: Logo rimosso dalla politica delle etichette scure
lockout:
added: Aggiunta politica di blocco
changed: La politica di blocco è cambiata
login:
added: Criteri di accesso aggiunti
changed: La politica di accesso è cambiata
idpprovider:
added: Provider di identità aggiunto alla policy di accesso
cascade:
removed: Cascata di provider di identità rimossa dalla policy di accesso
removed: Provider di identità rimosso dalla policy di accesso
multifactor:
added: Multifattore aggiunto alla policy di accesso
removed: Multifattore rimosso dalla policy di accesso
secondfactor:
added: Secondo fattore aggiunto alla politica di accesso
removed: Secondo fattore rimosso dalla politica di accesso
password:
age:
added: Aggiunta politica sull'età della password
changed: La politica di validità della password è cambiata
complexity:
added: Aggiunta policy sulla complessità della password
changed: Criterio di complessità della password rimosso
privacy:
added: Aggiunta informativa sulla privacy
changed: L'informativa sulla privacy è cambiata
security:
set: Insieme di politiche di sicurezza
removed: Istanza rimossa
secret:
generator:
added: Aggiunto generatore segreto
changed: Il generatore segreto è cambiato
removed: Generatore segreto rimosso
sms:
configtwilio:
activated: Configurazione SMS Twilio attivata
added: Aggiunta la configurazione SMS di Twilio
changed: La configurazione SMS di Twilio è stata modificata
deactivated: Configurazione SMS Twilio disattivata
removed: Configurazione SMS di Twilio rimossa
token:
changed: La configurazione del token di Twilio SMS è stata modificata
smtp:
config:
added: Aggiunta configurazione SMTP
changed: La configurazione SMTP è stata modificata
activated: Configurazione SMTP attivata
deactivated: Configurazione SMTP disattivata
password:
changed: La password della configurazione SMTP è cambiata
removed: Configurazione SMTP rimossa
Application:
OIDC:

View File

@ -56,6 +56,7 @@ Errors:
SMTPConfig:
NotFound: SMTP構成が見つかりません
AlreadyExists: すでに存在するSMTP構成です
AlreadyDeactivated: SMTP設定はすでに無効化されています
SenderAdressNotCustomDomain: 送信者アドレスは、インスタンスのカスタムドメインとして構成する必要があります。
Notification:
NoDomain: メッセージのドメインが見つかりません
@ -1117,6 +1118,9 @@ EventTypes:
config:
added: SMTP構成の追加
changed: SMTP構成の変更
activated: SMTP設定が有効化されました
deactivated: SMTP設定が無効化されました
removed: SMTP設定が削除されました
password:
changed: SMTP構成シークレットの変更
sms:
@ -1262,6 +1266,8 @@ EventTypes:
config:
added: SMTP構成の追加
changed: SMTP構成の変更
activated: SMTP設定が有効化されました
deactivated: SMTP設定が無効化されました
password:
changed: SMTP構成パスワードの変更
removed: SMTP構成の削除

View File

@ -56,6 +56,7 @@ Errors:
SMTPConfig:
NotFound: SMTP конфигурацијата не е пронајдена
AlreadyExists: SMTP конфигурацијата веќе постои
AlreadyDeactivated: SMTP конфигурацијата е веќе деактивирана
SenderAdressNotCustomDomain: Адресата на испраќачот мора да биде конфигурирана како прилагоден домен на инстанцата.
Notification:
NoDomain: Не е пронајден домен за пораката
@ -1127,6 +1128,9 @@ EventTypes:
config:
added: Додадена SMTP конфигурација
changed: Променета SMTP конфигурација
activated: SMTP конфигурацијата е активирана
deactivated: SMTP конфигурацијата е деактивирана
removed: SMTP конфигурацијата е отстранета
password:
changed: Променена тајна на SMTP конфигурација
sms:
@ -1271,6 +1275,8 @@ EventTypes:
config:
added: Додадена SMTP конфигурација
changed: Променета SMTP конфигурација
activated: SMTP конфигурацијата е активирана
deactivated: SMTP конфигурацијата е деактивирана
password:
changed: Променета лозинка на SMTP конфигурацијата
removed: Отстранета SMTP конфигурација

View File

@ -56,6 +56,7 @@ Errors:
SMTPConfig:
NotFound: Konfiguracja SMTP nie znaleziona
AlreadyExists: Konfiguracja SMTP już istnieje
AlreadyDeactivated: Konfiguracja SMTP jest już dezaktywowana
SenderAdressNotCustomDomain: Adres nadawcy musi być skonfigurowany jako domena niestandardowa na instancji.
Notification:
NoDomain: Nie znaleziono domeny dla wiadomości
@ -1128,6 +1129,9 @@ EventTypes:
config:
added: Dodano konfigurację SMTP
changed: Zmieniono konfigurację SMTP
activated: Konfiguracja SMTP została aktywowana
deactivated: Konfiguracja SMTP dezaktywowana
removed: Konfiguracja SMTP została usunięta
password:
changed: Zmieniono sekret konfiguracji SMTP
sms:
@ -1273,6 +1277,8 @@ EventTypes:
config:
added: Konfiguracja SMTP dodana
changed: Konfiguracja SMTP zmieniona
activated: Konfiguracja SMTP została aktywowana
deactivated: Konfiguracja SMTP dezaktywowana
password:
changed: Hasło konfiguracji SMTP zmienione
removed: Konfiguracja SMTP usunięta

View File

@ -56,6 +56,7 @@ Errors:
SMTPConfig:
NotFound: Configuração de SMTP não encontrada
AlreadyExists: Configuração de SMTP já existe
AlreadyDeactivated: Configuração SMTP já desativada
SenderAdressNotCustomDomain: O endereço do remetente deve ser configurado como um domínio personalizado na instância.
Notification:
NoDomain: Nenhum domínio encontrado para a mensagem
@ -1122,6 +1123,9 @@ EventTypes:
config:
added: Configuração SMTP adicionada
changed: Configuração SMTP alterada
activated: Configuração SMTP ativada
deactivated: Configuração SMTP desativada
removed: Configuração SMTP removida
password:
changed: Segredo da configuração SMTP alterado
sms:
@ -1267,6 +1271,8 @@ EventTypes:
config:
added: Configuração SMTP adicionada
changed: Configuração SMTP alterada
activated: Configuração SMTP ativada
deactivated: Configuração SMTP desativada
password:
changed: Senha da configuração SMTP alterada
removed: Configuração SMTP removida

View File

@ -56,7 +56,8 @@ Errors:
SMTPConfig:
NotFound: Конфигурация SMTP не найдена
AlreadyExists: Конфигурация SMTP уже существует
SenderAdressNotCustomDomain: Адрес отправителя должен быть настроен как личный домен в экземпляре
AlreadyDeactivated: Конфигурация SMTP уже деактивирована
SenderAdressNotCustomDomain: Адрес отправителя должен быть настроен как личный домен на экземпляре.
Notification:
NoDomain: Домен не найден
User:
@ -1117,6 +1118,9 @@ EventTypes:
config:
added: Конфигурация SMTP добавлена
changed: Конфигурация SMTP изменена
activated: Конфигурация SMTP активирована
deactivated: Конфигурация SMTP деактивирована
removed: Конфигурация SMTP удалена.
password:
changed: Ключ конфигурации SMTP изменён
sms:
@ -1262,6 +1266,8 @@ EventTypes:
config:
added: Конфигурация SMTP добавлена
changed: Конфигурация SMTP изменена
activated: Конфигурация SMTP активирована
deactivated: Конфигурация SMTP деактивирована
password:
changed: Пароль конфигурации SMTP изменён
removed: Конфигурация SMTP удалена

View File

@ -56,6 +56,7 @@ Errors:
SMTPConfig:
NotFound: 未找到 SMTP 配置
AlreadyExists: SMTP 配置已存在
AlreadyDeactivated: SMTP 配置已停用
SenderAdressNotCustomDomain: 发件人地址必须在在实例的域名设置中验证。
Notification:
NoDomain: 未找到对应的域名
@ -615,6 +616,7 @@ EventTypes:
username:
reserved: 保留用户名
released: 用户名已发布
changed: 用户名已更改
email:
reserved: 电子邮件地址已保留
released: 电子邮件地址已发布
@ -670,6 +672,10 @@ EventTypes:
check:
succeeded: 密码检查成功
failed: 密码检查失败
change:
sent: 密码更改已发送
hash:
updated: 密码哈希已更新
externallogin:
check:
succeeded: 外部登录成功
@ -773,10 +779,6 @@ EventTypes:
check:
succeeded: 密码检查成功
failed: 密码检查失败
change:
sent: 密码更改已发送
hash:
updated: 密码哈希已更新
phone:
changed: 更改手机号码
verified: 验证手机号码
@ -785,6 +787,7 @@ EventTypes:
code:
added: 生成的手机号码验证码
sent: 发送手机号码验证码
removed: 电话号码已删除
profile:
changed: 修改用户资料
address:
@ -797,8 +800,10 @@ EventTypes:
check:
succeeded: MFA OTP 验证成功
failed: MFA OTP 验证失败
init:
skipped: 跳过 MFA 初始化
init:
skipped: 跳过 MFA 初始化
skipped: 跳过多因素初始化
signed:
out: 用户退出登录
grant:
@ -816,6 +821,12 @@ EventTypes:
set: 用户元数据集
removed: 删除用户元数据
removed.all: 删除所有用户元数据
domain:
claimed: 已认领域名
claimed.sent: 已发送域声明通知
pat:
added: 添加个人访问令牌
removed: 个人访问令牌已删除
org:
added: 添加组织
changed: 更改组织
@ -862,6 +873,10 @@ EventTypes:
config:
added: 添加 SAML IDP 配置
changed: 更改 SAML IDP 配置
jwt:
config:
added: 添加了 JWT IDP 配置
changed: JWT IDP 配置已更改
customtext:
set: 设置自定义文本
removed: 删除自定义文本
@ -875,6 +890,8 @@ EventTypes:
idpprovider:
added: 添加 IDP 到登录策略
removed: 从登录策略删除 IDP
cascade:
removed: 从登录策略中删除了身份提供者级联
secondfactor:
added: 添加两步认证到登录策略
removed: 删除两步认证到登录策略
@ -918,6 +935,14 @@ EventTypes:
removed: 从标签策略中删除的资产
privacy:
added: 添加隐私政策和服务条款
changed: 隐私政策和服务条款已更改
removed: 隐私政策和 TOS 已删除
domain:
added: 添加了域策略
changed: 域策略已更改
removed: 域策略已删除
lockout:
added: 添加了锁定策略
changed: 更改隐私政策和服务条款
removed: 删除隐私政策和服务条款
notification:
@ -930,6 +955,20 @@ EventTypes:
cascade:
removed: 删除动作级联
removed: 删除动作
cleared: 流量已清除
mail:
template:
added: 添加了电子邮件模板
changed: 电子邮件模板已更改
removed: 电子邮件模板已删除
text:
added: 添加了电子邮件文本
changed: 电子邮件文本已更改
removed: 电子邮件文本已删除
metadata:
removed: 电子邮件文本已删除
removed.all: 所有元数据已删除
set: 元数据集
project:
added: 添加项目
changed: 更改项目
@ -1037,6 +1076,10 @@ EventTypes:
config:
added: 添加 SAML IDP 配置
changed: 更改 SAML IDP 配置
jwt:
config:
added: 添加了身份提供者的 JWT 配置
changed: 身份提供商的 JWT 配置已删除
customtext:
set: 设置文本
removed: 删除文本
@ -1085,6 +1128,9 @@ EventTypes:
config:
added: 添加 SMTP 配置
changed: 更改 SMTP 配置
activated: SMTP 配置已激活
deactivated: SMTP 配置已停用
removed: SMTP 配置已删除
password:
changed: 更改 SMTP 安全设置
sms:
@ -1099,6 +1145,8 @@ EventTypes:
deactivated: 停用 Twilio SMS 提供者
key_pair:
added: 添加密钥对
certificate:
added: 证书已添加
action:
added: 添加动作
changed: 更改动作
@ -1111,6 +1159,134 @@ EventTypes:
deactivated: 用户架构已停用
reactivated: 用户架构已重新激活
deleted: 用户架构已删除
instance:
added: 实例已添加
changed: 实例已更改
customtext:
removed: 自定义文本已删除
set: 自定义文本集
template:
removed: 删除了自定义文本模板
default:
language:
set: 默认语言设置
org:
set: 默认组织集
domain:
added: 已添加域名
primary:
set: 主域集
removed: 域名已删除
iam:
console:
set: ZITADEL 控制台应用程序集
project:
set: ZITADEL 项目集
mail:
template:
added: 添加了电子邮件模板
changed: 电子邮件模板已更改
text:
added: 添加了电子邮件文本
changed: 电子邮件文本已更改
member:
added: 已添加实例成员
changed: 实例成员发生变化
removed: 实例成员已删除
cascade:
removed: 实例成员级联已删除
notification:
provider:
debug:
fileadded: 添加了文件调试通知提供程序
filechanged: 文件调试通知提供程序已更改
fileremoved: 删除文件调试通知提供程序
logadded: 添加了日志调试通知提供程序
logchanged: 日志调试通知提供程序已更改
logremoved: 日志调试通知提供程序已删除
oidc:
settings:
added: 添加了 OIDC 设置
changed: OIDC 设置已更改
policy:
domain:
added: 添加了域策略
changed: 域策略已更改
label:
activated: 标签政策已激活
added: 添加了标签策略
assets:
removed: 已删除标签政策中的资产
changed: 标签政策已更改
font:
added: 添加到标签政策的字体
removed: 从标签政策中删除的字体
icon:
added: 图标已添加到标签策略
removed: 图标已从标签政策中删除
dark:
added: 图标已添加到暗标签政策
removed: 图标已从暗标签政策中删除
logo:
added: 徽标已添加到标签政策中
removed: 徽标已从标签政策中删除
dark:
added: 徽标已添加到暗标签政策中
removed: 徽标从暗标签政策中删除
lockout:
added: 添加了锁定策略
changed: 锁定政策已更改
login:
added: 添加了登录策略
changed: 登录政策已更改
idpprovider:
added: 身份提供商已添加到登录策略中
cascade:
removed: 身份提供者级联从登录策略中删除
removed: 身份提供商已从登录策略中删除
multifactor:
added: 登录策略中添加了多因素
removed: 从登录策略中删除了多因素
secondfactor:
added: 添加到登录策略的第二个因素
removed: 从登录策略中删除了第二个因素
password:
age:
added: 添加了密码年龄策略
changed: 密码期限政策已更改
complexity:
added: 添加了密码复杂性策略
changed: 删除了密码复杂性策略
privacy:
added: 添加了隐私政策
changed: 隐私政策已更改
security:
set: 安全策略集
removed: 实例已删除
secret:
generator:
added: 添加了秘密生成器
changed: 秘密生成器已更改
removed: 秘密生成器已移除
sms:
configtwilio:
activated: Twilio SMS 配置已激活
added: 添加了 Twilio SMS 配置
changed: Twilio SMS 配置已更改
deactivated: Twilio SMS 配置已停用
removed: Twilio SMS 配置已删除
token:
changed: Twilio SMS 配置的令牌已更改
smtp:
config:
added: 添加了 SMTP 配置
changed: SMTP 配置已更改
activated: SMTP 配置已激活
deactivated: SMTP 配置已停用
password:
changed: SMTP 配置密码已更改
removed: SMTP 配置已删除
Application:
OIDC:

View File

@ -381,8 +381,24 @@ service AdminService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP";
summary: "Get SMTP Configuration";
description: "Returns the SMTP configuration from the system. This is used to send E-Mails to the users."
summary: "Get active SMTP Configuration";
description: "Returns the active SMTP configuration from the system. This is used to send E-Mails to the users."
};
}
rpc GetSMTPConfigById(GetSMTPConfigByIdRequest) returns (GetSMTPConfigByIdResponse) {
option (google.api.http) = {
get: "/smtp/{id}";
};
option (zitadel.v1.auth_option) = {
permission: "iam.read";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP";
summary: "Get SMTP provider configuration by its id";
description: "Get a specific SMTP provider configuration by its ID.";
};
}
@ -405,7 +421,7 @@ service AdminService {
rpc UpdateSMTPConfig(UpdateSMTPConfigRequest) returns (UpdateSMTPConfigResponse) {
option (google.api.http) = {
put: "/smtp";
put: "/smtp/{id}";
body: "*"
};
@ -422,7 +438,7 @@ service AdminService {
rpc UpdateSMTPConfigPassword(UpdateSMTPConfigPasswordRequest) returns (UpdateSMTPConfigPasswordResponse) {
option (google.api.http) = {
put: "/smtp/password";
put: "/smtp/{id}/password";
body: "*"
};
@ -437,9 +453,43 @@ service AdminService {
};
}
rpc ActivateSMTPConfig(ActivateSMTPConfigRequest) returns (ActivateSMTPConfigResponse) {
option (google.api.http) = {
post: "/smtp/{id}/_activate";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP Provider";
summary: "Activate SMTP Provider";
description: "Activate an SMTP provider."
};
}
rpc DeactivateSMTPConfig(DeactivateSMTPConfigRequest) returns (DeactivateSMTPConfigResponse) {
option (google.api.http) = {
post: "/smtp/{id}/_deactivate";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP Provider";
summary: "Deactivate SMTP Provider";
description: "Deactivate an SMTP provider. After deactivating the provider, the users will not be able to receive SMTP notifications from that provider anymore."
};
}
rpc RemoveSMTPConfig(RemoveSMTPConfigRequest) returns (RemoveSMTPConfigResponse) {
option (google.api.http) = {
delete: "/smtp";
delete: "/smtp/{id}";
};
option (zitadel.v1.auth_option) = {
@ -453,6 +503,23 @@ service AdminService {
};
}
rpc ListSMTPConfigs(ListSMTPConfigsRequest) returns (ListSMTPConfigsResponse) {
option (google.api.http) = {
post: "/smtp/_search"
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.read";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP Configs";
summary: "List SMTP Configs";
description: "Returns a list of SMTP configurations."
};
}
rpc ListSMSProviders(ListSMSProvidersRequest) returns (ListSMSProvidersResponse) {
option (google.api.http) = {
post: "/sms/_search"
@ -4007,6 +4074,23 @@ message GetSMTPConfigResponse {
zitadel.settings.v1.SMTPConfig smtp_config = 1;
}
message GetSMTPConfigByIdRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 100}];
}
message GetSMTPConfigByIdResponse {
zitadel.settings.v1.SMTPConfig smtp_config = 1;
}
message ListSMTPConfigsRequest {
zitadel.v1.ListQuery query = 1;
}
message ListSMTPConfigsResponse {
zitadel.v1.ListDetails details = 1;
repeated zitadel.settings.v1.SMTPConfig result = 2;
}
message AddSMTPConfigRequest {
string sender_address = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
@ -4051,7 +4135,15 @@ message AddSMTPConfigRequest {
(validate.rules).string = {min_len: 0, max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"replyto@m.zitadel.cloud\"";
min_length: 1;
min_length: 0;
max_length: 200;
}
];
string description = 8 [
(validate.rules).string = {min_len: 0, max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"provider description\"";
min_length: 0;
max_length: 200;
}
];
@ -4059,6 +4151,7 @@ message AddSMTPConfigRequest {
message AddSMTPConfigResponse {
zitadel.v1.ObjectDetails details = 1;
string id = 2;
}
message UpdateSMTPConfigRequest {
@ -4104,6 +4197,20 @@ message UpdateSMTPConfigRequest {
max_length: 200;
}
];
string password = 7 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"this-is-my-password\"";
}
];
string description = 8 [
(validate.rules).string = {min_len: 0, max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"provider description\"";
min_length: 1;
max_length: 200;
}
];
string id = 9 [(validate.rules).string = {min_len: 1, max_len: 100}];
}
message UpdateSMTPConfigResponse {
@ -4116,14 +4223,32 @@ message UpdateSMTPConfigPasswordRequest {
example: "\"this-is-my-updated-password\"";
}
];
string id = 2 [(validate.rules).string = {min_len: 1, max_len: 100}];
}
message UpdateSMTPConfigPasswordResponse {
zitadel.v1.ObjectDetails details = 1;
}
//this is an empty request
message RemoveSMTPConfigRequest {}
message ActivateSMTPConfigRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
}
message ActivateSMTPConfigResponse {
zitadel.v1.ObjectDetails details = 1;
}
message DeactivateSMTPConfigRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
}
message DeactivateSMTPConfigResponse {
zitadel.v1.ObjectDetails details = 1;
}
message RemoveSMTPConfigRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 100}];
}
message RemoveSMTPConfigResponse {
zitadel.v1.ObjectDetails details = 1;

View File

@ -40,6 +40,12 @@ message SecretGeneratorTypeQuery {
SecretGeneratorType generator_type = 1;
}
enum SMTPConfigState {
SMTP_CONFIG_STATE_UNSPECIFIED = 0;
SMTP_CONFIG_ACTIVE = 1;
SMTP_CONFIG_INACTIVE = 2;
}
enum SecretGeneratorType {
SECRET_GENERATOR_TYPE_UNSPECIFIED = 0;
SECRET_GENERATOR_TYPE_INIT_CODE = 1;
@ -80,6 +86,13 @@ message SMTPConfig {
example: "\"replyto@m.zitadel.cloud\"";
}
];
SMTPConfigState state = 8;
string description = 9 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"Mailjet\"";
}
];
string id = 10;
}
message SMSProvider {