mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-13 19:44:21 +00:00
Merge branch 'next' into next-rc
This commit is contained in:
commit
2c93bcbf24
@ -2,7 +2,7 @@
|
||||
title="{{
|
||||
id ? ('SMTP.DETAIL.TITLE' | translate) : ('SMTP.CREATE.STEPS.TITLE' | translate: { value: providerDefaultSetting.name })
|
||||
}}"
|
||||
[createSteps]="3"
|
||||
[createSteps]="4"
|
||||
[currentCreateStep]="currentCreateStep"
|
||||
(closed)="close()"
|
||||
>
|
||||
@ -93,7 +93,7 @@
|
||||
[disabled]="firstFormGroup.invalid"
|
||||
color="primary"
|
||||
matStepperNext
|
||||
data-e2e="continue-button"
|
||||
data-e2e="continue-to-2nd-form"
|
||||
>
|
||||
{{ 'ACTIONS.CONTINUE' | translate }}
|
||||
</button>
|
||||
@ -130,19 +130,74 @@
|
||||
<button mat-stroked-button matStepperPrevious class="bck-button">{{ 'ACTIONS.BACK' | translate }}</button>
|
||||
<button
|
||||
mat-raised-button
|
||||
class="create-button"
|
||||
[disabled]="secondFormGroup.invalid"
|
||||
color="primary"
|
||||
data-e2e="create-button"
|
||||
(click)="savePolicy(stepper)"
|
||||
[disabled]="
|
||||
firstFormGroup.invalid || secondFormGroup.invalid || (['iam.policy.write'] | hasRole | async) === false
|
||||
"
|
||||
matStepperNext
|
||||
data-e2e="continue-button"
|
||||
>
|
||||
{{ !hasSMTPConfig ? ('ACTIONS.CREATE' | translate) : ('ACTIONS.SAVE' | translate) }}
|
||||
{{ 'ACTIONS.CONTINUE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [editable]="true">
|
||||
<form>
|
||||
<ng-template matStepLabel>{{ 'SMTP.CREATE.STEPS.SAVE_SETTINGS' | translate }}</ng-template>
|
||||
<cnsl-info-section>
|
||||
<div class="title-row">
|
||||
<div class="left">
|
||||
<h2 class="title">{{ 'SMTP.CREATE.STEPS.TEST.TITLE' | translate }}</h2>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button color="primary" mat-raised-button class="continue-button" (click)="testEmailConfiguration()">
|
||||
{{ 'ACTIONS.TEST' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="cnsl-secondary-text description">{{ 'SMTP.CREATE.STEPS.TEST.DESCRIPTION' | translate }}</p>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'SMTP.LIST.DIALOG.TEST_EMAIL' | translate }}</cnsl-label>
|
||||
<input
|
||||
cnslInput
|
||||
[(ngModel)]="email"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
data-e2e="email-test-dialog-input"
|
||||
/>
|
||||
</cnsl-form-field>
|
||||
|
||||
<div class="is-loading" *ngIf="isLoading()">
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-form-field class="formfield" *ngIf="testResult">
|
||||
<cnsl-label>{{ 'SMTP.LIST.DIALOG.TEST_RESULT' | translate }}</cnsl-label>
|
||||
<textarea
|
||||
cnslInput
|
||||
class="{{ resultClass }}"
|
||||
[(ngModel)]="testResult"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
></textarea>
|
||||
</cnsl-form-field>
|
||||
</cnsl-info-section>
|
||||
|
||||
<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(stepper)"
|
||||
[disabled]="
|
||||
firstFormGroup.invalid || secondFormGroup.invalid || (['iam.policy.write'] | hasRole | async) === false
|
||||
"
|
||||
>
|
||||
{{ !hasSMTPConfig ? ('ACTIONS.CREATE' | translate) : ('ACTIONS.SAVE' | translate) }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [editable]="true">
|
||||
<form>
|
||||
<ng-template matStepLabel>{{ 'SMTP.CREATE.STEPS.NEXT_STEPS' | translate }}</ng-template>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
|
||||
import { Location } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, signal } from '@angular/core';
|
||||
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { Subject } from 'rxjs';
|
||||
import { Subject, take } 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';
|
||||
@ -11,6 +11,7 @@ import { PolicyComponentServiceType } from '../policies/policy-component-types.e
|
||||
import {
|
||||
AddSMTPConfigRequest,
|
||||
AddSMTPConfigResponse,
|
||||
TestSMTPConfigRequest,
|
||||
UpdateSMTPConfigRequest,
|
||||
UpdateSMTPConfigResponse,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
@ -31,8 +32,10 @@ import {
|
||||
OutlookDefaultSettings,
|
||||
SendgridDefaultSettings,
|
||||
} from './known-smtp-providers-settings';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { MatStepper } from '@angular/material/stepper';
|
||||
import { SMTPConfigState } from 'src/app/proto/generated/zitadel/settings_pb';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-smtp-provider',
|
||||
@ -61,6 +64,11 @@ export class SMTPProviderComponent {
|
||||
|
||||
public senderEmailPlaceholder = 'sender@example.com';
|
||||
|
||||
public resultClass = 'test-success';
|
||||
public isLoading = signal(false);
|
||||
public email: string = '';
|
||||
public testResult: string = '';
|
||||
|
||||
constructor(
|
||||
private service: AdminService,
|
||||
private _location: Location,
|
||||
@ -68,6 +76,8 @@ export class SMTPProviderComponent {
|
||||
private toast: ToastService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private authService: GrpcAuthService,
|
||||
private translate: TranslateService,
|
||||
) {
|
||||
this.route.parent?.url.subscribe((urlPath) => {
|
||||
const providerName = urlPath[urlPath.length - 1].path;
|
||||
@ -136,6 +146,17 @@ export class SMTPProviderComponent {
|
||||
this.fetchData(this.id);
|
||||
}
|
||||
}
|
||||
|
||||
this.authService
|
||||
.getMyUser()
|
||||
.then((resp) => {
|
||||
if (resp.user) {
|
||||
this.email = resp.user.human?.email?.email || '';
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -281,6 +302,38 @@ export class SMTPProviderComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public testEmailConfiguration(): void {
|
||||
this.isLoading.set(true);
|
||||
|
||||
const req = new TestSMTPConfigRequest();
|
||||
req.setSenderAddress(this.senderAddress?.value ?? '');
|
||||
req.setSenderName(this.senderName?.value ?? '');
|
||||
req.setHost(this.hostAndPort?.value ?? '');
|
||||
req.setUser(this.user?.value);
|
||||
req.setPassword(this.password?.value ?? '');
|
||||
req.setTls(this.tls?.value ?? false);
|
||||
req.setId(this.id ?? '');
|
||||
req.setReceiverAddress(this.email ?? '');
|
||||
|
||||
this.service
|
||||
.testSMTPConfig(req)
|
||||
.then(() => {
|
||||
this.resultClass = 'test-success';
|
||||
this.isLoading.set(false);
|
||||
this.translate
|
||||
.get('SMTP.CREATE.STEPS.TEST.RESULT')
|
||||
.pipe(take(1))
|
||||
.subscribe((msg) => {
|
||||
this.testResult = msg;
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.resultClass = 'test-error';
|
||||
this.isLoading.set(false);
|
||||
this.testResult = error;
|
||||
});
|
||||
}
|
||||
|
||||
public get description(): AbstractControl | null {
|
||||
return this.firstFormGroup.get('description');
|
||||
}
|
||||
|
@ -96,4 +96,26 @@
|
||||
width: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.continue-button {
|
||||
margin-top: 3rem;
|
||||
display: block;
|
||||
height: 3.5rem;
|
||||
padding: 0 4rem;
|
||||
}
|
||||
|
||||
.is-loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.test-error {
|
||||
border-color: #ff3b5b !important;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.test-success {
|
||||
border-color: #10b981 !important;
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
|
@ -93,6 +93,18 @@
|
||||
<i class="las la-times-circle"></i>
|
||||
</button>
|
||||
|
||||
<button
|
||||
actions
|
||||
[disabled]="(['iam.write'] | hasRole | async) === false"
|
||||
mat-icon-button
|
||||
color="primary"
|
||||
matTooltip="{{ 'SMTP.LIST.TEST' | translate }}"
|
||||
data-e2e="test-provider-button"
|
||||
(click)="testSMTPConfig(config.id); $event.stopPropagation()"
|
||||
>
|
||||
<i class="las la-flask"></i>
|
||||
</button>
|
||||
|
||||
<button
|
||||
actions
|
||||
[disabled]="(['iam.write'] | hasRole | async) === false"
|
||||
|
@ -16,6 +16,7 @@ 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';
|
||||
import { SmtpTestDialogComponent } from '../smtp-test-dialog/smtp-test-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-smtp-table',
|
||||
@ -149,6 +150,21 @@ export class SMTPTableComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
public testSMTPConfig(id: string): void {
|
||||
this.dialog.open(SmtpTestDialogComponent, {
|
||||
data: {
|
||||
id: id,
|
||||
confirmKey: 'ACTIONS.TEST',
|
||||
cancelKey: 'ACTIONS.CLOSE',
|
||||
titleKey: 'SMTP.LIST.DIALOG.TEST_TITLE',
|
||||
descriptionKey: 'SMTP.LIST.DIALOG.TEST_DESCRIPTION',
|
||||
emailKey: 'SMTP.LIST.DIALOG.TEST_EMAIL',
|
||||
testResultKey: 'SMTP.LIST.DIALOG.TEST_RESULT',
|
||||
},
|
||||
width: '500px',
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(limit: number, offset: number): Promise<void> {
|
||||
this.loadingSubject.next(true);
|
||||
|
||||
|
@ -18,6 +18,7 @@ import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { SmtpTestDialogModule } from '../smtp-test-dialog/smtp-test-dialog.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [SMTPTableComponent],
|
||||
@ -40,6 +41,7 @@ import { MatTableModule } from '@angular/material/table';
|
||||
HasRoleModule,
|
||||
HasRolePipeModule,
|
||||
TruncatePipeModule,
|
||||
SmtpTestDialogModule,
|
||||
],
|
||||
exports: [SMTPTableComponent],
|
||||
})
|
||||
|
@ -0,0 +1,47 @@
|
||||
<h1 mat-dialog-title>
|
||||
<span>{{ data.titleKey | translate: data.titleParam }}</span>
|
||||
</h1>
|
||||
|
||||
<div mat-dialog-content>
|
||||
<div class="icon-wrapper" *ngIf="data.icon">
|
||||
<i class="icon {{ data.icon }}"></i>
|
||||
</div>
|
||||
<p class="desc cnsl-secondary-text">{{ data.descriptionKey | translate: data.descriptionParam }}</p>
|
||||
|
||||
<cnsl-info-section *ngIf="data.warnSectionKey" [type]="InfoSectionType.WARN">
|
||||
{{ data.warnSectionKey | translate }}
|
||||
</cnsl-info-section>
|
||||
|
||||
<p *ngIf="data.hintKey" class="desc cnsl-secondary-text">{{ data.hintKey | translate: { value: data.confirmation } }}</p>
|
||||
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ data.emailKey | translate }}</cnsl-label>
|
||||
<input cnslInput [(ngModel)]="email" data-e2e="email-test-dialog-input" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<div class="is-loading" *ngIf="isLoading()">
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-form-field class="formfield" *ngIf="testResult">
|
||||
<cnsl-label>{{ data.testResultKey | translate }}</cnsl-label>
|
||||
<textarea cnslInput class="{{ resultClass }}" [(ngModel)]="testResult"></textarea>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
<div class="action" mat-dialog-actions>
|
||||
<button *ngIf="data.cancelKey" mat-stroked-button (click)="closeDialog()">
|
||||
{{ data.cancelKey | translate }}
|
||||
</button>
|
||||
<span class="fill-space"></span>
|
||||
<button
|
||||
color="primary"
|
||||
[disabled]="!email || isLoading()"
|
||||
mat-raised-button
|
||||
class="ok-button"
|
||||
(click)="testEmailConfiguration()"
|
||||
data-e2e="test-email-button"
|
||||
>
|
||||
{{ data.confirmKey | translate }}
|
||||
</button>
|
||||
</div>
|
@ -0,0 +1,51 @@
|
||||
h1 {
|
||||
font-size: 1.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
margin: 1rem;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
font-size: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
|
||||
button {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.ok-button {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.is-loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.test-error {
|
||||
border-color: #ff3b5b;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.test-success {
|
||||
border-color: #10b981;
|
||||
min-height: 200px;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { SmtpTestDialogComponent } from './smtp-test-dialog.component';
|
||||
|
||||
describe('SmtpTestDialogComponent', () => {
|
||||
let component: SmtpTestDialogComponent;
|
||||
let fixture: ComponentFixture<SmtpTestDialogComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SmtpTestDialogComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SmtpTestDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,72 @@
|
||||
import { Component, Inject, signal } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
|
||||
import { InfoSectionType } from '../info-section/info-section.component';
|
||||
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 { TranslateService } from '@ngx-translate/core';
|
||||
import { TestSMTPConfigByIdRequest } from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { take } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-smtp-test-dialog',
|
||||
templateUrl: './smtp-test-dialog.component.html',
|
||||
styleUrls: ['./smtp-test-dialog.component.scss'],
|
||||
})
|
||||
export class SmtpTestDialogComponent {
|
||||
public resultClass = 'test-success';
|
||||
public isLoading = signal(false);
|
||||
public email: string = '';
|
||||
public testResult: string = '';
|
||||
InfoSectionType: any = InfoSectionType;
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<SmtpTestDialogComponent>,
|
||||
private adminService: AdminService,
|
||||
private authService: GrpcAuthService,
|
||||
private toast: ToastService,
|
||||
private translate: TranslateService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
) {
|
||||
this.authService
|
||||
.getMyUser()
|
||||
.then((resp) => {
|
||||
if (resp.user) {
|
||||
this.email = resp.user.human?.email?.email || '';
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public testEmailConfiguration(): void {
|
||||
this.isLoading.set(true);
|
||||
|
||||
const req = new TestSMTPConfigByIdRequest();
|
||||
req.setId(this.data.id);
|
||||
req.setReceiverAddress(this.email);
|
||||
|
||||
this.adminService
|
||||
.testSMTPConfigById(req)
|
||||
.then(() => {
|
||||
this.resultClass = 'test-success';
|
||||
this.isLoading.set(false);
|
||||
this.translate
|
||||
.get('SMTP.CREATE.STEPS.TEST.RESULT')
|
||||
.pipe(take(1))
|
||||
.subscribe((msg) => {
|
||||
this.testResult = msg;
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.resultClass = 'test-error';
|
||||
this.isLoading.set(false);
|
||||
this.testResult = error;
|
||||
});
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { InfoSectionModule } from '../info-section/info-section.module';
|
||||
import { InputModule } from '../input/input.module';
|
||||
import { SmtpTestDialogComponent } from './smtp-test-dialog.component';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
|
||||
@NgModule({
|
||||
declarations: [SmtpTestDialogComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
MatDialogModule,
|
||||
MatProgressSpinnerModule,
|
||||
TranslateModule,
|
||||
InfoSectionModule,
|
||||
MatButtonModule,
|
||||
InputModule,
|
||||
],
|
||||
})
|
||||
export class SmtpTestDialogModule {}
|
@ -240,6 +240,10 @@ import {
|
||||
SetSecurityPolicyResponse,
|
||||
SetUpOrgRequest,
|
||||
SetUpOrgResponse,
|
||||
TestSMTPConfigByIdRequest,
|
||||
TestSMTPConfigByIdResponse,
|
||||
TestSMTPConfigRequest,
|
||||
TestSMTPConfigResponse,
|
||||
UpdateAppleProviderRequest,
|
||||
UpdateAppleProviderResponse,
|
||||
UpdateAzureADProviderRequest,
|
||||
@ -330,6 +334,7 @@ import {
|
||||
} from '../proto/generated/zitadel/milestone/v1/milestone_pb';
|
||||
import { OrgFieldName, OrgQuery } from '../proto/generated/zitadel/org_pb';
|
||||
import { SortDirection } from '@angular/material/sort';
|
||||
import { SMTPConfig } from '../proto/generated/zitadel/settings_pb';
|
||||
|
||||
export interface OnboardingActions {
|
||||
order: number;
|
||||
@ -946,6 +951,14 @@ export class AdminService {
|
||||
return this.grpcService.admin.deactivateSMTPConfig(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public testSMTPConfigById(req: TestSMTPConfigByIdRequest): Promise<TestSMTPConfigByIdResponse.AsObject> {
|
||||
return this.grpcService.admin.testSMTPConfigById(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public testSMTPConfig(req: TestSMTPConfigRequest): Promise<TestSMTPConfigResponse.AsObject> {
|
||||
return this.grpcService.admin.testSMTPConfig(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public removeSMTPConfig(id: string): Promise<RemoveSMTPConfigResponse.AsObject> {
|
||||
const req = new RemoveSMTPConfigRequest();
|
||||
req.setId(id);
|
||||
|
@ -485,8 +485,15 @@
|
||||
"NEXT": "Следващ",
|
||||
"MORE": "Повече ▼",
|
||||
"STEP": "стъпка",
|
||||
"SETUP": "Настройвам",
|
||||
"COMINGSOON": "Очаквайте скоро",
|
||||
"TEST": "Тест",
|
||||
"UNSAVEDCHANGES": "Незапазени промени",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
"DESCRIPTION": "Сигурни ли сте, че искате да отхвърлите това ново действие? Вашето действие ще бъде загубено",
|
||||
"CANCEL": "Отказ",
|
||||
"DISCARD": "Изхвърлете"
|
||||
}
|
||||
},
|
||||
"TABLE": {
|
||||
"SHOWUSER": "Покажи потребител {{value}}"
|
||||
},
|
||||
@ -2228,6 +2235,7 @@
|
||||
"ACTIVATED": "Активиран",
|
||||
"ACTIVATE": "Активирайте доставчика",
|
||||
"DEACTIVATE": "Деактивирайте доставчика",
|
||||
"TEST": "Тествайте вашия доставчик",
|
||||
"TYPE": "Тип",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "SMTP конфигурацията е активирана",
|
||||
@ -2251,7 +2259,6 @@
|
||||
"CURRENT_DESC_TITLE": "Това са вашите SMTP настройки",
|
||||
"PROVIDER_SETTINGS": "Настройки на SMTP доставчик",
|
||||
"SENDER_SETTINGS": "Настройки на изпращача",
|
||||
"TEST_SETTINGS": "Тествайте настройките на SMTP",
|
||||
"NEXT_STEPS": "Следващи стъпки",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "Активирайте вашия SMTP доставчик",
|
||||
@ -2260,6 +2267,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Деактивирайте вашия SMTP доставчик",
|
||||
"DESCRIPTION": "Ако деактивирате този SMTP доставчик, ZITADEL не може да го използва за изпращане на известия, докато не го активирате отново."
|
||||
},
|
||||
"SAVE_SETTINGS": "Запазете вашите настройки",
|
||||
"TEST": {
|
||||
"TITLE": "Тествайте настройките си",
|
||||
"DESCRIPTION": "Можете да тествате настройките на вашия SMTP доставчик и да проверите резултата от теста, преди да ги запазите",
|
||||
"RESULT": "Вашият имейл беше изпратен успешно"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -486,6 +486,7 @@
|
||||
"MORE": "více",
|
||||
"STEP": "Krok",
|
||||
"SETUP": "Nastavit",
|
||||
"TEST": "Test",
|
||||
"UNSAVEDCHANGES": "Neuložené změny",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
@ -2247,6 +2248,7 @@
|
||||
"ACTIVATED": "Aktivováno",
|
||||
"ACTIVATE": "Aktivujte poskytovatele",
|
||||
"DEACTIVATE": "Deaktivovat poskytovatele",
|
||||
"TEST": "Otestujte svého poskytovatele",
|
||||
"TYPE": "Typ",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "Konfigurace SMTP byla aktivována",
|
||||
@ -2270,7 +2272,6 @@
|
||||
"CURRENT_DESC_TITLE": "Toto jsou vaše nastavení SMTP",
|
||||
"PROVIDER_SETTINGS": "Nastavení poskytovatele SMTP",
|
||||
"SENDER_SETTINGS": "Nastavení odesílatele",
|
||||
"TEST_SETTINGS": "Otestujte nastavení SMTP",
|
||||
"NEXT_STEPS": "Další kroky",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "Aktivujte svého poskytovatele SMTP",
|
||||
@ -2279,6 +2280,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Deaktivujte svého poskytovatele SMTP",
|
||||
"DESCRIPTION": "Pokud deaktivujete tohoto poskytovatele SMTP, ZITADEL jej nebude moci používat k odesílání upozornění, dokud jej znovu neaktivujete."
|
||||
},
|
||||
"SAVE_SETTINGS": "Uložte nastavení",
|
||||
"TEST": {
|
||||
"TITLE": "Otestujte svá nastavení",
|
||||
"DESCRIPTION": "Nastavení poskytovatele SMTP můžete otestovat a před uložením zkontrolovat výsledek testu",
|
||||
"RESULT": "Váš email byl úspěšně odeslán"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -485,6 +485,8 @@
|
||||
"NEXT": "Weiter",
|
||||
"MORE": "mehr",
|
||||
"STEP": "Schritt",
|
||||
"SETUP": "Aufstellen",
|
||||
"TEST": "Prüfen",
|
||||
"UNSAVEDCHANGES": "Nicht gespeicherte Änderungen",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
@ -2237,6 +2239,7 @@
|
||||
"ACTIVATED": "Aktiviert",
|
||||
"ACTIVATE": "Anbieter aktivieren",
|
||||
"DEACTIVATE": "Anbieter deaktivieren",
|
||||
"TEST": "Testen Sie Ihren Anbieter",
|
||||
"TYPE": "Typ",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "Die SMTP-Konfiguration wurde aktiviert",
|
||||
@ -2260,7 +2263,6 @@
|
||||
"CURRENT_DESC_TITLE": "Dies sind Ihre SMTP-Einstellungen",
|
||||
"PROVIDER_SETTINGS": "SMTP-Anbietereinstellungen",
|
||||
"SENDER_SETTINGS": "Absendereinstellungen",
|
||||
"TEST_SETTINGS": "Testen Sie die SMTP-Einstellungen",
|
||||
"NEXT_STEPS": "Nächste Schritte",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "Aktivieren Sie Ihren SMTP-Anbieter",
|
||||
@ -2269,6 +2271,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Deaktivieren Sie Ihren SMTP-Anbieter",
|
||||
"DESCRIPTION": "Wenn Sie diesen SMTP-Anbieter deaktivieren, kann ZITADEL ihn nicht zum Versenden von Benachrichtigungen verwenden, bis Sie ihn erneut aktivieren."
|
||||
},
|
||||
"SAVE_SETTINGS": "Speichern Sie Ihre Einstellungen",
|
||||
"TEST": {
|
||||
"TITLE": "Testen Sie Ihre Einstellungen",
|
||||
"DESCRIPTION": "Sie können die Einstellungen Ihres SMTP-Anbieters testen und das Testergebnis überprüfen, bevor Sie sie speichern",
|
||||
"RESULT": "Deine E-Mail wurde erfolgreich versandt"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -486,6 +486,7 @@
|
||||
"MORE": "more",
|
||||
"STEP": "Step",
|
||||
"SETUP": "Setup",
|
||||
"TEST": "Test",
|
||||
"UNSAVEDCHANGES": "Unsaved changes",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
@ -2259,6 +2260,7 @@
|
||||
"ACTIVATED": "Activated",
|
||||
"ACTIVATE": "Activate provider",
|
||||
"DEACTIVATE": "Deactivate provider",
|
||||
"TEST": "Test your provider",
|
||||
"TYPE": "Type",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "SMTP config has been activated",
|
||||
@ -2270,7 +2272,11 @@
|
||||
"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."
|
||||
"SENDER": "Type {{value}}, to delete this SMTP configuration.",
|
||||
"TEST_TITLE": "Test your SMTP config",
|
||||
"TEST_DESCRIPTION": "Specify an email address to test your SMTP configuration for this provider",
|
||||
"TEST_EMAIL": "Email address",
|
||||
"TEST_RESULT": "Test result"
|
||||
}
|
||||
},
|
||||
"CREATE": {
|
||||
@ -2282,7 +2288,6 @@
|
||||
"CURRENT_DESC_TITLE": "These are your SMTP settings",
|
||||
"PROVIDER_SETTINGS": "SMTP Provider Settings",
|
||||
"SENDER_SETTINGS": "Sender Settings",
|
||||
"TEST_SETTINGS": "Test SMTP Settings",
|
||||
"NEXT_STEPS": "Next Steps",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "Activate your SMTP Provider",
|
||||
@ -2291,6 +2296,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Deactivate your SMTP Provider",
|
||||
"DESCRIPTION": "If you deactivate this SMTP Provider, ZITADEL cannot use it to send notifications until you activate it again."
|
||||
},
|
||||
"SAVE_SETTINGS": "Save your settings",
|
||||
"TEST": {
|
||||
"TITLE": "Test your settings",
|
||||
"DESCRIPTION": "You can test your SMTP provider settings and check the test result before saving them",
|
||||
"RESULT": "Your email was succesfully sent"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -486,6 +486,7 @@
|
||||
"MORE": "más",
|
||||
"STEP": "Paso",
|
||||
"SETUP": "Configurar",
|
||||
"TEST": "Probar",
|
||||
"UNSAVEDCHANGES": "Cambios no guardados",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
@ -2235,6 +2236,7 @@
|
||||
"ACTIVATED": "Activado",
|
||||
"ACTIVATE": "Activar proveedor",
|
||||
"DEACTIVATE": "Desactivar proveedor",
|
||||
"TEST": "Prueba tu proveedor",
|
||||
"TYPE": "Tipo",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "Tu configuración SMTP ha sido activada",
|
||||
@ -2258,7 +2260,6 @@
|
||||
"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",
|
||||
"NEXT_STEPS": "Pŕoximos pasos",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "Activa tu proveedor SMTP",
|
||||
@ -2267,6 +2268,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Desactiva tu proveedor SMTP",
|
||||
"DESCRIPTION": "Si desactivas este proveedor SMTP, ZITADEL no puede utilizarlo para enviar notificationes hasta que lo actives otra vez."
|
||||
},
|
||||
"SAVE_SETTINGS": "Guarda tu configuración",
|
||||
"TEST": {
|
||||
"TITLE": "Prueba tu configuración",
|
||||
"DESCRIPTION": "Puedes probar tu configuración SMTP y revisar el resultado de la prueba antes de guardar la configuración",
|
||||
"RESULT": "Tu email fue enviado con éxito"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -485,6 +485,8 @@
|
||||
"NEXT": "Suivant",
|
||||
"MORE": "Plus",
|
||||
"STEP": "Étape",
|
||||
"SETUP": "Installation",
|
||||
"TEST": "Test",
|
||||
"UNSAVEDCHANGES": "Modifications non enregistrées",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
@ -2238,6 +2240,7 @@
|
||||
"ACTIVATED": "Activé",
|
||||
"ACTIVATE": "Activer le fournisseur",
|
||||
"DEACTIVATE": "Désactiver le fournisseur",
|
||||
"TEST": "Testez votre fournisseur",
|
||||
"TYPE": "Type",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "La configuration SMTP a été activée",
|
||||
@ -2261,7 +2264,6 @@
|
||||
"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",
|
||||
"NEXT_STEPS": "Prochaines étapes",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "Activez votre fournisseur SMTP",
|
||||
@ -2270,6 +2272,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Désactivez votre fournisseur SMTP",
|
||||
"DESCRIPTION": "Si vous désactivez l'option SMTP, vous ne pourrez pas vous connecter à l'application, le dossier sera alors désactivé."
|
||||
},
|
||||
"SAVE_SETTINGS": "Enregistrez vos paramètres",
|
||||
"TEST": {
|
||||
"TITLE": "Testez vos paramètres",
|
||||
"DESCRIPTION": "Vous pouvez tester les paramètres de votre fournisseur SMTP et vérifier le résultat du test avant de les enregistrer",
|
||||
"RESULT": "Votre email a été envoyé avec succès"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -485,6 +485,8 @@
|
||||
"NEXT": "Avanti",
|
||||
"MORE": "azioni",
|
||||
"STEP": "Passo",
|
||||
"SETUP": "Setup",
|
||||
"TEST": "Test",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
"DESCRIPTION": "Sei sicuro di voler eliminare questa nuova azione? La tua azione andrà persa",
|
||||
@ -2238,6 +2240,7 @@
|
||||
"ACTIVATED": "Attivato",
|
||||
"ACTIVATE": "Attiva fornitore",
|
||||
"DEACTIVATE": "Disattiva fornitore",
|
||||
"TEST": "Metti alla prova il tuo fornitore",
|
||||
"TYPE": "Tipo",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "La configurazione SMTP è stata attivata",
|
||||
@ -2261,7 +2264,6 @@
|
||||
"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",
|
||||
"NEXT_STEPS": "Prossimi passi",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "Attiva il tuo provider SMTP",
|
||||
@ -2270,6 +2272,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Disattiva il tuo provider SMTP",
|
||||
"DESCRIPTION": "Dopo aver disattivato l'archivio SMTP, la schermata non è disponibile per l'utente, il documento è nuovo e non è attivo."
|
||||
},
|
||||
"SAVE_SETTINGS": "Salva le tue impostazioni",
|
||||
"TEST": {
|
||||
"TITLE": "Metti alla prova le tue impostazioni",
|
||||
"DESCRIPTION": "Puoi testare le impostazioni del tuo provider SMTP e controllare il risultato del test prima di salvarle",
|
||||
"RESULT": "La tua email è stata inviata correttamente"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -486,6 +486,7 @@
|
||||
"MORE": "さらに",
|
||||
"STEP": "ステップ",
|
||||
"SETUP": "セットアップ",
|
||||
"TEST": "テスト",
|
||||
"UNSAVEDCHANGES": "未保存の変更",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
@ -2230,6 +2231,7 @@
|
||||
"ACTIVATED": "アクティブ化された",
|
||||
"ACTIVATE": "プロバイダーをアクティブ化する",
|
||||
"DEACTIVATE": "プロバイダーを非アクティブ化する",
|
||||
"TEST": "プロバイダーをテストする",
|
||||
"TYPE": "タイプ",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "SMTP設定が有効化されました",
|
||||
@ -2253,7 +2255,6 @@
|
||||
"CURRENT_DESC_TITLE": "これらは SMTP 設定です",
|
||||
"PROVIDER_SETTINGS": "SMTPプロバイダーの設定",
|
||||
"SENDER_SETTINGS": "送信者の設定",
|
||||
"TEST_SETTINGS": "SMTP設定をテストする",
|
||||
"NEXT_STEPS": "次のステップ",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "SMTP プロバイダーをアクティブ化する",
|
||||
@ -2262,6 +2263,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "SMTPプロバイダーを非アクティブ化します",
|
||||
"DESCRIPTION": "この SMTP プロバイダーを非アクティブ化すると、再度アクティブ化するまで、Zitadel はそれを使用して通知を送信できなくなります。"
|
||||
},
|
||||
"SAVE_SETTINGS": "設定を保存する",
|
||||
"TEST": {
|
||||
"TITLE": "設定をテストする",
|
||||
"DESCRIPTION": "SMTP プロバイダーの設定をテストし、保存する前にテスト結果を確認できます。",
|
||||
"RESULT": "あなたの電子メールが正常に送信されました"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -486,6 +486,7 @@
|
||||
"MORE": "повеќе",
|
||||
"STEP": "Чекор",
|
||||
"SETUP": "Подесување",
|
||||
"TEST": "Тест",
|
||||
"UNSAVEDCHANGES": "Незачувани промени",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
@ -2235,6 +2236,7 @@
|
||||
"ACTIVATED": "Активиран",
|
||||
"ACTIVATE": "Активирајте го провајдерот",
|
||||
"DEACTIVATE": "Деактивирајте го провајдерот",
|
||||
"TEST": "Тестирајте го вашиот провајдер",
|
||||
"TYPE": "Тип",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "SMTP конфигурацијата е активирана",
|
||||
@ -2258,7 +2260,6 @@
|
||||
"CURRENT_DESC_TITLE": "Ова се вашите поставки за SMTP",
|
||||
"PROVIDER_SETTINGS": "Поставки на провајдерот SMTP",
|
||||
"SENDER_SETTINGS": "Поставки на испраќачот",
|
||||
"TEST_SETTINGS": "Тестирајте ги поставките за SMTP",
|
||||
"NEXT_STEPS": "Следните чекори",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "Активирајте го вашиот SMTP провајдер",
|
||||
@ -2267,6 +2268,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Деактивирајте го вашиот SMTP провајдер",
|
||||
"DESCRIPTION": "Ако го деактивирате овој SMTP провајдер, ZITADEL не може да го користи за испраќање известувања додека не го активирате повторно."
|
||||
},
|
||||
"SAVE_SETTINGS": "Зачувајте ги вашите поставки",
|
||||
"TEST": {
|
||||
"TITLE": "Тестирајте ги вашите поставки",
|
||||
"DESCRIPTION": "Можете да ги тестирате поставките на провајдерот SMTP и да го проверите резултатот од тестот пред да ги зачувате",
|
||||
"RESULT": "Вашата е-пошта беше успешно испратена"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -486,6 +486,7 @@
|
||||
"MORE": "meer",
|
||||
"STEP": "Stap",
|
||||
"SETUP": "Instellen",
|
||||
"TEST": "Test",
|
||||
"UNSAVEDCHANGES": "Niet-opgeslagen wijzigingen",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
@ -2256,6 +2257,7 @@
|
||||
"ACTIVATED": "Geactiveerd",
|
||||
"ACTIVATE": "Aanbieder activeren",
|
||||
"DEACTIVATE": "Aanbieder deactiveren",
|
||||
"TEST": "Test uw aanbieder",
|
||||
"TYPE": "Type",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "SMTP-configuratie is geactiveerd",
|
||||
@ -2279,7 +2281,6 @@
|
||||
"CURRENT_DESC_TITLE": "Dit zijn uw SMTP-instellingen",
|
||||
"PROVIDER_SETTINGS": "SMTP-providerinstellingen",
|
||||
"SENDER_SETTINGS": "Afzenderinstellingen",
|
||||
"TEST_SETTINGS": "SMTP-instellingen testen",
|
||||
"NEXT_STEPS": "Volgende stappen",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "Activeer uw SMTP-provider",
|
||||
@ -2288,6 +2289,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Deactiveer uw SMTP-provider",
|
||||
"DESCRIPTION": "Als u deze SMTP-provider deactiveert, kan ZITADEL deze niet gebruiken om meldingen te verzenden totdat u deze opnieuw activeert."
|
||||
},
|
||||
"SAVE_SETTINGS": "Sla uw instellingen op",
|
||||
"TEST": {
|
||||
"TITLE": "Test uw instellingen",
|
||||
"DESCRIPTION": "U kunt de instellingen van uw SMTP-provider testen en het testresultaat controleren voordat u deze opslaat",
|
||||
"RESULT": "Je email is succesvol verzonden"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -486,6 +486,7 @@
|
||||
"MORE": "więcej",
|
||||
"STEP": "Krok",
|
||||
"UNSAVEDCHANGES": "Niezapisane zmiany",
|
||||
"TEST": "Test",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
"DESCRIPTION": "Czy na pewno chcesz odrzucić to nowe działanie? Twoje działanie zostanie utracone",
|
||||
@ -2238,6 +2239,7 @@
|
||||
"ACTIVATED": "Aktywowany",
|
||||
"ACTIVATE": "Aktywuj dostawcę",
|
||||
"DEACTIVATE": "Dezaktywuj dostawcę",
|
||||
"TEST": "Przetestuj swojego dostawcę",
|
||||
"TYPE": "Typ",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "Konfiguracja SMTP została aktywowana",
|
||||
@ -2261,7 +2263,6 @@
|
||||
"CURRENT_DESC_TITLE": "To są Twoje ustawienia SMTP",
|
||||
"PROVIDER_SETTINGS": "Ustawienia dostawcy SMTP",
|
||||
"SENDER_SETTINGS": "Ustawienia nadawcy",
|
||||
"TEST_SETTINGS": "Przetestuj ustawienia SMTP",
|
||||
"NEXT_STEPS": "Następne kroki",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "Aktywuj swojego dostawcę SMTP",
|
||||
@ -2270,6 +2271,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Dezaktywuj swojego dostawcę SMTP",
|
||||
"DESCRIPTION": "Jeśli dezaktywujesz tego dostawcę SMTP, ZITADEL nie będzie mógł go używać do wysyłania powiadomień, dopóki nie aktywujesz go ponownie."
|
||||
},
|
||||
"SAVE_SETTINGS": "Zapisz swoje ustawienia",
|
||||
"TEST": {
|
||||
"TITLE": "Przetestuj swoje ustawienia",
|
||||
"DESCRIPTION": "Możesz przetestować ustawienia dostawcy SMTP i sprawdzić wynik testu przed ich zapisaniem",
|
||||
"RESULT": "Twój email został wysłany"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -486,6 +486,7 @@
|
||||
"MORE": "mais",
|
||||
"STEP": "Passo",
|
||||
"SETUP": "Configuração",
|
||||
"TEST": "Teste",
|
||||
"UNSAVEDCHANGES": "Alterações não salvas",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
@ -2233,6 +2234,7 @@
|
||||
"ACTIVATED": "Ativado",
|
||||
"ACTIVATE": "Ativar provedor",
|
||||
"DEACTIVATE": "Desativar provedor",
|
||||
"TEST": "Teste seu provedor",
|
||||
"TYPE": "Tipo",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "A configuração SMTP foi ativada",
|
||||
@ -2256,7 +2258,6 @@
|
||||
"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",
|
||||
"NEXT_STEPS": "Próximos passos",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "Ative seu provedor SMTP",
|
||||
@ -2265,6 +2266,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Desative seu provedor SMTP",
|
||||
"DESCRIPTION": "Se você desativar este provedor SMTP, a ZITADEL não poderá usá-lo para enviar notificações até que você o ative novamente."
|
||||
},
|
||||
"SAVE_SETTINGS": "Salve suas configurações",
|
||||
"TEST": {
|
||||
"TITLE": "Teste suas configurações",
|
||||
"DESCRIPTION": "Você pode testar as configurações do seu provedor SMTP e verificar o resultado do teste antes de salvá-las",
|
||||
"RESULT": "Seu e-mail foi enviado com sucesso"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -485,6 +485,7 @@
|
||||
"MORE": "больше",
|
||||
"STEP": "Шаг",
|
||||
"SETUP": "Настроить",
|
||||
"TEST": "Тест",
|
||||
"UNSAVEDCHANGES": "Несохраненные изменения",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
@ -2350,6 +2351,7 @@
|
||||
"ACTIVATED": "Активировано",
|
||||
"ACTIVATE": "Активировать провайдера",
|
||||
"DEACTIVATE": "Деактивировать провайдера",
|
||||
"TEST": "Проверьте своего провайдера",
|
||||
"TYPE": "Тип",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "Конфигурация SMTP активирована",
|
||||
@ -2373,7 +2375,6 @@
|
||||
"CURRENT_DESC_TITLE": "Это ваши настройки SMTP",
|
||||
"PROVIDER_SETTINGS": "Настройки SMTP-провайдера",
|
||||
"SENDER_SETTINGS": "Настройки отправителя",
|
||||
"TEST_SETTINGS": "Проверка настроек SMTP",
|
||||
"NEXT_STEPS": "Следующие шаги",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "Активируйте своего SMTP-провайдера",
|
||||
@ -2382,6 +2383,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Деактивируйте своего SMTP-провайдера",
|
||||
"DESCRIPTION": "Если вы деактивируете этого поставщика SMTP, ZITADEL не сможет использовать его для отправки уведомлений, пока вы не активируете его снова."
|
||||
},
|
||||
"SAVE_SETTINGS": "Сохраните настройки",
|
||||
"TEST": {
|
||||
"TITLE": "Проверьте свои настройки",
|
||||
"DESCRIPTION": "Вы можете проверить настройки своего провайдера SMTP и проверить результаты проверки перед их сохранением.",
|
||||
"RESULT": "Ваше письмо было успешно отправлено"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -486,6 +486,7 @@
|
||||
"MORE": "mer",
|
||||
"STEP": "Steg",
|
||||
"SETUP": "Inställning",
|
||||
"TEST": "Testa",
|
||||
"UNSAVEDCHANGES": "Osparade ändringar",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
@ -2259,6 +2260,7 @@
|
||||
"ACTIVATED": "Aktiverad",
|
||||
"ACTIVATE": "Aktivera leverantör",
|
||||
"DEACTIVATE": "Inaktivera leverantör",
|
||||
"TEST": "Testa din leverantör",
|
||||
"TYPE": "Typ",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "SMTP-konfigurationen har aktiverats",
|
||||
@ -2270,7 +2272,11 @@
|
||||
"DELETE_TITLE": "Radera SMTP-konfiguration",
|
||||
"DELETE_DESCRIPTION": "Du håller på att radera en konfiguration. Bekräfta denna åtgärd genom att skriva avsändarnamnet",
|
||||
"DELETED": "SMTP-konfigurationen har raderats",
|
||||
"SENDER": "Skriv {{value}}, för att radera denna SMTP-konfiguration."
|
||||
"SENDER": "Skriv {{value}}, för att radera denna SMTP-konfiguration.",
|
||||
"TEST_TITLE": "Testa din SMTP-konfiguration",
|
||||
"TEST_DESCRIPTION": "Ange en e-postadress för att testa din SMTP-konfiguration för den här leverantören",
|
||||
"TEST_EMAIL": "E-postadress",
|
||||
"TEST_RESULT": "Testresultat"
|
||||
}
|
||||
},
|
||||
"CREATE": {
|
||||
@ -2282,7 +2288,6 @@
|
||||
"CURRENT_DESC_TITLE": "Dessa är dina SMTP-inställningar",
|
||||
"PROVIDER_SETTINGS": "SMTP-leverantörsinställningar",
|
||||
"SENDER_SETTINGS": "Avsändarinställningar",
|
||||
"TEST_SETTINGS": "Testa SMTP-inställningar",
|
||||
"NEXT_STEPS": "Nästa steg",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "Aktivera din SMTP-leverantör",
|
||||
@ -2291,6 +2296,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Inaktivera din SMTP-leverantör",
|
||||
"DESCRIPTION": "Om du inaktiverar denna SMTP-leverantör kan ZITADEL inte använda den för att skicka meddelanden förrän du aktiverar den igen."
|
||||
},
|
||||
"SAVE_SETTINGS": "Spara dina inställningar",
|
||||
"TEST": {
|
||||
"TITLE": "Testa dina inställningar",
|
||||
"DESCRIPTION": "Du kan testa inställningarna för din SMTP-leverantör och kontrollera testresultatet innan du sparar dem",
|
||||
"RESULT": "Ditt e-postmeddelande har skickats"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -485,6 +485,8 @@
|
||||
"NEXT": "下一页",
|
||||
"MORE": "更多",
|
||||
"STEP": "步",
|
||||
"SETUP": "设置",
|
||||
"TEST": "测试",
|
||||
"UNSAVEDCHANGES": "未保存的更改",
|
||||
"UNSAVED": {
|
||||
"DIALOG": {
|
||||
@ -2237,6 +2239,7 @@
|
||||
"ACTIVATED": "活性",
|
||||
"ACTIVATE": "激活提供商",
|
||||
"DEACTIVATE": "停用提供商",
|
||||
"TEST": "测试您的提供商",
|
||||
"TYPE": "类型",
|
||||
"DIALOG": {
|
||||
"ACTIVATED": "SMTP 配置已激活",
|
||||
@ -2260,7 +2263,6 @@
|
||||
"CURRENT_DESC_TITLE": "这些是您的 SMTP 设置",
|
||||
"PROVIDER_SETTINGS": "SMTP 提供商设置",
|
||||
"SENDER_SETTINGS": "发件人设置",
|
||||
"TEST_SETTINGS": "测试 SMTP 设置",
|
||||
"NEXT_STEPS": "下一步",
|
||||
"ACTIVATE": {
|
||||
"TITLE": "激活您的 SMTP 提供商",
|
||||
@ -2269,6 +2271,12 @@
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "停用您的 SMTP 提供商",
|
||||
"DESCRIPTION": "如果您停用此 SMTP 提供程序,Zitadel 将无法使用它发送通知,直到您再次激活它。"
|
||||
},
|
||||
"SAVE_SETTINGS": "保存您的设置",
|
||||
"TEST": {
|
||||
"TITLE": "测试您的设置",
|
||||
"DESCRIPTION": "您可以测试您的 SMTP 提供商设置并在保存之前检查测试结果",
|
||||
"RESULT": "您的电子邮件已成功发送"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -32,7 +32,7 @@ Please check below the matrix for an overview where which scope is asserted.
|
||||
| phone_verified | When requested | When requested | When requested and response_type `id_token` | No |
|
||||
| preferred_username (username when Introspect) | When requested | When requested | Yes | No |
|
||||
| sub | Yes | Yes | Yes | When JWT |
|
||||
| urn:zitadel:iam:org:domain:primary:{domainname} | When requested | When requested | When requested | When JWT and requested |
|
||||
| urn:zitadel:iam:org:domain:primary:\{domainname} | When requested | When requested | When requested | When JWT and requested |
|
||||
| urn:zitadel:iam:org:project:roles | When requested | When requested | When requested or configured | When JWT and requested or configured |
|
||||
| urn:zitadel:iam:user:metadata | When requested | When requested | When requested | When JWT and requested |
|
||||
| urn:zitadel:iam:user:resourceowner:id | When requested | When requested | When requested | When JWT and requested |
|
||||
@ -101,11 +101,11 @@ ZITADEL reserves some claims to assert certain data. Please check out the [reser
|
||||
|
||||
| Claims | Example | Description |
|
||||
| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| urn:zitadel:iam:action:{actionname}:log | `{"urn:zitadel:iam:action:appendCustomClaims:log": ["test log", "another test log"]}` | This claim is set during Actions as a log, e.g. if two custom claims with the same keys are set. |
|
||||
| urn:zitadel:iam:org:domain:primary:{domainname} | `{"urn:zitadel:iam:org:domain:primary": "acme.ch"}` | This claim represents the primary domain of the organization the user belongs to. |
|
||||
| urn:zitadel:iam:action:\{actionname}:log | `{"urn:zitadel:iam:action:appendCustomClaims:log": ["test log", "another test log"]}` | This claim is set during Actions as a log, e.g. if two custom claims with the same keys are set. |
|
||||
| urn:zitadel:iam:org:domain:primary:\{domainname} | `{"urn:zitadel:iam:org:domain:primary": "acme.ch"}` | This claim represents the primary domain of the organization the user belongs to. |
|
||||
| urn:zitadel:iam:org:project:roles | `{"urn:zitadel:iam:org:project:roles": [ {"user": {"id1": "acme.zitade.ch", "id2": "caos.ch"} } ] }` | When roles are asserted, ZITADEL does this by providing the `id` and `primaryDomain` below the role. This gives you the option to check in which organization a user has the role on the current project (where your client belongs to). |
|
||||
| urn:zitadel:iam:org:project:{projectid}:roles | `{"urn:zitadel:iam:org:project:id3:roles": [ {"user": {"id1": "acme.zitade.ch", "id2": "caos.ch"} } ] }` | When roles are asserted, ZITADEL does this by providing the `id` and `primaryDomain` below the role. This gives you the option to check in which organization a user has the role on a specific project. |
|
||||
| urn:zitadel:iam:roles:{rolename} | TBA | TBA |
|
||||
| urn:zitadel:iam:org:project:\{projectid}:roles | `{"urn:zitadel:iam:org:project:id3:roles": [ {"user": {"id1": "acme.zitade.ch", "id2": "caos.ch"} } ] }` | When roles are asserted, ZITADEL does this by providing the `id` and `primaryDomain` below the role. This gives you the option to check in which organization a user has the role on a specific project. |
|
||||
| urn:zitadel:iam:roles:\{rolename} | TBA | TBA |
|
||||
| urn:zitadel:iam:user:metadata | `{"urn:zitadel:iam:user:metadata": [ {"key": "VmFsdWU=" } ] }` | The metadata claim will include all metadata of a user. The values are base64 encoded. |
|
||||
| urn:zitadel:iam:user:resourceowner:id | `{"urn:zitadel:iam:user:resourceowner:id": "orgid"}` | This claim represents the id of the resource owner organisation of the user. |
|
||||
| urn:zitadel:iam:user:resourceowner:name | `{"urn:zitadel:iam:user:resourceowner:name": "ACME"}` | This claim represents the name of the resource owner organisation of the user. |
|
||||
|
@ -12,13 +12,13 @@ import TokenExchangeTypes from "./_token_exchange_types.mdx";
|
||||
## OpenID Connect 1.0 Discovery
|
||||
|
||||
The OpenID Connect Discovery Endpoint is located within the issuer domain.
|
||||
This would give us {your_domain}/.well-known/openid-configuration.
|
||||
This would give us `{your_domain}/.well-known/openid-configuration`.
|
||||
|
||||
**Link to spec.** [OpenID Connect Discovery 1.0 incorporating errata set 1](https://openid.net/specs/openid-connect-discovery-1_0.html)
|
||||
|
||||
## authorization_endpoint
|
||||
|
||||
{your_domain}/oauth/v2/authorize
|
||||
`{your_domain}/oauth/v2/authorize`
|
||||
|
||||
:::note
|
||||
The authorization_endpoint is located with the login page, due to the need of accessing the same cookie domain
|
||||
@ -172,7 +172,7 @@ the error will be display directly to the user on the auth server
|
||||
|
||||
## token_endpoint
|
||||
|
||||
{your_domain}/oauth/v2/token
|
||||
`{your_domain}/oauth/v2/token`
|
||||
|
||||
The token_endpoint will as the name suggests return various tokens (access, id and refresh) depending on the used `grant_type`.
|
||||
When using [`authorization_code`](#authorization-code-grant-code-exchange) flow call this endpoint after receiving the code from the authorization_endpoint.
|
||||
@ -471,7 +471,7 @@ Send a `client_assertion` as JWT for us to validate the signature against the re
|
||||
|
||||
## introspection_endpoint
|
||||
|
||||
{your_domain}/oauth/v2/introspect
|
||||
`{your_domain}/oauth/v2/introspect`
|
||||
|
||||
This endpoint enables clients to validate an `acccess_token`, either opaque or JWT. Unlike client side JWT validation,
|
||||
this endpoint will check if the token is not revoked (by client or logout).
|
||||
@ -554,7 +554,7 @@ If the authorization fails, an HTTP 401 with `invalid_client` will be returned.
|
||||
|
||||
## userinfo_endpoint
|
||||
|
||||
{your_domain}/oidc/v1/userinfo
|
||||
`{your_domain}/oidc/v1/userinfo`
|
||||
|
||||
This endpoint will return information about the authorized user.
|
||||
|
||||
@ -577,7 +577,7 @@ If the token is invalid or expired, an HTTP 401 will be returned.
|
||||
|
||||
## revocation_endpoint
|
||||
|
||||
{your_domain}/oauth/v2/revoke
|
||||
`{your_domain}/oauth/v2/revoke`
|
||||
|
||||
This endpoint enables clients to revoke an `access_token` or `refresh_token` they have been granted.
|
||||
|
||||
@ -649,7 +649,7 @@ curl --request POST \
|
||||
|
||||
## end_session_endpoint
|
||||
|
||||
{your_domain}/oidc/v1/end_session
|
||||
`{your_domain}/oidc/v1/end_session`
|
||||
|
||||
The endpoint has to be opened in the user agent (browser) to terminate the user sessions.
|
||||
|
||||
@ -667,7 +667,7 @@ If both parameters are provided, they must be equal.
|
||||
|
||||
## jwks_uri
|
||||
|
||||
{your_domain}/oauth/v2/keys
|
||||
`{your_domain}/oauth/v2/keys`
|
||||
|
||||
The endpoint returns a JSON Web Key Set (JWKS) containing the public keys that can be used to locally validate JWTs you received from ZITADEL.
|
||||
The alternative would be to validate tokens with the [introspection endpoint](#introspection_endpoint).
|
||||
|
@ -134,21 +134,21 @@ Additional to the standard CRUD methods:
|
||||
- ListAvailableExecutionMethods
|
||||
- ListAvailableExecutionFunctions
|
||||
|
||||
<details><summary>action_service.proto</summary>
|
||||
\<details><summary>action_service.proto</summary>
|
||||
<CodeBlock language="protobuf">{ActionServiceProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
<details><summary>action_target.proto</summary>
|
||||
\<details><summary>action_target.proto</summary>
|
||||
<CodeBlock language="protobuf">{ActionTargetProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
<details><summary>action_execution.proto</summary>
|
||||
\<details><summary>action_execution.proto</summary>
|
||||
<CodeBlock language="protobuf">{ActionExecutionProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
<details><summary>action_query.proto</summary>
|
||||
\<details><summary>action_query.proto</summary>
|
||||
<CodeBlock language="protobuf">{ActionSearchProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
### ZITADELUsers
|
||||
|
||||
@ -163,29 +163,29 @@ Standard CRUD methods
|
||||
- Standard CRUD and methods for all IDPs
|
||||
- Resources have additional properties for reusability capabilities.
|
||||
|
||||
<details><summary>idp_service.proto</summary>
|
||||
\<details><summary>idp_service.proto</summary>
|
||||
<CodeBlock language="protobuf">{IDPServiceProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
<details><summary>idp.proto</summary>
|
||||
\<details><summary>idp.proto</summary>
|
||||
<CodeBlock language="protobuf">{IDPProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
<details><summary>idp_search.proto</summary>
|
||||
\<details><summary>idp_search.proto</summary>
|
||||
<CodeBlock language="protobuf">{IDPSearchProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
<details><summary>idp_gitlab.proto</summary>
|
||||
\<details><summary>idp_gitlab.proto</summary>
|
||||
<CodeBlock language="protobuf">{IDPGitLabProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
<details><summary>object.proto</summary>
|
||||
\<details><summary>object.proto</summary>
|
||||
<CodeBlock language="protobuf">{ObjectProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
<details><summary>resource_object.proto</summary>
|
||||
\<details><summary>resource_object.proto</summary>
|
||||
<CodeBlock language="protobuf">{ResourceObjectProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
### ZITADELInstances
|
||||
|
||||
@ -265,21 +265,21 @@ For a full proto example, have a look at the [ZITADELLanguageSettings service](#
|
||||
|
||||
Default language, restricted languages, supported languages
|
||||
|
||||
<details><summary>language_service.proto</summary>
|
||||
\<details><summary>language_service.proto</summary>
|
||||
<CodeBlock language="protobuf">{LanguageServiceProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
<details><summary>language.proto</summary>
|
||||
\<details><summary>language.proto</summary>
|
||||
<CodeBlock language="protobuf">{LanguageProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
<details><summary>object.proto</summary>
|
||||
\<details><summary>object.proto</summary>
|
||||
<CodeBlock language="protobuf">{ObjectProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
<details><summary>settings_object.proto</summary>
|
||||
\<details><summary>settings_object.proto</summary>
|
||||
<CodeBlock language="protobuf">{SettingsObjectProto}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
### ZITADELTextSettings
|
||||
|
||||
|
@ -45,7 +45,7 @@ Go to your instance settings and then click on the Tab **Events** to open the Ev
|
||||
Since everything that is available in Console can also be called with our APIs, you can access all events and audit data trough our APIs:
|
||||
|
||||
- [Event API Guide](/docs/guides/integrate/zitadel-apis/event-api)
|
||||
- [API Documentation](/docs/category/apis/resources/admin/events)
|
||||
- [API Documentation](/docs/apis/resources/admin/events)
|
||||
|
||||
Access to the API is possible with a [Service User](/docs/guides/integrate/service-users/authenticate-service-users) account, allowing you to integrate the events with your own business logic.
|
||||
|
||||
|
@ -35,6 +35,6 @@ In some use cases, external user grants are a simple way to allow users access t
|
||||
|
||||
## References
|
||||
|
||||
* [API reference for user grants](/docs/category/apis/resources/mgmt/user-grants)
|
||||
* [API reference for user grants](/docs/apis/resources/mgmt/user-grants)
|
||||
* [How to manage user grants through ZITADEL's console](/docs/guides/manage/console/roles#authorizations)
|
||||
* [More about multi-tenancy with ZITADEL](https://zitadel.com/blog/multi-tenancy-with-organizations)
|
||||
|
@ -75,4 +75,4 @@ To address this, ZITADEL provides generic templates that enable connection to vi
|
||||
|
||||
* [Detailed integration guide for many identity providers](/guides/integrate/identity-providers/introduction)
|
||||
* [Setup identity providers with Console](/guides/manage/console/default-settings#identity-providers)
|
||||
* [Configure identity providers with the ZITADEL API](/docs/category/apis/resources/mgmt/identity-providers)
|
||||
* [Configure identity providers with the ZITADEL API](/docs/apis/resources/mgmt/identity-providers)
|
||||
|
@ -25,4 +25,4 @@ Ensure the configuration of application settings is limited to authorized users
|
||||
## References
|
||||
|
||||
- [Configure Applications in the Console](../../guides/manage/console/applications)
|
||||
- [ZITADEL API: Applications](/docs/category/apis/resources/mgmt/applications)
|
||||
- [ZITADEL API: Applications](/docs/apis/resources/mgmt/applications)
|
||||
|
@ -78,5 +78,5 @@ You can use organization metadata or your own business logic to describe a hiera
|
||||
## References
|
||||
|
||||
- [Manage users in the Console](../../guides/manage/console/users)
|
||||
- [ZITADEL APIs: Users](/docs/category/apis/resources/mgmt/users)
|
||||
- [ZITADEL APIs: Users](/docs/apis/resources/mgmt/users)
|
||||
- [User onboarding and registration](/docs/guides/integrate/onboarding)
|
||||
|
@ -21,7 +21,7 @@ We recommend that you use [Authorization Code](/apis/openidoauth/grant-types#aut
|
||||
|
||||
A redirect URL is a URL in your application where ZITADEL redirects the user after they have authenticated. Set your url to the domain the proxy will be deployed to or use the default one `http://127.0.0.1:4180/oauth2/callback`.
|
||||
|
||||
> If you are following along with the sample project you downloaded from our templates, you should set the Allowed Callback URL to <http://localhost:4200/auth/callback>. You will also have to set dev mode to `true` as this will enable unsecure http for the moment.
|
||||
> If you are following along with the sample project you downloaded from our templates, you should set the Allowed Callback URL to `http://localhost:4200/auth/callback`. You will also have to set dev mode to `true` as this will enable unsecure http for the moment.
|
||||
|
||||
If you want to redirect the users back to a route on your application after they have logged out, add an optional redirect in the post redirectURI field.
|
||||
|
||||
|
@ -25,7 +25,7 @@ We recommend that you use [Proof Key for Code Exchange (PKCE)](/apis/openidoauth
|
||||
The Redirect URIs field tells ZITADEL where it's allowed to redirect users after authentication. For development, you can set dev mode to `true` to enable insecure HTTP and redirect to a `localhost` URI.
|
||||
The Post-logout redirect send the users back to a route on your application after they have logged out.
|
||||
|
||||
> If you are following along with the [example](https://github.com/zitadel/zitadel-angular), set the dev mode to `true`, the Redirect URIs to <http://localhost:4200/auth/callback> and Post redirect URI to <http://localhost:4200/signedout>.
|
||||
> If you are following along with the [example](https://github.com/zitadel/zitadel-angular), set the dev mode to `true`, the Redirect URIs to `http://localhost:4200/auth/callback` and Post redirect URI to `http://localhost:4200/signedout`.
|
||||
|
||||
Continue and create the application.
|
||||
|
||||
|
@ -28,7 +28,7 @@ We recommend that you use [Proof Key for Code Exchange (PKCE)](/apis/openidoauth
|
||||
The Redirect URIs field tells ZITADEL where it's allowed to redirect users after authentication. For development, you can set dev mode to `true` to enable insecure HTTP and redirect to a `localhost` URI.
|
||||
The Post-logout redirect send the users back to a route on your application after they have logged out.
|
||||
|
||||
> If you are following along with the [example](https://github.com/zitadel/zitadel-go), set the dev mode to `true`, the Redirect URIs to <http://localhost:8089/auth/callback> and Post-logout redirect URI to <http://localhost:8089/>.
|
||||
> If you are following along with the [example](https://github.com/zitadel/zitadel-go), set the dev mode to `true`, the Redirect URIs to `http://localhost:8089/auth/callback` and Post-logout redirect URI to [http://localhost:8089/](http://localhost:8089/)>.
|
||||
|
||||
![Create app in console - set redirectURI](/img/go/app-create-redirect.png)
|
||||
|
||||
|
@ -162,7 +162,7 @@ java \
|
||||
-jar web/target/web-0.0.2-SNAPSHOT.jar
|
||||
```
|
||||
|
||||
If you then visit on <http://localhost:18080/webapp> you should directly be redirected to your ZITADEL instance.
|
||||
If you then visit on [http://localhost:18080/webapp](http://localhost:18080/webapp) you should directly be redirected to your ZITADEL instance.
|
||||
After login with your existing user you will be presented the profile page:
|
||||
|
||||
![Profile Page](/img/java-spring/app-profile.png)
|
||||
|
@ -22,7 +22,7 @@ First we need to create an organization that holds the Vendor's users, projects
|
||||
|
||||
### Vendor Organization
|
||||
|
||||
Navigate to `https://{YourDomain}.zitadel.cloud/ui/console/orgs` (replace {YourDomain}), and click on the button "New".
|
||||
Navigate to `https://{YourDomain}.zitadel.cloud/ui/console/orgs` (replace \{YourDomain}), and click on the button "New".
|
||||
Toggle the setting "Use your personal account as organization owner".
|
||||
|
||||
Enter the name `Demo-Vendor`, and click "Create". Then click on that organization.
|
||||
|
@ -41,7 +41,7 @@ Select `CODE` in the next step. This makes sure you still get a secret. Note tha
|
||||
|
||||
With the Redirect URIs field, you tell ZITADEL where it is allowed to redirect users to after authentication. For development, you can set dev mode to `true` to enable insecure HTTP and redirect to a `localhost` URI.
|
||||
|
||||
> If you are following along with the [example](https://github.com/zitadel/zitadel-angular), set dev mode to `true` and the Redirect URIs to <http://localhost:3000/api/auth/callback/zitadel>.
|
||||
> If you are following along with the [example](https://github.com/zitadel/zitadel-angular), set dev mode to `true` and the Redirect URIs to `http://localhost:3000/api/auth/callback/zitadel`.
|
||||
|
||||
If you want to redirect the users back to a route on your application after they have logged out, add an optional redirect in the Post Logout URIs field.
|
||||
|
||||
|
@ -18,7 +18,7 @@ Before we can start building our application, we have to do a few configuration
|
||||
|
||||
### Project roles
|
||||
|
||||
The Example expects [user roles](guides/integrate/retrieve-user-roles) to be returned after login.
|
||||
The Example expects [user roles](/docs/guides/integrate/retrieve-user-roles) to be returned after login.
|
||||
Symfony uses `ROLE_USER` format.
|
||||
The application will take care of upper-casing and prefixing for us.
|
||||
Inside ZITADEL, you can use regular lower-case role names without prefixes, if you prefer.
|
||||
|
@ -177,5 +177,5 @@ Deciding whether to configure an external Identity Provider (IdP) at the organiz
|
||||
## References
|
||||
|
||||
- [Identity brokering in ZITADEL](/docs/concepts/features/identity-brokering)
|
||||
- [The ZITADEL API reference for managing external IdPs](/docs/category/apis/resources/admin/identity-providers)
|
||||
- [Handle external logins in a custom login UI](/docs/guides/integrate/login-ui/external-login)
|
||||
- [The ZITADEL API reference for managing external IdPs](/docs/apis/resources/admin/identity-providers)
|
||||
- [Handle external logins in a custom login UI](/docs/guides/integrate/login-ui/external-login)
|
||||
|
@ -26,7 +26,7 @@ You will have to paste it in the Keycloak Client later.
|
||||
|
||||
### Register a new client
|
||||
|
||||
1. Login to your Keycloak account and go to the clients list: <$KEYCLOAK-DOMAIN/auth/admin/$REALM/console/#/$REALM/clients>
|
||||
1. Login to your Keycloak account and go to the clients list: `$KEYCLOAK-DOMAIN/auth/admin/$REALM/console/#/$REALM/clients`
|
||||
2. Click on "Create Client"
|
||||
3. Choose OpenID Connect as Client Type and give your client an ID
|
||||
4. Enable Client authentication and the standard flow and direct access grants as authentication flow
|
||||
|
@ -23,7 +23,7 @@ import TestSetup from './_test_setup.mdx';
|
||||
2. Add your App Name, your Company Page and a Logo
|
||||
3. Add "Sign In with LinkedIn using OpenID Connect" by clicking "Request access"
|
||||
4. Go to the Auth Settings of the App and add the following URL to the "Authorized redirect URLs"
|
||||
- {your-domain}/ui/login/login/externalidp/callback
|
||||
- `{your_domain}/ui/login/login/externalidp/callback`
|
||||
- Example redirect url for the domain `https://acme.zitadel.cloud` would look like this: `https://acme.zitadel.cloud/ui/login/login/externalidp/callback`
|
||||
5. Verify the app as your company
|
||||
6. In the Auth - OAuth 2.0 scopes section you should see `openid`, `profile` and `email` listed
|
||||
|
@ -44,7 +44,7 @@ All the necessary configuration is contained in the metadata which has to be exc
|
||||
### Download metadata
|
||||
|
||||
Normally, you would need to download the ServiceProvider metadata from ZITADEL to upload to the IdentityProvider.
|
||||
They are available under [https://{CUSTOMDOMAIN}/idps/{ID of the provider in ZITADEL}/saml/metadata], but this step can be skipped due to the fact that MockSAML is only for testing purposes.
|
||||
They are available under `https://${CUSTOMDOMAIN}/idps/\{ID of the provider in ZITADEL}/saml/metadata`, but this step can be skipped due to the fact that MockSAML is only for testing purposes.
|
||||
|
||||
### Activate IdP
|
||||
|
||||
|
@ -27,7 +27,7 @@ You will have to paste it in the OKTA application later.
|
||||
|
||||
### Register a new client
|
||||
|
||||
1. Login to your OKTA Account and go to the applications list: <OKTA-DOMAIN/admin/apps/active>
|
||||
1. Login to your OKTA Account and go to the applications list: `OKTA-DOMAIN/admin/apps/active^
|
||||
2. Click on "Create App Integration" and choose "OIDC - OpenID Connect"
|
||||
3. Choose Web application as Application type and give a name
|
||||
4. [Paste the ZITADEL Callback URL you copied before](#open-the-generic-oidc-provider-template) to the Sign-in redirect URIs
|
||||
@ -65,7 +65,6 @@ ZITADEL ensures that at least the `openid`-scope is always sent.
|
||||
|
||||
<TestSetup loginscreen="your OKTA login"/>
|
||||
|
||||
<!-- TODO: Image highlights Google -->
|
||||
![OKTA Button](/img/guides/zitadel_login_okta.png)
|
||||
|
||||
![OKTA Login](/img/guides/okta_login.png)
|
||||
|
@ -41,7 +41,7 @@ After you created the SAML SP in ZITADEL, you can copy the URLs you need to conf
|
||||
|
||||
### Register a new client
|
||||
|
||||
1. Log in to your OKTA Account and go to the applications list: <OKTA-DOMAIN/admin/apps/active>
|
||||
1. Log in to your OKTA Account and go to the applications list: `OKTA-DOMAIN/admin/apps/active`
|
||||
2. Click on "Create App Integration" and choose "SAML 2.0"
|
||||
3. Give the application a name
|
||||
4. Click on the ZITADEL URLs that your SAML IDP shows since you created it in ZITADEL and paste them accordingly:
|
||||
|
@ -26,7 +26,7 @@ ZITADEL does have multiple possibilities to achieve that process.
|
||||
## Onboard customers through the ZITADEL Management Console
|
||||
|
||||
Each ZITADEL instance does automatically bring a management console with it. The [console](/docs/guides/manage/console/overview) can be used to manage all your resources through a UI.
|
||||
You can access it by calling the following URL: $CUSTOM-DOMAIN/ui/
|
||||
You can access it by calling the following URL: `${CUSTOM-DOMAIN}/ui/`
|
||||
Make sure that your admins have a [Manager role](/docs/guides/manage/console/managers) with permissions on an instance level such as "INSTANCE_OWNER"
|
||||
|
||||
### Create a customer
|
||||
@ -139,7 +139,7 @@ If you want to start automating the process of onboarding your customers the fol
|
||||
### Built-in register organization form
|
||||
|
||||
A basic form that allows a customer to enter an organization name and a user account is hosted on the following URL:
|
||||
{custom-domain}/ui/login/register/org
|
||||
`{custom-domain}/ui/login/register/org`
|
||||
|
||||
When a user registers through this form, an organization and a user are created.
|
||||
The user will automatically get the role "ORG_OWNER" withing ZITADEL and is able to manage the whole organization.
|
||||
|
@ -18,8 +18,8 @@ Follow the links below to assign roles to your users.
|
||||
|
||||
- [Add application roles via the ZITADEL Console](/docs/guides/manage/console/roles)
|
||||
- [Add manager roles via the ZITADEL Console](/docs/guides/manage/console/managers)
|
||||
- [Add application roles via the ZITADEL Management API](/docs/category/apis/resources/mgmt/project-roles)
|
||||
- [Add manager roles to users via the ZITADEL Management API](/category/apis/resources/mgmt/members)
|
||||
- [Add application roles via the ZITADEL Management API](/docs/apis/resources/mgmt/project-roles)
|
||||
- [Add manager roles to users via the ZITADEL Management API](/docs/apis/resources/mgmt/members)
|
||||
|
||||
## Retrieve roles
|
||||
|
||||
@ -210,7 +210,7 @@ https://github.com/zitadel/actions/blob/main/examples/custom_roles.js
|
||||
Now we will use the auth API to retrieve roles from a logged in user using the user’s token
|
||||
The base URL is: **https://$ZITADEL_DOMAIN/auth/v1**
|
||||
|
||||
Let’s start with a user who has multiple roles in different organizations in a multi-tenanted set up. You can use the logged in user’s token or the machine user’s token to retrieve the authorizations using the [APIs listed under user authorizations/grants in the auth API](/docs/category/apis/resources/auth/user-authorizations-grants).
|
||||
Let’s start with a user who has multiple roles in different organizations in a multi-tenanted set up. You can use the logged in user’s token or the machine user’s token to retrieve the authorizations using the [APIs listed under user authorizations/grants in the auth API](/docs/apis/resources/auth/user-authorizations-grants).
|
||||
|
||||
**Scope used:** `openid urn:zitadel:iam:org:project:id:zitadel:aud`
|
||||
|
||||
@ -394,7 +394,7 @@ Now we will use the management API to retrieve user roles under an admin user.
|
||||
|
||||
The base URL is: **https://$CUSTOM-DOMAIN/management/v1**
|
||||
|
||||
In [APIs listed under user grants in the management API](/docs/category/apis/resources/mgmt/user-grants), you will see that you can use the management API to retrieve and modify user grants. The two API paths that we are interested in to fetch user roles are given below.
|
||||
In [APIs listed under user grants in the management API](/docs/apis/resources/mgmt/user-grants), you will see that you can use the management API to retrieve and modify user grants. The two API paths that we are interested in to fetch user roles are given below.
|
||||
|
||||
**Scope used:** `openid urn:zitadel:iam:org:project:id:zitadel:aud`
|
||||
|
||||
|
@ -127,7 +127,7 @@ However, [client credentials authentication](./client-credentials.md) might be c
|
||||
## Further resources
|
||||
|
||||
* Read about the [different methods to authenticate service users](./authenticate-service-users)
|
||||
* [Service User API reference](/docs/category/apis/resources/mgmt/user-machine)
|
||||
* [Service User API reference](/docs/apis/resources/mgmt/user-machine)
|
||||
* [OIDC JWT with private key](/docs/apis/openidoauth/authn-methods#jwt-with-private-key) authentication method reference
|
||||
* [Access ZITADEL APIs](../zitadel-apis/access-zitadel-apis)
|
||||
* Validate access tokens with [token introspection with private key jwt](../token-introspection/private-key-jwt.mdx)
|
||||
|
@ -100,7 +100,7 @@ By following these steps and adhering to security best practices, you can effect
|
||||
## Notes
|
||||
|
||||
* Read about the [different methods to authenticate service users](./authenticate-service-users)
|
||||
* [Service User API reference](/docs/category/apis/resources/mgmt/user-machine)
|
||||
* [Service User API reference](/docs/apis/resources/mgmt/user-machine)
|
||||
* [OIDC client secret basic](/docs/apis/openidoauth/authn-methods#client-secret-basic) authentication method reference
|
||||
* [Access ZITADEL APIs](../zitadel-apis/access-zitadel-apis)
|
||||
* Validate access tokens with [token introspection with basic auth](../token-introspection/basic-auth)
|
||||
|
@ -36,9 +36,9 @@ For Identity Provider select "Other provider" and enter a Directory Name.
|
||||
Follow the wizard.
|
||||
Fill in the following information:
|
||||
|
||||
- `Identity provider Entity ID`: {your_instance_domain}/saml/v2/metadata
|
||||
- `Identity provider SSO URL`: {your_instance_domain}/saml/v2/SSO
|
||||
- `Public x509 certificate`: You need to download and paste the value of the certificate from {your_instance_domain}/saml/v2/certificate
|
||||
- `Identity provider Entity ID`: \{your_instance_domain}/saml/v2/metadata
|
||||
- `Identity provider SSO URL`: \{your_instance_domain}/saml/v2/SSO
|
||||
- `Public x509 certificate`: You need to download and paste the value of the certificate from \{your_instance_domain}/saml/v2/certificate
|
||||
|
||||
![Add SAML details](/img/saml/atlassian/atlassian-03.png)
|
||||
|
||||
|
@ -42,7 +42,7 @@ This includes:
|
||||
All the information is filled out as an example, and to connect with any other environment you only have to change the
|
||||
used domain, for example "example.com" with "zitadel.cloud".
|
||||
Lastly, upload the certificate used to sign the reponses, provided for you under the
|
||||
URL {your_instance_domain}/saml/v2/certificate.
|
||||
URL \{your_instance_domain}/saml/v2/certificate.
|
||||
|
||||
Then just press the button "Create" and the connection on Auth0 is configured.
|
||||
|
||||
|
@ -45,7 +45,7 @@ can connect to ZITADEL as a SAML2 IDP.
|
||||
4. Download the ZITADEL-used certificate to sign the responses, so that AWS can validation the signature.
|
||||
|
||||
You can download the certificate from following
|
||||
URL: {your_instance_domain}/saml/v2/certificate
|
||||
URL: \{your_instance_domain}/saml/v2/certificate
|
||||
|
||||
5. Then upload the ".crt"-file to AWS and click "next".
|
||||
|
||||
|
@ -30,7 +30,7 @@ Cloudflare will return an error "User email was not returned. API permissions ar
|
||||
|
||||
1. On the Cloudflare dashboard go to Zero Trust, click settings, and then select "Authentication"
|
||||
2. Add a new login method with the type "OpenID Connect"
|
||||
3. Fill in the required information. Check the discovery endpoint of your instance (https://{your_domain}/.well-known/openid-configuration) for the urls. As mentioned in the Cloudflare docs the Certificate Url is jwks_uri.
|
||||
3. Fill in the required information. Check the discovery endpoint of your instance `https://{your_domain}/.well-known/openid-configuration` for the urls. As mentioned in the Cloudflare docs the Certificate Url is jwks_uri.
|
||||
4. Disable PKCE (Cloudflare requires a client secret for PKCE, which is currently not supported)
|
||||
5. Add the following claims: "openid", "profile", "email"
|
||||
6. Test the connection
|
||||
|
@ -52,7 +52,7 @@ Check your application, if everything is correct, press "Create".
|
||||
Complete the configuration as follows:
|
||||
|
||||
- `Identity provider single sign-on URL`: $CUSTOM-DOMAIN/saml/v2/SSO
|
||||
- `Certificate fingerprint`: You need to download the certificate from {your_instance_domain}/saml/v2/certificate and create a SHA1 fingerprint
|
||||
- `Certificate fingerprint`: You need to download the certificate from \{your_instance_domain}/saml/v2/certificate and create a SHA1 fingerprint
|
||||
|
||||
Save the changes.
|
||||
|
||||
|
@ -68,8 +68,8 @@ Create a new .xml file with the following minimal SAML metadata contents:
|
||||
|
||||
Set or replace the variables with the values from the next screen as follows:
|
||||
|
||||
- `${ENTITYID}`: google.com/a/<your_domain>
|
||||
- `${ACSURL}`: https://www.google.com/a/<your_domain>/acs
|
||||
- `${ENTITYID}`: `google.com/a/<your_domain>`
|
||||
- `${ACSURL}`: `https://www.google.com/a/<your_domain>/acs`
|
||||
|
||||
`<your_domain>` is the domain you have verified in Google Workspace.
|
||||
|
||||
@ -103,7 +103,7 @@ Save the settings.
|
||||
|
||||
Now you should be all set to verify your setup:
|
||||
|
||||
- Open Gmail in an incognito session with the following link: https://mail.google.com/a/<your_domain>
|
||||
- Open Gmail in an incognito session with the following link: `https://mail.google.com/a/<your_domain>`
|
||||
- Enter your username and credentials
|
||||
- You should be redirected to Gmail and logged in
|
||||
|
||||
@ -165,8 +165,8 @@ Create a new .xml file with the following minimal SAML metadata contents:
|
||||
|
||||
Set or replace the variables with the values from the next screen as follows:
|
||||
|
||||
- `${ENTITYID}`: https://accounts.google.com/samlrp/metadata?rpid=<your_value>
|
||||
- `${ACSURL}`: https://accounts.google.com/samlrp/acs?rpid=<your_value>
|
||||
- `${ENTITYID}`: `https://accounts.google.com/samlrp/metadata?rpid=<your_value>`
|
||||
- `${ACSURL}`: `https://accounts.google.com/samlrp/acs?rpid=<your_value>`
|
||||
|
||||
Replace `<your_value>` with the values from the [SSO profile](#entity-id-and-acs-url).
|
||||
|
||||
@ -200,7 +200,7 @@ Save the settings.
|
||||
|
||||
Now you should be all set to verify your setup:
|
||||
|
||||
- Open Gmail in an incognito session with the following link: https://mail.google.com/a/<your_domain>
|
||||
- Open Gmail in an incognito session with the following link: `https://mail.google.com/a/<your_domain>`
|
||||
- Enter your username and credentials
|
||||
- You should be redirected to Gmail and logged in
|
||||
|
||||
|
@ -40,7 +40,7 @@ With the following parameters
|
||||
|
||||
| Parameter | Description | Example value|
|
||||
|---|---|---|
|
||||
| OIDCProviderMetadataURL | Is the url to the discovery endpoint, which is typically located at {your-domain}/.well-known/openid-configuration| https://<your_domain>.zitadel.cloud/.well-known/openid-configuration |
|
||||
| OIDCProviderMetadataURL | Is the url to the discovery endpoint, which is typically located at `{your-domain}/.well-known/openid-configuration`| `https://<your_domain>.zitadel.cloud/.well-known/openid-configuration` |
|
||||
| OIDCClientID | Is the ID of the zitadel application. You can find it on the settings page of the application. | 123456789123@apache_test |
|
||||
| OIDCRedirectURI | Users will be redirected to this page after successful login. If you are using localhost or any other non-https endpoint, make sure to enable development mode in ZITADEL. | https://mysecureapp.io/secure/callback |
|
||||
| OIDCCryptoPassphrase | Create a secure passphrase. Consult the module's documentation for more details. | ... |
|
||||
|
@ -107,6 +107,14 @@ To configure your custom SMTP please fill the following fields:
|
||||
|
||||
<img src="/docs/img/guides/console/smtp.png" alt="SMTP" width="800px" />
|
||||
|
||||
While you create/update a SMTP provider you have the chance to test your SMTP configuration
|
||||
|
||||
<img src="/docs/img/guides/console/smtp_test.png" alt="SMTP" width="800px" />
|
||||
|
||||
In the SMTP providers table you can hover on a provider row to show buttons that allow you to activate/deactivate a provider, test your smtp settings and delete a provider
|
||||
|
||||
<img src="/docs/img/guides/console/smtp_table.png" alt="SMTP" width="800px" />
|
||||
|
||||
### SMS
|
||||
|
||||
No default provider is configured to send some SMS to your users. If you like to validate the phone numbers of your users make sure to add your twilio configuration by adding your Sid, Token and Sender Number.
|
||||
@ -139,7 +147,7 @@ The Login Policy defines how the login process should look like and which authen
|
||||
The Default Redirect URI will be used, if a user calls the login page directly.
|
||||
More specifically, typically a client will initiate login with an auth request.
|
||||
The auth request contains a client-id and a redirect uri, that must match the configuration in ZITADEL.
|
||||
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/
|
||||
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:
|
||||
|
||||
@ -148,7 +156,7 @@ Reasons why ZITADEL doesn't have a redirect URI:
|
||||
|
||||
We recommend setting your own default redirect URI, if you do not want end users to access ZITADEL console.
|
||||
|
||||
Change default redirect url of instance: https://<custom_domain>/ui/console/settings?id=login
|
||||
Change default redirect url of instance: `https://<custom_domain>/ui/console/settings?id=login`
|
||||
|
||||
<img
|
||||
src="/docs//img/guides/solution-scenarios/console-default-redirect.png"
|
||||
|
@ -18,7 +18,7 @@ You would have to create roles for administration and your clients in this very
|
||||
|
||||
## Create a project
|
||||
|
||||
To create a project, navigate to your organization, then projects or directly via <https://{your_domain}.zitadel.cloud/ui/console/projects>, and then click the button to create a new project.
|
||||
To create a project, navigate to your organization, then projects or directly via `https://{your_domain}.zitadel.cloud/ui/console/projects`, and then click the button to create a new project.
|
||||
|
||||
<img
|
||||
alt="Empty Project"
|
||||
|
@ -4,7 +4,7 @@ title: Feature Restrictions
|
||||
|
||||
New self-hosted and [ZITADEL Cloud instances](https://zitadel.com/signin) are unrestricted by default.
|
||||
Self-hosters can change this default using the DefaultInstance.Restrictions configuration section.
|
||||
Users with the role IAM_OWNER can change the restrictions of their instance using the [Feature Restrictions Admin API](/category/apis/resources/admin/feature-restrictions).
|
||||
Users with the role IAM_OWNER can change the restrictions of their instance using the [Feature Restrictions Admin API](/apis/resources/admin/feature-restrictions).
|
||||
Currently, the following restrictions are available:
|
||||
|
||||
- *Disallow public organization registrations* - If restricted, only users with the role IAM_OWNERS can create new organizations. The endpoint */ui/login/register/org* returns HTTP status 404 on GET requests, and 409 on POST requests.
|
||||
|
@ -232,4 +232,4 @@ The user that calls the management service must have [manager permissions](/docs
|
||||
A user can be either a human user or a service user.
|
||||
|
||||
You can get [metadata of a user filtered by your query](/docs/apis/resources/mgmt/management-service-list-user-metadata) or [get a metadata object from a user by a specific key](/docs/apis/resources/mgmt/management-service-get-user-metadata).
|
||||
The management service allows you to set and delete metadata, see the [API documentation for users](/docs/category/apis/resources/mgmt/users).
|
||||
The management service allows you to set and delete metadata, see the [API documentation for users](/docs/apis/resources/mgmt/users).
|
||||
|
@ -220,7 +220,7 @@ Use metadata to store additional attributes of the users, such as organizational
|
||||
|
||||
:::info
|
||||
Metadata must be added to users after the users were created. Currently metadata can't be added during user creation.
|
||||
[API reference: User Metadata](/docs/category/apis/resources/mgmt/user-metadata)
|
||||
[API reference: User Metadata](/docs/apis/resources/mgmt/user-metadata)
|
||||
:::
|
||||
|
||||
Request metadata from the userinfo endpoint by passing the required [reserved scope](/docs/apis/openidoauth/scopes#reserved-scopes) in your auth request.
|
||||
@ -232,5 +232,5 @@ You can assign roles from owned or granted projects to a user.
|
||||
|
||||
:::info
|
||||
Authorizations must be added to users after the users were created. Currently metadata can't be added during user creation.
|
||||
[API reference: User Authorization / Grants](/docs/category/apis/resources/auth/user-authorizations-grants)
|
||||
[API reference: User Authorization / Grants](/docs/apis/resources/auth/user-authorizations-grants)
|
||||
:::
|
@ -94,8 +94,8 @@ We use third-party services to provide the website and our offers. An up-to-date
|
||||
|
||||
This website uses external payment service providers through whose platforms users and we can make payment transactions. For example via
|
||||
|
||||
- Stripe (<https://stripe.com/ch/privacy>)
|
||||
- Bexio AG (<https://www.bexio.com/de-CH/datenschutz>)
|
||||
- [Stripe](https://stripe.com/ch/privacy)
|
||||
- [Bexio AG](https://www.bexio.com/de-CH/datenschutz)
|
||||
|
||||
As an alternative, we offer customers the option to pay by invoice instead of using external payment providers. However, this may require a positive credit check in advance.
|
||||
|
||||
|
@ -18,9 +18,9 @@ The setup is tested against Docker version 20.10.17 and Docker Compose version v
|
||||
|
||||
By executing the commands below, you will download the following file:
|
||||
|
||||
<details><summary>docker-compose.yaml</summary>
|
||||
\<details><summary>docker-compose.yaml</summary>
|
||||
<CodeBlock language="yaml">{DockerComposeSource}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
```bash
|
||||
# Download the docker compose example configuration.
|
||||
@ -41,9 +41,9 @@ docker compose up --detach
|
||||
|
||||
By executing the commands below, you will download the following file:
|
||||
|
||||
<details><summary>docker-compose-sa.yaml</summary>
|
||||
\<details><summary>docker-compose-sa.yaml</summary>
|
||||
<CodeBlock language="yaml">{DockerComposeSaSource}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
```bash
|
||||
# Download the docker compose example configuration.
|
||||
|
@ -21,21 +21,21 @@ The setup is tested against Docker version 20.10.17 and Docker Compose version v
|
||||
|
||||
By executing the commands below, you will download the following files:
|
||||
|
||||
<details><summary>docker-compose.yaml</summary>
|
||||
\<details><summary>docker-compose.yaml</summary>
|
||||
<CodeBlock language="yaml">{DockerComposeSource}</CodeBlock>
|
||||
</details>
|
||||
<details><summary>example-traefik.yaml</summary>
|
||||
\</details>
|
||||
\<details><summary>example-traefik.yaml</summary>
|
||||
<CodeBlock language="yaml">{ExampleTraefikSource}</CodeBlock>
|
||||
</details>
|
||||
<details><summary>example-zitadel-config.yaml</summary>
|
||||
\</details>
|
||||
\<details><summary>example-zitadel-config.yaml</summary>
|
||||
<CodeBlock language="yaml">{ExampleZITADELConfigSource}</CodeBlock>
|
||||
</details>
|
||||
<details><summary>example-zitadel-secrets.yaml</summary>
|
||||
\</details>
|
||||
\<details><summary>example-zitadel-secrets.yaml</summary>
|
||||
<CodeBlock language="yaml">{ExampleZITADELSecretsSource}</CodeBlock>
|
||||
</details>
|
||||
<details><summary>example-zitadel-init-steps.yaml</summary>
|
||||
\</details>
|
||||
\<details><summary>example-zitadel-init-steps.yaml</summary>
|
||||
<CodeBlock language="yaml">{ExampleZITADELInitStepsSource}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
```bash
|
||||
# Download the docker compose example configuration.
|
||||
|
@ -8,18 +8,18 @@ The docker compose example mounts the example zitadel configuration files to the
|
||||
|
||||
By executing the commands below, you will download the following files:
|
||||
|
||||
<details><summary>docker-compose.yaml</summary>
|
||||
\<details><summary>docker-compose.yaml</summary>
|
||||
<CodeBlock language="yaml">{DockerComposeSource}</CodeBlock>
|
||||
</details>
|
||||
<details><summary>example-zitadel-config.yaml</summary>
|
||||
\</details>
|
||||
\<details><summary>example-zitadel-config.yaml</summary>
|
||||
<CodeBlock language="yaml">{ExampleZITADELConfigSource}</CodeBlock>
|
||||
</details>
|
||||
<details><summary>example-zitadel-secrets.yaml</summary>
|
||||
\</details>
|
||||
\<details><summary>example-zitadel-secrets.yaml</summary>
|
||||
<CodeBlock language="yaml">{ExampleZITADELSecretsSource}</CodeBlock>
|
||||
</details>
|
||||
<details><summary>example-zitadel-init-steps.yaml</summary>
|
||||
\</details>
|
||||
\<details><summary>example-zitadel-init-steps.yaml</summary>
|
||||
<CodeBlock language="yaml">{ExampleZITADELInitStepsSource}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
```bash
|
||||
# Download the docker compose example configuration.
|
||||
|
@ -9,12 +9,12 @@ For a secure installation with Docker Compose, [go to the loadbalancing example]
|
||||
|
||||
By executing the commands below, you will download the following files:
|
||||
|
||||
<details><summary>example-zitadel-values.yaml</summary>
|
||||
\<details><summary>example-zitadel-values.yaml</summary>
|
||||
<CodeBlock language="yaml">{ExampleZITADELValuesSource}</CodeBlock>
|
||||
</details>
|
||||
<details><summary>example-zitadel-values-secrets.yaml</summary>
|
||||
\</details>
|
||||
\<details><summary>example-zitadel-values-secrets.yaml</summary>
|
||||
<CodeBlock language="yaml">{ExampleZITADELValuesSecretsSource}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
```bash
|
||||
# Download and adjust the example configuration file containing standard configuration
|
||||
|
@ -7,15 +7,15 @@ import ExampleZITADELInitStepsSource from '!!raw-loader!./example-zitadel-init-s
|
||||
|
||||
By executing the commands below, you will download the following files:
|
||||
|
||||
<details><summary>example-zitadel-config.yaml</summary>
|
||||
\<details><summary>example-zitadel-config.yaml</summary>
|
||||
<CodeBlock language="yaml">{ExampleZITADELConfigSource}</CodeBlock>
|
||||
</details>
|
||||
<details><summary>example-zitadel-secrets.yaml</summary>
|
||||
\</details>
|
||||
\<details><summary>example-zitadel-secrets.yaml</summary>
|
||||
<CodeBlock language="yaml">{ExampleZITADELSecretsSource}</CodeBlock>
|
||||
</details>
|
||||
<details><summary>example-zitadel-init-steps.yaml</summary>
|
||||
\</details>
|
||||
\<details><summary>example-zitadel-init-steps.yaml</summary>
|
||||
<CodeBlock language="yaml">{ExampleZITADELInitStepsSource}</CodeBlock>
|
||||
</details>
|
||||
\</details>
|
||||
|
||||
```bash
|
||||
# Download and adjust the example configuration file containing standard configuration
|
||||
|
@ -19,11 +19,9 @@ import Postgres from './_postgres.mdx'
|
||||
>
|
||||
<TabItem value="pg">
|
||||
<Postgres/>
|
||||
<More/>
|
||||
</TabItem>
|
||||
<TabItem value="crdb">
|
||||
<Cockroach/>
|
||||
<More/>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
@ -5,6 +5,7 @@ sidebar_label: Apache httpd
|
||||
|
||||
import ProxyGuideOverview from '../_proxy_guide_overview.mdx';
|
||||
import ProxyGuideTLSMode from '../_proxy_guide_tls_mode.mdx';
|
||||
import ProxyGuideMore from '../_proxy_guide_more.mdx';
|
||||
import Compose from "!!raw-loader!./docker-compose.yaml";
|
||||
import ConfigDisabled from "!!raw-loader!./httpd-disabled-tls.conf";
|
||||
import ConfigExternal from "!!raw-loader!./httpd-external-tls.conf";
|
||||
|
@ -5,6 +5,7 @@ sidebar_label: NGINX
|
||||
|
||||
import ProxyGuideOverview from '../_proxy_guide_overview.mdx';
|
||||
import ProxyGuideTLSMode from '../_proxy_guide_tls_mode.mdx';
|
||||
import ProxyGuideMore from '../_proxy_guide_more.mdx';
|
||||
import Compose from "!!raw-loader!./docker-compose.yaml";
|
||||
import ConfigDisabled from "!!raw-loader!./nginx-disabled-tls.conf";
|
||||
import ConfigExternal from "!!raw-loader!./nginx-external-tls.conf";
|
||||
|
@ -5,6 +5,7 @@ sidebar_label: Traefik
|
||||
|
||||
import ProxyGuideOverview from '../_proxy_guide_overview.mdx';
|
||||
import ProxyGuideTLSMode from '../_proxy_guide_tls_mode.mdx';
|
||||
import ProxyGuideMore from '../_proxy_guide_more.mdx';
|
||||
import Compose from "!!raw-loader!./docker-compose.yaml";
|
||||
import ConfigDisabled from "!!raw-loader!./traefik-disabled-tls.yaml";
|
||||
import ConfigExternal from "!!raw-loader!./traefik-external-tls.yaml";
|
||||
|
@ -9,7 +9,7 @@ The usage control features are currently limited to the instance level only.
|
||||
|
||||
## Block Instances
|
||||
|
||||
You can block an instance using the [system API](/category/apis/resources/system/limits).
|
||||
You can block an instance using the [system API](/apis/resources/system/limits).
|
||||
|
||||
Most requests to a blocked instance are rejected with the HTTP status *429 Too Many Requests* or the gRPC status *8 Resource Exhausted*.
|
||||
However, requests to the [system API](/apis/introduction#system) are still allowed.
|
||||
@ -55,7 +55,7 @@ DefaultInstance:
|
||||
AuditLogRetention: # ZITADEL_DEFAULTINSTANCE_LIMITS_AUDITLOGRETENTION
|
||||
```
|
||||
|
||||
You can also set a limit for [a specific virtual instance](/concepts/structure/instance#multiple-virtual-instances) using the [system API](/category/apis/resources/system/limits).
|
||||
You can also set a limit for [a specific virtual instance](/concepts/structure/instance#multiple-virtual-instances) using the [system API](/apis/resources/system/limits).
|
||||
|
||||
## Quotas
|
||||
|
||||
@ -85,7 +85,7 @@ Quotas:
|
||||
MaxBulkSize: 0 # ZITADEL_QUOTAS_EXECUTION_DEBOUNCE_MAXBULKSIZE
|
||||
```
|
||||
|
||||
Once you have activated the quotas feature, you can configure quotas [for your virtual instances](/concepts/structure/instance#multiple-virtual-instances) using the [system API](/category/apis/resources/system/quotas) or the *DefaultInstances.Quotas* section.
|
||||
Once you have activated the quotas feature, you can configure quotas [for your virtual instances](/concepts/structure/instance#multiple-virtual-instances) using the [system API](/apis/resources/system/quotas) or the *DefaultInstances.Quotas* section.
|
||||
The following snippets shows the defaults:
|
||||
|
||||
```yaml
|
||||
|
@ -4,8 +4,9 @@ module.exports = {
|
||||
trailingSlash: false,
|
||||
url: "https://zitadel.com",
|
||||
baseUrl: "/docs",
|
||||
onBrokenLinks: "throw",
|
||||
onBrokenMarkdownLinks: "warn",
|
||||
onBrokenLinks: "warn",
|
||||
onBrokenAnchors: "warn",
|
||||
onBrokenMarkdownLinks: "throw",
|
||||
favicon: "img/favicon.ico",
|
||||
organizationName: "zitadel",
|
||||
projectName: "zitadel",
|
||||
@ -203,6 +204,11 @@ module.exports = {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
},
|
||||
},
|
||||
target: 'es2017',
|
||||
},
|
||||
module: {
|
||||
@ -223,7 +229,7 @@ module.exports = {
|
||||
showLastUpdateTime: true,
|
||||
editUrl: "https://github.com/zitadel/zitadel/edit/main/docs/",
|
||||
remarkPlugins: [require("mdx-mermaid")],
|
||||
docLayoutComponent: "@theme/DocPage",
|
||||
|
||||
docItemComponent: '@theme/ApiItem'
|
||||
},
|
||||
theme: {
|
||||
@ -245,6 +251,7 @@ module.exports = {
|
||||
outputDir: "docs/apis/resources/auth",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
categoryLinkSource: "tag",
|
||||
},
|
||||
},
|
||||
mgmt: {
|
||||
@ -252,6 +259,7 @@ module.exports = {
|
||||
outputDir: "docs/apis/resources/mgmt",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
categoryLinkSource: "tag",
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
@ -259,6 +267,7 @@ module.exports = {
|
||||
outputDir: "docs/apis/resources/admin",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
categoryLinkSource: "tag",
|
||||
},
|
||||
},
|
||||
system: {
|
||||
@ -266,6 +275,7 @@ module.exports = {
|
||||
outputDir: "docs/apis/resources/system",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
categoryLinkSource: "tag",
|
||||
},
|
||||
},
|
||||
user: {
|
||||
@ -273,6 +283,7 @@ module.exports = {
|
||||
outputDir: "docs/apis/resources/user_service",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
categoryLinkSource: "tag",
|
||||
},
|
||||
},
|
||||
session: {
|
||||
@ -280,6 +291,7 @@ module.exports = {
|
||||
outputDir: "docs/apis/resources/session_service",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
categoryLinkSource: "tag",
|
||||
},
|
||||
},
|
||||
oidc: {
|
||||
@ -287,6 +299,7 @@ module.exports = {
|
||||
outputDir: "docs/apis/resources/oidc_service",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
categoryLinkSource: "tag",
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
@ -294,6 +307,7 @@ module.exports = {
|
||||
outputDir: "docs/apis/resources/settings_service",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
categoryLinkSource: "tag",
|
||||
},
|
||||
},
|
||||
user_schema: {
|
||||
@ -301,6 +315,7 @@ module.exports = {
|
||||
outputDir: "docs/apis/resources/user_schema_service_v3",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
categoryLinkSource: "tag",
|
||||
},
|
||||
},
|
||||
user_v3: {
|
||||
@ -308,6 +323,7 @@ module.exports = {
|
||||
outputDir: "docs/apis/resources/user_service_v3",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
categoryLinkSource: "tag",
|
||||
},
|
||||
},
|
||||
action_v3: {
|
||||
@ -315,6 +331,7 @@ module.exports = {
|
||||
outputDir: "docs/apis/resources/action_service_v3",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
categoryLinkSource: "tag",
|
||||
},
|
||||
},
|
||||
feature_v2: {
|
||||
@ -322,6 +339,7 @@ module.exports = {
|
||||
outputDir: "docs/apis/resources/feature_service_v2",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
categoryLinkSource: "tag",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -5,39 +5,45 @@
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
"start": "docusaurus start",
|
||||
"start:api": "yarn generate && docusaurus start",
|
||||
"build": "yarn generate && docusaurus build",
|
||||
"start:api": "yarn run generate && docusaurus start",
|
||||
"build": "yarn run generate && docusaurus build",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
"deploy": "docusaurus deploy",
|
||||
"clear": "docusaurus clear",
|
||||
"serve": "docusaurus serve",
|
||||
"write-translations": "docusaurus write-translations",
|
||||
"write-heading-ids": "docusaurus write-heading-ids",
|
||||
"generate": "yarn generate:grpc && yarn generate:apidocs && yarn generate:configdocs",
|
||||
"generate": "yarn run generate:grpc && yarn run generate:apidocs && yarn run generate:configdocs",
|
||||
"generate:grpc": "buf generate ../proto",
|
||||
"generate:apidocs": "docusaurus clean-api-docs all && docusaurus gen-api-docs all",
|
||||
"generate:configdocs": "cp -r ../cmd/defaults.yaml ./docs/self-hosting/manage/configure/ && cp -r ../cmd/setup/steps.yaml ./docs/self-hosting/manage/configure/"
|
||||
"generate:apidocs": "docusaurus gen-api-docs all",
|
||||
"generate:configdocs": "cp -r ../cmd/defaults.yaml ./docs/self-hosting/manage/configure/ && cp -r ../cmd/setup/steps.yaml ./docs/self-hosting/manage/configure/",
|
||||
"generate:re-gen": "yarn clean-all && yarn gen-all",
|
||||
"generate:clean-all": "docusaurus clean-api-docs all"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bufbuild/buf": "^1.14.0",
|
||||
"@docusaurus/core": "2.2.0",
|
||||
"@docusaurus/preset-classic": "2.2.0",
|
||||
"@docusaurus/theme-search-algolia": "2.2.0",
|
||||
"@docusaurus/core": "3.4.0",
|
||||
"@docusaurus/preset-classic": "3.4.0",
|
||||
"@docusaurus/theme-mermaid": "3.4.0",
|
||||
"@docusaurus/theme-search-algolia": "3.4.0",
|
||||
"@headlessui/react": "^1.7.4",
|
||||
"@heroicons/react": "^2.0.13",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"@saucelabs/theme-github-codeblock": "^0.2.3",
|
||||
"@swc/core": "^1.3.74",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"clsx": "^1.2.1",
|
||||
"docusaurus-plugin-image-zoom": "^1.0.1",
|
||||
"docusaurus-plugin-openapi-docs": "^1.7.3",
|
||||
"docusaurus-theme-openapi-docs": "^1.7.3",
|
||||
"mdx-mermaid": "^1.1.0",
|
||||
"docusaurus-plugin-openapi-docs": "3.0.0-beta.10",
|
||||
"docusaurus-theme-openapi-docs": "3.0.0-beta.10",
|
||||
"mdx-mermaid": "^2.0.0",
|
||||
"mermaid": "^10.9.1",
|
||||
"postcss": "^8.4.31",
|
||||
"prism-react-renderer": "^2.1.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react": "17.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "17.0.2",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-player": "^2.15.1",
|
||||
"sitemap": "7.1.1",
|
||||
"swc-loader": "^0.2.3",
|
||||
@ -56,7 +62,9 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "2.2.0",
|
||||
"@docusaurus/module-type-aliases": "3.4.0",
|
||||
"@docusaurus/types": "3.4.0",
|
||||
"tailwindcss": "^3.2.4"
|
||||
}
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
|
@ -585,7 +585,7 @@ module.exports = {
|
||||
description:
|
||||
"The authentication API (aka Auth API) is used for all operations on the currently logged in user. The user id is taken from the sub claim in the token.",
|
||||
},
|
||||
items: require("./docs/apis/resources/auth/sidebar.js"),
|
||||
items: require("./docs/apis/resources/auth/sidebar.ts"),
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
@ -597,7 +597,7 @@ module.exports = {
|
||||
description:
|
||||
"The management API is as the name states the interface where systems can mutate IAM objects like, organizations, projects, clients, users and so on if they have the necessary access rights. To identify the current organization you can send a header x-zitadel-orgid or if no header is set, the organization of the authenticated user is set.",
|
||||
},
|
||||
items: require("./docs/apis/resources/mgmt/sidebar.js"),
|
||||
items: require("./docs/apis/resources/mgmt/sidebar.ts"),
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
@ -609,7 +609,7 @@ module.exports = {
|
||||
description:
|
||||
"This API is intended to configure and manage one ZITADEL instance itself.",
|
||||
},
|
||||
items: require("./docs/apis/resources/admin/sidebar.js"),
|
||||
items: require("./docs/apis/resources/admin/sidebar.ts"),
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
@ -623,7 +623,7 @@ module.exports = {
|
||||
"\n" +
|
||||
"Checkout the guide how to access the ZITADEL System API.",
|
||||
},
|
||||
items: require("./docs/apis/resources/system/sidebar.js"),
|
||||
items: require("./docs/apis/resources/system/sidebar.ts"),
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -648,7 +648,7 @@ module.exports = {
|
||||
"\n" +
|
||||
"This project is in beta state. It can AND will continue breaking until the services provide the same functionality as the current login.",
|
||||
},
|
||||
items: require("./docs/apis/resources/user_service/sidebar.js"),
|
||||
items: require("./docs/apis/resources/user_service/sidebar.ts"),
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
@ -662,7 +662,7 @@ module.exports = {
|
||||
"\n" +
|
||||
"This project is in beta state. It can AND will continue breaking until the services provide the same functionality as the current login.",
|
||||
},
|
||||
items: require("./docs/apis/resources/session_service/sidebar.js"),
|
||||
items: require("./docs/apis/resources/session_service/sidebar.ts"),
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
@ -676,7 +676,7 @@ module.exports = {
|
||||
"\n" +
|
||||
"This project is in beta state. It can AND will continue breaking until the services provide the same functionality as the current login.",
|
||||
},
|
||||
items: require("./docs/apis/resources/oidc_service/sidebar.js"),
|
||||
items: require("./docs/apis/resources/oidc_service/sidebar.ts"),
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
@ -690,7 +690,7 @@ module.exports = {
|
||||
"\n" +
|
||||
"This project is in beta state. It can AND will continue to break until the services provide the same functionality as the current login.",
|
||||
},
|
||||
items: require("./docs/apis/resources/settings_service/sidebar.js"),
|
||||
items: require("./docs/apis/resources/settings_service/sidebar.ts"),
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
@ -704,7 +704,7 @@ module.exports = {
|
||||
"\n" +
|
||||
"This project is in beta state. It can AND will continue breaking until a stable version is released.",
|
||||
},
|
||||
items: require("./docs/apis/resources/feature_service_v2/sidebar.js"),
|
||||
items: require("./docs/apis/resources/feature_service_v2/sidebar.ts"),
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -729,7 +729,7 @@ module.exports = {
|
||||
"\n" +
|
||||
"This project is in Preview state. It can AND will continue breaking until the service provides the same functionality as the v1 and v2 user services.",
|
||||
},
|
||||
items: require("./docs/apis/resources/user_schema_service_v3/sidebar.js"),
|
||||
items: require("./docs/apis/resources/user_schema_service_v3/sidebar.ts"),
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
@ -743,7 +743,7 @@ module.exports = {
|
||||
"\n" +
|
||||
"This project is in Preview state. It can AND will continue breaking until the service provides the same functionality as the v1 and v2 user services.",
|
||||
},
|
||||
items: require("./docs/apis/resources/user_service_v3/sidebar.js"),
|
||||
items: require("./docs/apis/resources/user_service_v3/sidebar.ts"),
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
@ -757,7 +757,7 @@ module.exports = {
|
||||
"\n" +
|
||||
"This project is in Preview state. It can AND will continue breaking until the services provide the same functionality as the current actions.",
|
||||
},
|
||||
items: require("./docs/apis/resources/action_service_v3/sidebar.js"),
|
||||
items: require("./docs/apis/resources/action_service_v3/sidebar.ts"),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
BIN
docs/static/img/guides/console/smtp_table.png
vendored
Normal file
BIN
docs/static/img/guides/console/smtp_table.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
BIN
docs/static/img/guides/console/smtp_test.png
vendored
Normal file
BIN
docs/static/img/guides/console/smtp_test.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
@ -19,6 +19,7 @@
|
||||
],
|
||||
"redirects": [
|
||||
{ "source": "/", "destination": "/docs" },
|
||||
{ "source": "/docs/category/apis/:slug*", "destination": "/docs/apis/:slug*", "permanent": true },
|
||||
{ "source": "/docs/apis/mgmt/:slug*", "destination": "/docs/apis/resources/mgmt/:slug*", "permanent": true },
|
||||
{ "source": "/docs/apis/auth/:slug*", "destination": "/docs/apis/resources/auth/:slug*", "permanent": true },
|
||||
{ "source": "/docs/apis/system/:slug*", "destination": "/docs/apis/resources/system/:slug*", "permanent": true },
|
||||
|
11404
docs/yarn.lock
11404
docs/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -26,10 +26,11 @@ describe('instance notifications', () => {
|
||||
cy.get('[formcontrolname="hostAndPort"]').should('have.value', 'smtp.mailgun.org: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('[data-e2e="continue-to-2nd-form"]').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="continue-button"]').click();
|
||||
cy.get('[data-e2e="create-button"]').click();
|
||||
cy.shouldConfirmSuccess();
|
||||
cy.get('[data-e2e="close-button"]').click();
|
||||
@ -44,12 +45,13 @@ describe('instance notifications', () => {
|
||||
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('[data-e2e="continue-to-2nd-form"]').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="continue-button"]').click();
|
||||
cy.get('[data-e2e="create-button"]').click();
|
||||
cy.shouldConfirmSuccess();
|
||||
cy.get('[data-e2e="close-button"]').click();
|
||||
@ -77,10 +79,11 @@ describe('instance notifications', () => {
|
||||
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('[data-e2e="continue-to-2nd-form"]').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="continue-button"]').click();
|
||||
cy.get('[data-e2e="create-button"]').click();
|
||||
cy.shouldConfirmSuccess();
|
||||
cy.get('[data-e2e="close-button"]').click();
|
||||
@ -140,10 +143,11 @@ describe('instance notifications', () => {
|
||||
cy.get('[formcontrolname="hostAndPort"]').should('have.value', 'smtp.mailgun.org: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('[data-e2e="continue-to-2nd-form"]').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="continue-button"]').click();
|
||||
cy.get('[data-e2e="create-button"]').click();
|
||||
cy.shouldConfirmSuccess();
|
||||
cy.get('[data-e2e="activate-button"]').click();
|
||||
@ -159,6 +163,7 @@ describe('instance notifications', () => {
|
||||
let rowSelector = `tr:contains('mailgun')`;
|
||||
cy.visit(smtpPath);
|
||||
cy.get(rowSelector).click();
|
||||
cy.get('[data-e2e="continue-to-2nd-form"]').click();
|
||||
cy.get('[data-e2e="continue-button"]').click();
|
||||
cy.get('[data-e2e="create-button"]').click();
|
||||
cy.shouldConfirmSuccess();
|
||||
|
2
go.mod
2
go.mod
@ -179,7 +179,7 @@ require (
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762 // indirect
|
||||
github.com/muesli/kmeans v0.3.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.54.0 // indirect
|
||||
|
492
internal/api/grpc/admin/import_integration_test.go
Normal file
492
internal/api/grpc/admin/import_integration_test.go
Normal file
@ -0,0 +1,492 @@
|
||||
//go:build integration
|
||||
|
||||
package admin_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/management"
|
||||
v1 "github.com/zitadel/zitadel/pkg/grpc/v1"
|
||||
)
|
||||
|
||||
func TestServer_ImportData(t *testing.T) {
|
||||
orgIDs := generateIDs(10)
|
||||
projectIDs := generateIDs(10)
|
||||
userIDs := generateIDs(10)
|
||||
grantIDs := generateIDs(10)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *admin.ImportDataRequest
|
||||
want *admin.ImportDataResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
req: &admin.ImportDataRequest{
|
||||
Data: &admin.ImportDataRequest_DataOrgs{
|
||||
DataOrgs: &admin.ImportDataOrg{
|
||||
Orgs: []*admin.DataOrg{
|
||||
{
|
||||
OrgId: orgIDs[0],
|
||||
Org: &management.AddOrgRequest{
|
||||
Name: gofakeit.ProductName(),
|
||||
},
|
||||
Projects: []*v1.DataProject{
|
||||
{
|
||||
ProjectId: projectIDs[0],
|
||||
Project: &management.AddProjectRequest{
|
||||
Name: gofakeit.AppName(),
|
||||
ProjectRoleAssertion: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
ProjectId: projectIDs[1],
|
||||
Project: &management.AddProjectRequest{
|
||||
Name: gofakeit.AppName(),
|
||||
ProjectRoleAssertion: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
ProjectRoles: []*management.AddProjectRoleRequest{
|
||||
{
|
||||
ProjectId: projectIDs[0],
|
||||
RoleKey: "role1",
|
||||
DisplayName: "role1",
|
||||
},
|
||||
{
|
||||
ProjectId: projectIDs[0],
|
||||
RoleKey: "role2",
|
||||
DisplayName: "role2",
|
||||
},
|
||||
{
|
||||
ProjectId: projectIDs[1],
|
||||
RoleKey: "role3",
|
||||
DisplayName: "role3",
|
||||
},
|
||||
{
|
||||
ProjectId: projectIDs[1],
|
||||
RoleKey: "role4",
|
||||
DisplayName: "role4",
|
||||
},
|
||||
},
|
||||
HumanUsers: []*v1.DataHumanUser{
|
||||
{
|
||||
UserId: userIDs[0],
|
||||
User: &management.ImportHumanUserRequest{
|
||||
UserName: gofakeit.Username(),
|
||||
Profile: &management.ImportHumanUserRequest_Profile{
|
||||
FirstName: gofakeit.FirstName(),
|
||||
LastName: gofakeit.LastName(),
|
||||
DisplayName: gofakeit.Username(),
|
||||
PreferredLanguage: gofakeit.LanguageBCP(),
|
||||
},
|
||||
Email: &management.ImportHumanUserRequest_Email{
|
||||
Email: gofakeit.Email(),
|
||||
IsEmailVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
UserId: userIDs[1],
|
||||
User: &management.ImportHumanUserRequest{
|
||||
UserName: gofakeit.Username(),
|
||||
Profile: &management.ImportHumanUserRequest_Profile{
|
||||
FirstName: gofakeit.FirstName(),
|
||||
LastName: gofakeit.LastName(),
|
||||
DisplayName: gofakeit.Username(),
|
||||
PreferredLanguage: gofakeit.LanguageBCP(),
|
||||
},
|
||||
Email: &management.ImportHumanUserRequest_Email{
|
||||
Email: gofakeit.Email(),
|
||||
IsEmailVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProjectGrants: []*v1.DataProjectGrant{
|
||||
{
|
||||
GrantId: grantIDs[0],
|
||||
ProjectGrant: &management.AddProjectGrantRequest{
|
||||
ProjectId: projectIDs[0],
|
||||
GrantedOrgId: orgIDs[1],
|
||||
RoleKeys: []string{"role1", "role2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GrantId: grantIDs[1],
|
||||
ProjectGrant: &management.AddProjectGrantRequest{
|
||||
ProjectId: projectIDs[1],
|
||||
GrantedOrgId: orgIDs[1],
|
||||
RoleKeys: []string{"role3", "role4"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GrantId: grantIDs[2],
|
||||
ProjectGrant: &management.AddProjectGrantRequest{
|
||||
ProjectId: projectIDs[0],
|
||||
GrantedOrgId: orgIDs[2],
|
||||
RoleKeys: []string{"role1", "role2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GrantId: grantIDs[3],
|
||||
ProjectGrant: &management.AddProjectGrantRequest{
|
||||
ProjectId: projectIDs[1],
|
||||
GrantedOrgId: orgIDs[2],
|
||||
RoleKeys: []string{"role3", "role4"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
OrgId: orgIDs[1],
|
||||
Org: &management.AddOrgRequest{
|
||||
Name: gofakeit.ProductName(),
|
||||
},
|
||||
UserGrants: []*management.AddUserGrantRequest{
|
||||
{
|
||||
UserId: userIDs[0],
|
||||
ProjectId: projectIDs[0],
|
||||
ProjectGrantId: grantIDs[0],
|
||||
},
|
||||
{
|
||||
UserId: userIDs[0],
|
||||
ProjectId: projectIDs[1],
|
||||
ProjectGrantId: grantIDs[1],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
OrgId: orgIDs[2],
|
||||
Org: &management.AddOrgRequest{
|
||||
Name: gofakeit.ProductName(),
|
||||
},
|
||||
UserGrants: []*management.AddUserGrantRequest{
|
||||
{
|
||||
UserId: userIDs[1],
|
||||
ProjectId: projectIDs[0],
|
||||
ProjectGrantId: grantIDs[2],
|
||||
},
|
||||
{
|
||||
UserId: userIDs[1],
|
||||
ProjectId: projectIDs[1],
|
||||
ProjectGrantId: grantIDs[3],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Timeout: time.Minute.String(),
|
||||
},
|
||||
want: &admin.ImportDataResponse{
|
||||
Success: &admin.ImportDataSuccess{
|
||||
Orgs: []*admin.ImportDataSuccessOrg{
|
||||
{
|
||||
OrgId: orgIDs[0],
|
||||
ProjectIds: projectIDs[0:2],
|
||||
ProjectRoles: []string{
|
||||
projectIDs[0] + "_role1",
|
||||
projectIDs[0] + "_role2",
|
||||
projectIDs[1] + "_role3",
|
||||
projectIDs[1] + "_role4",
|
||||
},
|
||||
HumanUserIds: userIDs[0:2],
|
||||
ProjectGrants: []*admin.ImportDataSuccessProjectGrant{
|
||||
{
|
||||
GrantId: grantIDs[0],
|
||||
ProjectId: projectIDs[0],
|
||||
OrgId: orgIDs[1],
|
||||
},
|
||||
{
|
||||
GrantId: grantIDs[1],
|
||||
ProjectId: projectIDs[1],
|
||||
OrgId: orgIDs[1],
|
||||
},
|
||||
{
|
||||
GrantId: grantIDs[2],
|
||||
ProjectId: projectIDs[0],
|
||||
OrgId: orgIDs[2],
|
||||
},
|
||||
{
|
||||
GrantId: grantIDs[3],
|
||||
ProjectId: projectIDs[1],
|
||||
OrgId: orgIDs[2],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
OrgId: orgIDs[1],
|
||||
UserGrants: []*admin.ImportDataSuccessUserGrant{
|
||||
{
|
||||
ProjectId: projectIDs[0],
|
||||
UserId: userIDs[0],
|
||||
},
|
||||
{
|
||||
UserId: userIDs[0],
|
||||
ProjectId: projectIDs[1],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
OrgId: orgIDs[2],
|
||||
UserGrants: []*admin.ImportDataSuccessUserGrant{
|
||||
{
|
||||
ProjectId: projectIDs[0],
|
||||
UserId: userIDs[1],
|
||||
},
|
||||
{
|
||||
UserId: userIDs[1],
|
||||
ProjectId: projectIDs[1],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "duplicate project grant error",
|
||||
req: &admin.ImportDataRequest{
|
||||
Data: &admin.ImportDataRequest_DataOrgs{
|
||||
DataOrgs: &admin.ImportDataOrg{
|
||||
Orgs: []*admin.DataOrg{
|
||||
{
|
||||
OrgId: orgIDs[3],
|
||||
Org: &management.AddOrgRequest{
|
||||
Name: gofakeit.ProductName(),
|
||||
},
|
||||
Projects: []*v1.DataProject{
|
||||
{
|
||||
ProjectId: projectIDs[2],
|
||||
Project: &management.AddProjectRequest{
|
||||
Name: gofakeit.AppName(),
|
||||
ProjectRoleAssertion: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
ProjectId: projectIDs[3],
|
||||
Project: &management.AddProjectRequest{
|
||||
Name: gofakeit.AppName(),
|
||||
ProjectRoleAssertion: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
ProjectRoles: []*management.AddProjectRoleRequest{
|
||||
{
|
||||
ProjectId: projectIDs[2],
|
||||
RoleKey: "role1",
|
||||
DisplayName: "role1",
|
||||
},
|
||||
{
|
||||
ProjectId: projectIDs[2],
|
||||
RoleKey: "role2",
|
||||
DisplayName: "role2",
|
||||
},
|
||||
{
|
||||
ProjectId: projectIDs[3],
|
||||
RoleKey: "role3",
|
||||
DisplayName: "role3",
|
||||
},
|
||||
{
|
||||
ProjectId: projectIDs[3],
|
||||
RoleKey: "role4",
|
||||
DisplayName: "role4",
|
||||
},
|
||||
},
|
||||
ProjectGrants: []*v1.DataProjectGrant{
|
||||
{
|
||||
GrantId: grantIDs[4],
|
||||
ProjectGrant: &management.AddProjectGrantRequest{
|
||||
ProjectId: projectIDs[2],
|
||||
GrantedOrgId: orgIDs[4],
|
||||
RoleKeys: []string{"role1", "role2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GrantId: grantIDs[4],
|
||||
ProjectGrant: &management.AddProjectGrantRequest{
|
||||
ProjectId: projectIDs[2],
|
||||
GrantedOrgId: orgIDs[4],
|
||||
RoleKeys: []string{"role1", "role2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Timeout: time.Minute.String(),
|
||||
},
|
||||
want: &admin.ImportDataResponse{
|
||||
Errors: []*admin.ImportDataError{
|
||||
{
|
||||
Type: "project_grant",
|
||||
Id: orgIDs[3] + "_" + projectIDs[2] + "_" + orgIDs[4],
|
||||
Message: "ID=V3-DKcYh Message=Errors.Project.Grant.AlreadyExists Parent=(ERROR: duplicate key value violates unique constraint \"unique_constraints_pkey\" (SQLSTATE 23505))",
|
||||
},
|
||||
},
|
||||
Success: &admin.ImportDataSuccess{
|
||||
Orgs: []*admin.ImportDataSuccessOrg{
|
||||
{
|
||||
OrgId: orgIDs[3],
|
||||
ProjectIds: projectIDs[2:4],
|
||||
ProjectRoles: []string{
|
||||
projectIDs[2] + "_role1",
|
||||
projectIDs[2] + "_role2",
|
||||
projectIDs[3] + "_role3",
|
||||
projectIDs[3] + "_role4",
|
||||
},
|
||||
ProjectGrants: []*admin.ImportDataSuccessProjectGrant{
|
||||
{
|
||||
GrantId: grantIDs[4],
|
||||
ProjectId: projectIDs[2],
|
||||
OrgId: orgIDs[4],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "duplicate project grant member error",
|
||||
req: &admin.ImportDataRequest{
|
||||
Data: &admin.ImportDataRequest_DataOrgs{
|
||||
DataOrgs: &admin.ImportDataOrg{
|
||||
Orgs: []*admin.DataOrg{
|
||||
{
|
||||
OrgId: orgIDs[5],
|
||||
Org: &management.AddOrgRequest{
|
||||
Name: gofakeit.ProductName(),
|
||||
},
|
||||
Projects: []*v1.DataProject{
|
||||
{
|
||||
ProjectId: projectIDs[4],
|
||||
Project: &management.AddProjectRequest{
|
||||
Name: gofakeit.AppName(),
|
||||
ProjectRoleAssertion: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
ProjectRoles: []*management.AddProjectRoleRequest{
|
||||
{
|
||||
ProjectId: projectIDs[4],
|
||||
RoleKey: "role1",
|
||||
DisplayName: "role1",
|
||||
},
|
||||
},
|
||||
HumanUsers: []*v1.DataHumanUser{
|
||||
{
|
||||
UserId: userIDs[2],
|
||||
User: &management.ImportHumanUserRequest{
|
||||
UserName: gofakeit.Username(),
|
||||
Profile: &management.ImportHumanUserRequest_Profile{
|
||||
FirstName: gofakeit.FirstName(),
|
||||
LastName: gofakeit.LastName(),
|
||||
DisplayName: gofakeit.Username(),
|
||||
PreferredLanguage: gofakeit.LanguageBCP(),
|
||||
},
|
||||
Email: &management.ImportHumanUserRequest_Email{
|
||||
Email: gofakeit.Email(),
|
||||
IsEmailVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProjectGrants: []*v1.DataProjectGrant{
|
||||
{
|
||||
GrantId: grantIDs[5],
|
||||
ProjectGrant: &management.AddProjectGrantRequest{
|
||||
ProjectId: projectIDs[4],
|
||||
GrantedOrgId: orgIDs[6],
|
||||
RoleKeys: []string{"role1", "role2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProjectGrantMembers: []*management.AddProjectGrantMemberRequest{
|
||||
{
|
||||
ProjectId: projectIDs[4],
|
||||
GrantId: grantIDs[5],
|
||||
UserId: userIDs[2],
|
||||
Roles: []string{"PROJECT_GRANT_OWNER"},
|
||||
},
|
||||
{
|
||||
ProjectId: projectIDs[4],
|
||||
GrantId: grantIDs[5],
|
||||
UserId: userIDs[2],
|
||||
Roles: []string{"PROJECT_GRANT_OWNER"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Timeout: time.Minute.String(),
|
||||
},
|
||||
want: &admin.ImportDataResponse{
|
||||
Errors: []*admin.ImportDataError{
|
||||
{
|
||||
Type: "project_grant_member",
|
||||
Id: orgIDs[5] + "_" + projectIDs[4] + "_" + grantIDs[5] + "_" + userIDs[2],
|
||||
Message: "ID=V3-DKcYh Message=Errors.Project.Member.AlreadyExists Parent=(ERROR: duplicate key value violates unique constraint \"unique_constraints_pkey\" (SQLSTATE 23505))",
|
||||
},
|
||||
},
|
||||
Success: &admin.ImportDataSuccess{
|
||||
Orgs: []*admin.ImportDataSuccessOrg{
|
||||
{
|
||||
OrgId: orgIDs[5],
|
||||
ProjectIds: projectIDs[4:5],
|
||||
ProjectRoles: []string{
|
||||
projectIDs[4] + "_role1",
|
||||
},
|
||||
HumanUserIds: userIDs[2:3],
|
||||
ProjectGrants: []*admin.ImportDataSuccessProjectGrant{
|
||||
{
|
||||
GrantId: grantIDs[5],
|
||||
ProjectId: projectIDs[4],
|
||||
OrgId: orgIDs[6],
|
||||
},
|
||||
},
|
||||
ProjectGrantMembers: []*admin.ImportDataSuccessProjectGrantMember{
|
||||
{
|
||||
ProjectId: projectIDs[4],
|
||||
GrantId: grantIDs[5],
|
||||
UserId: userIDs[2],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.ImportData(AdminCTX, tt.req)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
integration.EqualProto(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func generateIDs(n int) []string {
|
||||
ids := make([]string, n)
|
||||
for i := range ids {
|
||||
ids[i] = uuid.NewString()
|
||||
}
|
||||
return ids
|
||||
}
|
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
"github.com/zitadel/zitadel/internal/notification/channels/smtp"
|
||||
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||
)
|
||||
|
||||
@ -128,3 +129,29 @@ func (s *Server) DeactivateSMTPConfig(ctx context.Context, req *admin_pb.Deactiv
|
||||
Details: object.DomainToAddDetailsPb(result),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) TestSMTPConfigById(ctx context.Context, req *admin_pb.TestSMTPConfigByIdRequest) (*admin_pb.TestSMTPConfigByIdResponse, error) {
|
||||
err := s.command.TestSMTPConfigById(ctx, authz.GetInstance(ctx).InstanceID(), req.Id, req.ReceiverAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &admin_pb.TestSMTPConfigByIdResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *Server) TestSMTPConfig(ctx context.Context, req *admin_pb.TestSMTPConfigRequest) (*admin_pb.TestSMTPConfigResponse, error) {
|
||||
config := smtp.Config{}
|
||||
config.Tls = req.Tls
|
||||
config.From = req.SenderAddress
|
||||
config.FromName = req.SenderName
|
||||
config.SMTP.Host = req.Host
|
||||
config.SMTP.User = req.User
|
||||
config.SMTP.Password = req.Password
|
||||
|
||||
err := s.command.TestSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id, req.ReceiverAddress, &config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &admin_pb.TestSMTPConfigResponse{}, nil
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ func StartCommands(
|
||||
defaultRefreshTokenLifetime: defaultRefreshTokenLifetime,
|
||||
defaultRefreshTokenIdleLifetime: defaultRefreshTokenIdleLifetime,
|
||||
defaultSecretGenerators: defaultSecretGenerators,
|
||||
samlCertificateAndKeyGenerator: samlCertificateAndKeyGenerator(defaults.KeyConfig.Size),
|
||||
samlCertificateAndKeyGenerator: samlCertificateAndKeyGenerator(defaults.KeyConfig.CertificateSize, defaults.KeyConfig.CertificateLifetime),
|
||||
// always true for now until we can check with an eventlist
|
||||
EventExisting: func(event string) bool { return true },
|
||||
// always true for now until we can check with an eventlist
|
||||
@ -223,7 +223,7 @@ func exists(ctx context.Context, filter preparation.FilterToQueryReducer, wm exi
|
||||
return wm.Exists(), nil
|
||||
}
|
||||
|
||||
func samlCertificateAndKeyGenerator(keySize int) func(id string) ([]byte, []byte, error) {
|
||||
func samlCertificateAndKeyGenerator(keySize int, lifetime time.Duration) func(id string) ([]byte, []byte, error) {
|
||||
return func(id string) ([]byte, []byte, error) {
|
||||
priv, pub, err := crypto.GenerateKeyPair(keySize)
|
||||
if err != nil {
|
||||
@ -234,12 +234,15 @@ func samlCertificateAndKeyGenerator(keySize int) func(id string) ([]byte, []byte
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
now := time.Now()
|
||||
template := x509.Certificate{
|
||||
SerialNumber: big.NewInt(int64(serial)),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"ZITADEL"},
|
||||
SerialNumber: id,
|
||||
},
|
||||
NotBefore: now,
|
||||
NotAfter: now.Add(lifetime),
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
|
@ -17,14 +17,6 @@ func (c *Commands) AddProjectGrantWithID(ctx context.Context, grant *domain.Proj
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
existingMember, err := c.projectGrantWriteModelByID(ctx, grantID, grant.AggregateID, resourceOwner)
|
||||
if err != nil && !zerrors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
if existingMember != nil && existingMember.State != domain.ProjectGrantStateUnspecified {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "PROJECT-2b8fs", "Errors.Project.Grant.AlreadyExisting")
|
||||
}
|
||||
|
||||
return c.addProjectGrantWithID(ctx, grant, grantID, resourceOwner)
|
||||
}
|
||||
|
||||
|
@ -26,13 +26,6 @@ func (c *Commands) AddProjectGrantMember(ctx context.Context, member *domain.Pro
|
||||
return nil, err
|
||||
}
|
||||
addedMember := NewProjectGrantMemberWriteModel(member.AggregateID, member.UserID, member.GrantID)
|
||||
err = c.eventstore.FilterToQueryReducer(ctx, addedMember)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addedMember.State == domain.MemberStateActive {
|
||||
return nil, zerrors.ThrowAlreadyExists(nil, "PROJECT-16dVN", "Errors.Project.Member.AlreadyExists")
|
||||
}
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedMember.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(
|
||||
ctx,
|
||||
|
@ -104,58 +104,6 @@ func TestCommandSide_AddProjectGrantMember(t *testing.T) {
|
||||
err: zerrors.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member already exists, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username1",
|
||||
"firstname1",
|
||||
"lastname1",
|
||||
"nickname1",
|
||||
"displayname1",
|
||||
language.German,
|
||||
domain.GenderMale,
|
||||
"email1",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectGrantMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"user1",
|
||||
"projectgrant1",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: "PROJECT_GRANT_OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
member: &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "project1",
|
||||
},
|
||||
GrantID: "projectgrant1",
|
||||
UserID: "user1",
|
||||
Roles: []string{"PROJECT_GRANT_OWNER"},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "member add uniqueconstraint err, already exists",
|
||||
fields: fields{
|
||||
@ -177,7 +125,6 @@ func TestCommandSide_AddProjectGrantMember(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(),
|
||||
expectPushFailed(zerrors.ThrowAlreadyExists(nil, "ERROR", "internal"),
|
||||
project.NewProjectGrantMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "").Aggregate,
|
||||
@ -229,7 +176,6 @@ func TestCommandSide_AddProjectGrantMember(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
project.NewProjectGrantMemberAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "").Aggregate,
|
||||
|
@ -285,6 +285,89 @@ func (c *Commands) RemoveSMTPConfig(ctx context.Context, instanceID, id string)
|
||||
return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) TestSMTPConfig(ctx context.Context, instanceID, id, email string, config *smtp.Config) error {
|
||||
password := config.SMTP.Password
|
||||
|
||||
if email == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "SMTP-p9uy", "Errors.SMTPConfig.TestEmailNotFound")
|
||||
}
|
||||
|
||||
if id == "" && password == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "SMTP-p9kj", "Errors.SMTPConfig.TestPassword")
|
||||
}
|
||||
|
||||
// If the password is not sent it'd mean that the password hasn't been changed for
|
||||
// the stored configuration identified by its id so we can try to retrieve it
|
||||
if id != "" && password == "" {
|
||||
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !smtpConfigWriteModel.State.Exists() {
|
||||
return zerrors.ThrowNotFound(nil, "SMTP-p9cc", "Errors.SMTPConfig.NotFound")
|
||||
}
|
||||
|
||||
password, err = crypto.DecryptString(smtpConfigWriteModel.Password, c.smtpEncryption)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
config.SMTP.Password = password
|
||||
|
||||
// Try to send an email
|
||||
err := smtp.TestConfiguration(config, email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) TestSMTPConfigById(ctx context.Context, instanceID, id, email string) error {
|
||||
if id == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "SMTP-99oki", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
if email == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "SMTP-99yth", "Errors.SMTPConfig.TestEmailNotFound")
|
||||
}
|
||||
|
||||
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !smtpConfigWriteModel.State.Exists() {
|
||||
return zerrors.ThrowNotFound(nil, "SMTP-99klw", "Errors.SMTPConfig.NotFound")
|
||||
}
|
||||
|
||||
password, err := crypto.DecryptString(smtpConfigWriteModel.Password, c.smtpEncryption)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
smtpConfig := &smtp.Config{
|
||||
Description: smtpConfigWriteModel.Description,
|
||||
Tls: smtpConfigWriteModel.TLS,
|
||||
From: smtpConfigWriteModel.SenderAddress,
|
||||
FromName: smtpConfigWriteModel.SenderName,
|
||||
SMTP: smtp.SMTP{
|
||||
Host: smtpConfigWriteModel.Host,
|
||||
User: smtpConfigWriteModel.User,
|
||||
Password: password,
|
||||
},
|
||||
}
|
||||
|
||||
// Try to send an email
|
||||
err = smtp.TestConfiguration(smtpConfig, email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkSenderAddress(writeModel *IAMSMTPConfigWriteModel) error {
|
||||
if !writeModel.smtpSenderAddressMatchesInstanceDomain {
|
||||
return nil
|
||||
|
@ -1196,6 +1196,329 @@ func TestCommandSide_RemoveSMTPConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_TestSMTPConfig(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
alg crypto.EncryptionAlgorithm
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
instanceID string
|
||||
id string
|
||||
email string
|
||||
config smtp.Config
|
||||
}
|
||||
type res struct {
|
||||
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: "email empty, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
instanceID: "INSTANCE",
|
||||
id: "id",
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "if password is empty, smtp id must not",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
instanceID: "INSTANCE",
|
||||
id: "",
|
||||
email: "email",
|
||||
config: smtp.Config{
|
||||
From: "test@example,com",
|
||||
FromName: "Test",
|
||||
SMTP: smtp.SMTP{
|
||||
User: "user",
|
||||
Password: "",
|
||||
Host: "example.com:2525",
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password empty and smtp config not found, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
instanceID: "INSTANCE",
|
||||
id: "id",
|
||||
email: "email",
|
||||
config: smtp.Config{
|
||||
From: "test@example,com",
|
||||
FromName: "Test",
|
||||
SMTP: smtp.SMTP{
|
||||
User: "user",
|
||||
Password: "",
|
||||
Host: "example.com:2525",
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid new smtp config, wrong auth, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
instanceID: "INSTANCE",
|
||||
email: "email",
|
||||
config: smtp.Config{
|
||||
From: "test@example.com",
|
||||
FromName: "Test",
|
||||
SMTP: smtp.SMTP{
|
||||
User: "user",
|
||||
Password: "password",
|
||||
Host: "mail.smtp2go.com:2525",
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid smtp config using stored password, wrong auth, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSMTPConfigAddedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"ID",
|
||||
"test",
|
||||
true,
|
||||
"from",
|
||||
"name",
|
||||
"",
|
||||
"mail.smtp2go.com:2525",
|
||||
"user",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("password"),
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
instanceID: "INSTANCE",
|
||||
email: "email",
|
||||
id: "ID",
|
||||
config: smtp.Config{
|
||||
From: "test@example.com",
|
||||
FromName: "Test",
|
||||
SMTP: smtp.SMTP{
|
||||
User: "user",
|
||||
Password: "",
|
||||
Host: "mail.smtp2go.com:2525",
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsInternal,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
smtpEncryption: tt.fields.alg,
|
||||
}
|
||||
err := r.TestSMTPConfig(tt.args.ctx, tt.args.instanceID, tt.args.id, tt.args.email, &tt.args.config)
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_TestSMTPConfigById(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
alg crypto.EncryptionAlgorithm
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
instanceID string
|
||||
id string
|
||||
email string
|
||||
}
|
||||
type res struct {
|
||||
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: "email empty, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
instanceID: "INSTANCE",
|
||||
id: "id",
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "smtp config not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
instanceID: "INSTANCE",
|
||||
id: "id",
|
||||
email: "email",
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid smtp config, wrong auth, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSMTPConfigAddedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"ID",
|
||||
"test",
|
||||
true,
|
||||
"from",
|
||||
"name",
|
||||
"",
|
||||
"mail.smtp2go.com:2525",
|
||||
"user",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("password"),
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
id: "ID",
|
||||
instanceID: "INSTANCE",
|
||||
email: "test@example.com",
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsInternal,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
smtpEncryption: tt.fields.alg,
|
||||
}
|
||||
err := r.TestSMTPConfigById(tt.args.ctx, tt.args.instanceID, tt.args.id, tt.args.email)
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newSMTPConfigChangedEvent(ctx context.Context, id, description string, tls bool, fromAddress, fromName, replyTo, host, user string) *instance.SMTPConfigChangedEvent {
|
||||
changes := []instance.SMTPConfigChanges{
|
||||
instance.ChangeSMTPConfigDescription(description),
|
||||
|
@ -4,7 +4,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
|
||||
@ -71,3 +74,36 @@ func AssertListDetails[D ListDetailsMsg](t testing.TB, expected, actual D) {
|
||||
assert.WithinRange(t, gotCD, wantCD.Add(-time.Minute), wantCD.Add(time.Minute))
|
||||
}
|
||||
}
|
||||
|
||||
// EqualProto is inspired by [assert.Equal], only that it tests equality of a proto message.
|
||||
// A message diff is printed on the error test log if the messages are not equal.
|
||||
//
|
||||
// As [assert.Equal] is based on reflection, comparing 2 proto messages sometimes fails,
|
||||
// due to their internal state.
|
||||
// Expected messages are usually with a vanilla state, eg only exported fields contain data.
|
||||
// Actual messages obtained from the gRPC client had unexported fields with data.
|
||||
// This makes them hard to compare.
|
||||
func EqualProto(t testing.TB, expected, actual proto.Message) bool {
|
||||
t.Helper()
|
||||
if proto.Equal(expected, actual) {
|
||||
return true
|
||||
}
|
||||
t.Errorf("Proto messages not equal: %s", diffProto(expected, actual))
|
||||
return false
|
||||
}
|
||||
|
||||
func diffProto(expected, actual proto.Message) string {
|
||||
diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
|
||||
A: difflib.SplitLines(protojson.Format(expected)),
|
||||
B: difflib.SplitLines(protojson.Format(actual)),
|
||||
FromFile: "Expected",
|
||||
FromDate: "",
|
||||
ToFile: "Actual",
|
||||
ToDate: "",
|
||||
Context: 1,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return "\n\nDiff:\n" + diff
|
||||
}
|
||||
|
@ -41,22 +41,22 @@ func (email *Email) HandleMessage(message channels.Message) error {
|
||||
defer email.smtpClient.Close()
|
||||
emailMsg, ok := message.(*messages.Email)
|
||||
if !ok {
|
||||
return zerrors.ThrowInternal(nil, "EMAIL-s8JLs", "message is not EmailMessage")
|
||||
return zerrors.ThrowInternal(nil, "EMAIL-s8JLs", "Errors.SMTP.NotEmailMessage")
|
||||
}
|
||||
|
||||
if emailMsg.Content == "" || emailMsg.Subject == "" || len(emailMsg.Recipients) == 0 {
|
||||
return zerrors.ThrowInternalf(nil, "EMAIL-zGemZ", "subject, recipients and content must be set but got subject %s, recipients length %d and content length %d", emailMsg.Subject, len(emailMsg.Recipients), len(emailMsg.Content))
|
||||
return zerrors.ThrowInternal(nil, "EMAIL-zGemZ", "Errors.SMTP.RequiredAttributes")
|
||||
}
|
||||
emailMsg.SenderEmail = email.senderAddress
|
||||
emailMsg.SenderName = email.senderName
|
||||
emailMsg.ReplyToAddress = email.replyToAddress
|
||||
// To && From
|
||||
if err := email.smtpClient.Mail(emailMsg.SenderEmail); err != nil {
|
||||
return zerrors.ThrowInternalf(err, "EMAIL-s3is3", "could not set sender: %v", emailMsg.SenderEmail)
|
||||
return zerrors.ThrowInternal(err, "EMAIL-s3is3", "Errors.SMTP.CouldNotSetSender")
|
||||
}
|
||||
for _, recp := range append(append(emailMsg.Recipients, emailMsg.CC...), emailMsg.BCC...) {
|
||||
if err := email.smtpClient.Rcpt(recp); err != nil {
|
||||
return zerrors.ThrowInternalf(err, "EMAIL-s4is4", "could not set recipient: %v", recp)
|
||||
return zerrors.ThrowInternal(err, "EMAIL-s4is4", "Errors.SMTP.CouldNotSetRecipient")
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ func (email *Email) HandleMessage(message channels.Message) error {
|
||||
func (smtpConfig SMTP) connectToSMTP(tlsRequired bool) (client *smtp.Client, err error) {
|
||||
host, _, err := net.SplitHostPort(smtpConfig.Host)
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "EMAIL-spR56", "could not split host and port for connect to smtp")
|
||||
return nil, zerrors.ThrowInternal(err, "EMAIL-spR56", "Errors.SMTP.CouldNotSplit")
|
||||
}
|
||||
|
||||
if !tlsRequired {
|
||||
@ -109,7 +109,7 @@ func (smtpConfig SMTP) connectToSMTP(tlsRequired bool) (client *smtp.Client, err
|
||||
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")
|
||||
return nil, zerrors.ThrowInternal(err, "EMAIL-skwos", "Errors.SMTP.CouldNotDial")
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
@ -118,17 +118,17 @@ 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...")
|
||||
logging.OnError(err).Warn("could not connect using normal tls. trying starttls instead...")
|
||||
return smtpConfig.getSMTPClientWithStartTls(host)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "EMAIL-sl39s", "could not make tls dial")
|
||||
return nil, zerrors.ThrowInternal(err, "EMAIL-sl39s", "Errors.SMTP.CouldNotDialTLS")
|
||||
}
|
||||
|
||||
client, err := smtp.NewClient(conn, host)
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "EMAIL-skwi4", "could not create smtp client")
|
||||
return nil, zerrors.ThrowInternal(err, "EMAIL-skwi4", "Errors.SMTP.CouldNotCreateClient")
|
||||
}
|
||||
return client, err
|
||||
}
|
||||
@ -142,7 +142,7 @@ func (smtpConfig SMTP) getSMTPClientWithStartTls(host string) (*smtp.Client, err
|
||||
if err := client.StartTLS(&tls.Config{
|
||||
ServerName: host,
|
||||
}); err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "EMAIL-guvsQ", "could not start tls")
|
||||
return nil, zerrors.ThrowInternal(err, "EMAIL-guvsQ", "Errors.SMTP.CouldNotStartTLS")
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
@ -154,7 +154,56 @@ func (smtpConfig SMTP) smtpAuth(client *smtp.Client, host string) error {
|
||||
// Auth
|
||||
err := client.Auth(PlainOrLoginAuth(smtpConfig.User, smtpConfig.Password, host))
|
||||
if err != nil {
|
||||
return zerrors.ThrowInternalf(err, "EMAIL-s9kfs", "could not add smtp auth for user %s", smtpConfig.User)
|
||||
return zerrors.ThrowInternal(err, "EMAIL-s9kfs", "Errors.SMTP.CouldNotAuth")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestConfiguration(cfg *Config, testEmail string) error {
|
||||
client, err := cfg.SMTP.connectToSMTP(cfg.Tls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer client.Close()
|
||||
|
||||
message := &messages.Email{
|
||||
Recipients: []string{testEmail},
|
||||
Subject: "Test email",
|
||||
Content: "This is a test email to check if your SMTP provider works fine",
|
||||
SenderEmail: cfg.From,
|
||||
SenderName: cfg.FromName,
|
||||
}
|
||||
|
||||
if err := client.Mail(cfg.From); err != nil {
|
||||
return zerrors.ThrowInternal(err, "EMAIL-s3is3", "Errors.SMTP.CouldNotSetSender")
|
||||
}
|
||||
|
||||
if err := client.Rcpt(testEmail); err != nil {
|
||||
return zerrors.ThrowInternal(err, "EMAIL-s4is4", "Errors.SMTP.CouldNotSetRecipient")
|
||||
}
|
||||
|
||||
// Open data connection
|
||||
w, err := client.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send content
|
||||
content, err := message.GetContent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write([]byte(content))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Close IO and quit smtp connection
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return client.Quit()
|
||||
}
|
||||
|
@ -53,13 +53,26 @@ Errors:
|
||||
NotFound: SMS конфигурацията не е намерена
|
||||
AlreadyActive: SMS конфигурацията вече е активна
|
||||
AlreadyDeactivated: SMS конфигурацията вече е деактивирана
|
||||
SMTP:
|
||||
NotEmailMessage: съобщението не е имейл съобщение
|
||||
RequiredAttributes: темата, получателите и съдържанието трябва да бъдат зададени, но някои или всички са празни
|
||||
CouldNotSplit: не можа да раздели хост и порт за свързване към smtp
|
||||
CouldNotDial: не може да се свърже със SMTP сървъра, проверете порта, проблеми със защитната стена...
|
||||
CouldNotDialTLS: не може да се свърже със SMTP сървъра чрез TLS, проверете порта, проблеми със защитната стена...
|
||||
CouldNotCreateClient: не може да създаде smtp клиент
|
||||
CouldNotStartTLS: не можа да стартира TLS
|
||||
CouldNotAuth: не може да добави smtp auth, проверете дали потребителят и паролата ви са правилни, ако са правилни, може би вашият доставчик изисква метод за удостоверяване, който не се поддържа от ZITADEL
|
||||
CouldNotSetSender: не можа да зададе подател
|
||||
CouldNotSetRecipient: не можа да зададе получател
|
||||
SMTPConfig:
|
||||
TestPassword: Паролата за тест не е намерена
|
||||
NotFound: SMTP конфигурацията не е намерена
|
||||
AlreadyExists: SMTP конфигурация вече съществува
|
||||
AlreadyDeactivated: SMTP конфигурацията вече е деактивирана
|
||||
SenderAdressNotCustomDomain: >-
|
||||
Адресът на изпращача трябва да бъде конфигуриран като персонализиран
|
||||
домейн в екземпляра.
|
||||
TestEmailNotFound: Имейл адресът за теста не е намерен
|
||||
Notification:
|
||||
NoDomain: Няма намерен домейн за съобщение
|
||||
User:
|
||||
|
@ -53,11 +53,24 @@ Errors:
|
||||
NotFound: Konfigurace SMS nebyla nalezena
|
||||
AlreadyActive: Konfigurace SMS je již aktivní
|
||||
AlreadyDeactivated: Konfigurace SMS je již deaktivovaná
|
||||
SMTP:
|
||||
NotEmailMessage: zpráva není EmailMessage
|
||||
RequiredAttributes: předmět, příjemci a obsah musí být nastaveny, ale některé nebo všechny jsou prázdné
|
||||
CouldNotSplit: nemohl rozdělit hostitele a port pro připojení k smtp
|
||||
CouldNotDial: nelze se spojit se serverem SMTP, zkontrolovat port, problémy s firewallem...
|
||||
CouldNotDialTLS: nelze se spojit se serverem SMTP pomocí TLS, zkontrolovat port, problémy s firewallem...
|
||||
CouldNotCreateClient: nelze vytvořit smtp klienta
|
||||
CouldNotStartTLS: nelze spustit tls
|
||||
CouldNotAuth: nemohlo se přidat smtp auth, zkontrolujte, zda je váš uživatel i heslo správné, pokud jsou správné, možná váš poskytovatel vyžaduje metodu auth, kterou ZITADEL nepodporuje
|
||||
CouldNotSetSender: nelze nastavit odesílatele
|
||||
CouldNotSetRecipient: nelze nastavit příjemce
|
||||
SMTPConfig:
|
||||
TestPassword: Heslo pro test nenalezeno
|
||||
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.
|
||||
TestEmailNotFound: E-mailová adresa pro test nebyla nalezena
|
||||
Notification:
|
||||
NoDomain: Pro zprávu nebyla nalezena žádná doména
|
||||
User:
|
||||
|
@ -53,11 +53,24 @@ Errors:
|
||||
NotFound: SMS Konfiguration nicht gefunden
|
||||
AlreadyActive: SMS Konfiguration ist bereits aktiviert
|
||||
AlreadyDeactivated: SMS Konfiguration ist bereits deaktiviert
|
||||
SMTP:
|
||||
NotEmailMessage: Die Nachricht ist nicht EmailMessage
|
||||
RequiredAttributes: Betreff, Empfänger und Inhalt müssen festgelegt werden, aber einige oder alle davon sind leer
|
||||
CouldNotSplit: Host und Port konnten für die Verbindung zu SMTP nicht getrennt werden
|
||||
CouldNotDial: Es konnte keine Verbindung zum SMTP-Server hergestellt werden, Port überprüfen, Firewall-Probleme ...
|
||||
CouldNotDialTLS: Es konnte keine Verbindung zum SMTP-Server über TLS hergestellt werden, überprüfen Sie den Port, Firewall-Probleme ...
|
||||
CouldNotCreateClient: Der SMTP-Client konnte nicht erstellt werden
|
||||
CouldNotStartTLS: TLS konnte nicht gestartet werden
|
||||
CouldNotAuth: Die SMTP-Authentifizierung konnte nicht hinzugefügt werden. Überprüfen Sie, ob sowohl Ihr Benutzername als auch Ihr Passwort korrekt sind. Wenn sie korrekt sind, erfordert Ihr Anbieter möglicherweise eine Authentifizierungsmethode, die von ZITADEL nicht unterstützt wird
|
||||
CouldNotSetSender: Absender konnte nicht eingestellt werden
|
||||
CouldNotSetRecipient: Der Empfänger konnte nicht festgelegt werden
|
||||
SMTPConfig:
|
||||
TestPassword: Passwort für Test nicht gefunden
|
||||
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.
|
||||
TestEmailNotFound: E-Mail-Adresse für den Test nicht gefunden
|
||||
Notification:
|
||||
NoDomain: Keine Domäne für Nachricht gefunden
|
||||
User:
|
||||
|
@ -53,11 +53,24 @@ Errors:
|
||||
NotFound: SMS configuration not found
|
||||
AlreadyActive: SMS configuration already active
|
||||
AlreadyDeactivated: SMS configuration already deactivated
|
||||
SMTP:
|
||||
NotEmailMessage: message is not EmailMessage
|
||||
RequiredAttributes: subject, recipients and content must be set but some or all of them are empty
|
||||
CouldNotSplit: could not split host and port for connect to smtp
|
||||
CouldNotDial: could not contact with the SMTP server, check the port, firewall issues...
|
||||
CouldNotDialTLS: could not contact with the SMTP server using TLS, check the port, firewall issues...
|
||||
CouldNotCreateClient: could not create smtp client
|
||||
CouldNotStartTLS: could not start tls
|
||||
CouldNotAuth: could not add smtp auth, check if both your user and password are correct, if they're correct maybe your provider requires an auth method not supported by ZITADEL
|
||||
CouldNotSetSender: could not set sender
|
||||
CouldNotSetRecipient: could not set recipient
|
||||
SMTPConfig:
|
||||
TestPassword: Password for test not found
|
||||
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.
|
||||
TestEmailNotFound: Email address for test not found
|
||||
Notification:
|
||||
NoDomain: No Domain found for message
|
||||
User:
|
||||
|
@ -53,11 +53,24 @@ Errors:
|
||||
NotFound: configuración SMS no encontrada
|
||||
AlreadyActive: la configuración SMS ya está activa
|
||||
AlreadyDeactivated: la configuracion SMS ya está desactivada
|
||||
SMTP:
|
||||
NotEmailMessage: el mensaje no es EmailMessage
|
||||
RequiredAttributes: Se deben configurar el asunto, los destinatarios y el contenido, pero algunos o todos están vacíos.
|
||||
CouldNotSplit: No se pudo dividir el host y el puerto para conectarse a SMTP
|
||||
CouldNotDial: No se pudo contactar con el servidor SMTP, verifique el puerto, problemas con el firewall...
|
||||
CouldNotDialTLS: No se pudo contactar con el servidor SMTP usando TLS, verifique el puerto, problemas con el firewall...
|
||||
CouldNotCreateClient: no se pudo crear el cliente smtp
|
||||
CouldNotStartTLS: no se pudo iniciar TLS
|
||||
CouldNotAuth: no se pudo agregar la autenticación smtp, verifique si tanto su usuario como su contraseña son correctos, si son correctos tal vez su proveedor requiera un método de autenticación no admitido por ZITADEL
|
||||
CouldNotSetSender: no se pudo configurar el remitente
|
||||
CouldNotSetRecipient: No se pudo establecer el destinatario
|
||||
SMTPConfig:
|
||||
TestPassword: Contraseña para la prueba no encontrada
|
||||
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.
|
||||
TestEmailNotFound: Dirección de correo electrónico para la prueba no encontrada
|
||||
Notification:
|
||||
NoDomain: No se encontró el dominio para el mensaje
|
||||
User:
|
||||
|
@ -53,11 +53,24 @@ Errors:
|
||||
NotFound: Configuration SMS non trouvée
|
||||
AlreadyActive: Configuration SMS déjà active
|
||||
AlreadyDeactivated: Configuration SMS déjà désactivée
|
||||
SMTP:
|
||||
NotEmailMessage: le message n'est pas un EmailMessage
|
||||
RequiredAttributes: le sujet, les destinataires et le contenu doivent être définis mais certains ou la totalité d'entre eux sont vides
|
||||
CouldNotSplit: impossible de diviser l'hôte et le port pour la connexion à SMTP
|
||||
CouldNotDial: impossible de contacter le serveur SMTP, vérifier le port, problèmes de pare-feu...
|
||||
CouldNotDialTLS: Impossible de contacter le serveur SMTP via TLS, vérifiez le port, problèmes de pare-feu...
|
||||
CouldNotCreateClient: impossible de créer un client SMTP
|
||||
CouldNotStartTLS: je n'ai pas pu démarrer tls
|
||||
CouldNotAuth: Impossible d'ajouter l'authentification SMTP, vérifiez si votre utilisateur et votre mot de passe sont corrects. S'ils sont corrects, votre fournisseur nécessite peut-être une méthode d'authentification non prise en charge par ZITADEL.
|
||||
CouldNotSetSender: impossible de définir l'expéditeur
|
||||
CouldNotSetRecipient: impossible de définir le destinataire
|
||||
SMTPConfig:
|
||||
TestPassword: Mot de passe pour le test introuvable
|
||||
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.
|
||||
TestEmailNotFound: Adresse e-mail pour le test introuvable
|
||||
Notification:
|
||||
NoDomain: Aucun domaine trouvé pour le message
|
||||
User:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user