Reorder |
@@ -48,7 +48,7 @@
actions
matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"
color="warn"
- (click)="removeTarget(i)"
+ (click)="removeTarget(i, form)"
mat-icon-button
>
@@ -65,7 +65,7 @@
{{ 'ACTIONS.BACK' | translate }}
-
diff --git a/console/src/app/modules/sidenav/sidenav.component.scss b/console/src/app/modules/sidenav/sidenav.component.scss
index 383857751c..bb55a6999d 100644
--- a/console/src/app/modules/sidenav/sidenav.component.scss
+++ b/console/src/app/modules/sidenav/sidenav.component.scss
@@ -90,6 +90,10 @@
flex-shrink: 0;
}
+ .state {
+ margin-left: 0.5rem;
+ }
+
&:hover {
span {
opacity: 1;
diff --git a/console/src/app/modules/sidenav/sidenav.component.ts b/console/src/app/modules/sidenav/sidenav.component.ts
index 33539750a2..4b73491f08 100644
--- a/console/src/app/modules/sidenav/sidenav.component.ts
+++ b/console/src/app/modules/sidenav/sidenav.component.ts
@@ -11,6 +11,7 @@ export interface SidenavSetting {
[PolicyComponentServiceType.ADMIN]?: string[];
};
showWarn?: boolean;
+ beta?: boolean;
}
@Component({
diff --git a/console/src/app/pages/actions/actions.component.html b/console/src/app/pages/actions/actions.component.html
index 4c55308ced..af936e2351 100644
--- a/console/src/app/pages/actions/actions.component.html
+++ b/console/src/app/pages/actions/actions.component.html
@@ -6,6 +6,9 @@
info_outline
+
+ {{ 'DESCRIPTIONS.ACTIONS.ACTIONSTWO_NOTE' | translate }}
+
{{ 'DESCRIPTIONS.ACTIONS.DESCRIPTION' | translate }}
= new Subject();
+ protected maxActions: number | null = null;
+ protected ActionState = ActionState;
constructor(
private mgmtService: ManagementService,
breadcrumbService: BreadcrumbService,
private dialog: MatDialog,
private toast: ToastService,
+ destroyRef: DestroyRef,
) {
const bread: Breadcrumb = {
type: BreadcrumbType.ORG,
@@ -45,31 +45,24 @@ export class ActionsComponent implements OnDestroy {
};
breadcrumbService.setBreadcrumb([bread]);
- this.getFlowTypes();
+ this.getFlowTypes().then();
- this.typeControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
+ this.typeControl.valueChanges.pipe(takeUntilDestroyed(destroyRef)).subscribe((value) => {
this.loadFlow((value as FlowType.AsObject).id);
});
}
- ngOnDestroy(): void {
- this.destroy$.next();
- this.destroy$.complete();
- }
-
- private getFlowTypes(): Promise {
- return this.mgmtService
- .listFlowTypes()
- .then((resp) => {
- this.typesForSelection = resp.resultList;
- if (!this.flow && resp.resultList[0]) {
- const type = resp.resultList[0];
- this.typeControl.setValue(type);
- }
- })
- .catch((error: any) => {
- this.toast.showError(error);
- });
+ private async getFlowTypes(): Promise {
+ try {
+ let resp = await this.mgmtService.listFlowTypes();
+ this.typesForSelection = resp.resultList;
+ if (!this.flow && resp.resultList[0]) {
+ const type = resp.resultList[0];
+ this.typeControl.setValue(type);
+ }
+ } catch (error) {
+ this.toast.showError(error);
+ }
}
private loadFlow(id: string) {
@@ -106,7 +99,7 @@ export class ActionsComponent implements OnDestroy {
});
}
- public openAddTrigger(flow: FlowType.AsObject, trigger?: TriggerType.AsObject): void {
+ protected openAddTrigger(flow: FlowType.AsObject, trigger?: TriggerType.AsObject): void {
const dialogRef = this.dialog.open(AddFlowDialogComponent, {
data: {
flowType: flow,
@@ -119,7 +112,7 @@ export class ActionsComponent implements OnDestroy {
if (req) {
this.mgmtService
.setTriggerActions(req.getActionIdsList(), req.getFlowType(), req.getTriggerType())
- .then((resp) => {
+ .then(() => {
this.toast.showInfo('FLOWS.FLOWCHANGED', true);
this.loadFlow(flow.id);
})
@@ -157,7 +150,7 @@ export class ActionsComponent implements OnDestroy {
}
}
- public removeTriggerActionsList(index: number) {
+ protected removeTriggerActionsList(index: number) {
if (this.flow.type && this.flow.triggerActionsList && this.flow.triggerActionsList[index]) {
const dialogRef = this.dialog.open(WarnDialogComponent, {
data: {
diff --git a/console/src/app/pages/instance/instance.component.ts b/console/src/app/pages/instance/instance.component.ts
index 546553132d..e52cdd7198 100644
--- a/console/src/app/pages/instance/instance.component.ts
+++ b/console/src/app/pages/instance/instance.component.ts
@@ -42,8 +42,6 @@ import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { EnvironmentService } from 'src/app/services/environment.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
-import { NewFeatureService } from '../../services/new-feature.service';
-import { withLatestFromSynchronousFix } from '../../utils/withLatestFromSynchronousFix';
@Component({
selector: 'cnsl-instance',
templateUrl: './instance.component.html',
@@ -106,7 +104,6 @@ export class InstanceComponent {
private readonly envService: EnvironmentService,
activatedRoute: ActivatedRoute,
private readonly destroyRef: DestroyRef,
- private readonly featureService: NewFeatureService,
) {
this.loadMembers();
@@ -139,32 +136,7 @@ export class InstanceComponent {
}
private getSettingsList(): Observable {
- const features$ = this.getFeatures().pipe(shareReplay({ refCount: true, bufferSize: 1 }));
-
- const actionsEnabled$ = features$.pipe(map((features) => features?.actions?.enabled));
-
- return this.authService
- .isAllowedMapper(this.defaultSettingsList, (setting) => setting.requiredRoles.admin || [])
- .pipe(
- withLatestFromSynchronousFix(actionsEnabled$),
- map(([settings, actionsEnabled]) =>
- settings
- .filter((setting) => actionsEnabled || setting.id !== ACTIONS.id)
- .filter((setting) => actionsEnabled || setting.id !== ACTIONS_TARGETS.id),
- ),
- );
- }
-
- private getFeatures() {
- return defer(() => this.featureService.getInstanceFeatures()).pipe(
- timeout(1000),
- catchError((error) => {
- if (!(error instanceof TimeoutError)) {
- this.toast.showError(error);
- }
- return of(undefined);
- }),
- );
+ return this.authService.isAllowedMapper(this.defaultSettingsList, (setting) => setting.requiredRoles.admin || []);
}
public loadMembers(): void {
diff --git a/console/src/app/services/grpc-auth.service.ts b/console/src/app/services/grpc-auth.service.ts
index 3967f1df06..198d048b6a 100644
--- a/console/src/app/services/grpc-auth.service.ts
+++ b/console/src/app/services/grpc-auth.service.ts
@@ -1,7 +1,18 @@
import { Injectable } from '@angular/core';
import { SortDirection } from '@angular/material/sort';
import { OAuthService } from 'angular-oauth2-oidc';
-import { BehaviorSubject, combineLatestWith, EMPTY, mergeWith, NEVER, Observable, of, shareReplay, Subject } from 'rxjs';
+import {
+ BehaviorSubject,
+ combineLatestWith,
+ EMPTY,
+ identity,
+ mergeWith,
+ NEVER,
+ Observable,
+ of,
+ shareReplay,
+ Subject,
+} from 'rxjs';
import { catchError, distinctUntilChanged, filter, finalize, map, startWith, switchMap, tap, timeout } from 'rxjs/operators';
import {
@@ -326,7 +337,7 @@ export class GrpcAuthService {
return new RegExp(reqRegexp).test(role);
});
- const allCheck = requestedRoles.map(test).every((x) => !!x);
+ const allCheck = requestedRoles.map(test).every(identity);
const oneCheck = requestedRoles.some(test);
return requiresAll ? allCheck : oneCheck;
diff --git a/console/src/app/services/grpc.service.ts b/console/src/app/services/grpc.service.ts
index b2f89ca648..d2add12f41 100644
--- a/console/src/app/services/grpc.service.ts
+++ b/console/src/app/services/grpc.service.ts
@@ -15,7 +15,6 @@ import { AuthInterceptor, AuthInterceptorProvider, NewConnectWebAuthInterceptor
import { ExhaustedGrpcInterceptor } from './interceptors/exhausted.grpc.interceptor';
import { I18nInterceptor } from './interceptors/i18n.interceptor';
import { NewConnectWebOrgInterceptor, OrgInterceptor, OrgInterceptorProvider } from './interceptors/org.interceptor';
-import { StorageService } from './storage.service';
import { UserServiceClient } from '../proto/generated/zitadel/user/v2/User_serviceServiceClientPb';
//@ts-ignore
import { createFeatureServiceClient, createUserServiceClient, createSessionServiceClient } from '@zitadel/client/v2';
@@ -24,14 +23,10 @@ import { createAuthServiceClient, createManagementServiceClient } from '@zitadel
import { createGrpcWebTransport } from '@connectrpc/connect-web';
// @ts-ignore
import { createClientFor } from '@zitadel/client';
-import { Client, Transport } from '@connectrpc/connect';
import { WebKeyService } from '@zitadel/proto/zitadel/webkey/v2beta/webkey_service_pb';
import { ActionService } from '@zitadel/proto/zitadel/action/v2beta/action_service_pb';
-// @ts-ignore
-import { createClientFor } from '@zitadel/client';
-
const createWebKeyServiceClient = createClientFor(WebKeyService);
const createActionServiceClient = createClientFor(ActionService);
diff --git a/console/src/app/services/posthog.service.ts b/console/src/app/services/posthog.service.ts
index 2f9630282a..17855d7eb5 100644
--- a/console/src/app/services/posthog.service.ts
+++ b/console/src/app/services/posthog.service.ts
@@ -26,8 +26,16 @@ export class PosthogService implements OnDestroy {
maskAllInputs: true,
maskTextSelector: '*',
},
+ disable_session_recording: true,
enable_heatmaps: true,
persistence: 'memory',
+ loaded: (posthog) => {
+ posthog.onFeatureFlags((flags) => {
+ if (posthog.isFeatureEnabled('session_recording')) {
+ posthog.startSessionRecording();
+ }
+ });
+ },
});
}
}
diff --git a/console/src/assets/i18n/bg.json b/console/src/assets/i18n/bg.json
index 4bd94d5ce6..b98204a917 100644
--- a/console/src/assets/i18n/bg.json
+++ b/console/src/assets/i18n/bg.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Потоци",
"DESCRIPTION": "Изберете поток за удостоверяване и активирайте вашето действие при конкретно събитие в този поток."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, нова и подобрена версия на Actions, вече е налична. Настоящата версия все още е достъпна, но бъдещото развитие ще бъде фокусирано върху новата, която в крайна сметка ще замени текущата версия."
},
"SETTINGS": {
"INSTANCE": {
@@ -528,13 +529,14 @@
"APPLY": "Прилагам"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "В момента използвате новата версия Actions V2, която е в бета фаза. Предишната версия 1 все още е достъпна, но ще бъде спряна в бъдеще. Моля, съобщавайте за всякакви проблеми или изпратете обратна връзка.",
"EXECUTION": {
"TITLE": "Действия",
"DESCRIPTION": "Действията ви позволяват да изпълнявате персонализиран код в отговор на API заявки, събития или специфични функции. Използвайте ги, за да разширите Zitadel, да автоматизирате работни процеси и да се интегрирате с други системи.",
"TYPES": {
"request": "Заявка",
"response": "Отговор",
- "events": "Събития",
+ "event": "Събития",
"function": "Функция"
},
"DIALOG": {
@@ -565,6 +567,7 @@
"TITLE": "Всички",
"DESCRIPTION": "Изберете това, ако искате да изпълните действието си при всяка заявка"
},
+ "ALL_EVENTS": "Изберете това, ако искате действието да се изпълнява при всяко събитие",
"SELECT_SERVICE": {
"TITLE": "Избор на услуга",
"DESCRIPTION": "Изберете услуга на Zitadel за вашето действие."
@@ -618,6 +621,7 @@
"restCall": "REST извикване",
"restAsync": "REST асинхронно"
},
+ "TYPES_DESCRIPTION": "Webhook, обаждането обработва кода на състоянието, но отговорът е без значение\nCall, обаждането обработва кода на състоянието и отговора\nAsync, обаждането не обработва нито кода на състоянието, нито отговора, но може да бъде извикано паралелно с други цели",
"ENDPOINT": "Крайна точка",
"ENDPOINT_DESCRIPTION": "Въведете крайната точка, където се хоства вашият код. Уверете се, че е достъпна за нас!",
"TIMEOUT": "Време за изчакване",
@@ -1507,7 +1511,8 @@
"APPEARANCE": "Външен вид",
"OTHER": "други",
"STORAGE": "Съхранение"
- }
+ },
+ "BETA": "БЕТА"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/cs.json b/console/src/assets/i18n/cs.json
index 6dd066789e..390c5dcdbd 100644
--- a/console/src/assets/i18n/cs.json
+++ b/console/src/assets/i18n/cs.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Flows",
"DESCRIPTION": "Vyberte proces autentizace a spusťte vaši akci na konkrétní události v rámci tohoto procesu."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, nová a vylepšená verze Actions, je nyní k dispozici. Aktuální verze je stále přístupná, ale budoucí vývoj se zaměří na novou verzi, která nakonec nahradí tu současnou."
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "Platit"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "Aktuálně používáte novou verzi Actions V2, která je v beta verzi. Předchozí verze 1 je stále k dispozici, ale v budoucnu bude ukončena. Prosím, hlaste jakékoliv problémy nebo zpětnou vazbu.",
"EXECUTION": {
"TITLE": "Akce",
"DESCRIPTION": "Akce vám umožňují spouštět vlastní kód v reakci na požadavky API, události nebo specifické funkce. Použijte je k rozšíření Zitadel, automatizaci pracovních postupů a integraci s dalšími systémy.",
"TYPES": {
"request": "Požadavek",
"response": "Odpověď",
- "events": "Události",
+ "event": "Události",
"function": "Funkce"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "Všechny",
"DESCRIPTION": "Vyberte tuto možnost, pokud chcete spustit akci pro každý požadavek"
},
+ "ALL_EVENTS": "Vyberte toto, pokud chcete spustit akci při každé události",
"SELECT_SERVICE": {
"TITLE": "Vybrat službu",
"DESCRIPTION": "Vyberte službu Zitadel pro svou akci."
@@ -619,6 +622,7 @@
"restCall": "REST Volání",
"restAsync": "REST Asynchronní"
},
+ "TYPES_DESCRIPTION": "Webhook, volání zpracovává stavový kód, ale odpověď je irelevantní\nCall, volání zpracovává stavový kód a odpověď\nAsync, volání nezpracovává ani stavový kód, ani odpověď, ale může být spuštěno paralelně s jinými cíli",
"ENDPOINT": "Koncový bod",
"ENDPOINT_DESCRIPTION": "Zadejte koncový bod, kde je hostován váš kód. Ujistěte se, že je pro nás přístupný!",
"TIMEOUT": "Časový limit",
@@ -1508,7 +1512,8 @@
"APPEARANCE": "Vzhled",
"OTHER": "Ostatní",
"STORAGE": "Data"
- }
+ },
+ "BETA": "BETA"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json
index cb973006e4..e73c883bd2 100644
--- a/console/src/assets/i18n/de.json
+++ b/console/src/assets/i18n/de.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Flows",
"DESCRIPTION": "Wähle einen Authentifizierungsflow und löse deine Aktionen bei einem spezifischen Ereignis innerhalb dieses Flows aus."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, eine neue und verbesserte Version von Actions, ist jetzt verfügbar. Die aktuelle Version ist weiterhin zugänglich, aber unsere zukünftige Entwicklung wird sich auf die neue Version konzentrieren, die schließlich die aktuelle ersetzen wird."
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "Anwenden"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "Sie verwenden derzeit die neuen Actions V2, die sich in der Beta-Phase befinden. Version 1 ist weiterhin verfügbar, wird jedoch in Zukunft eingestellt. Bitte melden Sie Probleme oder Feedback.",
"EXECUTION": {
"TITLE": "Aktionen",
"DESCRIPTION": "Aktionen ermöglichen es Ihnen, benutzerdefinierten Code als Reaktion auf API-Anfragen, Ereignisse oder bestimmte Funktionen auszuführen. Verwenden Sie sie, um Zitadel zu erweitern, Arbeitsabläufe zu automatisieren und sich in andere Systeme zu integrieren.",
"TYPES": {
"request": "Anfrage",
"response": "Antwort",
- "events": "Ereignisse",
+ "event": "Ereignisse",
"function": "Funktion"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "Alle",
"DESCRIPTION": "Wählen Sie dies aus, wenn Sie Ihre Aktion bei jeder Anfrage ausführen möchten"
},
+ "ALL_EVENTS": "Wähle dies aus, wenn du deine Aktion bei jedem Ereignis ausführen möchtest",
"SELECT_SERVICE": {
"TITLE": "Dienst auswählen",
"DESCRIPTION": "Wählen Sie einen Zitadel-Dienst für Ihre Aktion aus."
@@ -619,6 +622,7 @@
"restCall": "REST Aufruf",
"restAsync": "REST Asynchron"
},
+ "TYPES_DESCRIPTION": "Webhook, der Aufruf verarbeitet den Statuscode, aber die Antwort ist irrelevant\nCall, der Aufruf verarbeitet den Statuscode und die Antwort\nAsync, der Aufruf verarbeitet weder Statuscode noch Antwort, kann aber parallel zu anderen Zielen aufgerufen werden",
"ENDPOINT": "Endpunkt",
"ENDPOINT_DESCRIPTION": "Geben Sie den Endpunkt ein, an dem Ihr Code gehostet wird. Stellen Sie sicher, dass er für uns zugänglich ist!",
"TIMEOUT": "Timeout",
@@ -1508,7 +1512,8 @@
"APPEARANCE": "Erscheinungsbild",
"OTHER": "Anderes",
"STORAGE": "Speicher"
- }
+ },
+ "BETA": "BETA"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json
index be0a3d3f17..5e2cc3f4c9 100644
--- a/console/src/assets/i18n/en.json
+++ b/console/src/assets/i18n/en.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Flows",
"DESCRIPTION": "Choose an authentication flow and trigger your action on a specific event within this flow."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2 a new, improved version of Actions is now available. The current version is still accessible, but our future development will focus on the new one, which will eventually replace the current version."
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "Apply"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "You are currently using the new Actions V2, which is in beta. The previous Version 1 is still available but will be discontinued in the future. Please report any issues or feedback.",
"EXECUTION": {
"TITLE": "Actions",
"DESCRIPTION": "Actions let you run custom code in response to API requests, events or specific functions. Use them to extend Zitadel, automate workflows, and itegrate with other systems.",
"TYPES": {
"request": "Request",
"response": "Response",
- "events": "Events",
+ "event": "Events",
"function": "Function"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "All",
"DESCRIPTION": "Select this if you want to run your action on every request"
},
+ "ALL_EVENTS": "Select this if you want to run your action on every event",
"SELECT_SERVICE": {
"TITLE": "Select Service",
"DESCRIPTION": "Choose a Zitadel Service for you action."
@@ -619,6 +622,7 @@
"restCall": "REST Call",
"restAsync": "REST Async"
},
+ "TYPES_DESCRIPTION": "Webhook, the call handles the status code but response is irrelevant\nCall, the call handles the status code and response\nAsync, the call handles neither status code nor response, but can be called in parallel with other Targets",
"ENDPOINT": "Endpoint",
"ENDPOINT_DESCRIPTION": "Enter the endpoint where your code is hosted. Make sure it is accessible to us!",
"TIMEOUT": "Timeout",
@@ -1511,7 +1515,8 @@
"OTHER": "Other",
"STORAGE": "Storage",
"ACTIONS": "Actions"
- }
+ },
+ "BETA": "BETA"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/es.json b/console/src/assets/i18n/es.json
index 0fc95241af..198bb3ca8b 100644
--- a/console/src/assets/i18n/es.json
+++ b/console/src/assets/i18n/es.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Flujos",
"DESCRIPTION": "Elige un flujo de autenticación y activa tu acción en un evento específico dentro de este flujo."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, una nueva y mejorada versión de Actions, ya está disponible. La versión actual sigue siendo accesible, pero nuestro desarrollo futuro se centrará en la nueva, que acabará reemplazando la versión actual."
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "Aplicar"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "Actualmente estás usando la nueva versión Actions V2, que está en fase beta. La versión anterior 1 todavía está disponible, pero será descontinuada en el futuro. Por favor, informa de cualquier problema o comentario.",
"EXECUTION": {
"TITLE": "Acciones",
"DESCRIPTION": "Las acciones te permiten ejecutar código personalizado en respuesta a solicitudes de API, eventos o funciones específicas. Úsalas para extender Zitadel, automatizar flujos de trabajo e integrarte con otros sistemas.",
"TYPES": {
"request": "Solicitud",
"response": "Respuesta",
- "events": "Eventos",
+ "event": "Eventos",
"function": "Función"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "Todas",
"DESCRIPTION": "Selecciona esto si quieres ejecutar tu acción en cada solicitud"
},
+ "ALL_EVENTS": "Selecciona esto si quieres ejecutar tu acción en cada evento",
"SELECT_SERVICE": {
"TITLE": "Seleccionar servicio",
"DESCRIPTION": "Elige un servicio de Zitadel para tu acción."
@@ -619,6 +622,7 @@
"restCall": "Llamada REST",
"restAsync": "REST Asíncrono"
},
+ "TYPES_DESCRIPTION": "Webhook, la llamada maneja el código de estado pero la respuesta es irrelevante\nCall, la llamada maneja el código de estado y la respuesta\nAsync, la llamada no maneja ni el código de estado ni la respuesta, pero puede ser llamada en paralelo con otros objetivos",
"ENDPOINT": "Punto de conexión",
"ENDPOINT_DESCRIPTION": "Introduce el punto de conexión donde se aloja tu código. ¡Asegúrate de que sea accesible para nosotros!",
"TIMEOUT": "Tiempo de espera",
@@ -1509,7 +1513,8 @@
"APPEARANCE": "Apariencia",
"OTHER": "Otros",
"STORAGE": "Datos"
- }
+ },
+ "BETA": "BETA"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json
index 60a0a3e482..0d66c4193e 100644
--- a/console/src/assets/i18n/fr.json
+++ b/console/src/assets/i18n/fr.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Flux",
"DESCRIPTION": "Choisissez un flux d'authentification et déclenchez votre action sur un événement spécifique dans ce flux."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, une nouvelle version améliorée de Actions, est désormais disponible. La version actuelle reste accessible, mais notre développement futur se concentrera sur la nouvelle, qui finira par remplacer la version actuelle."
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "Appliquer"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "Vous utilisez actuellement la nouvelle version Actions V2, qui est en phase bêta. L'ancienne version 1 est toujours disponible mais sera arrêtée à l'avenir. Veuillez signaler tout problème ou commentaire.",
"EXECUTION": {
"TITLE": "Actions",
"DESCRIPTION": "Les actions vous permettent d'exécuter du code personnalisé en réponse à des requêtes API, des événements ou des fonctions spécifiques. Utilisez-les pour étendre Zitadel, automatiser les flux de travail et vous intégrer à d'autres systèmes.",
"TYPES": {
"request": "Requête",
"response": "Réponse",
- "events": "Événements",
+ "event": "Événements",
"function": "Fonction"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "Tous",
"DESCRIPTION": "Sélectionnez ceci si vous souhaitez exécuter votre action sur chaque requête"
},
+ "ALL_EVENTS": "Sélectionnez ceci si vous souhaitez exécuter votre action à chaque événement",
"SELECT_SERVICE": {
"TITLE": "Sélectionner un service",
"DESCRIPTION": "Choisissez un service Zitadel pour votre action."
@@ -619,6 +622,7 @@
"restCall": "Appel REST",
"restAsync": "REST Asynchrone"
},
+ "TYPES_DESCRIPTION": "Webhook, l'appel gère le code d'état mais la réponse est sans importance\nCall, l'appel gère le code d'état et la réponse\nAsync, l'appel ne gère ni le code d'état ni la réponse, mais peut être appelé en parallèle avec d'autres cibles",
"ENDPOINT": "Point de terminaison",
"ENDPOINT_DESCRIPTION": "Entrez le point de terminaison où votre code est hébergé. Assurez-vous qu'il nous est accessible !",
"TIMEOUT": "Délai d'attente",
@@ -1508,7 +1512,8 @@
"APPEARANCE": "Apparence",
"OTHER": "Autres",
"STORAGE": "Stockage"
- }
+ },
+ "BETA": "BÊTA"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/hu.json b/console/src/assets/i18n/hu.json
index 5724c45a51..96d1fe16df 100644
--- a/console/src/assets/i18n/hu.json
+++ b/console/src/assets/i18n/hu.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Folyamatok",
"DESCRIPTION": "Válassz egy hitelesítési folyamatot, és váltasd ki a műveletedet egy adott esemény bekövetkezésekor ebben a folyamatban."
- }
+ },
+ "ACTIONSTWO_NOTE": "Az Actions V2, az Actions új, továbbfejlesztett verziója mostantól elérhető. A jelenlegi verzió továbbra is elérhető, de a jövőbeli fejlesztéseink az új verzióra összpontosítanak, amely végül felváltja a jelenlegi verziót."
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "Alkalmaz"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "Jelenleg az új Actions V2-t használja, amely béta verzióban van. Az előző 1-es verzió továbbra is elérhető, de a jövőben megszűnik. Kérjük, jelezze az esetleges problémákat vagy visszajelzéseit.",
"EXECUTION": {
"TITLE": "Műveletek",
"DESCRIPTION": "A műveletek lehetővé teszik egyedi kód futtatását API-kérésekre, eseményekre vagy konkrét függvényekre válaszul. Használja őket a Zitadel kiterjesztéséhez, a munkafolyamatok automatizálásához és más rendszerekkel való integrációhoz.",
"TYPES": {
"request": "Kérés",
"response": "Válasz",
- "events": "Események",
+ "event": "Események",
"function": "Függvény"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "Összes",
"DESCRIPTION": "Válassza ezt, ha minden kérésnél futtatni szeretné a műveletet"
},
+ "ALL_EVENTS": "Válaszd ezt, ha minden eseménynél futtatni szeretnéd a műveletet",
"SELECT_SERVICE": {
"TITLE": "Szolgáltatás kiválasztása",
"DESCRIPTION": "Válasszon egy Zitadel szolgáltatást a művelethez."
@@ -619,6 +622,7 @@
"restCall": "REST Hívás",
"restAsync": "REST Aszinkron"
},
+ "TYPES_DESCRIPTION": "Webhook, a hívás kezeli az állapotkódot, de a válasz lényegtelen\nCall, a hívás kezeli az állapotkódot és a választ\nAsync, a hívás sem az állapotkódot, sem a választ nem kezeli, de párhuzamosan hívható más célokkal",
"ENDPOINT": "Végpont",
"ENDPOINT_DESCRIPTION": "Adja meg azt a végpontot, ahol a kódja található. Győződjön meg arról, hogy elérhető számunkra!",
"TIMEOUT": "Időtúllépés",
@@ -1508,7 +1512,8 @@
"APPEARANCE": "Megjelenés",
"OTHER": "Egyéb",
"STORAGE": "Tárolás"
- }
+ },
+ "BETA": "BÉTA"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/id.json b/console/src/assets/i18n/id.json
index 77584e883b..ca788a9467 100644
--- a/console/src/assets/i18n/id.json
+++ b/console/src/assets/i18n/id.json
@@ -69,7 +69,8 @@
"FLOWS": {
"TITLE": "Mengalir",
"DESCRIPTION": "Pilih alur autentikasi dan picu tindakan Anda pada peristiwa tertentu dalam alur ini."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, versi baru dan lebih baik dari Actions, sekarang tersedia. Versi saat ini masih dapat diakses, tetapi pengembangan di masa depan akan difokuskan pada versi baru ini yang pada akhirnya akan menggantikan versi saat ini."
},
"SETTINGS": {
"INSTANCE": {
@@ -496,13 +497,14 @@
"APPLY": "Menerapkan"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "Anda saat ini menggunakan Actions V2 baru, yang masih dalam versi beta. Versi sebelumnya, Versi 1, masih tersedia tetapi akan dihentikan di masa depan. Silakan laporkan masalah atau berikan masukan.",
"EXECUTION": {
"TITLE": "Tindakan",
"DESCRIPTION": "Tindakan memungkinkan Anda menjalankan kode khusus sebagai respons terhadap permintaan API, peristiwa, atau fungsi tertentu. Gunakan ini untuk memperluas Zitadel, mengotomatiskan alur kerja, dan berintegrasi dengan sistem lain.",
"TYPES": {
"request": "Permintaan",
"response": "Respons",
- "events": "Peristiwa",
+ "event": "Peristiwa",
"function": "Fungsi"
},
"DIALOG": {
@@ -533,6 +535,7 @@
"TITLE": "Semua",
"DESCRIPTION": "Pilih ini jika Anda ingin menjalankan tindakan Anda pada setiap permintaan"
},
+ "ALL_EVENTS": "Pilih ini jika Anda ingin menjalankan aksi Anda pada setiap peristiwa",
"SELECT_SERVICE": {
"TITLE": "Pilih Layanan",
"DESCRIPTION": "Pilih Layanan Zitadel untuk tindakan Anda."
@@ -586,6 +589,7 @@
"restCall": "Panggilan REST",
"restAsync": "REST Asinkron"
},
+ "TYPES_DESCRIPTION": "Webhook, panggilan menangani kode status tetapi respons tidak relevan\nCall, panggilan menangani kode status dan respons\nAsync, panggilan tidak menangani kode status maupun respons, tetapi dapat dipanggil secara paralel dengan Target lain",
"ENDPOINT": "Titik Akhir",
"ENDPOINT_DESCRIPTION": "Masukkan titik akhir tempat kode Anda dihosting. Pastikan dapat diakses oleh kami!",
"TIMEOUT": "Batas Waktu",
@@ -1386,7 +1390,8 @@
"APPEARANCE": "Penampilan",
"OTHER": "Lainnya",
"STORAGE": "Penyimpanan"
- }
+ },
+ "BETA": "BETA"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json
index b01762b175..60266bdac5 100644
--- a/console/src/assets/i18n/it.json
+++ b/console/src/assets/i18n/it.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Flussi",
"DESCRIPTION": "Scegli un flusso di autenticazione e attiva la tua azione su un evento specifico all'interno di questo flusso."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, una nuova versione migliorata di Actions, è ora disponibile. La versione attuale è ancora accessibile, ma i futuri sviluppi si concentreranno su quella nuova, che alla fine sostituirà la versione corrente."
},
"SETTINGS": {
"INSTANCE": {
@@ -528,13 +529,14 @@
"APPLY": "Applicare"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "Stai attualmente utilizzando la nuova versione Actions V2, che è in beta. La precedente Versione 1 è ancora disponibile, ma sarà dismessa in futuro. Ti preghiamo di segnalare eventuali problemi o feedback.",
"EXECUTION": {
"TITLE": "Azioni",
"DESCRIPTION": "Le azioni consentono di eseguire codice personalizzato in risposta a richieste API, eventi o funzioni specifiche. Usale per estendere Zitadel, automatizzare i flussi di lavoro e integrarti con altri sistemi.",
"TYPES": {
"request": "Richiesta",
"response": "Risposta",
- "events": "Eventi",
+ "event": "Eventi",
"function": "Funzione"
},
"DIALOG": {
@@ -565,6 +567,7 @@
"TITLE": "Tutte",
"DESCRIPTION": "Seleziona questa opzione se vuoi eseguire la tua azione su ogni richiesta"
},
+ "ALL_EVENTS": "Seleziona questo se vuoi eseguire la tua azione a ogni evento",
"SELECT_SERVICE": {
"TITLE": "Seleziona servizio",
"DESCRIPTION": "Scegli un servizio Zitadel per la tua azione."
@@ -618,6 +621,7 @@
"restCall": "Chiamata REST",
"restAsync": "REST Asincrono"
},
+ "TYPES_DESCRIPTION": "Webhook, la chiamata gestisce il codice di stato ma la risposta è irrilevante\nCall, la chiamata gestisce il codice di stato e la risposta\nAsync, la chiamata non gestisce né il codice di stato né la risposta, ma può essere eseguita in parallelo con altri obiettivi",
"ENDPOINT": "Endpoint",
"ENDPOINT_DESCRIPTION": "Inserisci l'endpoint in cui è ospitato il tuo codice. Assicurati che sia accessibile per noi!",
"TIMEOUT": "Timeout",
@@ -1508,7 +1512,8 @@
"APPEARANCE": "Aspetto",
"OTHER": "Altro",
"STORAGE": "Dati"
- }
+ },
+ "BETA": "BETA"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json
index c0429ec816..288d491ce7 100644
--- a/console/src/assets/i18n/ja.json
+++ b/console/src/assets/i18n/ja.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "フロー",
"DESCRIPTION": "認証フローを選択し、そのフロー内の特定のイベントでアクションをトリガーします。"
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2(アクションズV2)、改善された新しいバージョンが利用可能になりました。現在のバージョンも引き続き利用可能ですが、今後の開発は新バージョンに集中し、最終的には現在のバージョンを置き換える予定です。"
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "アプライ"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "現在、新しいActions V2(ベータ版)を使用しています。以前のバージョン1はまだ利用可能ですが、今後廃止される予定です。問題やフィードバックがあればお知らせください。",
"EXECUTION": {
"TITLE": "アクション",
"DESCRIPTION": "アクションを使用すると、APIリクエスト、イベント、または特定の関数に応答してカスタムコードを実行できます。これらを使用して、Zitadelを拡張し、ワークフローを自動化し、他のシステムと統合します。",
"TYPES": {
"request": "リクエスト",
"response": "レスポンス",
- "events": "イベント",
+ "event": "イベント",
"function": "関数"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "すべて",
"DESCRIPTION": "すべてのリクエストでアクションを実行する場合は、これを選択します"
},
+ "ALL_EVENTS": "すべてのイベントでアクションを実行する場合はこれを選択してください",
"SELECT_SERVICE": {
"TITLE": "サービスを選択",
"DESCRIPTION": "アクションのZitadelサービスを選択します。"
@@ -619,6 +622,7 @@
"restCall": "REST 呼び出し",
"restAsync": "REST 非同期"
},
+ "TYPES_DESCRIPTION": "Webhook、呼び出しはステータスコードを処理しますが、応答は無関係です\nCall、呼び出しはステータスコードと応答を処理します\nAsync、呼び出しはステータスコードも応答も処理しませんが、他のターゲットと並行して呼び出すことができます",
"ENDPOINT": "エンドポイント",
"ENDPOINT_DESCRIPTION": "コードがホストされているエンドポイントを入力します。アクセス可能であることを確認してください。",
"TIMEOUT": "タイムアウト",
@@ -1508,7 +1512,8 @@
"APPEARANCE": "設定",
"OTHER": "その他",
"STORAGE": "ストレージ"
- }
+ },
+ "BETA": "ベータ"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/ko.json b/console/src/assets/i18n/ko.json
index b791234cd5..437c43a3a1 100644
--- a/console/src/assets/i18n/ko.json
+++ b/console/src/assets/i18n/ko.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "플로우",
"DESCRIPTION": "인증 플로우를 선택하고 이 플로우 내의 특정 이벤트에서 작업을 트리거하세요."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, 개선된 새로운 버전이 출시되었습니다. 현재 버전은 여전히 접근할 수 있지만, 앞으로의 개발은 새로운 버전에 집중될 것이며, 결국 현재 버전을 대체할 것입니다."
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "적용"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "현재 베타 버전인 새로운 Actions V2를 사용하고 있습니다. 이전 버전 1은 여전히 사용 가능하지만, 향후 중단될 예정입니다. 문제나 피드백이 있으면 알려주세요.",
"EXECUTION": {
"TITLE": "작업",
"DESCRIPTION": "작업을 통해 API 요청, 이벤트 또는 특정 함수에 대한 응답으로 사용자 지정 코드를 실행할 수 있습니다. 이를 사용하여 Zitadel을 확장하고 워크플로를 자동화하며 다른 시스템과 통합합니다.",
"TYPES": {
"request": "요청",
"response": "응답",
- "events": "이벤트",
+ "event": "이벤트",
"function": "함수"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "모두",
"DESCRIPTION": "모든 요청에서 작업을 실행하려면 이것을 선택하십시오."
},
+ "ALL_EVENTS": "모든 이벤트에서 작업을 실행하려면 이 항목을 선택하세요",
"SELECT_SERVICE": {
"TITLE": "서비스 선택",
"DESCRIPTION": "작업에 대한 Zitadel 서비스를 선택하십시오."
@@ -619,6 +622,7 @@
"restCall": "REST 호출",
"restAsync": "REST 비동기"
},
+ "TYPES_DESCRIPTION": "Webhook, 호출은 상태 코드를 처리하지만 응답은 중요하지 않습니다\nCall, 호출은 상태 코드와 응답을 처리합니다\nAsync, 호출은 상태 코드나 응답을 처리하지 않지만 다른 대상과 병렬로 호출할 수 있습니다",
"ENDPOINT": "엔드포인트",
"ENDPOINT_DESCRIPTION": "코드가 호스팅되는 엔드포인트를 입력하십시오. 우리에게 액세스할 수 있는지 확인하십시오!",
"TIMEOUT": "시간 초과",
@@ -1508,7 +1512,8 @@
"APPEARANCE": "외형",
"OTHER": "기타",
"STORAGE": "저장소"
- }
+ },
+ "BETA": "베타"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/mk.json b/console/src/assets/i18n/mk.json
index 22e0e6d3d7..2e62723939 100644
--- a/console/src/assets/i18n/mk.json
+++ b/console/src/assets/i18n/mk.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Текови",
"DESCRIPTION": "Изберете тек на автентификација и активирајте ја вашата акција на специфичен настан во тој тек."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, нова и подобрена верзија на Actions, сега е достапна. Сегашната верзија сè уште е достапна, но идниот развој ќе биде насочен кон новата верзија, која на крајот ќе ја замени сегашната."
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "Пријавете се"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "",
"EXECUTION": {
"TITLE": "Акции",
"DESCRIPTION": "Акциите ви овозможуваат да извршувате прилагоден код како одговор на API барања, настани или специфични функции. Користете ги за да го проширите Zitadel, да ги автоматизирате работните процеси и да се интегрирате со други системи.",
"TYPES": {
"request": "Барање",
"response": "Одговор",
- "events": "Настани",
+ "event": "Настани",
"function": "Функција"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "Сите",
"DESCRIPTION": "Изберете го ова ако сакате да ја извршите вашата акција на секое барање"
},
+ "ALL_EVENTS": "Изберете го ова ако сакате вашата акција да се извршува на секој настан",
"SELECT_SERVICE": {
"TITLE": "Изберете услуга",
"DESCRIPTION": "Изберете Zitadel услуга за вашата акција."
@@ -619,6 +622,7 @@
"restCall": "REST Повик",
"restAsync": "REST Асинхроно"
},
+ "TYPES_DESCRIPTION": "Webhook, повикот го обработува статусниот код но одговорот е ирелевантен\nCall, повикот го обработува статусниот код и одговорот\nAsync, повикот не го обработува ниту статусниот код ниту одговорот, но може да се повика паралелно со други цели",
"ENDPOINT": "Крајна точка",
"ENDPOINT_DESCRIPTION": "Внесете ја крајната точка каде што е хостиран вашиот код. Осигурете се дека е достапна за нас!",
"TIMEOUT": "Време на истекување",
@@ -1509,7 +1513,8 @@
"APPEARANCE": "Изглед",
"OTHER": "Друго",
"STORAGE": "складирање"
- }
+ },
+ "BETA": "БЕТА"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/nl.json b/console/src/assets/i18n/nl.json
index 112474e770..7e549f64ba 100644
--- a/console/src/assets/i18n/nl.json
+++ b/console/src/assets/i18n/nl.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Stromen",
"DESCRIPTION": "Kies een authenticatiestroom en activeer je actie bij een specifieke gebeurtenis binnen deze stroom."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, een nieuwe en verbeterde versie van Actions, is nu beschikbaar. De huidige versie blijft toegankelijk, maar onze toekomstige ontwikkeling zal zich richten op de nieuwe versie, die uiteindelijk de huidige zal vervangen."
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "Toepassen"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "U gebruikt momenteel de nieuwe Actions V2, die zich in de bètaversie bevindt. De vorige versie 1 is nog beschikbaar maar zal in de toekomst worden stopgezet. Meld alstublieft eventuele problemen of feedback.",
"EXECUTION": {
"TITLE": "Acties",
"DESCRIPTION": "Met acties kunt u aangepaste code uitvoeren als reactie op API-verzoeken, gebeurtenissen of specifieke functies. Gebruik ze om Zitadel uit te breiden, workflows te automatiseren en te integreren met andere systemen.",
"TYPES": {
"request": "Verzoek",
"response": "Reactie",
- "events": "Gebeurtenissen",
+ "event": "Gebeurtenissen",
"function": "Functie"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "Alle",
"DESCRIPTION": "Selecteer dit als u uw actie bij elk verzoek wilt uitvoeren"
},
+ "ALL_EVENTS": "Selecteer dit als je je actie bij elk evenement wilt uitvoeren",
"SELECT_SERVICE": {
"TITLE": "Service selecteren",
"DESCRIPTION": "Kies een Zitadel-service voor uw actie."
@@ -619,6 +622,7 @@
"restCall": "REST Aanroep",
"restAsync": "REST Asynchroon"
},
+ "TYPES_DESCRIPTION": "Webhook, de oproep verwerkt de statuscode maar de reactie is irrelevant\nCall, de oproep verwerkt de statuscode en de reactie\nAsync, de oproep verwerkt noch de statuscode noch de reactie, maar kan parallel aan andere doelen worden aangeroepen",
"ENDPOINT": "Eindpunt",
"ENDPOINT_DESCRIPTION": "Voer het eindpunt in waar uw code wordt gehost. Zorg ervoor dat het voor ons toegankelijk is!",
"TIMEOUT": "Time-out",
@@ -1508,7 +1512,8 @@
"APPEARANCE": "Verschijning",
"OTHER": "Andere",
"STORAGE": "opslag"
- }
+ },
+ "BETA": "BÈTA"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json
index 3244ccb4a6..2f18c343f7 100644
--- a/console/src/assets/i18n/pl.json
+++ b/console/src/assets/i18n/pl.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Przepływy",
"DESCRIPTION": "Wybierz przepływ uwierzytelniania i wywołaj swoją akcję przy określonym zdarzeniu w tym przepływie."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, nowa, ulepszona wersja Actions, jest już dostępna. Obecna wersja jest nadal dostępna, ale przyszły rozwój będzie skoncentrowany na nowej wersji, która ostatecznie zastąpi obecną."
},
"SETTINGS": {
"INSTANCE": {
@@ -528,13 +529,14 @@
"APPLY": "Stosować"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "Obecnie korzystasz z nowej wersji Actions V2, która jest w fazie beta. Poprzednia wersja 1 jest nadal dostępna, ale w przyszłości zostanie wycofana. Prosimy o zgłaszanie wszelkich problemów lub opinii.",
"EXECUTION": {
"TITLE": "Akcje",
"DESCRIPTION": "Akcje umożliwiają uruchamianie niestandardowego kodu w odpowiedzi na żądania API, zdarzenia lub określone funkcje. Użyj ich, aby rozszerzyć Zitadel, zautomatyzować przepływy pracy i zintegrować się z innymi systemami.",
"TYPES": {
"request": "Żądanie",
"response": "Odpowiedź",
- "events": "Zdarzenia",
+ "event": "Zdarzenia",
"function": "Funkcja"
},
"DIALOG": {
@@ -565,6 +567,7 @@
"TITLE": "Wszystkie",
"DESCRIPTION": "Wybierz tę opcję, jeśli chcesz uruchomić akcję dla każdego żądania"
},
+ "ALL_EVENTS": "Wybierz to, jeśli chcesz uruchamiać swoją akcję przy każdym zdarzeniu",
"SELECT_SERVICE": {
"TITLE": "Wybierz usługę",
"DESCRIPTION": "Wybierz usługę Zitadel dla swojej akcji."
@@ -618,6 +621,7 @@
"restCall": "Wywołanie REST",
"restAsync": "REST Asynchroniczny"
},
+ "TYPES_DESCRIPTION": "Webhook, wywołanie obsługuje kod stanu, ale odpowiedź jest nieistotna\nCall, wywołanie obsługuje kod stanu i odpowiedź\nAsync, wywołanie nie obsługuje ani kodu stanu, ani odpowiedzi, ale może być wywoływane równolegle z innymi celami",
"ENDPOINT": "Punkt końcowy",
"ENDPOINT_DESCRIPTION": "Wprowadź punkt końcowy, w którym hostowany jest Twój kod. Upewnij się, że jest dla nas dostępny!",
"TIMEOUT": "Limit czasu",
@@ -1507,7 +1511,8 @@
"APPEARANCE": "Wygląd",
"OTHER": "Inne",
"STORAGE": "składowanie"
- }
+ },
+ "BETA": "BETA"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/pt.json b/console/src/assets/i18n/pt.json
index 30b0f1d4e8..08181f6ead 100644
--- a/console/src/assets/i18n/pt.json
+++ b/console/src/assets/i18n/pt.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Fluxos",
"DESCRIPTION": "Escolha um fluxo de autenticação e acione sua ação em um evento específico dentro desse fluxo."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, uma nova e melhorada versão de Actions, já está disponível. A versão atual ainda é acessível, mas o nosso desenvolvimento futuro se concentrará na nova versão, que acabará por substituir a atual."
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "Aplicar"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "Você está atualmente usando a nova Actions V2, que está em versão beta. A versão anterior 1 ainda está disponível, mas será descontinuada no futuro. Por favor, reporte quaisquer problemas ou envie feedback.",
"EXECUTION": {
"TITLE": "Ações",
"DESCRIPTION": "As ações permitem que você execute código personalizado em resposta a solicitações de API, eventos ou funções específicas. Use-as para estender o Zitadel, automatizar fluxos de trabalho e integrar-se a outros sistemas.",
"TYPES": {
"request": "Solicitação",
"response": "Resposta",
- "events": "Eventos",
+ "event": "Eventos",
"function": "Função"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "Todas",
"DESCRIPTION": "Selecione isso se você quiser executar sua ação em cada solicitação"
},
+ "ALL_EVENTS": "Selecione isto se quiser executar sua ação em cada evento",
"SELECT_SERVICE": {
"TITLE": "Selecionar Serviço",
"DESCRIPTION": "Escolha um Serviço Zitadel para sua ação."
@@ -619,6 +622,7 @@
"restCall": "Chamada REST",
"restAsync": "REST Assíncrono"
},
+ "TYPES_DESCRIPTION": "Webhook, a chamada lida com o código de status, mas a resposta é irrelevante\nCall, a chamada lida com o código de status e a resposta\nAsync, a chamada não lida nem com o código de status nem com a resposta, mas pode ser chamada em paralelo com outros alvos",
"ENDPOINT": "Ponto de Extremidade",
"ENDPOINT_DESCRIPTION": "Insira o ponto de extremidade onde seu código está hospedado. Certifique-se de que ele esteja acessível para nós!",
"TIMEOUT": "Tempo Limite",
@@ -1509,7 +1513,8 @@
"APPEARANCE": "Aparência",
"OTHER": "Outro",
"STORAGE": "armazenar"
- }
+ },
+ "BETA": "BETA"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/ro.json b/console/src/assets/i18n/ro.json
index 6c73852beb..b07897f316 100644
--- a/console/src/assets/i18n/ro.json
+++ b/console/src/assets/i18n/ro.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Fluxuri",
"DESCRIPTION": "Alegeți un flux de autentificare și declanșați acțiunea dvs. la un anumit eveniment din cadrul acestui flux."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, o nouă versiune îmbunătățită a Actions, este acum disponibilă. Versiunea actuală este încă accesibilă, dar dezvoltarea viitoare se va concentra pe cea nouă, care în cele din urmă va înlocui versiunea actuală."
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "Aplicați"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "În prezent utilizați noua versiune Actions V2, care este în faza beta. Versiunea anterioară 1 este încă disponibilă, dar va fi întreruptă în viitor. Vă rugăm să raportați orice problemă sau feedback.",
"EXECUTION": {
"TITLE": "Acțiuni",
"DESCRIPTION": "Acțiunile vă permit să rulați cod personalizat ca răspuns la cereri API, evenimente sau funcții specifice. Folosiți-le pentru a extinde Zitadel, a automatiza fluxurile de lucru și a vă integra cu alte sisteme.",
"TYPES": {
"request": "Cerere",
"response": "Răspuns",
- "events": "Evenimente",
+ "event": "Evenimente",
"function": "Funcție"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "Toate",
"DESCRIPTION": "Selectați aceasta dacă doriți să rulați acțiunea la fiecare cerere"
},
+ "ALL_EVENTS": "Selectează aceasta dacă vrei să rulezi acțiunea ta la fiecare eveniment",
"SELECT_SERVICE": {
"TITLE": "Selectați Serviciul",
"DESCRIPTION": "Alegeți un Serviciu Zitadel pentru acțiunea dvs."
@@ -619,6 +622,7 @@
"restCall": "Apel REST",
"restAsync": "REST Asincron"
},
+ "TYPES_DESCRIPTION": "Webhook, apelul gestionează codul de stare, dar răspunsul este irelevant\nCall, apelul gestionează codul de stare și răspunsul\nAsync, apelul nu gestionează nici codul de stare, nici răspunsul, dar poate fi apelat în paralel cu alte Ținte",
"ENDPOINT": "Punct Final",
"ENDPOINT_DESCRIPTION": "Introduceți punctul final unde este găzduit codul dvs. Asigurați-vă că este accesibil pentru noi!",
"TIMEOUT": "Timeout",
@@ -1506,7 +1510,8 @@
"APPEARANCE": "Aspect",
"OTHER": "Altele",
"STORAGE": "Stocare"
- }
+ },
+ "BETA": "BETA"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/ru.json b/console/src/assets/i18n/ru.json
index 6e2d7df7b4..c6ef31499e 100644
--- a/console/src/assets/i18n/ru.json
+++ b/console/src/assets/i18n/ru.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Потоки",
"DESCRIPTION": "Выберите поток аутентификации и активируйте ваше действие на определенном событии в этом потоке."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, новая и улучшенная версия Actions, теперь доступна. Текущая версия всё ещё доступна, но дальнейшая разработка будет сосредоточена на новой версии, которая в конечном итоге заменит текущую."
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "Применять"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "Вы используете новую версию Actions V2, которая находится в бета-тестировании. Предыдущая версия 1 всё ещё доступна, но будет отключена в будущем. Пожалуйста, сообщайте о любых проблемах или отправляйте отзывы.",
"EXECUTION": {
"TITLE": "Действия",
"DESCRIPTION": "Действия позволяют запускать пользовательский код в ответ на API-запросы, события или определенные функции. Используйте их для расширения Zitadel, автоматизации рабочих процессов и интеграции с другими системами.",
"TYPES": {
"request": "Запрос",
"response": "Ответ",
- "events": "События",
+ "event": "События",
"function": "Функция"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "Все",
"DESCRIPTION": "Выберите это, если вы хотите запустить свое действие при каждом запросе"
},
+ "ALL_EVENTS": "Выберите это, если хотите выполнять действие при каждом событии",
"SELECT_SERVICE": {
"TITLE": "Выбрать службу",
"DESCRIPTION": "Выберите службу Zitadel для вашего действия."
@@ -619,6 +622,7 @@
"restCall": "REST Вызов",
"restAsync": "REST Асинхронный"
},
+ "TYPES_DESCRIPTION": "Webhook, вызов обрабатывает код состояния, но ответ не имеет значения\nCall, вызов обрабатывает код состояния и ответ\nAsync, вызов не обрабатывает ни код состояния, ни ответ, но может выполняться параллельно с другими целями",
"ENDPOINT": "Конечная точка",
"ENDPOINT_DESCRIPTION": "Введите конечную точку, где размещен ваш код. Убедитесь, что он доступен для нас!",
"TIMEOUT": "Тайм-аут",
@@ -1553,7 +1557,8 @@
"APPEARANCE": "Вид",
"OTHER": "Другое",
"STORAGE": "хранилище"
- }
+ },
+ "BETA": "БЕТА"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/sv.json b/console/src/assets/i18n/sv.json
index e747571f7a..c356e635e0 100644
--- a/console/src/assets/i18n/sv.json
+++ b/console/src/assets/i18n/sv.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "Flöden",
"DESCRIPTION": "Välj ett autentiseringsflöde och trigga din åtgärd vid en specifik händelse inom detta flöde."
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2, en ny och förbättrad version av Actions, är nu tillgänglig. Den nuvarande versionen är fortfarande tillgänglig, men framtida utveckling kommer att fokusera på den nya, som så småningom kommer att ersätta den nuvarande."
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "Tillämpa"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "Du använder för närvarande nya Actions V2, som är i betaversion. Den tidigare versionen 1 är fortfarande tillgänglig men kommer att avvecklas i framtiden. Vänligen rapportera eventuella problem eller ge feedback.",
"EXECUTION": {
"TITLE": "Åtgärder",
"DESCRIPTION": "Åtgärder låter dig köra anpassad kod som svar på API-förfrågningar, händelser eller specifika funktioner. Använd dem för att utöka Zitadel, automatisera arbetsflöden och integrera med andra system.",
"TYPES": {
"request": "Förfrågan",
"response": "Svar",
- "events": "Händelser",
+ "event": "Händelser",
"function": "Funktion"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "Alla",
"DESCRIPTION": "Välj detta om du vill köra din åtgärd på varje förfrågan"
},
+ "ALL_EVENTS": "Välj detta om du vill köra din åtgärd vid varje händelse",
"SELECT_SERVICE": {
"TITLE": "Välj tjänst",
"DESCRIPTION": "Välj en Zitadel-tjänst för din åtgärd."
@@ -619,6 +622,7 @@
"restCall": "REST Anrop",
"restAsync": "REST Asynkron"
},
+ "TYPES_DESCRIPTION": "Webhook, anropet hanterar statuskoden men svaret är irrelevant\nCall, anropet hanterar statuskoden och svaret\nAsync, anropet hanterar varken statuskod eller svar men kan anropas parallellt med andra mål",
"ENDPOINT": "Slutpunkt",
"ENDPOINT_DESCRIPTION": "Ange slutpunkten där din kod finns. Se till att den är tillgänglig för oss!",
"TIMEOUT": "Tidsgräns",
@@ -1512,7 +1516,8 @@
"APPEARANCE": "Utseende",
"OTHER": "Övrigt",
"STORAGE": "Lagring"
- }
+ },
+ "BETA": "BETA"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json
index 945e4200ef..8be3316b0b 100644
--- a/console/src/assets/i18n/zh.json
+++ b/console/src/assets/i18n/zh.json
@@ -75,7 +75,8 @@
"FLOWS": {
"TITLE": "流程",
"DESCRIPTION": "选择一个认证流程,并在该流程中的特定事件上触发您的操作。"
- }
+ },
+ "ACTIONSTWO_NOTE": "Actions V2,一个全新改进版的Actions,现在已上线。目前版本仍可使用,但未来开发将专注于新版本,最终将取代当前版本。"
},
"SETTINGS": {
"INSTANCE": {
@@ -529,13 +530,14 @@
"APPLY": "申请"
},
"ACTIONSTWO": {
+ "BETA_NOTE": "您目前正在使用新的 Actions V2(测试版)。之前的版本1仍可使用,但未来将停止支持。请报告任何问题或反馈意见。",
"EXECUTION": {
"TITLE": "操作",
"DESCRIPTION": "操作允许您运行自定义代码以响应 API 请求、事件或特定函数。使用它们来扩展 Zitadel、自动化工作流程并与其他系统集成。",
"TYPES": {
"request": "请求",
"response": "响应",
- "events": "事件",
+ "event": "事件",
"function": "函数"
},
"DIALOG": {
@@ -566,6 +568,7 @@
"TITLE": "全部",
"DESCRIPTION": "如果您希望在每个请求上运行您的操作,请选择此项"
},
+ "ALL_EVENTS": "如果您想在每个事件上运行操作,请选择此项",
"SELECT_SERVICE": {
"TITLE": "选择服务",
"DESCRIPTION": "为您的操作选择一个 Zitadel 服务。"
@@ -619,6 +622,7 @@
"restCall": "REST 调用",
"restAsync": "REST 异步"
},
+ "TYPES_DESCRIPTION": "Webhook,调用处理状态码但响应无关紧要\nCall,调用处理状态码和响应\nAsync,调用既不处理状态码也不处理响应,但可以与其他目标并行调用",
"ENDPOINT": "端点",
"ENDPOINT_DESCRIPTION": "输入您的代码托管的端点。确保我们可以访问它!",
"TIMEOUT": "超时",
@@ -1508,7 +1512,8 @@
"APPEARANCE": "外观",
"OTHER": "其他",
"STORAGE": "贮存"
- }
+ },
+ "BETA": "测试版"
},
"SETTING": {
"LANGUAGES": {
diff --git a/console/yarn.lock b/console/yarn.lock
index 5dd602dfa8..2e586abedb 100644
--- a/console/yarn.lock
+++ b/console/yarn.lock
@@ -3452,29 +3452,22 @@
js-yaml "^3.10.0"
tslib "^2.4.0"
-"@zitadel/client@^1.0.7":
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/@zitadel/client/-/client-1.0.7.tgz#39dc8d3d10bfa01e5cf56205ba188f79c39f052d"
- integrity sha512-sZG4NEa8vQBt3+4W1AesY+5DstDBuZiqGH2EM+UqbO5D93dlDZInXqZ5oRE7RSl2Bk5ED9mbMFrB7b8DuRw72A==
+"@zitadel/client@1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@zitadel/client/-/client-1.2.0.tgz#8cdc3090f75fcf3a78c4f0266d3c56a0cca6821a"
+ integrity sha512-Q20nXhKD7VDb8D1UxhDxubC70GFrSPckrJviPR/rAfRR5slUIRTk3AvDS6Q1WvUn4Xtt+btnq52Z5O8lZtVG0w==
dependencies:
"@bufbuild/protobuf" "^2.2.2"
"@connectrpc/connect" "^2.0.0"
"@connectrpc/connect-node" "^2.0.0"
"@connectrpc/connect-web" "^2.0.0"
- "@zitadel/proto" "1.0.4"
+ "@zitadel/proto" "1.2.0"
jose "^5.3.0"
-"@zitadel/proto@1.0.4":
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/@zitadel/proto/-/proto-1.0.4.tgz#e2fe9895f2960643c3619191255aa2f4913ad873"
- integrity sha512-s13ZMhuOTe0b+geV+JgJud+kpYdq7TgkuCe7RIY+q4Xs5KC0FHMKfvbAk/jpFbD+TSQHiwo/TBNZlGHdwUR9Ig==
- dependencies:
- "@bufbuild/protobuf" "^2.2.2"
-
-"@zitadel/proto@1.0.5-sha-4118a9d":
- version "1.0.5-sha-4118a9d"
- resolved "https://registry.yarnpkg.com/@zitadel/proto/-/proto-1.0.5-sha-4118a9d.tgz#e09025f31b2992b061d5416a0d1e12ef370118cc"
- integrity sha512-7ZFwISL7TqdCkfEUx7/H6UJDqX8ZP2jqG1ulbELvEQ2smrK365Zs7AkJGeB/xbVdhQW9BOhWy2R+Jni7sfxd2w==
+"@zitadel/proto@1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@zitadel/proto/-/proto-1.2.0.tgz#9b9a40defcd9e8464627cc99ac3fd7bcf8994ffd"
+ integrity sha512-OqHgyCnD9l950xswdVNPIsLA01qSpOPf+0bYqYJWHafytIBbvGNJRnypu4X0LnaFXLM6LakkP4pWYeiGLmwxaw==
dependencies:
"@bufbuild/protobuf" "^2.2.2"
diff --git a/docs/docs/guides/integrate/actions/testing-event.md b/docs/docs/guides/integrate/actions/testing-event.md
index 69a33f6d3e..8b4502703b 100644
--- a/docs/docs/guides/integrate/actions/testing-event.md
+++ b/docs/docs/guides/integrate/actions/testing-event.md
@@ -145,7 +145,14 @@ the [Sent information Event](./usage#sent-information-event) payload description
"event_type": "user.human.added",
"created_at": "2025-03-27T10:22:43.262665+01:00",
"userID": "312909075212468632",
- "event_payload": "eyJ1c2VyTmFtZSI6ImV4YW1wbGVAdGVzdC5jb20iLCJmaXJzdE5hbWUiOiJUZXN0IiwibGFzdE5hbWUiOiJVc2VyIiwiZGlzcGxheU5hbWUiOiJUZXN0IFVzZXIiLCJwcmVmZXJyZWRMYW5ndWFnZSI6InVuZCIsImVtYWlsIjoiZXhhbXBsZUB0ZXN0LmNvbSJ9"
+ "event_payload": {
+ "userName":"example@test.com",
+ "firstName":"Test",
+ "lastName":"User",
+ "displayName":"Test User",
+ "preferredLanguage":"und",
+ "email":"example@test.com"
+ }
}
```
diff --git a/docs/docs/guides/integrate/actions/testing-request-manipulation.md b/docs/docs/guides/integrate/actions/testing-request-manipulation.md
index f727b56144..1cb4f1776a 100644
--- a/docs/docs/guides/integrate/actions/testing-request-manipulation.md
+++ b/docs/docs/guides/integrate/actions/testing-request-manipulation.md
@@ -18,6 +18,11 @@ Note that this guide assumes that ZITADEL is running on the same machine as the
In case you are using a different setup, you need to adjust the target URL accordingly and will need to make sure that the target is reachable from ZITADEL.
:::
+:::warning
+To marshal and unmarshal the request please use a package like [protojson](https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson),
+as the request is a protocol buffer message, to avoid potential problems with the attribute names.
+:::
+
## Start example target
To test the actions feature, you need to create a target that will be called when an API endpoint is called.
@@ -37,10 +42,28 @@ import (
"net/http"
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
+ "google.golang.org/protobuf/encoding/protojson"
)
type contextRequest struct {
- Request *user.AddHumanUserRequest `json:"request"`
+ Request *addHumanUserRequestWrapper `json:"request"`
+}
+
+// addHumanUserRequestWrapper necessary to marshal and unmarshal the JSON into the proto message correctly
+type addHumanUserRequestWrapper struct {
+ user.AddHumanUserRequest
+}
+
+func (r *addHumanUserRequestWrapper) MarshalJSON() ([]byte, error) {
+ data, err := protojson.Marshal(r)
+ if err != nil {
+ return nil, err
+ }
+ return data, nil
+}
+
+func (r *addHumanUserRequestWrapper) UnmarshalJSON(data []byte) error {
+ return protojson.Unmarshal(data, r)
}
// call HandleFunc to read the request body, manipulate the content and return the manipulated request
diff --git a/docs/docs/guides/integrate/actions/testing-request-signature.md b/docs/docs/guides/integrate/actions/testing-request-signature.md
index 4565328e63..c1932a7d5b 100644
--- a/docs/docs/guides/integrate/actions/testing-request-signature.md
+++ b/docs/docs/guides/integrate/actions/testing-request-signature.md
@@ -18,6 +18,11 @@ Note that this guide assumes that ZITADEL is running on the same machine as the
In case you are using a different setup, you need to adjust the target URL accordingly and will need to make sure that the target is reachable from ZITADEL.
:::
+:::warning
+To marshal and unmarshal the request please use a package like [protojson](https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson),
+as the request is a protocol buffer message, to avoid potential problems with the attribute names.
+:::
+
## Start example target
To test the actions feature, you need to create a target that will be called when an API endpoint is called.
diff --git a/docs/docs/guides/integrate/actions/testing-request.md b/docs/docs/guides/integrate/actions/testing-request.md
index e97b0ef25f..b2413e606e 100644
--- a/docs/docs/guides/integrate/actions/testing-request.md
+++ b/docs/docs/guides/integrate/actions/testing-request.md
@@ -18,6 +18,11 @@ Note that this guide assumes that ZITADEL is running on the same machine as the
In case you are using a different setup, you need to adjust the target URL accordingly and will need to make sure that the target is reachable from ZITADEL.
:::
+:::warning
+To marshal and unmarshal the request please use a package like [protojson](https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson),
+as the request is a protocol buffer message, to avoid potential problems with the attribute names.
+:::
+
## Start example target
To test the actions feature, you need to create a target that will be called when an API endpoint is called.
diff --git a/docs/docs/guides/integrate/actions/testing-response-manipulation.md b/docs/docs/guides/integrate/actions/testing-response-manipulation.md
index cc10b8252a..9d95479b05 100644
--- a/docs/docs/guides/integrate/actions/testing-response-manipulation.md
+++ b/docs/docs/guides/integrate/actions/testing-response-manipulation.md
@@ -18,6 +18,11 @@ Note that this guide assumes that ZITADEL is running on the same machine as the
In case you are using a different setup, you need to adjust the target URL accordingly and will need to make sure that the target is reachable from ZITADEL.
:::
+:::warning
+To marshal and unmarshal the request and response please use a package like [protojson](https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson),
+as the request and response are protocol buffer messages, to avoid potential problems with the attribute names.
+:::
+
## Start example target
To test the actions feature, you need to create a target that will be called when an API endpoint is called.
@@ -37,11 +42,46 @@ import (
"net/http"
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
+ "google.golang.org/protobuf/encoding/protojson"
)
-type response struct {
- Request *user.RetrieveIdentityProviderIntentRequest `json:"request"`
- Response *user.RetrieveIdentityProviderIntentResponse `json:"response"`
+type contextResponse struct {
+ Request *retrieveIdentityProviderIntentRequestWrapper `json:"request"`
+ Response *retrieveIdentityProviderIntentResponseWrapper `json:"response"`
+}
+
+// RetrieveIdentityProviderIntentRequestWrapper necessary to marshal and unmarshal the JSON into the proto message correctly
+type retrieveIdentityProviderIntentRequestWrapper struct {
+ user.RetrieveIdentityProviderIntentRequest
+}
+
+func (r *retrieveIdentityProviderIntentRequestWrapper) MarshalJSON() ([]byte, error) {
+ data, err := protojson.Marshal(r)
+ if err != nil {
+ return nil, err
+ }
+ return data, nil
+}
+
+func (r *retrieveIdentityProviderIntentRequestWrapper) UnmarshalJSON(data []byte) error {
+ return protojson.Unmarshal(data, r)
+}
+
+// RetrieveIdentityProviderIntentResponseWrapper necessary to marshal and unmarshal the JSON into the proto message correctly
+type retrieveIdentityProviderIntentResponseWrapper struct {
+ user.RetrieveIdentityProviderIntentResponse
+}
+
+func (r *retrieveIdentityProviderIntentResponseWrapper) MarshalJSON() ([]byte, error) {
+ data, err := protojson.Marshal(r)
+ if err != nil {
+ return nil, err
+ }
+ return data, nil
+}
+
+func (r *retrieveIdentityProviderIntentResponseWrapper) UnmarshalJSON(data []byte) error {
+ return protojson.Unmarshal(data, r)
}
// call HandleFunc to read the response body, manipulate the content and return the response
@@ -56,7 +96,7 @@ func call(w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
// read the response into the expected structure
- request := new(response)
+ request := new(contextResponse)
if err := json.Unmarshal(sentBody, request); err != nil {
http.Error(w, "error", http.StatusInternalServerError)
}
diff --git a/docs/docs/guides/integrate/actions/testing-response.md b/docs/docs/guides/integrate/actions/testing-response.md
index 3eb824e95b..a2ab736505 100644
--- a/docs/docs/guides/integrate/actions/testing-response.md
+++ b/docs/docs/guides/integrate/actions/testing-response.md
@@ -18,6 +18,11 @@ Note that this guide assumes that ZITADEL is running on the same machine as the
In case you are using a different setup, you need to adjust the target URL accordingly and will need to make sure that the target is reachable from ZITADEL.
:::
+:::warning
+To marshal and unmarshal the request and response please use a package like [protojson](https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson),
+as the request and response are protocol buffer messages, to avoid potential problems with the attribute names.
+:::
+
## Start example target
To test the actions feature, you need to create a target that will be called when an API endpoint is called.
diff --git a/docs/docs/guides/integrate/actions/usage.md b/docs/docs/guides/integrate/actions/usage.md
index 8a639f3c27..ba512ae549 100644
--- a/docs/docs/guides/integrate/actions/usage.md
+++ b/docs/docs/guides/integrate/actions/usage.md
@@ -36,6 +36,11 @@ The information sent to the Endpoint is structured as JSON:
}
```
+:::warning
+To marshal and unmarshal the request please use a package like [protojson](https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson),
+as the request is a protocol buffer message, to avoid potential problems with the attribute names.
+:::
+
### Sent information Response
The information sent to the Endpoint is structured as JSON:
@@ -56,6 +61,11 @@ The information sent to the Endpoint is structured as JSON:
}
```
+:::warning
+To marshal and unmarshal the request and response please use a package like [protojson](https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson),
+as the request and response are protocol buffer messages, to avoid potential problems with the attribute names.
+:::
+
### Sent information Function
Information sent and expected back are specific to the function.
@@ -338,7 +348,7 @@ The information sent to the Endpoint is structured as JSON:
"event_type": "Type of the event",
"created_at": "Time the event was created",
"userID": "ID of the creator of the event",
- "event_payload": "Base64 encoded content of the event"
+ "event_payload": "Content of the event in JSON format"
}
```
diff --git a/docs/docs/sdk-examples/java.mdx b/docs/docs/sdk-examples/java.mdx
index e7afb5188b..e4862422d0 100644
--- a/docs/docs/sdk-examples/java.mdx
+++ b/docs/docs/sdk-examples/java.mdx
@@ -41,7 +41,7 @@ The following features are covered by Java Spring Security:
The goal is to have a ZITADEL Java SDK in the future which will cover the following:
- Wrapper around Java Spring Security
- Authentication with OIDC
-- Authorization and checking Rolls
+- Authorization and checking Roles
- Integrate ZITADEL APIs to read and manage resources
- Integrate ZITADEL Session API to create your own login UI
diff --git a/docs/docs/self-hosting/deploy/loadbalancing-example/.gitignore b/docs/docs/self-hosting/deploy/loadbalancing-example/.gitignore
new file mode 100644
index 0000000000..bd98bacd66
--- /dev/null
+++ b/docs/docs/self-hosting/deploy/loadbalancing-example/.gitignore
@@ -0,0 +1 @@
+.env-file
diff --git a/docs/docs/self-hosting/deploy/loadbalancing-example/docker-compose.yaml b/docs/docs/self-hosting/deploy/loadbalancing-example/docker-compose.yaml
index d1d8c95bb2..013fc2aa22 100644
--- a/docs/docs/self-hosting/deploy/loadbalancing-example/docker-compose.yaml
+++ b/docs/docs/self-hosting/deploy/loadbalancing-example/docker-compose.yaml
@@ -1,48 +1,157 @@
services:
- traefik:
+ db:
+ image: postgres:17-alpine
+ restart: unless-stopped
+ environment:
+ - POSTGRES_USER=root
+ - POSTGRES_PASSWORD=postgres
networks:
- - 'zitadel'
- image: "traefik:latest"
- ports:
- - "80:80"
- - "443:443"
+ - 'storage'
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready", "-d", "db_prod"]
+ interval: 10s
+ timeout: 60s
+ retries: 5
+ start_period: 10s
volumes:
- - "./example-traefik.yaml:/etc/traefik/traefik.yaml"
+ - 'data:/var/lib/postgresql/data:rw'
- zitadel:
- restart: 'always'
+ zitadel-init:
+ restart: 'no'
networks:
- - 'zitadel'
+ - 'storage'
image: 'ghcr.io/zitadel/zitadel:latest'
- command: 'start-from-init --config /example-zitadel-config.yaml --config /example-zitadel-secrets.yaml --steps /example-zitadel-init-steps.yaml --masterkey "${ZITADEL_MASTERKEY}" --tlsMode external'
+ command: 'init --config /example-zitadel-config.yaml --config /example-zitadel-secrets.yaml'
depends_on:
db:
condition: 'service_healthy'
volumes:
- './example-zitadel-config.yaml:/example-zitadel-config.yaml:ro'
- './example-zitadel-secrets.yaml:/example-zitadel-secrets.yaml:ro'
+
+ zitadel-setup:
+ restart: 'no'
+ networks:
+ - 'storage'
+ # We use the debug image so we have the environment to
+ # - create the .env file for the login to authenticate at Zitadel
+ # - set the correct permissions for the .env-file folder
+ image: 'ghcr.io/zitadel/zitadel:latest-debug'
+ user: root
+ entrypoint: '/bin/sh'
+ command:
+ - -c
+ - >
+ /app/zitadel setup
+ --config /example-zitadel-config.yaml
+ --config /example-zitadel-secrets.yaml
+ --steps /example-zitadel-init-steps.yaml
+ --masterkey ${ZITADEL_MASTERKEY} &&
+ mv /pat /.env-file/pat || exit 0 &&
+ echo ZITADEL_SERVICE_USER_TOKEN=$(cat /.env-file/pat) > /.env-file/.env &&
+ chown -R 1001:${GID} /.env-file &&
+ chmod -R 770 /.env-file
+ environment:
+ - GID
+ depends_on:
+ zitadel-init:
+ condition: 'service_completed_successfully'
+ restart: false
+ volumes:
+ - './.env-file:/.env-file:rw'
+ - './example-zitadel-config.yaml:/example-zitadel-config.yaml:ro'
+ - './example-zitadel-secrets.yaml:/example-zitadel-secrets.yaml:ro'
- './example-zitadel-init-steps.yaml:/example-zitadel-init-steps.yaml:ro'
- db:
- image: postgres:17-alpine
- restart: always
- environment:
- - POSTGRES_USER=root
- - POSTGRES_PASSWORD=postgres
+ zitadel:
+ restart: 'unless-stopped'
networks:
- - 'zitadel'
+ - 'backend'
+ - 'storage'
+ image: 'ghcr.io/zitadel/zitadel:latest'
+ command: >
+ start --config /example-zitadel-config.yaml
+ --config /example-zitadel-secrets.yaml
+ --masterkey ${ZITADEL_MASTERKEY}
+ depends_on:
+ zitadel-setup:
+ condition: 'service_completed_successfully'
+ restart: true
+ volumes:
+ - './example-zitadel-config.yaml:/example-zitadel-config.yaml:ro'
+ - './example-zitadel-secrets.yaml:/example-zitadel-secrets.yaml:ro'
+ ports:
+ - "8080:8080"
healthcheck:
- test: ["CMD-SHELL", "pg_isready", "-d", "db_prod"]
+ test: [
+ "CMD", "/app/zitadel", "ready",
+ "--config", "/example-zitadel-config.yaml",
+ "--config", "/example-zitadel-secrets.yaml"
+ ]
interval: 10s
timeout: 60s
retries: 5
- start_period: 10s
+ start_period: 10s
+
+ # The use-new-login service configures Zitadel to use the new login v2 for all applications.
+ # It also gives the setupped machine user the necessary IAM_LOGIN_CLIENT role.
+ use-new-login:
+ restart: 'on-failure'
+ user: "1001"
+ networks:
+ - 'backend'
+ image: 'badouralix/curl-jq:alpine'
+ entrypoint: '/bin/sh'
+ command:
+ - -c
+ - >
+ curl -X PUT -H "Host: 127.0.0.1.sslip.io" -H "Authorization: Bearer $(cat ./.env-file/pat)" --insecure http://zitadel:8080/v2/features/instance -d '{"loginV2": {"required": true}}' &&
+ LOGIN_USER=$(curl --fail-with-body -H "Host: 127.0.0.1.sslip.io" -H "Authorization: Bearer $(cat ./.env-file/pat)" --insecure http://zitadel:8080/auth/v1/users/me | jq -r '.user.id') &&
+ curl -X PUT -H "Host: 127.0.0.1.sslip.io" -H "Authorization: Bearer $(cat ./.env-file/pat)" --insecure http://zitadel:8080/admin/v1/members/$${LOGIN_USER} -d '{"roles": ["IAM_OWNER", "IAM_LOGIN_CLIENT"]}'
volumes:
- - 'data:/var/lib/postgresql/data:rw'
+ - './.env-file:/.env-file:ro'
+ depends_on:
+ zitadel:
+ condition: 'service_healthy'
+ restart: false
+
+ login:
+ restart: 'unless-stopped'
+ networks:
+ - 'backend'
+ image: 'ghcr.io/zitadel/login:main'
+ environment:
+ - ZITADEL_API_URL=http://zitadel:8080
+ - CUSTOM_REQUEST_HEADERS=Host:127.0.0.1.sslip.io
+ - NEXT_PUBLIC_BASE_PATH="/ui/v2/login"
+ user: "${UID:-1000}"
+ volumes:
+ - './.env-file:/.env-file:ro'
+ depends_on:
+ zitadel:
+ condition: 'service_healthy'
+ restart: false
+
+ traefik:
+ restart: 'unless-stopped'
+ networks:
+ - 'backend'
+ image: "traefik:latest"
+ ports:
+ - "80:80"
+ - "443:443"
+ volumes:
+ - "./example-traefik.yaml:/etc/traefik/traefik.yaml"
+ depends_on:
+ zitadel:
+ condition: 'service_healthy'
+ login:
+ condition: 'service_started'
networks:
- zitadel:
+ storage:
+ backend:
volumes:
data:
diff --git a/docs/docs/self-hosting/deploy/loadbalancing-example/example-traefik.yaml b/docs/docs/self-hosting/deploy/loadbalancing-example/example-traefik.yaml
index c16f74a46d..a3af425172 100644
--- a/docs/docs/self-hosting/deploy/loadbalancing-example/example-traefik.yaml
+++ b/docs/docs/self-hosting/deploy/loadbalancing-example/example-traefik.yaml
@@ -4,66 +4,37 @@ log:
accessLog: {}
entrypoints:
- web:
- address: ":80"
-
websecure:
address: ":443"
-tls:
- stores:
- default:
- # generates self-signed certificates
- defaultCertificate:
-
providers:
file:
filename: /etc/traefik/traefik.yaml
http:
- middlewares:
- zitadel:
- headers:
- isDevelopment: false
- allowedHosts:
- - 'my.domain'
- customRequestHeaders:
- authority: 'my.domain'
- redirect-to-https:
- redirectScheme:
- scheme: https
- port: 443
- permanent: true
-
routers:
- # Redirect HTTP to HTTPS
- router0:
+ login:
entryPoints:
- - web
- middlewares:
- - redirect-to-https
- rule: 'HostRegexp(`my.domain`, `{subdomain:[a-z]+}.my.domain`)'
- service: zitadel
- # The actual ZITADEL router
- router1:
+ - websecure
+ service: login
+ rule: 'Host(`127.0.0.1.sslip.io`) && PathPrefix(`/ui/v2/login`)'
+ tls: {}
+ zitadel:
entryPoints:
- websecure
service: zitadel
- middlewares:
- - zitadel
- rule: 'HostRegexp(`my.domain`, `{subdomain:[a-z]+}.my.domain`)'
- tls:
- domains:
- - main: "my.domain"
- sans:
- - "*.my.domain"
- - "my.domain"
+ rule: 'Host(`127.0.0.1.sslip.io`) && !PathPrefix(`/ui/v2/login`)'
+ tls: {}
- # Add the service
services:
+ login:
+ loadBalancer:
+ servers:
+ - url: http://login:3000
+ passHostHeader: true
zitadel:
loadBalancer:
servers:
- # h2c is the scheme for unencrypted HTTP/2
- url: h2c://zitadel:8080
passHostHeader: true
+
diff --git a/docs/docs/self-hosting/deploy/loadbalancing-example/example-zitadel-config.yaml b/docs/docs/self-hosting/deploy/loadbalancing-example/example-zitadel-config.yaml
index 392bf1148e..fadd39373d 100644
--- a/docs/docs/self-hosting/deploy/loadbalancing-example/example-zitadel-config.yaml
+++ b/docs/docs/self-hosting/deploy/loadbalancing-example/example-zitadel-config.yaml
@@ -1,26 +1,29 @@
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/main/cmd/defaults.yaml
-Log:
- Level: 'info'
-# Make ZITADEL accessible over HTTPs, not HTTP
ExternalSecure: true
-ExternalDomain: my.domain
+ExternalDomain: 127.0.0.1.sslip.io
ExternalPort: 443
+# Traefik terminates TLS. Inside the Docker network, we use plain text.
+TLS.Enabled: false
+
# If not using the docker compose example, adjust these values for connecting ZITADEL to your PostgreSQL
Database:
postgres:
Host: 'db'
Port: 5432
Database: zitadel
- User:
- SSL:
- Mode: 'disable'
- Admin:
- SSL:
- Mode: 'disable'
+ User.SSL.Mode: 'disable'
+ Admin.SSL.Mode: 'disable'
-LogStore:
- Access:
- Stdout:
- Enabled: true
+# By default, ZITADEL should redirect to /ui/v2/login
+OIDC:
+ DefaultLoginURLV2: "/ui/v2/login/login?authRequest=" # ZITADEL_OIDC_DEFAULTLOGINURLV2
+ DefaultLogoutURLV2: "/ui/v2/login/logout?post_logout_redirect=" # ZITADEL_OIDC_DEFAULTLOGOUTURLV2
+SAML.DefaultLoginURLV2: "/ui/v2/login/login?authRequest=" # ZITADEL_SAML_DEFAULTLOGINURLV2
+
+# Access logs allow us to debug Network issues
+LogStore.Access.Stdout.Enabled: true
+
+# Skipping the MFA init step allows us to immediately authenticate at the console
+DefaultInstance.LoginPolicy.MfaInitSkipLifetime: "0s"
diff --git a/docs/docs/self-hosting/deploy/loadbalancing-example/example-zitadel-init-steps.yaml b/docs/docs/self-hosting/deploy/loadbalancing-example/example-zitadel-init-steps.yaml
index 804e3d18d8..be63164ced 100644
--- a/docs/docs/self-hosting/deploy/loadbalancing-example/example-zitadel-init-steps.yaml
+++ b/docs/docs/self-hosting/deploy/loadbalancing-example/example-zitadel-init-steps.yaml
@@ -1,8 +1,12 @@
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/main/cmd/setup/steps.yaml
FirstInstance:
+ PatPath: '/pat'
Org:
- Name: 'My Org'
+ # We want to authenticate immediately at the console without changing the password
Human:
- # use the loginname root@my-org.my.domain
- Username: 'root'
- Password: 'RootPassword1!'
+ PasswordChangeRequired: false
+ Machine:
+ Machine:
+ Username: 'login-container'
+ Name: 'Login Container'
+ Pat.ExpirationDate: '2029-01-01T00:00:00Z'
diff --git a/docs/docs/self-hosting/deploy/loadbalancing-example/loadbalancing-example.mdx b/docs/docs/self-hosting/deploy/loadbalancing-example/loadbalancing-example.mdx
index d5e3984568..d4c27ccd95 100644
--- a/docs/docs/self-hosting/deploy/loadbalancing-example/loadbalancing-example.mdx
+++ b/docs/docs/self-hosting/deploy/loadbalancing-example/loadbalancing-example.mdx
@@ -1,5 +1,5 @@
---
-title: A ZITADEL Load Balancing Example
+title: A Zitadel Load Balancing Example
---
import CodeBlock from '@theme/CodeBlock';
@@ -8,16 +8,16 @@ import ExampleTraefikSource from '!!raw-loader!./example-traefik.yaml'
import ExampleZITADELConfigSource from '!!raw-loader!./example-zitadel-config.yaml'
import ExampleZITADELSecretsSource from '!!raw-loader!./example-zitadel-secrets.yaml'
import ExampleZITADELInitStepsSource from '!!raw-loader!./example-zitadel-init-steps.yaml'
-import NoteInstanceNotFound from '../troubleshooting/_note_instance_not_found.mdx';
-With this example configuration, you create a near production environment for ZITADEL with [Docker Compose](https://docs.docker.com/compose/).
-
-The stack consists of three long-running containers:
-- A [Traefik](https://doc.traefik.io/traefik/) reverse proxy with upstream HTTP/2 enabled, issuing a self-signed TLS certificate.
-- A secure ZITADEL container configured for a custom domain. As we terminate TLS with Traefik, we configure ZITADEL for `--tlsMode external`.
+The stack consists of four long-running containers and a couple of short-lived containers:
+- A [Traefik](https://doc.traefik.io/traefik/) reverse proxy container with upstream HTTP/2 enabled, issuing a self-signed TLS certificate.
+- A Login container that is accessible via Traefik at `/ui/v2/login`
+- A Zitadel container that is accessible via Traefik at all other paths than `/ui/v2/login`.
- An insecure [PostgreSQL](https://www.postgresql.org/docs/current/index.html).
-The setup is tested against Docker version 20.10.17 and Docker Compose version v2.2.3
+The Traefik container and the login container call the Zitadel container via the internal Docker network at `h2c://zitadel:8080`
+
+The setup is tested against Docker version 28.0.4 and Docker Compose version v2.34.0
By executing the commands below, you will download the following files:
@@ -60,26 +60,15 @@ wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosti
# A single ZITADEL instance always needs the same 32 bytes long masterkey
# Generate one to a file if you haven't done so already and pass it as environment variable
-tr -dc A-Za-z0-9 ./zitadel-masterkey
+LC_ALL=C tr -dc '[:graph:]' ./zitadel-masterkey
export ZITADEL_MASTERKEY="$(cat ./zitadel-masterkey)"
# Run the database and application containers
-docker compose up --detach
+docker compose up --detach --wait
```
-Make `127.0.0.1` available at `my.domain`. For example, this can be achieved with an entry `127.0.0.1 my.domain` in the `/etc/hosts` file.
-
-Open your favorite internet browser at [https://my.domain/ui/console/](https://my.domain/ui/console/).
-You can safely proceed, if your browser warns you about the insecure self-signed TLS certificate.
-This is the IAM admin users login according to your configuration in the [example-zitadel-init-steps.yaml](./example-zitadel-init-steps.yaml):
-- **username**: *root@my-org.my.domain*
-- **password**: *RootPassword1!*
+Open your favorite internet browser at https://127.0.0.1.sslip.io/ui/console?login_hint=zitadel-admin@zitadel.127.0.0.1.sslip.io.
+Your browser warns you about the insecure self-signed TLS certificate. As 127.0.0.1.sslip.io resolves to your localhost, you can safely proceed.
+Use the password *Password1!* to log in.
Read more about [the login process](/guides/integrate/login/oidc/login-users).
-
-
-
-## Troubleshooting
-
-You can connect to the database like this: `docker exec -it loadbalancing-example-db-1 psql --host localhost`
-For example, to show all login names: `docker exec -it loadbalancing-example-db-1 psql -d zitadel --host localhost -c 'select * from projections.login_names3'`
diff --git a/docs/docs/self-hosting/manage/cli/mirror.mdx b/docs/docs/self-hosting/manage/cli/mirror.mdx
index 45bac9b279..ae81800e39 100644
--- a/docs/docs/self-hosting/manage/cli/mirror.mdx
+++ b/docs/docs/self-hosting/manage/cli/mirror.mdx
@@ -158,6 +158,9 @@ Destination:
# As cockroachdb first copies the data into memory this parameter is used to iterate through the events table and fetch only the given amount of events per iteration
EventBulkSize: 10000 # ZITADEL_EVENTBULKSIZE
+# The maximum duration an auth request was last updated before it gets ignored.
+# Default is 30 days
+MaxAuthRequestAge: 720h # ZITADEL_MAXAUTHREQUESTAGE
Projections:
# Defines how many projections are allowed to run in parallel
diff --git a/docs/docs/self-hosting/manage/configure/_compose.mdx b/docs/docs/self-hosting/manage/configure/_compose.mdx
index 5e8b1c3937..837d4c6e62 100644
--- a/docs/docs/self-hosting/manage/configure/_compose.mdx
+++ b/docs/docs/self-hosting/manage/configure/_compose.mdx
@@ -43,7 +43,7 @@ wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosti
# A single ZITADEL instance always needs the same 32 bytes long masterkey
# Generate one to a file if you haven't done so already and pass it as environment variable
-tr -dc A-Za-z0-9 ./zitadel-masterkey
+LC_ALL=C tr -dc '[:graph:]' ./zitadel-masterkey
export ZITADEL_MASTERKEY="$(cat ./zitadel-masterkey)"
# Run the database and application containers
diff --git a/docs/docs/self-hosting/manage/configure/_helm.mdx b/docs/docs/self-hosting/manage/configure/_helm.mdx
index b35957abb8..17fb8165a6 100644
--- a/docs/docs/self-hosting/manage/configure/_helm.mdx
+++ b/docs/docs/self-hosting/manage/configure/_helm.mdx
@@ -3,4 +3,4 @@ Configure Zitadel using native Helm values.
You can manage secrets through Helm values, letting Helm create Kubernetes secrets.
Alternatively, reference existing Kubernetes secrets managed outside of Helm.
See the [referenced secrets example](https://github.com/zitadel/zitadel-charts/tree/main/examples/3-referenced-secrets) in the charts */examples* folder.
-For a quick setup, check out the [insecure Postgres example](https://github.com/zitadel/zitadel-charts/tree/main/examples/1-insecure-postgres).
+For a quick setup, check out the [insecure Postgres example](https://github.com/zitadel/zitadel-charts/tree/main/examples/1-postgres-insecure).
diff --git a/docs/docs/self-hosting/manage/configure/_linuxunix.mdx b/docs/docs/self-hosting/manage/configure/_linuxunix.mdx
index 6be833caea..65130ea195 100644
--- a/docs/docs/self-hosting/manage/configure/_linuxunix.mdx
+++ b/docs/docs/self-hosting/manage/configure/_linuxunix.mdx
@@ -35,7 +35,7 @@ wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosti
# A single ZITADEL instance always needs the same 32 characters long masterkey
# If you haven't done so already, you can generate a new one
# The key must be passed as argument
-ZITADEL_MASTERKEY="$(tr -dc A-Za-z0-9 ./zitadel-masterkey
+LC_ALL=C tr -dc '[:graph:]' ./zitadel-masterkey
# Let the zitadel binary read configuration from environment variables
zitadel start-from-init --masterkey "${ZITADEL_MASTERKEY}" --tlsMode disabled --masterkeyFile ./zitadel-masterkey
diff --git a/internal/actions/http_module.go b/internal/actions/http_module.go
index 2f9d09932c..db7253428d 100644
--- a/internal/actions/http_module.go
+++ b/internal/actions/http_module.go
@@ -176,16 +176,16 @@ type transport struct {
}
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
- if httpConfig == nil {
+ if httpConfig == nil || len(httpConfig.DenyList) == 0 {
return http.DefaultTransport.RoundTrip(req)
}
- if t.isHostBlocked(httpConfig.DenyList, req.URL) {
- return nil, zerrors.ThrowInvalidArgument(nil, "ACTIO-N72d0", "host is denied")
+ if err := t.isHostBlocked(httpConfig.DenyList, req.URL); err != nil {
+ return nil, zerrors.ThrowInvalidArgument(err, "ACTIO-N72d0", "host is denied")
}
return http.DefaultTransport.RoundTrip(req)
}
-func (t *transport) isHostBlocked(denyList []AddressChecker, address *url.URL) bool {
+func (t *transport) isHostBlocked(denyList []AddressChecker, address *url.URL) error {
host := address.Hostname()
ip := net.ParseIP(host)
ips := []net.IP{ip}
@@ -194,17 +194,17 @@ func (t *transport) isHostBlocked(denyList []AddressChecker, address *url.URL) b
var err error
ips, err = t.lookup(host)
if err != nil {
- return true
+ return zerrors.ThrowInternal(err, "ACTIO-4m9s2", "lookup failed")
}
}
- for _, blocked := range denyList {
- if blocked.Matches(ips, host) {
- return true
+ for _, denied := range denyList {
+ if err := denied.IsDenied(ips, host); err != nil {
+ return err
}
}
- return false
+ return nil
}
type AddressChecker interface {
- Matches([]net.IP, string) bool
+ IsDenied([]net.IP, string) error
}
diff --git a/internal/actions/http_module_config.go b/internal/actions/http_module_config.go
index d1b965814e..eaab9e754e 100644
--- a/internal/actions/http_module_config.go
+++ b/internal/actions/http_module_config.go
@@ -1,6 +1,8 @@
package actions
import (
+ "errors"
+ "fmt"
"net"
"reflect"
"strings"
@@ -60,6 +62,9 @@ func HTTPConfigDecodeHook(from, to reflect.Value) (interface{}, error) {
}
func NewHostChecker(entry string) (AddressChecker, error) {
+ if entry == "" {
+ return nil, nil
+ }
_, network, err := net.ParseCIDR(entry)
if err == nil {
return &HostChecker{Net: network}, nil
@@ -76,19 +81,39 @@ type HostChecker struct {
Domain string
}
-func (c *HostChecker) Matches(ips []net.IP, address string) bool {
+type AddressDeniedError struct {
+ deniedBy string
+}
+
+func NewAddressDeniedError(deniedBy string) *AddressDeniedError {
+ return &AddressDeniedError{deniedBy: deniedBy}
+}
+
+func (e *AddressDeniedError) Error() string {
+ return fmt.Sprintf("address is denied by '%s'", e.deniedBy)
+}
+
+func (e *AddressDeniedError) Is(target error) bool {
+ var addressDeniedErr *AddressDeniedError
+ if !errors.As(target, &addressDeniedErr) {
+ return false
+ }
+ return e.deniedBy == addressDeniedErr.deniedBy
+}
+
+func (c *HostChecker) IsDenied(ips []net.IP, address string) error {
// if the address matches the domain, no additional checks as needed
if c.Domain == address {
- return true
+ return NewAddressDeniedError(c.Domain)
}
// otherwise we need to check on ips (incl. the resolved ips of the host)
for _, ip := range ips {
if c.Net != nil && c.Net.Contains(ip) {
- return true
+ return NewAddressDeniedError(c.Net.String())
}
if c.IP != nil && c.IP.Equal(ip) {
- return true
+ return NewAddressDeniedError(c.IP.String())
}
}
- return false
+ return nil
}
diff --git a/internal/actions/http_module_test.go b/internal/actions/http_module_test.go
index 7a1f8d7816..50a007feeb 100644
--- a/internal/actions/http_module_test.go
+++ b/internal/actions/http_module_test.go
@@ -3,6 +3,7 @@ package actions
import (
"bytes"
"context"
+ "errors"
"io"
"net"
"net/http"
@@ -11,6 +12,7 @@ import (
"testing"
"github.com/dop251/goja"
+ "github.com/stretchr/testify/assert"
"github.com/zitadel/zitadel/internal/logstore"
"github.com/zitadel/zitadel/internal/logstore/record"
@@ -34,21 +36,21 @@ func Test_isHostBlocked(t *testing.T) {
name string
fields fields
args args
- want bool
+ want error
}{
{
name: "in range",
args: args{
address: mustNewURL(t, "https://192.168.5.4/hodor"),
},
- want: true,
+ want: NewAddressDeniedError("192.168.5.0/24"),
},
{
name: "exact ip",
args: args{
address: mustNewURL(t, "http://127.0.0.1:8080/hodor"),
},
- want: true,
+ want: NewAddressDeniedError("127.0.0.1"),
},
{
name: "address match",
@@ -60,7 +62,7 @@ func Test_isHostBlocked(t *testing.T) {
args: args{
address: mustNewURL(t, "https://test.com:42/hodor"),
},
- want: true,
+ want: NewAddressDeniedError("test.com"),
},
{
name: "address not match",
@@ -72,7 +74,7 @@ func Test_isHostBlocked(t *testing.T) {
args: args{
address: mustNewURL(t, "https://test2.com/hodor"),
},
- want: false,
+ want: nil,
},
{
name: "looked up ip matches",
@@ -84,7 +86,19 @@ func Test_isHostBlocked(t *testing.T) {
args: args{
address: mustNewURL(t, "https://test2.com/hodor"),
},
- want: true,
+ want: NewAddressDeniedError("127.0.0.1"),
+ },
+ {
+ name: "looked up failure",
+ fields: fields{
+ lookup: func(host string) ([]net.IP, error) {
+ return nil, errors.New("some error")
+ },
+ },
+ args: args{
+ address: mustNewURL(t, "https://test2.com/hodor"),
+ },
+ want: zerrors.ThrowInternal(nil, "ACTIO-4m9s2", "lookup failed"),
},
}
for _, tt := range tests {
@@ -92,9 +106,8 @@ func Test_isHostBlocked(t *testing.T) {
trans := &transport{
lookup: tt.fields.lookup,
}
- if got := trans.isHostBlocked(denyList, tt.args.address); got != tt.want {
- t.Errorf("isHostBlocked() = %v, want %v", got, tt.want)
- }
+ got := trans.isHostBlocked(denyList, tt.args.address)
+ assert.ErrorIs(t, got, tt.want)
})
}
}
diff --git a/internal/admin/repository/eventsourcing/handler/handler.go b/internal/admin/repository/eventsourcing/handler/handler.go
index ec268c25a1..76584b55b0 100644
--- a/internal/admin/repository/eventsourcing/handler/handler.go
+++ b/internal/admin/repository/eventsourcing/handler/handler.go
@@ -2,9 +2,13 @@ package handler
import (
"context"
+ "fmt"
"time"
+ "github.com/zitadel/logging"
+
"github.com/zitadel/zitadel/internal/admin/repository/eventsourcing/view"
+ "github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
@@ -57,11 +61,13 @@ func Start(ctx context.Context) {
}
func ProjectInstance(ctx context.Context) error {
- for _, projection := range projections {
+ for i, projection := range projections {
+ logging.WithFields("name", projection.ProjectionName(), "instance", authz.GetInstance(ctx).InstanceID(), "index", fmt.Sprintf("%d/%d", i, len(projections))).Info("starting admin projection")
_, err := projection.Trigger(ctx)
if err != nil {
return err
}
+ logging.WithFields("name", projection.ProjectionName(), "instance", authz.GetInstance(ctx).InstanceID(), "index", fmt.Sprintf("%d/%d", i, len(projections))).Info("admin projection done")
}
return nil
}
diff --git a/internal/api/grpc/action/v2beta/execution.go b/internal/api/grpc/action/v2beta/execution.go
index 8a7cd18ab4..5477a8128e 100644
--- a/internal/api/grpc/action/v2beta/execution.go
+++ b/internal/api/grpc/action/v2beta/execution.go
@@ -14,22 +14,10 @@ import (
)
func (s *Server) SetExecution(ctx context.Context, req *action.SetExecutionRequest) (*action.SetExecutionResponse, error) {
- if err := checkActionsEnabled(ctx); err != nil {
- return nil, err
- }
reqTargets := req.GetTargets()
targets := make([]*execution.Target, len(reqTargets))
for i, target := range reqTargets {
- switch t := target.GetType().(type) {
- case *action.ExecutionTargetType_Include:
- include, err := conditionToInclude(t.Include)
- if err != nil {
- return nil, err
- }
- targets[i] = &execution.Target{Type: domain.ExecutionTargetTypeInclude, Target: include}
- case *action.ExecutionTargetType_Target:
- targets[i] = &execution.Target{Type: domain.ExecutionTargetTypeTarget, Target: t.Target}
- }
+ targets[i] = &execution.Target{Type: domain.ExecutionTargetTypeTarget, Target: target}
}
set := &command.SetExecution{
Targets: targets,
@@ -60,59 +48,19 @@ func (s *Server) SetExecution(ctx context.Context, req *action.SetExecutionReque
}, nil
}
-func conditionToInclude(cond *action.Condition) (string, error) {
- switch t := cond.GetConditionType().(type) {
- case *action.Condition_Request:
- cond := executionConditionFromRequest(t.Request)
- if err := cond.IsValid(); err != nil {
- return "", err
- }
- return cond.ID(domain.ExecutionTypeRequest), nil
- case *action.Condition_Response:
- cond := executionConditionFromResponse(t.Response)
- if err := cond.IsValid(); err != nil {
- return "", err
- }
- return cond.ID(domain.ExecutionTypeRequest), nil
- case *action.Condition_Event:
- cond := executionConditionFromEvent(t.Event)
- if err := cond.IsValid(); err != nil {
- return "", err
- }
- return cond.ID(), nil
- case *action.Condition_Function:
- cond := command.ExecutionFunctionCondition(t.Function.GetName())
- if err := cond.IsValid(); err != nil {
- return "", err
- }
- return cond.ID(), nil
- default:
- return "", zerrors.ThrowInvalidArgument(nil, "ACTION-9BBob", "Errors.Execution.ConditionInvalid")
- }
-}
-
func (s *Server) ListExecutionFunctions(ctx context.Context, _ *action.ListExecutionFunctionsRequest) (*action.ListExecutionFunctionsResponse, error) {
- if err := checkActionsEnabled(ctx); err != nil {
- return nil, err
- }
return &action.ListExecutionFunctionsResponse{
Functions: s.ListActionFunctions(),
}, nil
}
func (s *Server) ListExecutionMethods(ctx context.Context, _ *action.ListExecutionMethodsRequest) (*action.ListExecutionMethodsResponse, error) {
- if err := checkActionsEnabled(ctx); err != nil {
- return nil, err
- }
return &action.ListExecutionMethodsResponse{
Methods: s.ListGRPCMethods(),
}, nil
}
func (s *Server) ListExecutionServices(ctx context.Context, _ *action.ListExecutionServicesRequest) (*action.ListExecutionServicesResponse, error) {
- if err := checkActionsEnabled(ctx); err != nil {
- return nil, err
- }
return &action.ListExecutionServicesResponse{
Services: s.ListGRPCServices(),
}, nil
diff --git a/internal/api/grpc/action/v2beta/integration_test/execution_target_test.go b/internal/api/grpc/action/v2beta/integration_test/execution_target_test.go
index 6e3ab76fac..0c5018dbb6 100644
--- a/internal/api/grpc/action/v2beta/integration_test/execution_target_test.go
+++ b/internal/api/grpc/action/v2beta/integration_test/execution_target_test.go
@@ -48,7 +48,6 @@ var (
func TestServer_ExecutionTarget(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
fullMethod := action.ActionService_GetTarget_FullMethodName
@@ -77,14 +76,14 @@ func TestServer_ExecutionTarget(t *testing.T) {
targetCreated := instance.CreateTarget(ctx, t, targetCreatedName, targetCreatedURL, domain.TargetTypeCall, false)
// request received by target
- wantRequest := &middleware.ContextInfoRequest{FullMethod: fullMethod, InstanceID: instance.ID(), OrgID: orgID, ProjectID: projectID, UserID: userID, Request: request}
+ wantRequest := &middleware.ContextInfoRequest{FullMethod: fullMethod, InstanceID: instance.ID(), OrgID: orgID, ProjectID: projectID, UserID: userID, Request: middleware.Message{Message: request}}
changedRequest := &action.GetTargetRequest{Id: targetCreated.GetId()}
// replace original request with different targetID
- urlRequest, closeRequest, calledRequest, _ := integration.TestServerCall(wantRequest, 0, http.StatusOK, changedRequest)
+ urlRequest, closeRequest, calledRequest, _ := integration.TestServerCallProto(wantRequest, 0, http.StatusOK, changedRequest)
targetRequest := waitForTarget(ctx, t, instance, urlRequest, domain.TargetTypeCall, false)
- waitForExecutionOnCondition(ctx, t, instance, conditionRequestFullMethod(fullMethod), executionTargetsSingleTarget(targetRequest.GetId()))
+ waitForExecutionOnCondition(ctx, t, instance, conditionRequestFullMethod(fullMethod), []string{targetRequest.GetId()})
// expected response from the GetTarget
expectedResponse := &action.GetTargetResponse{
@@ -103,9 +102,26 @@ func TestServer_ExecutionTarget(t *testing.T) {
SigningKey: targetCreated.GetSigningKey(),
},
}
- // has to be set separately because of the pointers
+
+ changedResponse := &action.GetTargetResponse{
+ Target: &action.Target{
+ Id: "changed",
+ CreationDate: targetCreated.GetCreationDate(),
+ ChangeDate: targetCreated.GetCreationDate(),
+ Name: targetCreatedName,
+ TargetType: &action.Target_RestCall{
+ RestCall: &action.RESTCall{
+ InterruptOnError: false,
+ },
+ },
+ Timeout: durationpb.New(5 * time.Second),
+ Endpoint: targetCreatedURL,
+ SigningKey: targetCreated.GetSigningKey(),
+ },
+ }
+ // content for update
response.Target = &action.Target{
- Id: targetCreated.GetId(),
+ Id: "changed",
CreationDate: targetCreated.GetCreationDate(),
ChangeDate: targetCreated.GetCreationDate(),
Name: targetCreatedName,
@@ -119,18 +135,6 @@ func TestServer_ExecutionTarget(t *testing.T) {
SigningKey: targetCreated.GetSigningKey(),
}
- // content for partial update
- changedResponse := &action.GetTargetResponse{
- Target: &action.Target{
- Id: targetCreated.GetId(),
- TargetType: &action.Target_RestCall{
- RestCall: &action.RESTCall{
- InterruptOnError: false,
- },
- },
- },
- }
-
// response received by target
wantResponse := &middleware.ContextInfoResponse{
FullMethod: fullMethod,
@@ -138,14 +142,14 @@ func TestServer_ExecutionTarget(t *testing.T) {
OrgID: orgID,
ProjectID: projectID,
UserID: userID,
- Request: changedRequest,
- Response: expectedResponse,
+ Request: middleware.Message{Message: changedRequest},
+ Response: middleware.Message{Message: expectedResponse},
}
// after request with different targetID, return changed response
- targetResponseURL, closeResponse, calledResponse, _ := integration.TestServerCall(wantResponse, 0, http.StatusOK, changedResponse)
+ targetResponseURL, closeResponse, calledResponse, _ := integration.TestServerCallProto(wantResponse, 0, http.StatusOK, changedResponse)
targetResponse := waitForTarget(ctx, t, instance, targetResponseURL, domain.TargetTypeCall, false)
- waitForExecutionOnCondition(ctx, t, instance, conditionResponseFullMethod(fullMethod), executionTargetsSingleTarget(targetResponse.GetId()))
+ waitForExecutionOnCondition(ctx, t, instance, conditionResponseFullMethod(fullMethod), []string{targetResponse.GetId()})
return func() {
closeRequest()
closeResponse()
@@ -167,9 +171,7 @@ func TestServer_ExecutionTarget(t *testing.T) {
Id: "something",
},
want: &action.GetTargetResponse{
- Target: &action.Target{
- Id: "changed",
- },
+ // defined in the dependency function
},
},
{
@@ -181,11 +183,11 @@ func TestServer_ExecutionTarget(t *testing.T) {
userID := instance.Users.Get(integration.UserTypeIAMOwner).ID
// request received by target
- wantRequest := &middleware.ContextInfoRequest{FullMethod: fullMethod, InstanceID: instance.ID(), OrgID: orgID, ProjectID: projectID, UserID: userID, Request: request}
- urlRequest, closeRequest, calledRequest, _ := integration.TestServerCall(wantRequest, 0, http.StatusInternalServerError, &action.GetTargetRequest{Id: "notchanged"})
+ wantRequest := &middleware.ContextInfoRequest{FullMethod: fullMethod, InstanceID: instance.ID(), OrgID: orgID, ProjectID: projectID, UserID: userID, Request: middleware.Message{Message: request}}
+ urlRequest, closeRequest, calledRequest, _ := integration.TestServerCallProto(wantRequest, 0, http.StatusInternalServerError, nil)
targetRequest := waitForTarget(ctx, t, instance, urlRequest, domain.TargetTypeCall, true)
- waitForExecutionOnCondition(ctx, t, instance, conditionRequestFullMethod(fullMethod), executionTargetsSingleTarget(targetRequest.GetId()))
+ waitForExecutionOnCondition(ctx, t, instance, conditionRequestFullMethod(fullMethod), []string{targetRequest.GetId()})
// GetTarget with used target
request.Id = targetRequest.GetId()
return func() {
@@ -234,17 +236,6 @@ func TestServer_ExecutionTarget(t *testing.T) {
SigningKey: targetCreated.GetSigningKey(),
},
}
- // content for partial update
- changedResponse := &action.GetTargetResponse{
- Target: &action.Target{
- Id: "changed",
- TargetType: &action.Target_RestCall{
- RestCall: &action.RESTCall{
- InterruptOnError: false,
- },
- },
- },
- }
// response received by target
wantResponse := &middleware.ContextInfoResponse{
@@ -253,14 +244,14 @@ func TestServer_ExecutionTarget(t *testing.T) {
OrgID: orgID,
ProjectID: projectID,
UserID: userID,
- Request: request,
- Response: expectedResponse,
+ Request: middleware.Message{Message: request},
+ Response: middleware.Message{Message: expectedResponse},
}
// after request with different targetID, return changed response
- targetResponseURL, closeResponse, calledResponse, _ := integration.TestServerCall(wantResponse, 0, http.StatusInternalServerError, changedResponse)
+ targetResponseURL, closeResponse, calledResponse, _ := integration.TestServerCallProto(wantResponse, 0, http.StatusInternalServerError, nil)
targetResponse := waitForTarget(ctx, t, instance, targetResponseURL, domain.TargetTypeCall, true)
- waitForExecutionOnCondition(ctx, t, instance, conditionResponseFullMethod(fullMethod), executionTargetsSingleTarget(targetResponse.GetId()))
+ waitForExecutionOnCondition(ctx, t, instance, conditionResponseFullMethod(fullMethod), []string{targetResponse.GetId()})
return func() {
closeResponse()
}, func() bool {
@@ -301,7 +292,6 @@ func TestServer_ExecutionTarget(t *testing.T) {
func TestServer_ExecutionTarget_Event(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
event := "session.added"
@@ -309,7 +299,7 @@ func TestServer_ExecutionTarget_Event(t *testing.T) {
defer closeF()
targetResponse := waitForTarget(isolatedIAMOwnerCTX, t, instance, urlRequest, domain.TargetTypeWebhook, true)
- waitForExecutionOnCondition(isolatedIAMOwnerCTX, t, instance, conditionEvent(event), executionTargetsSingleTarget(targetResponse.GetId()))
+ waitForExecutionOnCondition(isolatedIAMOwnerCTX, t, instance, conditionEvent(event), []string{targetResponse.GetId()})
tests := []struct {
name string
@@ -359,7 +349,6 @@ func TestServer_ExecutionTarget_Event(t *testing.T) {
func TestServer_ExecutionTarget_Event_LongerThanTargetTimeout(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
event := "session.added"
@@ -368,7 +357,7 @@ func TestServer_ExecutionTarget_Event_LongerThanTargetTimeout(t *testing.T) {
defer closeF()
targetResponse := waitForTarget(isolatedIAMOwnerCTX, t, instance, urlRequest, domain.TargetTypeWebhook, true)
- waitForExecutionOnCondition(isolatedIAMOwnerCTX, t, instance, conditionEvent(event), executionTargetsSingleTarget(targetResponse.GetId()))
+ waitForExecutionOnCondition(isolatedIAMOwnerCTX, t, instance, conditionEvent(event), []string{targetResponse.GetId()})
tests := []struct {
name string
@@ -412,7 +401,6 @@ func TestServer_ExecutionTarget_Event_LongerThanTargetTimeout(t *testing.T) {
func TestServer_ExecutionTarget_Event_LongerThanTransactionTimeout(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
event := "session.added"
@@ -420,7 +408,7 @@ func TestServer_ExecutionTarget_Event_LongerThanTransactionTimeout(t *testing.T)
defer closeF()
targetResponse := waitForTarget(isolatedIAMOwnerCTX, t, instance, urlRequest, domain.TargetTypeWebhook, true)
- waitForExecutionOnCondition(isolatedIAMOwnerCTX, t, instance, conditionEvent(event), executionTargetsSingleTarget(targetResponse.GetId()))
+ waitForExecutionOnCondition(isolatedIAMOwnerCTX, t, instance, conditionEvent(event), []string{targetResponse.GetId()})
tests := []struct {
name string
@@ -474,7 +462,7 @@ func TestServer_ExecutionTarget_Event_LongerThanTransactionTimeout(t *testing.T)
}
}
-func waitForExecutionOnCondition(ctx context.Context, t *testing.T, instance *integration.Instance, condition *action.Condition, targets []*action.ExecutionTargetType) {
+func waitForExecutionOnCondition(ctx context.Context, t *testing.T, instance *integration.Instance, condition *action.Condition, targets []string) {
instance.SetExecution(ctx, t, condition, targets)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, time.Minute)
@@ -496,7 +484,7 @@ func waitForExecutionOnCondition(ctx context.Context, t *testing.T, instance *in
// always first check length, otherwise its failed anyway
if assert.Len(ttt, gotTargets, len(targets)) {
for i := range targets {
- assert.EqualExportedValues(ttt, targets[i].GetType(), gotTargets[i].GetType())
+ assert.EqualExportedValues(ttt, targets[i], gotTargets[i])
}
}
}, retryDuration, tick, "timeout waiting for expected execution result")
@@ -589,7 +577,6 @@ func conditionFunction(function string) *action.Condition {
func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMCtx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
ctxLoginClient := instance.WithAuthorization(CTX, integration.UserTypeLogin)
@@ -785,7 +772,7 @@ func expectPreUserinfoExecution(ctx context.Context, t *testing.T, instance *int
targetURL, closeF, _, _ := integration.TestServerCall(expectedContextInfo, 0, http.StatusOK, response)
targetResp := waitForTarget(ctx, t, instance, targetURL, domain.TargetTypeCall, true)
- waitForExecutionOnCondition(ctx, t, instance, conditionFunction("preuserinfo"), executionTargetsSingleTarget(targetResp.GetId()))
+ waitForExecutionOnCondition(ctx, t, instance, conditionFunction("preuserinfo"), []string{targetResp.GetId()})
return userResp.GetUserId(), closeF
}
@@ -903,7 +890,6 @@ func contextInfoForUserOIDC(instance *integration.Instance, function string, use
func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMCtx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
ctxLoginClient := instance.WithAuthorization(CTX, integration.UserTypeLogin)
@@ -1091,13 +1077,12 @@ func expectPreAccessTokenExecution(ctx context.Context, t *testing.T, instance *
targetURL, closeF, _, _ := integration.TestServerCall(expectedContextInfo, 0, http.StatusOK, response)
targetResp := waitForTarget(ctx, t, instance, targetURL, domain.TargetTypeCall, true)
- waitForExecutionOnCondition(ctx, t, instance, conditionFunction("preaccesstoken"), executionTargetsSingleTarget(targetResp.GetId()))
+ waitForExecutionOnCondition(ctx, t, instance, conditionFunction("preaccesstoken"), []string{targetResp.GetId()})
return userResp.GetUserId(), closeF
}
func TestServer_ExecutionTargetPreSAMLResponse(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMCtx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
ctxLoginClient := instance.WithAuthorization(CTX, integration.UserTypeLogin)
@@ -1257,7 +1242,7 @@ func expectPreSAMLResponseExecution(ctx context.Context, t *testing.T, instance
targetURL, closeF, _, _ := integration.TestServerCall(expectedContextInfo, 0, http.StatusOK, response)
targetResp := waitForTarget(ctx, t, instance, targetURL, domain.TargetTypeCall, true)
- waitForExecutionOnCondition(ctx, t, instance, conditionFunction("presamlresponse"), executionTargetsSingleTarget(targetResp.GetId()))
+ waitForExecutionOnCondition(ctx, t, instance, conditionFunction("presamlresponse"), []string{targetResp.GetId()})
return userResp.GetUserId(), closeF
}
diff --git a/internal/api/grpc/action/v2beta/integration_test/execution_test.go b/internal/api/grpc/action/v2beta/integration_test/execution_test.go
index 3af419d97b..2199b9f454 100644
--- a/internal/api/grpc/action/v2beta/integration_test/execution_test.go
+++ b/internal/api/grpc/action/v2beta/integration_test/execution_test.go
@@ -15,17 +15,8 @@ import (
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
)
-func executionTargetsSingleTarget(id string) []*action.ExecutionTargetType {
- return []*action.ExecutionTargetType{{Type: &action.ExecutionTargetType_Target{Target: id}}}
-}
-
-func executionTargetsSingleInclude(include *action.Condition) []*action.ExecutionTargetType {
- return []*action.ExecutionTargetType{{Type: &action.ExecutionTargetType_Include{Include: include}}}
-}
-
func TestServer_SetExecution_Request(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
@@ -59,7 +50,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
Request: &action.RequestExecution{},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -76,7 +67,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -93,7 +84,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -110,7 +101,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -127,7 +118,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -144,7 +135,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -181,125 +172,8 @@ func assertSetExecutionResponse(t *testing.T, creationDate, setDate time.Time, e
}
}
-func TestServer_SetExecution_Request_Include(t *testing.T) {
- instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
- isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
- targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
- executionCond := &action.Condition{
- ConditionType: &action.Condition_Request{
- Request: &action.RequestExecution{
- Condition: &action.RequestExecution_All{
- All: true,
- },
- },
- },
- }
- instance.SetExecution(isolatedIAMOwnerCTX, t,
- executionCond,
- executionTargetsSingleTarget(targetResp.GetId()),
- )
-
- circularExecutionService := &action.Condition{
- ConditionType: &action.Condition_Request{
- Request: &action.RequestExecution{
- Condition: &action.RequestExecution_Service{
- Service: "zitadel.session.v2beta.SessionService",
- },
- },
- },
- }
- instance.SetExecution(isolatedIAMOwnerCTX, t,
- circularExecutionService,
- executionTargetsSingleInclude(executionCond),
- )
- circularExecutionMethod := &action.Condition{
- ConditionType: &action.Condition_Request{
- Request: &action.RequestExecution{
- Condition: &action.RequestExecution_Method{
- Method: "/zitadel.session.v2beta.SessionService/ListSessions",
- },
- },
- },
- }
- instance.SetExecution(isolatedIAMOwnerCTX, t,
- circularExecutionMethod,
- executionTargetsSingleInclude(circularExecutionService),
- )
-
- tests := []struct {
- name string
- ctx context.Context
- req *action.SetExecutionRequest
- wantSetDate bool
- wantErr bool
- }{
- {
- name: "method, circular error",
- ctx: isolatedIAMOwnerCTX,
- req: &action.SetExecutionRequest{
- Condition: circularExecutionService,
- Targets: executionTargetsSingleInclude(circularExecutionMethod),
- },
- wantErr: true,
- },
- {
- name: "method, ok",
- ctx: isolatedIAMOwnerCTX,
- req: &action.SetExecutionRequest{
- Condition: &action.Condition{
- ConditionType: &action.Condition_Request{
- Request: &action.RequestExecution{
- Condition: &action.RequestExecution_Method{
- Method: "/zitadel.session.v2beta.SessionService/ListSessions",
- },
- },
- },
- },
- Targets: executionTargetsSingleInclude(executionCond),
- },
- wantSetDate: true,
- },
- {
- name: "service, ok",
- ctx: isolatedIAMOwnerCTX,
- req: &action.SetExecutionRequest{
- Condition: &action.Condition{
- ConditionType: &action.Condition_Request{
- Request: &action.RequestExecution{
- Condition: &action.RequestExecution_Service{
- Service: "zitadel.user.v2beta.UserService",
- },
- },
- },
- },
- Targets: executionTargetsSingleInclude(executionCond),
- },
- wantSetDate: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- creationDate := time.Now().UTC()
- got, err := instance.Client.ActionV2beta.SetExecution(tt.ctx, tt.req)
- setDate := time.Now().UTC()
- if tt.wantErr {
- require.Error(t, err)
- return
- }
- require.NoError(t, err)
-
- assertSetExecutionResponse(t, creationDate, setDate, tt.wantSetDate, got)
-
- // cleanup to not impact other requests
- instance.DeleteExecution(tt.ctx, t, tt.req.GetCondition())
- })
- }
-}
-
func TestServer_SetExecution_Response(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
@@ -333,7 +207,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
Response: &action.ResponseExecution{},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -350,7 +224,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -367,7 +241,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -384,7 +258,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -401,7 +275,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -418,7 +292,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -444,7 +318,6 @@ func TestServer_SetExecution_Response(t *testing.T) {
func TestServer_SetExecution_Event(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
@@ -480,7 +353,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
Event: &action.EventExecution{},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -497,7 +370,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -514,7 +387,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -531,7 +404,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -548,7 +421,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -565,7 +438,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -582,7 +455,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -608,7 +481,6 @@ func TestServer_SetExecution_Event(t *testing.T) {
func TestServer_SetExecution_Function(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
@@ -642,7 +514,7 @@ func TestServer_SetExecution_Function(t *testing.T) {
Response: &action.ResponseExecution{},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -655,7 +527,7 @@ func TestServer_SetExecution_Function(t *testing.T) {
Function: &action.FunctionExecution{Name: "xxx"},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -668,7 +540,7 @@ func TestServer_SetExecution_Function(t *testing.T) {
Function: &action.FunctionExecution{Name: "presamlresponse"},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
diff --git a/internal/api/grpc/action/v2beta/integration_test/query_test.go b/internal/api/grpc/action/v2beta/integration_test/query_test.go
index c5159d39da..5c59bee5d1 100644
--- a/internal/api/grpc/action/v2beta/integration_test/query_test.go
+++ b/internal/api/grpc/action/v2beta/integration_test/query_test.go
@@ -8,6 +8,7 @@ import (
"time"
"github.com/brianvoe/gofakeit/v6"
+ "github.com/muhlemmer/gu"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/durationpb"
@@ -20,7 +21,6 @@ import (
func TestServer_GetTarget(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type args struct {
ctx context.Context
@@ -213,7 +213,6 @@ func TestServer_GetTarget(t *testing.T) {
func TestServer_ListTargets(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type args struct {
ctx context.Context
@@ -446,7 +445,6 @@ func assertPaginationResponse(t *assert.CollectT, expected *filter.PaginationRes
func TestServer_ListExecutions(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false)
@@ -475,7 +473,7 @@ func TestServer_ListExecutions(t *testing.T) {
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.ListExecutionsRequest, response *action.ListExecutionsResponse) {
cond := request.Filters[0].GetInConditionsFilter().GetConditions()[0]
- resp := instance.SetExecution(ctx, t, cond, executionTargetsSingleTarget(targetResp.GetId()))
+ resp := instance.SetExecution(ctx, t, cond, []string{targetResp.GetId()})
// Set expected response with used values for SetExecution
response.Result[0].CreationDate = resp.GetSetDate()
@@ -516,7 +514,7 @@ func TestServer_ListExecutions(t *testing.T) {
},
},
},
- Targets: executionTargetsSingleTarget(targetResp.GetId()),
+ Targets: []string{targetResp.GetId()},
},
},
},
@@ -544,13 +542,12 @@ func TestServer_ListExecutions(t *testing.T) {
},
},
}
- targets := executionTargetsSingleTarget(target.GetId())
- resp := instance.SetExecution(ctx, t, cond, targets)
+ resp := instance.SetExecution(ctx, t, cond, []string{target.GetId()})
response.Result[0].CreationDate = resp.GetSetDate()
response.Result[0].ChangeDate = resp.GetSetDate()
response.Result[0].Condition = cond
- response.Result[0].Targets = targets
+ response.Result[0].Targets = []string{target.GetId()}
},
req: &action.ListExecutionsRequest{
Filters: []*action.ExecutionSearchFilter{{}},
@@ -564,63 +561,10 @@ func TestServer_ListExecutions(t *testing.T) {
Result: []*action.Execution{
{
Condition: &action.Condition{},
- Targets: executionTargetsSingleTarget(""),
+ Targets: []string{""},
},
},
},
- }, {
- name: "list request single include",
- args: args{
- ctx: isolatedIAMOwnerCTX,
- dep: func(ctx context.Context, request *action.ListExecutionsRequest, response *action.ListExecutionsResponse) {
- cond := &action.Condition{
- ConditionType: &action.Condition_Request{
- Request: &action.RequestExecution{
- Condition: &action.RequestExecution_Method{
- Method: "/zitadel.management.v1.ManagementService/GetAction",
- },
- },
- },
- }
- instance.SetExecution(ctx, t, cond, executionTargetsSingleTarget(targetResp.GetId()))
- request.Filters[0].GetIncludeFilter().Include = cond
-
- includeCond := &action.Condition{
- ConditionType: &action.Condition_Request{
- Request: &action.RequestExecution{
- Condition: &action.RequestExecution_Method{
- Method: "/zitadel.management.v1.ManagementService/ListActions",
- },
- },
- },
- }
- includeTargets := executionTargetsSingleInclude(cond)
- resp2 := instance.SetExecution(ctx, t, includeCond, includeTargets)
-
- response.Result[0] = &action.Execution{
- Condition: includeCond,
- CreationDate: resp2.GetSetDate(),
- ChangeDate: resp2.GetSetDate(),
- Targets: includeTargets,
- }
- },
- req: &action.ListExecutionsRequest{
- Filters: []*action.ExecutionSearchFilter{{
- Filter: &action.ExecutionSearchFilter_IncludeFilter{
- IncludeFilter: &action.IncludeFilter{},
- },
- }},
- },
- },
- want: &action.ListExecutionsResponse{
- Pagination: &filter.PaginationResponse{
- TotalResult: 1,
- AppliedLimit: 100,
- },
- Result: []*action.Execution{
- {},
- },
- },
},
{
name: "list multiple conditions",
@@ -659,33 +603,30 @@ func TestServer_ListExecutions(t *testing.T) {
}
cond1 := request.Filters[0].GetInConditionsFilter().GetConditions()[0]
- targets1 := executionTargetsSingleTarget(targetResp.GetId())
- resp1 := instance.SetExecution(ctx, t, cond1, targets1)
+ resp1 := instance.SetExecution(ctx, t, cond1, []string{targetResp.GetId()})
response.Result[2] = &action.Execution{
CreationDate: resp1.GetSetDate(),
ChangeDate: resp1.GetSetDate(),
Condition: cond1,
- Targets: targets1,
+ Targets: []string{targetResp.GetId()},
}
cond2 := request.Filters[0].GetInConditionsFilter().GetConditions()[1]
- targets2 := executionTargetsSingleTarget(targetResp.GetId())
- resp2 := instance.SetExecution(ctx, t, cond2, targets2)
+ resp2 := instance.SetExecution(ctx, t, cond2, []string{targetResp.GetId()})
response.Result[1] = &action.Execution{
CreationDate: resp2.GetSetDate(),
ChangeDate: resp2.GetSetDate(),
Condition: cond2,
- Targets: targets2,
+ Targets: []string{targetResp.GetId()},
}
cond3 := request.Filters[0].GetInConditionsFilter().GetConditions()[2]
- targets3 := executionTargetsSingleTarget(targetResp.GetId())
- resp3 := instance.SetExecution(ctx, t, cond3, targets3)
+ resp3 := instance.SetExecution(ctx, t, cond3, []string{targetResp.GetId()})
response.Result[0] = &action.Execution{
CreationDate: resp3.GetSetDate(),
ChangeDate: resp3.GetSetDate(),
Condition: cond3,
- Targets: targets3,
+ Targets: []string{targetResp.GetId()},
}
},
req: &action.ListExecutionsRequest{
@@ -709,15 +650,14 @@ func TestServer_ListExecutions(t *testing.T) {
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.ListExecutionsRequest, response *action.ListExecutionsResponse) {
- targets := executionTargetsSingleTarget(targetResp.GetId())
conditions := request.Filters[0].GetInConditionsFilter().GetConditions()
for i, cond := range conditions {
- resp := instance.SetExecution(ctx, t, cond, targets)
+ resp := instance.SetExecution(ctx, t, cond, []string{targetResp.GetId()})
response.Result[(len(conditions)-1)-i] = &action.Execution{
CreationDate: resp.GetSetDate(),
ChangeDate: resp.GetSetDate(),
Condition: cond,
- Targets: targets,
+ Targets: []string{targetResp.GetId()},
}
}
},
@@ -761,6 +701,63 @@ func TestServer_ListExecutions(t *testing.T) {
},
},
},
+ {
+ name: "list multiple conditions all types, sort id",
+ args: args{
+ ctx: isolatedIAMOwnerCTX,
+ dep: func(ctx context.Context, request *action.ListExecutionsRequest, response *action.ListExecutionsResponse) {
+ conditions := request.Filters[0].GetInConditionsFilter().GetConditions()
+ for i, cond := range conditions {
+ resp := instance.SetExecution(ctx, t, cond, []string{targetResp.GetId()})
+ response.Result[i] = &action.Execution{
+ CreationDate: resp.GetSetDate(),
+ ChangeDate: resp.GetSetDate(),
+ Condition: cond,
+ Targets: []string{targetResp.GetId()},
+ }
+ }
+ },
+ req: &action.ListExecutionsRequest{
+ SortingColumn: gu.Ptr(action.ExecutionFieldName_EXECUTION_FIELD_NAME_ID),
+ Filters: []*action.ExecutionSearchFilter{{
+ Filter: &action.ExecutionSearchFilter_InConditionsFilter{
+ InConditionsFilter: &action.InConditionsFilter{
+ Conditions: []*action.Condition{
+ {ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_Method{Method: "/zitadel.session.v2.SessionService/GetSession"}}}},
+ {ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_Service{Service: "zitadel.session.v2.SessionService"}}}},
+ {ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_All{All: true}}}},
+ {ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_Method{Method: "/zitadel.session.v2.SessionService/GetSession"}}}},
+ {ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_Service{Service: "zitadel.session.v2.SessionService"}}}},
+ {ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_All{All: true}}}},
+ {ConditionType: &action.Condition_Function{Function: &action.FunctionExecution{Name: "presamlresponse"}}},
+ {ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_Event{Event: "user.added"}}}},
+ {ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_Group{Group: "user"}}}},
+ {ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_All{All: true}}}},
+ },
+ },
+ },
+ }},
+ },
+ },
+ want: &action.ListExecutionsResponse{
+ Pagination: &filter.PaginationResponse{
+ TotalResult: 10,
+ AppliedLimit: 100,
+ },
+ Result: []*action.Execution{
+ {},
+ {},
+ {},
+ {},
+ {},
+ {},
+ {},
+ {},
+ {},
+ {},
+ },
+ },
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
diff --git a/internal/api/grpc/action/v2beta/integration_test/server_test.go b/internal/api/grpc/action/v2beta/integration_test/server_test.go
index 89a33dd40e..07ee051c63 100644
--- a/internal/api/grpc/action/v2beta/integration_test/server_test.go
+++ b/internal/api/grpc/action/v2beta/integration_test/server_test.go
@@ -7,14 +7,6 @@ import (
"os"
"testing"
"time"
-
- "github.com/muhlemmer/gu"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/zitadel/zitadel/internal/integration"
- action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
- "github.com/zitadel/zitadel/pkg/grpc/feature/v2"
)
var (
@@ -29,41 +21,3 @@ func TestMain(m *testing.M) {
return m.Run()
}())
}
-
-func ensureFeatureEnabled(t *testing.T, instance *integration.Instance) {
- ctx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
- f, err := instance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{
- Inheritance: true,
- })
- require.NoError(t, err)
- if f.Actions.GetEnabled() {
- return
- }
- _, err = instance.Client.FeatureV2.SetInstanceFeatures(ctx, &feature.SetInstanceFeaturesRequest{
- Actions: gu.Ptr(true),
- })
- require.NoError(t, err)
-
- retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, 5*time.Minute)
- require.EventuallyWithT(t,
- func(ttt *assert.CollectT) {
- f, err := instance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{
- Inheritance: true,
- })
- assert.NoError(ttt, err)
- assert.True(ttt, f.Actions.GetEnabled())
- },
- retryDuration,
- tick,
- "timed out waiting for ensuring instance feature")
-
- retryDuration, tick = integration.WaitForAndTickWithMaxDuration(ctx, 5*time.Minute)
- require.EventuallyWithT(t,
- func(ttt *assert.CollectT) {
- _, err := instance.Client.ActionV2beta.ListExecutionMethods(ctx, &action.ListExecutionMethodsRequest{})
- assert.NoError(ttt, err)
- },
- retryDuration,
- tick,
- "timed out waiting for ensuring instance feature call")
-}
diff --git a/internal/api/grpc/action/v2beta/integration_test/target_test.go b/internal/api/grpc/action/v2beta/integration_test/target_test.go
index 2fda64b86a..8238d3146d 100644
--- a/internal/api/grpc/action/v2beta/integration_test/target_test.go
+++ b/internal/api/grpc/action/v2beta/integration_test/target_test.go
@@ -19,7 +19,6 @@ import (
func TestServer_CreateTarget(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type want struct {
id bool
@@ -244,7 +243,6 @@ func assertCreateTargetResponse(t *testing.T, creationDate, changeDate time.Time
func TestServer_UpdateTarget(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type args struct {
ctx context.Context
@@ -463,7 +461,6 @@ func assertUpdateTargetResponse(t *testing.T, creationDate, changeDate time.Time
func TestServer_DeleteTarget(t *testing.T) {
instance := integration.NewInstance(CTX)
- ensureFeatureEnabled(t, instance)
iamOwnerCtx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
tests := []struct {
name string
diff --git a/internal/api/grpc/action/v2beta/query.go b/internal/api/grpc/action/v2beta/query.go
index d8d6cd3e95..1dbe80a8f7 100644
--- a/internal/api/grpc/action/v2beta/query.go
+++ b/internal/api/grpc/action/v2beta/query.go
@@ -23,10 +23,6 @@ const (
)
func (s *Server) GetTarget(ctx context.Context, req *action.GetTargetRequest) (*action.GetTargetResponse, error) {
- if err := checkActionsEnabled(ctx); err != nil {
- return nil, err
- }
-
resp, err := s.query.GetTargetByID(ctx, req.GetId())
if err != nil {
return nil, err
@@ -46,9 +42,6 @@ type Context interface {
}
func (s *Server) ListTargets(ctx context.Context, req *action.ListTargetsRequest) (*action.ListTargetsResponse, error) {
- if err := checkActionsEnabled(ctx); err != nil {
- return nil, err
- }
queries, err := s.ListTargetsRequestToModel(req)
if err != nil {
return nil, err
@@ -64,9 +57,6 @@ func (s *Server) ListTargets(ctx context.Context, req *action.ListTargetsRequest
}
func (s *Server) ListExecutions(ctx context.Context, req *action.ListExecutionsRequest) (*action.ListExecutionsResponse, error) {
- if err := checkActionsEnabled(ctx); err != nil {
- return nil, err
- }
queries, err := s.ListExecutionsRequestToModel(req)
if err != nil {
return nil, err
@@ -174,7 +164,7 @@ func targetFieldNameToSortingColumn(field *action.TargetFieldName) query.Column
}
switch *field {
case action.TargetFieldName_TARGET_FIELD_NAME_UNSPECIFIED:
- return query.TargetColumnID
+ return query.TargetColumnCreationDate
case action.TargetFieldName_TARGET_FIELD_NAME_ID:
return query.TargetColumnID
case action.TargetFieldName_TARGET_FIELD_NAME_CREATED_DATE:
@@ -203,7 +193,7 @@ func executionFieldNameToSortingColumn(field *action.ExecutionFieldName) query.C
}
switch *field {
case action.ExecutionFieldName_EXECUTION_FIELD_NAME_UNSPECIFIED:
- return query.ExecutionColumnID
+ return query.ExecutionColumnCreationDate
case action.ExecutionFieldName_EXECUTION_FIELD_NAME_ID:
return query.ExecutionColumnID
case action.ExecutionFieldName_EXECUTION_FIELD_NAME_CREATED_DATE:
@@ -252,12 +242,6 @@ func executionQueryToQuery(searchQuery *action.ExecutionSearchFilter) (query.Sea
return inConditionsQueryToQuery(q.InConditionsFilter)
case *action.ExecutionSearchFilter_ExecutionTypeFilter:
return executionTypeToQuery(q.ExecutionTypeFilter)
- case *action.ExecutionSearchFilter_IncludeFilter:
- include, err := conditionToInclude(q.IncludeFilter.GetInclude())
- if err != nil {
- return nil, err
- }
- return query.NewIncludeSearchQuery(include)
case *action.ExecutionSearchFilter_TargetFilter:
return query.NewTargetSearchQuery(q.TargetFilter.GetTargetId())
default:
@@ -333,14 +317,12 @@ func executionsToPb(executions []*query.Execution) []*action.Execution {
}
func executionToPb(e *query.Execution) *action.Execution {
- targets := make([]*action.ExecutionTargetType, len(e.Targets))
+ targets := make([]string, len(e.Targets))
for i := range e.Targets {
switch e.Targets[i].Type {
- case domain.ExecutionTargetTypeInclude:
- targets[i] = &action.ExecutionTargetType{Type: &action.ExecutionTargetType_Include{Include: executionIDToCondition(e.Targets[i].Target)}}
case domain.ExecutionTargetTypeTarget:
- targets[i] = &action.ExecutionTargetType{Type: &action.ExecutionTargetType_Target{Target: e.Targets[i].Target}}
- case domain.ExecutionTargetTypeUnspecified:
+ targets[i] = e.Targets[i].Target
+ case domain.ExecutionTargetTypeInclude, domain.ExecutionTargetTypeUnspecified:
continue
default:
continue
diff --git a/internal/api/grpc/action/v2beta/server.go b/internal/api/grpc/action/v2beta/server.go
index 069f456ceb..ef0d8eb2ba 100644
--- a/internal/api/grpc/action/v2beta/server.go
+++ b/internal/api/grpc/action/v2beta/server.go
@@ -1,8 +1,6 @@
package action
import (
- "context"
-
"google.golang.org/grpc"
"github.com/zitadel/zitadel/internal/api/authz"
@@ -10,7 +8,6 @@ import (
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/config/systemdefaults"
"github.com/zitadel/zitadel/internal/query"
- "github.com/zitadel/zitadel/internal/zerrors"
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
)
@@ -65,10 +62,3 @@ func (s *Server) AuthMethods() authz.MethodMapping {
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
return action.RegisterActionServiceHandler
}
-
-func checkActionsEnabled(ctx context.Context) error {
- if authz.GetInstance(ctx).Features().Actions {
- return nil
- }
- return zerrors.ThrowPreconditionFailed(nil, "ACTION-8o6pvqfjhs", "Errors.Action.NotEnabled")
-}
diff --git a/internal/api/grpc/action/v2beta/target.go b/internal/api/grpc/action/v2beta/target.go
index 7dc636f29a..26c88b9683 100644
--- a/internal/api/grpc/action/v2beta/target.go
+++ b/internal/api/grpc/action/v2beta/target.go
@@ -14,9 +14,6 @@ import (
)
func (s *Server) CreateTarget(ctx context.Context, req *action.CreateTargetRequest) (*action.CreateTargetResponse, error) {
- if err := checkActionsEnabled(ctx); err != nil {
- return nil, err
- }
add := createTargetToCommand(req)
instanceID := authz.GetInstance(ctx).InstanceID()
createdAt, err := s.command.AddTarget(ctx, add, instanceID)
@@ -35,9 +32,6 @@ func (s *Server) CreateTarget(ctx context.Context, req *action.CreateTargetReque
}
func (s *Server) UpdateTarget(ctx context.Context, req *action.UpdateTargetRequest) (*action.UpdateTargetResponse, error) {
- if err := checkActionsEnabled(ctx); err != nil {
- return nil, err
- }
instanceID := authz.GetInstance(ctx).InstanceID()
update := updateTargetToCommand(req)
changedAt, err := s.command.ChangeTarget(ctx, update, instanceID)
@@ -55,9 +49,6 @@ func (s *Server) UpdateTarget(ctx context.Context, req *action.UpdateTargetReque
}
func (s *Server) DeleteTarget(ctx context.Context, req *action.DeleteTargetRequest) (*action.DeleteTargetResponse, error) {
- if err := checkActionsEnabled(ctx); err != nil {
- return nil, err
- }
instanceID := authz.GetInstance(ctx).InstanceID()
deletedAt, err := s.command.DeleteTarget(ctx, req.GetId(), instanceID)
if err != nil {
diff --git a/internal/api/grpc/feature/v2/converter.go b/internal/api/grpc/feature/v2/converter.go
index 60cf569082..e146ac2db6 100644
--- a/internal/api/grpc/feature/v2/converter.go
+++ b/internal/api/grpc/feature/v2/converter.go
@@ -22,7 +22,6 @@ func systemFeaturesToCommand(req *feature_pb.SetSystemFeaturesRequest) (*command
TriggerIntrospectionProjections: req.OidcTriggerIntrospectionProjections,
LegacyIntrospection: req.OidcLegacyIntrospection,
UserSchema: req.UserSchema,
- Actions: req.Actions,
TokenExchange: req.OidcTokenExchange,
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination,
@@ -41,7 +40,6 @@ func systemFeaturesToPb(f *query.SystemFeatures) *feature_pb.GetSystemFeaturesRe
OidcLegacyIntrospection: featureSourceToFlagPb(&f.LegacyIntrospection),
UserSchema: featureSourceToFlagPb(&f.UserSchema),
OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange),
- Actions: featureSourceToFlagPb(&f.Actions),
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination),
DisableUserTokenEvent: featureSourceToFlagPb(&f.DisableUserTokenEvent),
@@ -62,7 +60,6 @@ func instanceFeaturesToCommand(req *feature_pb.SetInstanceFeaturesRequest) (*com
LegacyIntrospection: req.OidcLegacyIntrospection,
UserSchema: req.UserSchema,
TokenExchange: req.OidcTokenExchange,
- Actions: req.Actions,
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
WebKey: req.WebKey,
DebugOIDCParentError: req.DebugOidcParentError,
@@ -83,7 +80,6 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat
OidcLegacyIntrospection: featureSourceToFlagPb(&f.LegacyIntrospection),
UserSchema: featureSourceToFlagPb(&f.UserSchema),
OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange),
- Actions: featureSourceToFlagPb(&f.Actions),
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
WebKey: featureSourceToFlagPb(&f.WebKey),
DebugOidcParentError: featureSourceToFlagPb(&f.DebugOIDCParentError),
@@ -176,7 +172,7 @@ func improvedPerformanceTypesToPb(types []feature.ImprovedPerformanceType) []fea
func improvedPerformanceTypeToPb(typ feature.ImprovedPerformanceType) feature_pb.ImprovedPerformance {
switch typ {
- case feature.ImprovedPerformanceTypeUnknown:
+ case feature.ImprovedPerformanceTypeUnspecified:
return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_UNSPECIFIED
case feature.ImprovedPerformanceTypeOrgByID:
return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID
@@ -209,7 +205,7 @@ func improvedPerformanceListToDomain(list []feature_pb.ImprovedPerformance) []fe
func improvedPerformanceToDomain(typ feature_pb.ImprovedPerformance) feature.ImprovedPerformanceType {
switch typ {
case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_UNSPECIFIED:
- return feature.ImprovedPerformanceTypeUnknown
+ return feature.ImprovedPerformanceTypeUnspecified
case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID:
return feature.ImprovedPerformanceTypeOrgByID
case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_PROJECT_GRANT:
@@ -221,6 +217,6 @@ func improvedPerformanceToDomain(typ feature_pb.ImprovedPerformance) feature.Imp
case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_DOMAIN_VERIFIED:
return feature.ImprovedPerformanceTypeOrgDomainVerified
default:
- return feature.ImprovedPerformanceTypeUnknown
+ return feature.ImprovedPerformanceTypeUnspecified
}
}
diff --git a/internal/api/grpc/feature/v2/converter_test.go b/internal/api/grpc/feature/v2/converter_test.go
index 62cf701eec..f481e4f65a 100644
--- a/internal/api/grpc/feature/v2/converter_test.go
+++ b/internal/api/grpc/feature/v2/converter_test.go
@@ -23,7 +23,6 @@ func Test_systemFeaturesToCommand(t *testing.T) {
OidcTriggerIntrospectionProjections: gu.Ptr(false),
OidcLegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
- Actions: gu.Ptr(true),
OidcTokenExchange: gu.Ptr(true),
ImprovedPerformance: nil,
OidcSingleV1SessionTermination: gu.Ptr(true),
@@ -37,7 +36,6 @@ func Test_systemFeaturesToCommand(t *testing.T) {
TriggerIntrospectionProjections: gu.Ptr(false),
LegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
- Actions: gu.Ptr(true),
TokenExchange: gu.Ptr(true),
ImprovedPerformance: nil,
OIDCSingleV1SessionTermination: gu.Ptr(true),
@@ -74,10 +72,6 @@ func Test_systemFeaturesToPb(t *testing.T) {
Level: feature.LevelSystem,
Value: true,
},
- Actions: query.FeatureSource[bool]{
- Level: feature.LevelSystem,
- Value: true,
- },
TokenExchange: query.FeatureSource[bool]{
Level: feature.LevelSystem,
Value: false,
@@ -132,10 +126,6 @@ func Test_systemFeaturesToPb(t *testing.T) {
Enabled: false,
Source: feature_pb.Source_SOURCE_SYSTEM,
},
- Actions: &feature_pb.FeatureFlag{
- Enabled: true,
- Source: feature_pb.Source_SOURCE_SYSTEM,
- },
ImprovedPerformance: &feature_pb.ImprovedPerformanceFeatureFlag{
ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID},
Source: feature_pb.Source_SOURCE_SYSTEM,
@@ -173,7 +163,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
OidcLegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
OidcTokenExchange: gu.Ptr(true),
- Actions: gu.Ptr(true),
ImprovedPerformance: nil,
WebKey: gu.Ptr(true),
DebugOidcParentError: gu.Ptr(true),
@@ -191,7 +180,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
LegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
TokenExchange: gu.Ptr(true),
- Actions: gu.Ptr(true),
ImprovedPerformance: nil,
WebKey: gu.Ptr(true),
DebugOIDCParentError: gu.Ptr(true),
@@ -231,10 +219,6 @@ func Test_instanceFeaturesToPb(t *testing.T) {
Level: feature.LevelInstance,
Value: true,
},
- Actions: query.FeatureSource[bool]{
- Level: feature.LevelInstance,
- Value: true,
- },
TokenExchange: query.FeatureSource[bool]{
Level: feature.LevelSystem,
Value: false,
@@ -293,10 +277,6 @@ func Test_instanceFeaturesToPb(t *testing.T) {
Enabled: true,
Source: feature_pb.Source_SOURCE_INSTANCE,
},
- Actions: &feature_pb.FeatureFlag{
- Enabled: true,
- Source: feature_pb.Source_SOURCE_INSTANCE,
- },
OidcTokenExchange: &feature_pb.FeatureFlag{
Enabled: false,
Source: feature_pb.Source_SOURCE_SYSTEM,
diff --git a/internal/api/grpc/feature/v2/integration_test/feature_test.go b/internal/api/grpc/feature/v2/integration_test/feature_test.go
index 8d6c295350..f27b57ff8c 100644
--- a/internal/api/grpc/feature/v2/integration_test/feature_test.go
+++ b/internal/api/grpc/feature/v2/integration_test/feature_test.go
@@ -211,7 +211,6 @@ func TestServer_GetSystemFeatures(t *testing.T) {
assertFeatureFlag(t, tt.want.OidcTriggerIntrospectionProjections, got.OidcTriggerIntrospectionProjections)
assertFeatureFlag(t, tt.want.OidcLegacyIntrospection, got.OidcLegacyIntrospection)
assertFeatureFlag(t, tt.want.UserSchema, got.UserSchema)
- assertFeatureFlag(t, tt.want.Actions, got.Actions)
})
}
}
@@ -374,10 +373,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
Enabled: false,
Source: feature.Source_SOURCE_UNSPECIFIED,
},
- Actions: &feature.FeatureFlag{
- Enabled: false,
- Source: feature.Source_SOURCE_UNSPECIFIED,
- },
},
},
{
@@ -387,7 +382,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
LoginDefaultOrg: gu.Ptr(true),
OidcTriggerIntrospectionProjections: gu.Ptr(false),
UserSchema: gu.Ptr(true),
- Actions: gu.Ptr(true),
})
require.NoError(t, err)
},
@@ -408,10 +402,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
Enabled: true,
Source: feature.Source_SOURCE_INSTANCE,
},
- Actions: &feature.FeatureFlag{
- Enabled: true,
- Source: feature.Source_SOURCE_INSTANCE,
- },
},
},
{
@@ -445,10 +435,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
Enabled: false,
Source: feature.Source_SOURCE_UNSPECIFIED,
},
- Actions: &feature.FeatureFlag{
- Enabled: false,
- Source: feature.Source_SOURCE_UNSPECIFIED,
- },
},
},
}
diff --git a/internal/api/grpc/feature/v2beta/converter.go b/internal/api/grpc/feature/v2beta/converter.go
index 39f2284beb..9739e1c4c8 100644
--- a/internal/api/grpc/feature/v2beta/converter.go
+++ b/internal/api/grpc/feature/v2beta/converter.go
@@ -14,7 +14,6 @@ func systemFeaturesToCommand(req *feature_pb.SetSystemFeaturesRequest) *command.
TriggerIntrospectionProjections: req.OidcTriggerIntrospectionProjections,
LegacyIntrospection: req.OidcLegacyIntrospection,
UserSchema: req.UserSchema,
- Actions: req.Actions,
TokenExchange: req.OidcTokenExchange,
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination,
@@ -29,7 +28,6 @@ func systemFeaturesToPb(f *query.SystemFeatures) *feature_pb.GetSystemFeaturesRe
OidcLegacyIntrospection: featureSourceToFlagPb(&f.LegacyIntrospection),
UserSchema: featureSourceToFlagPb(&f.UserSchema),
OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange),
- Actions: featureSourceToFlagPb(&f.Actions),
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination),
}
@@ -42,7 +40,6 @@ func instanceFeaturesToCommand(req *feature_pb.SetInstanceFeaturesRequest) *comm
LegacyIntrospection: req.OidcLegacyIntrospection,
UserSchema: req.UserSchema,
TokenExchange: req.OidcTokenExchange,
- Actions: req.Actions,
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
WebKey: req.WebKey,
DebugOIDCParentError: req.DebugOidcParentError,
@@ -58,7 +55,6 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat
OidcLegacyIntrospection: featureSourceToFlagPb(&f.LegacyIntrospection),
UserSchema: featureSourceToFlagPb(&f.UserSchema),
OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange),
- Actions: featureSourceToFlagPb(&f.Actions),
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
WebKey: featureSourceToFlagPb(&f.WebKey),
DebugOidcParentError: featureSourceToFlagPb(&f.DebugOIDCParentError),
@@ -113,7 +109,7 @@ func improvedPerformanceTypesToPb(types []feature.ImprovedPerformanceType) []fea
func improvedPerformanceTypeToPb(typ feature.ImprovedPerformanceType) feature_pb.ImprovedPerformance {
switch typ {
- case feature.ImprovedPerformanceTypeUnknown:
+ case feature.ImprovedPerformanceTypeUnspecified:
return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_UNSPECIFIED
case feature.ImprovedPerformanceTypeOrgByID:
return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID
@@ -146,7 +142,7 @@ func improvedPerformanceListToDomain(list []feature_pb.ImprovedPerformance) []fe
func improvedPerformanceToDomain(typ feature_pb.ImprovedPerformance) feature.ImprovedPerformanceType {
switch typ {
case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_UNSPECIFIED:
- return feature.ImprovedPerformanceTypeUnknown
+ return feature.ImprovedPerformanceTypeUnspecified
case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID:
return feature.ImprovedPerformanceTypeOrgByID
case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_PROJECT_GRANT:
@@ -158,6 +154,6 @@ func improvedPerformanceToDomain(typ feature_pb.ImprovedPerformance) feature.Imp
case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_DOMAIN_VERIFIED:
return feature.ImprovedPerformanceTypeOrgDomainVerified
default:
- return feature.ImprovedPerformanceTypeUnknown
+ return feature.ImprovedPerformanceTypeUnspecified
}
}
diff --git a/internal/api/grpc/feature/v2beta/converter_test.go b/internal/api/grpc/feature/v2beta/converter_test.go
index 2896d8f77b..72d91b10d4 100644
--- a/internal/api/grpc/feature/v2beta/converter_test.go
+++ b/internal/api/grpc/feature/v2beta/converter_test.go
@@ -22,7 +22,6 @@ func Test_systemFeaturesToCommand(t *testing.T) {
OidcTriggerIntrospectionProjections: gu.Ptr(false),
OidcLegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
- Actions: gu.Ptr(true),
OidcTokenExchange: gu.Ptr(true),
ImprovedPerformance: nil,
OidcSingleV1SessionTermination: gu.Ptr(true),
@@ -32,7 +31,6 @@ func Test_systemFeaturesToCommand(t *testing.T) {
TriggerIntrospectionProjections: gu.Ptr(false),
LegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
- Actions: gu.Ptr(true),
TokenExchange: gu.Ptr(true),
ImprovedPerformance: nil,
OIDCSingleV1SessionTermination: gu.Ptr(true),
@@ -64,10 +62,6 @@ func Test_systemFeaturesToPb(t *testing.T) {
Level: feature.LevelSystem,
Value: true,
},
- Actions: query.FeatureSource[bool]{
- Level: feature.LevelSystem,
- Value: true,
- },
TokenExchange: query.FeatureSource[bool]{
Level: feature.LevelSystem,
Value: false,
@@ -107,10 +101,6 @@ func Test_systemFeaturesToPb(t *testing.T) {
Enabled: false,
Source: feature_pb.Source_SOURCE_SYSTEM,
},
- Actions: &feature_pb.FeatureFlag{
- Enabled: true,
- Source: feature_pb.Source_SOURCE_SYSTEM,
- },
ImprovedPerformance: &feature_pb.ImprovedPerformanceFeatureFlag{
ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID},
Source: feature_pb.Source_SOURCE_SYSTEM,
@@ -131,7 +121,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
OidcLegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
OidcTokenExchange: gu.Ptr(true),
- Actions: gu.Ptr(true),
ImprovedPerformance: nil,
WebKey: gu.Ptr(true),
OidcSingleV1SessionTermination: gu.Ptr(true),
@@ -142,7 +131,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
LegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
TokenExchange: gu.Ptr(true),
- Actions: gu.Ptr(true),
ImprovedPerformance: nil,
WebKey: gu.Ptr(true),
OIDCSingleV1SessionTermination: gu.Ptr(true),
@@ -174,10 +162,6 @@ func Test_instanceFeaturesToPb(t *testing.T) {
Level: feature.LevelInstance,
Value: true,
},
- Actions: query.FeatureSource[bool]{
- Level: feature.LevelInstance,
- Value: true,
- },
TokenExchange: query.FeatureSource[bool]{
Level: feature.LevelSystem,
Value: false,
@@ -217,10 +201,6 @@ func Test_instanceFeaturesToPb(t *testing.T) {
Enabled: true,
Source: feature_pb.Source_SOURCE_INSTANCE,
},
- Actions: &feature_pb.FeatureFlag{
- Enabled: true,
- Source: feature_pb.Source_SOURCE_INSTANCE,
- },
OidcTokenExchange: &feature_pb.FeatureFlag{
Enabled: false,
Source: feature_pb.Source_SOURCE_SYSTEM,
diff --git a/internal/api/grpc/feature/v2beta/integration_test/feature_test.go b/internal/api/grpc/feature/v2beta/integration_test/feature_test.go
index 69e05352d0..cbd9f5f939 100644
--- a/internal/api/grpc/feature/v2beta/integration_test/feature_test.go
+++ b/internal/api/grpc/feature/v2beta/integration_test/feature_test.go
@@ -202,10 +202,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
Enabled: false,
Source: feature.Source_SOURCE_UNSPECIFIED,
},
- Actions: &feature.FeatureFlag{
- Enabled: false,
- Source: feature.Source_SOURCE_UNSPECIFIED,
- },
},
},
{
@@ -215,7 +211,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
LoginDefaultOrg: gu.Ptr(true),
OidcTriggerIntrospectionProjections: gu.Ptr(false),
UserSchema: gu.Ptr(true),
- Actions: gu.Ptr(true),
})
require.NoError(t, err)
},
@@ -236,10 +231,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
Enabled: true,
Source: feature.Source_SOURCE_INSTANCE,
},
- Actions: &feature.FeatureFlag{
- Enabled: true,
- Source: feature.Source_SOURCE_INSTANCE,
- },
},
},
{
@@ -273,10 +264,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
Enabled: false,
Source: feature.Source_SOURCE_UNSPECIFIED,
},
- Actions: &feature.FeatureFlag{
- Enabled: false,
- Source: feature.Source_SOURCE_UNSPECIFIED,
- },
},
},
}
diff --git a/internal/api/grpc/server/middleware/execution_interceptor.go b/internal/api/grpc/server/middleware/execution_interceptor.go
index 053386caae..4aeea6c4da 100644
--- a/internal/api/grpc/server/middleware/execution_interceptor.go
+++ b/internal/api/grpc/server/middleware/execution_interceptor.go
@@ -5,12 +5,13 @@ import (
"encoding/json"
"google.golang.org/grpc"
+ "google.golang.org/protobuf/encoding/protojson"
+ "google.golang.org/protobuf/proto"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/execution"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
- "github.com/zitadel/zitadel/internal/zerrors"
)
func ExecutionHandler(queries *query.Queries) grpc.UnaryServerInterceptor {
@@ -48,7 +49,7 @@ func executeTargetsForRequest(ctx context.Context, targets []execution.Target, f
ProjectID: ctxData.ProjectID,
OrgID: ctxData.OrgID,
UserID: ctxData.UserID,
- Request: req,
+ Request: Message{req.(proto.Message)},
}
return execution.CallTargets(ctx, targets, info)
@@ -70,8 +71,8 @@ func executeTargetsForResponse(ctx context.Context, targets []execution.Target,
ProjectID: ctxData.ProjectID,
OrgID: ctxData.OrgID,
UserID: ctxData.UserID,
- Request: req,
- Response: resp,
+ Request: Message{req.(proto.Message)},
+ Response: Message{resp.(proto.Message)},
}
return execution.CallTargets(ctx, targets, info)
@@ -80,12 +81,28 @@ func executeTargetsForResponse(ctx context.Context, targets []execution.Target,
var _ execution.ContextInfo = &ContextInfoRequest{}
type ContextInfoRequest struct {
- FullMethod string `json:"fullMethod,omitempty"`
- InstanceID string `json:"instanceID,omitempty"`
- OrgID string `json:"orgID,omitempty"`
- ProjectID string `json:"projectID,omitempty"`
- UserID string `json:"userID,omitempty"`
- Request interface{} `json:"request,omitempty"`
+ FullMethod string `json:"fullMethod,omitempty"`
+ InstanceID string `json:"instanceID,omitempty"`
+ OrgID string `json:"orgID,omitempty"`
+ ProjectID string `json:"projectID,omitempty"`
+ UserID string `json:"userID,omitempty"`
+ Request Message `json:"request,omitempty"`
+}
+
+type Message struct {
+ proto.Message
+}
+
+func (r *Message) MarshalJSON() ([]byte, error) {
+ data, err := protojson.Marshal(r.Message)
+ if err != nil {
+ return nil, err
+ }
+ return data, nil
+}
+
+func (r *Message) UnmarshalJSON(data []byte) error {
+ return protojson.Unmarshal(data, r.Message)
}
func (c *ContextInfoRequest) GetHTTPRequestBody() []byte {
@@ -97,26 +114,23 @@ func (c *ContextInfoRequest) GetHTTPRequestBody() []byte {
}
func (c *ContextInfoRequest) SetHTTPResponseBody(resp []byte) error {
- if !json.Valid(resp) {
- return zerrors.ThrowPreconditionFailed(nil, "ACTION-4m9s2", "Errors.Execution.ResponseIsNotValidJSON")
- }
- return json.Unmarshal(resp, c.Request)
+ return json.Unmarshal(resp, &c.Request)
}
func (c *ContextInfoRequest) GetContent() interface{} {
- return c.Request
+ return c.Request.Message
}
var _ execution.ContextInfo = &ContextInfoResponse{}
type ContextInfoResponse struct {
- FullMethod string `json:"fullMethod,omitempty"`
- InstanceID string `json:"instanceID,omitempty"`
- OrgID string `json:"orgID,omitempty"`
- ProjectID string `json:"projectID,omitempty"`
- UserID string `json:"userID,omitempty"`
- Request interface{} `json:"request,omitempty"`
- Response interface{} `json:"response,omitempty"`
+ FullMethod string `json:"fullMethod,omitempty"`
+ InstanceID string `json:"instanceID,omitempty"`
+ OrgID string `json:"orgID,omitempty"`
+ ProjectID string `json:"projectID,omitempty"`
+ UserID string `json:"userID,omitempty"`
+ Request Message `json:"request,omitempty"`
+ Response Message `json:"response,omitempty"`
}
func (c *ContextInfoResponse) GetHTTPRequestBody() []byte {
@@ -128,9 +142,9 @@ func (c *ContextInfoResponse) GetHTTPRequestBody() []byte {
}
func (c *ContextInfoResponse) SetHTTPResponseBody(resp []byte) error {
- return json.Unmarshal(resp, c.Response)
+ return json.Unmarshal(resp, &c.Response)
}
func (c *ContextInfoResponse) GetContent() interface{} {
- return c.Response
+ return c.Response.Message
}
diff --git a/internal/api/grpc/server/middleware/execution_interceptor_test.go b/internal/api/grpc/server/middleware/execution_interceptor_test.go
index 6a5b74c5e4..281db4617a 100644
--- a/internal/api/grpc/server/middleware/execution_interceptor_test.go
+++ b/internal/api/grpc/server/middleware/execution_interceptor_test.go
@@ -11,6 +11,9 @@ import (
"time"
"github.com/stretchr/testify/assert"
+ "google.golang.org/protobuf/encoding/protojson"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/types/known/structpb"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/execution"
@@ -54,28 +57,28 @@ func (e *mockExecutionTarget) GetSigningKey() string {
return e.SigningKey
}
-type mockContentRequest struct {
- Content string
-}
-
-func newMockContentRequest(content string) *mockContentRequest {
- return &mockContentRequest{
- Content: content,
+func newMockContentRequest(content string) proto.Message {
+ return &structpb.Struct{
+ Fields: map[string]*structpb.Value{
+ "content": {
+ Kind: &structpb.Value_StringValue{StringValue: content},
+ },
+ },
}
}
func newMockContextInfoRequest(fullMethod, request string) *ContextInfoRequest {
return &ContextInfoRequest{
FullMethod: fullMethod,
- Request: newMockContentRequest(request),
+ Request: Message{Message: newMockContentRequest(request)},
}
}
func newMockContextInfoResponse(fullMethod, request, response string) *ContextInfoResponse {
return &ContextInfoResponse{
FullMethod: fullMethod,
- Request: newMockContentRequest(request),
- Response: newMockContentRequest(response),
+ Request: Message{Message: newMockContentRequest(request)},
+ Response: Message{Message: newMockContentRequest(response)},
}
}
@@ -591,7 +594,7 @@ func Test_executeTargetsForGRPCFullMethod_request(t *testing.T) {
} else {
assert.NoError(t, err)
}
- assert.Equal(t, tt.res.want, resp)
+ assert.EqualExportedValues(t, tt.res.want, resp)
for _, closeF := range closeFuncs {
closeF()
@@ -632,7 +635,7 @@ func testServerCall(
time.Sleep(sleep)
w.Header().Set("Content-Type", "application/json")
- resp, err := json.Marshal(respBody)
+ resp, err := protojson.Marshal(respBody.(proto.Message))
if err != nil {
http.Error(w, "error", http.StatusInternalServerError)
return
@@ -723,7 +726,8 @@ func Test_executeTargetsForGRPCFullMethod_response(t *testing.T) {
statusCode: http.StatusOK,
},
},
- req: []byte{},
+ req: newMockContentRequest(""),
+ resp: newMockContentRequest(""),
},
res{
wantErr: true,
@@ -790,7 +794,7 @@ func Test_executeTargetsForGRPCFullMethod_response(t *testing.T) {
} else {
assert.NoError(t, err)
}
- assert.Equal(t, tt.res.want, resp)
+ assert.EqualExportedValues(t, tt.res.want, resp)
for _, closeF := range closeFuncs {
closeF()
diff --git a/internal/api/grpc/session/v2/integration_test/session_test.go b/internal/api/grpc/session/v2/integration_test/session_test.go
index b9a060c749..0982a56121 100644
--- a/internal/api/grpc/session/v2/integration_test/session_test.go
+++ b/internal/api/grpc/session/v2/integration_test/session_test.go
@@ -354,7 +354,7 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) {
require.NoError(t, err)
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, User.GetUserId())
- intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId())
+ intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId(), time.Now().Add(time.Hour))
require.NoError(t, err)
updateResp, err := Client.SetSession(LoginCTX, &session.SetSessionRequest{
SessionId: createResp.GetSessionId(),
@@ -372,7 +372,7 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) {
func TestServer_CreateSession_successfulIntent_instant(t *testing.T) {
idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId()
- intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId())
+ intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId(), time.Now().Add(time.Hour))
require.NoError(t, err)
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{
Checks: &session.Checks{
@@ -396,7 +396,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) {
// successful intent without known / linked user
idpUserID := "id"
- intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, idpUserID, "")
+ intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, idpUserID, "", time.Now().Add(time.Hour))
// link the user (with info from intent)
Instance.CreateUserIDPlink(CTX, User.GetUserId(), idpUserID, idpID, User.GetUserId())
@@ -447,6 +447,80 @@ func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) {
require.Error(t, err)
}
+func TestServer_CreateSession_reuseIntent(t *testing.T) {
+ idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId()
+ createResp, err := Client.CreateSession(LoginCTX, &session.CreateSessionRequest{
+ Checks: &session.Checks{
+ User: &session.CheckUser{
+ Search: &session.CheckUser_UserId{
+ UserId: User.GetUserId(),
+ },
+ },
+ },
+ })
+ require.NoError(t, err)
+ verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, User.GetUserId())
+
+ intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId(), time.Now().Add(time.Hour))
+ require.NoError(t, err)
+ updateResp, err := Client.SetSession(LoginCTX, &session.SetSessionRequest{
+ SessionId: createResp.GetSessionId(),
+ Checks: &session.Checks{
+ IdpIntent: &session.CheckIDPIntent{
+ IdpIntentId: intentID,
+ IdpIntentToken: token,
+ },
+ },
+ })
+ require.NoError(t, err)
+ verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, User.GetUserId(), wantUserFactor, wantIntentFactor)
+
+ // the reuse of the intent token is not allowed, not even on the same session
+ session2, err := Client.SetSession(LoginCTX, &session.SetSessionRequest{
+ SessionId: createResp.GetSessionId(),
+ Checks: &session.Checks{
+ IdpIntent: &session.CheckIDPIntent{
+ IdpIntentId: intentID,
+ IdpIntentToken: token,
+ },
+ },
+ })
+ require.Error(t, err)
+ _ = session2
+}
+
+func TestServer_CreateSession_expiredIntent(t *testing.T) {
+ idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId()
+ createResp, err := Client.CreateSession(LoginCTX, &session.CreateSessionRequest{
+ Checks: &session.Checks{
+ User: &session.CheckUser{
+ Search: &session.CheckUser_UserId{
+ UserId: User.GetUserId(),
+ },
+ },
+ },
+ })
+ require.NoError(t, err)
+ verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, User.GetUserId())
+
+ intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId(), time.Now().Add(time.Second))
+ require.NoError(t, err)
+
+ // wait for the intent to expire
+ time.Sleep(2 * time.Second)
+
+ _, err = Client.SetSession(LoginCTX, &session.SetSessionRequest{
+ SessionId: createResp.GetSessionId(),
+ Checks: &session.Checks{
+ IdpIntent: &session.CheckIDPIntent{
+ IdpIntentId: intentID,
+ IdpIntentToken: token,
+ },
+ },
+ })
+ require.Error(t, err)
+}
+
func registerTOTP(ctx context.Context, t *testing.T, userID string) (secret string) {
resp, err := Instance.Client.UserV2.RegisterTOTP(ctx, &user.RegisterTOTPRequest{
UserId: userID,
diff --git a/internal/api/grpc/session/v2beta/integration_test/session_test.go b/internal/api/grpc/session/v2beta/integration_test/session_test.go
index d0fc1179ef..4c189e0f80 100644
--- a/internal/api/grpc/session/v2beta/integration_test/session_test.go
+++ b/internal/api/grpc/session/v2beta/integration_test/session_test.go
@@ -354,7 +354,7 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) {
require.NoError(t, err)
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, User.GetUserId())
- intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId())
+ intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId(), time.Now().Add(time.Hour))
require.NoError(t, err)
updateResp, err := Client.SetSession(CTX, &session.SetSessionRequest{
SessionId: createResp.GetSessionId(),
@@ -372,7 +372,7 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) {
func TestServer_CreateSession_successfulIntent_instant(t *testing.T) {
idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId()
- intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId())
+ intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId(), time.Now().Add(time.Hour))
require.NoError(t, err)
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{
Checks: &session.Checks{
@@ -396,7 +396,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) {
// successful intent without known / linked user
idpUserID := "id"
- intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId())
+ intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId(), time.Now().Add(time.Hour))
require.NoError(t, err)
// link the user (with info from intent)
@@ -448,6 +448,80 @@ func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) {
require.Error(t, err)
}
+func TestServer_CreateSession_reuseIntent(t *testing.T) {
+ idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId()
+ createResp, err := Client.CreateSession(IAMOwnerCTX, &session.CreateSessionRequest{
+ Checks: &session.Checks{
+ User: &session.CheckUser{
+ Search: &session.CheckUser_UserId{
+ UserId: User.GetUserId(),
+ },
+ },
+ },
+ })
+ require.NoError(t, err)
+ verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, User.GetUserId())
+
+ intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId(), time.Now().Add(time.Hour))
+ require.NoError(t, err)
+ updateResp, err := Client.SetSession(IAMOwnerCTX, &session.SetSessionRequest{
+ SessionId: createResp.GetSessionId(),
+ Checks: &session.Checks{
+ IdpIntent: &session.CheckIDPIntent{
+ IdpIntentId: intentID,
+ IdpIntentToken: token,
+ },
+ },
+ })
+ require.NoError(t, err)
+ verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, User.GetUserId(), wantUserFactor, wantIntentFactor)
+
+ // the reuse of the intent token is not allowed, not even on the same session
+ session2, err := Client.SetSession(IAMOwnerCTX, &session.SetSessionRequest{
+ SessionId: createResp.GetSessionId(),
+ Checks: &session.Checks{
+ IdpIntent: &session.CheckIDPIntent{
+ IdpIntentId: intentID,
+ IdpIntentToken: token,
+ },
+ },
+ })
+ require.Error(t, err)
+ _ = session2
+}
+
+func TestServer_CreateSession_expiredIntent(t *testing.T) {
+ idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId()
+ createResp, err := Client.CreateSession(IAMOwnerCTX, &session.CreateSessionRequest{
+ Checks: &session.Checks{
+ User: &session.CheckUser{
+ Search: &session.CheckUser_UserId{
+ UserId: User.GetUserId(),
+ },
+ },
+ },
+ })
+ require.NoError(t, err)
+ verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, User.GetUserId())
+
+ intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId(), time.Now().Add(time.Second))
+ require.NoError(t, err)
+
+ // wait for the intent to expire
+ time.Sleep(2 * time.Second)
+
+ _, err = Client.SetSession(IAMOwnerCTX, &session.SetSessionRequest{
+ SessionId: createResp.GetSessionId(),
+ Checks: &session.Checks{
+ IdpIntent: &session.CheckIDPIntent{
+ IdpIntentId: intentID,
+ IdpIntentToken: token,
+ },
+ },
+ })
+ require.Error(t, err)
+}
+
func registerTOTP(ctx context.Context, t *testing.T, userID string) (secret string) {
resp, err := Instance.Client.UserV2.RegisterTOTP(ctx, &user.RegisterTOTPRequest{
UserId: userID,
diff --git a/internal/api/grpc/user/v2/integration_test/user_test.go b/internal/api/grpc/user/v2/integration_test/user_test.go
index bf396fd25d..70e670bacc 100644
--- a/internal/api/grpc/user/v2/integration_test/user_test.go
+++ b/internal/api/grpc/user/v2/integration_test/user_test.go
@@ -2121,22 +2121,36 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
authURL, err := url.Parse(Instance.CreateIntent(CTX, oauthIdpID).GetAuthUrl())
require.NoError(t, err)
intentID := authURL.Query().Get("state")
+ expiry := time.Now().Add(1 * time.Hour)
+ expiryFormatted := expiry.Round(time.Millisecond).UTC().Format("2006-01-02T15:04:05.999Z07:00")
- successfulID, token, changeDate, sequence, err := sink.SuccessfulOAuthIntent(Instance.ID(), oauthIdpID, "id", "")
+ intentUser := Instance.CreateHumanUser(IamCTX)
+ _, err = Instance.CreateUserIDPlink(IamCTX, intentUser.GetUserId(), "idpUserID", oauthIdpID, "username")
require.NoError(t, err)
- successfulWithUserID, withUsertoken, withUserchangeDate, withUsersequence, err := sink.SuccessfulOAuthIntent(Instance.ID(), oauthIdpID, "id", "user")
+
+ successfulID, token, changeDate, sequence, err := sink.SuccessfulOAuthIntent(Instance.ID(), oauthIdpID, "id", "", expiry)
require.NoError(t, err)
- oidcSuccessful, oidcToken, oidcChangeDate, oidcSequence, err := sink.SuccessfulOIDCIntent(Instance.ID(), oidcIdpID, "id", "")
+ successfulWithUserID, withUsertoken, withUserchangeDate, withUsersequence, err := sink.SuccessfulOAuthIntent(Instance.ID(), oauthIdpID, "id", "user", expiry)
require.NoError(t, err)
- oidcSuccessfulWithUserID, oidcWithUserIDToken, oidcWithUserIDChangeDate, oidcWithUserIDSequence, err := sink.SuccessfulOIDCIntent(Instance.ID(), oidcIdpID, "id", "user")
+ successfulExpiredID, expiredToken, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), oauthIdpID, "id", "user", time.Now().Add(time.Second))
+ require.NoError(t, err)
+ // make sure the intent is expired
+ time.Sleep(2 * time.Second)
+ successfulConsumedID, consumedToken, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), oauthIdpID, "idpUserID", intentUser.GetUserId(), expiry)
+ require.NoError(t, err)
+ // make sure the intent is consumed
+ Instance.CreateIntentSession(t, IamCTX, intentUser.GetUserId(), successfulConsumedID, consumedToken)
+ oidcSuccessful, oidcToken, oidcChangeDate, oidcSequence, err := sink.SuccessfulOIDCIntent(Instance.ID(), oidcIdpID, "id", "", expiry)
+ require.NoError(t, err)
+ oidcSuccessfulWithUserID, oidcWithUserIDToken, oidcWithUserIDChangeDate, oidcWithUserIDSequence, err := sink.SuccessfulOIDCIntent(Instance.ID(), oidcIdpID, "id", "user", expiry)
require.NoError(t, err)
ldapSuccessfulID, ldapToken, ldapChangeDate, ldapSequence, err := sink.SuccessfulLDAPIntent(Instance.ID(), ldapIdpID, "id", "")
require.NoError(t, err)
ldapSuccessfulWithUserID, ldapWithUserToken, ldapWithUserChangeDate, ldapWithUserSequence, err := sink.SuccessfulLDAPIntent(Instance.ID(), ldapIdpID, "id", "user")
require.NoError(t, err)
- samlSuccessfulID, samlToken, samlChangeDate, samlSequence, err := sink.SuccessfulSAMLIntent(Instance.ID(), samlIdpID, "id", "")
+ samlSuccessfulID, samlToken, samlChangeDate, samlSequence, err := sink.SuccessfulSAMLIntent(Instance.ID(), samlIdpID, "id", "", expiry)
require.NoError(t, err)
- samlSuccessfulWithUserID, samlWithUserToken, samlWithUserChangeDate, samlWithUserSequence, err := sink.SuccessfulSAMLIntent(Instance.ID(), samlIdpID, "id", "user")
+ samlSuccessfulWithUserID, samlWithUserToken, samlWithUserChangeDate, samlWithUserSequence, err := sink.SuccessfulSAMLIntent(Instance.ID(), samlIdpID, "id", "user", expiry)
require.NoError(t, err)
type args struct {
ctx context.Context
@@ -2260,6 +2274,28 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
},
wantErr: false,
},
+ {
+ name: "retrieve successful expired intent",
+ args: args{
+ CTX,
+ &user.RetrieveIdentityProviderIntentRequest{
+ IdpIntentId: successfulExpiredID,
+ IdpIntentToken: expiredToken,
+ },
+ },
+ wantErr: true,
+ },
+ {
+ name: "retrieve successful consumed intent",
+ args: args{
+ CTX,
+ &user.RetrieveIdentityProviderIntentRequest{
+ IdpIntentId: successfulConsumedID,
+ IdpIntentToken: consumedToken,
+ },
+ },
+ wantErr: true,
+ },
{
name: "retrieve successful oidc intent",
args: args{
@@ -2469,7 +2505,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Saml{
Saml: &user.IDPSAMLAccessInformation{
- Assertion: []byte(""),
+ Assertion: []byte(fmt.Sprintf(``, expiryFormatted)),
},
},
IdpId: samlIdpID,
@@ -2518,7 +2554,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Saml{
Saml: &user.IDPSAMLAccessInformation{
- Assertion: []byte(""),
+ Assertion: []byte(fmt.Sprintf(``, expiryFormatted)),
},
},
IdpId: samlIdpID,
diff --git a/internal/api/grpc/user/v2/intent.go b/internal/api/grpc/user/v2/intent.go
index 6e46dfd5c3..8043a9bdae 100644
--- a/internal/api/grpc/user/v2/intent.go
+++ b/internal/api/grpc/user/v2/intent.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
+ "time"
oidc_pkg "github.com/zitadel/oidc/v3/pkg/oidc"
"google.golang.org/protobuf/types/known/structpb"
@@ -71,14 +72,14 @@ func (s *Server) startLDAPIntent(ctx context.Context, idpID string, ldapCredenti
if err != nil {
return nil, err
}
- externalUser, userID, attributes, err := s.ldapLogin(ctx, intentWriteModel.IDPID, ldapCredentials.GetUsername(), ldapCredentials.GetPassword())
+ externalUser, userID, session, err := s.ldapLogin(ctx, intentWriteModel.IDPID, ldapCredentials.GetUsername(), ldapCredentials.GetPassword())
if err != nil {
if err := s.command.FailIDPIntent(ctx, intentWriteModel, err.Error()); err != nil {
return nil, err
}
return nil, err
}
- token, err := s.command.SucceedLDAPIDPIntent(ctx, intentWriteModel, externalUser, userID, attributes)
+ token, err := s.command.SucceedLDAPIDPIntent(ctx, intentWriteModel, externalUser, userID, session)
if err != nil {
return nil, err
}
@@ -116,7 +117,7 @@ func (s *Server) checkLinkedExternalUser(ctx context.Context, idpID, externalUse
return "", nil
}
-func (s *Server) ldapLogin(ctx context.Context, idpID, username, password string) (idp.User, string, map[string][]string, error) {
+func (s *Server) ldapLogin(ctx context.Context, idpID, username, password string) (idp.User, string, *ldap.Session, error) {
provider, err := s.command.GetProvider(ctx, idpID, "", "")
if err != nil {
return nil, "", nil, err
@@ -137,12 +138,7 @@ func (s *Server) ldapLogin(ctx context.Context, idpID, username, password string
if err != nil {
return nil, "", nil, err
}
-
- attributes := make(map[string][]string, 0)
- for _, item := range session.Entry.Attributes {
- attributes[item.Name] = item.Values
- }
- return externalUser, userID, attributes, nil
+ return externalUser, userID, session, nil
}
func (s *Server) RetrieveIdentityProviderIntent(ctx context.Context, req *user.RetrieveIdentityProviderIntentRequest) (_ *user.RetrieveIdentityProviderIntentResponse, err error) {
@@ -156,6 +152,9 @@ func (s *Server) RetrieveIdentityProviderIntent(ctx context.Context, req *user.R
if intent.State != domain.IDPIntentStateSucceeded {
return nil, zerrors.ThrowPreconditionFailed(nil, "IDP-nme4gszsvx", "Errors.Intent.NotSucceeded")
}
+ if time.Now().After(intent.ExpiresAt()) {
+ return nil, zerrors.ThrowPreconditionFailed(nil, "IDP-SAf42", "Errors.Intent.Expired")
+ }
idpIntent, err := idpIntentToIDPIntentPb(intent, s.idpAlg)
if err != nil {
return nil, err
@@ -182,7 +181,7 @@ func (s *Server) RetrieveIdentityProviderIntent(ctx context.Context, req *user.R
case *gitlab.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, &oidc.User{UserInfo: &oidc_pkg.UserInfo{}})
case *google.Provider:
- idpUser, err = unmarshalIdpUser(intent.IDPUser, &oidc.User{UserInfo: &oidc_pkg.UserInfo{}})
+ idpUser, err = unmarshalIdpUser(intent.IDPUser, &google.User{User: &oidc.User{UserInfo: &oidc_pkg.UserInfo{}}})
case *saml.Provider:
idpUser, err = unmarshalIdpUser(intent.IDPUser, &saml.UserMapper{})
case *ldap.Provider:
diff --git a/internal/api/grpc/user/v2beta/integration_test/user_test.go b/internal/api/grpc/user/v2beta/integration_test/user_test.go
index a81de58761..a5a1309d1a 100644
--- a/internal/api/grpc/user/v2beta/integration_test/user_test.go
+++ b/internal/api/grpc/user/v2beta/integration_test/user_test.go
@@ -2153,22 +2153,36 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
authURL, err := url.Parse(Instance.CreateIntent(CTX, oauthIdpID).GetAuthUrl())
require.NoError(t, err)
intentID := authURL.Query().Get("state")
+ expiry := time.Now().Add(1 * time.Hour)
+ expiryFormatted := expiry.Round(time.Millisecond).UTC().Format("2006-01-02T15:04:05.999Z07:00")
- successfulID, token, changeDate, sequence, err := sink.SuccessfulOAuthIntent(Instance.ID(), oauthIdpID, "id", "")
+ intentUser := Instance.CreateHumanUser(IamCTX)
+ _, err = Instance.CreateUserIDPlink(IamCTX, intentUser.GetUserId(), "idpUserID", oauthIdpID, "username")
require.NoError(t, err)
- successfulWithUserID, withUsertoken, withUserchangeDate, withUsersequence, err := sink.SuccessfulOAuthIntent(Instance.ID(), oauthIdpID, "id", "user")
+
+ successfulID, token, changeDate, sequence, err := sink.SuccessfulOAuthIntent(Instance.ID(), oauthIdpID, "id", "", expiry)
require.NoError(t, err)
- oidcSuccessful, oidcToken, oidcChangeDate, oidcSequence, err := sink.SuccessfulOIDCIntent(Instance.ID(), oidcIdpID, "id", "")
+ successfulWithUserID, withUsertoken, withUserchangeDate, withUsersequence, err := sink.SuccessfulOAuthIntent(Instance.ID(), oauthIdpID, "id", "user", expiry)
require.NoError(t, err)
- oidcSuccessfulWithUserID, oidcWithUserIDToken, oidcWithUserIDChangeDate, oidcWithUserIDSequence, err := sink.SuccessfulOIDCIntent(Instance.ID(), oidcIdpID, "id", "user")
+ successfulExpiredID, expiredToken, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), oauthIdpID, "id", "user", time.Now().Add(time.Second))
+ require.NoError(t, err)
+ // make sure the intent is expired
+ time.Sleep(2 * time.Second)
+ successfulConsumedID, consumedToken, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), oauthIdpID, "idpUserID", intentUser.GetUserId(), expiry)
+ require.NoError(t, err)
+ // make sure the intent is consumed
+ Instance.CreateIntentSession(t, IamCTX, intentUser.GetUserId(), successfulConsumedID, consumedToken)
+ oidcSuccessful, oidcToken, oidcChangeDate, oidcSequence, err := sink.SuccessfulOIDCIntent(Instance.ID(), oidcIdpID, "id", "", expiry)
+ require.NoError(t, err)
+ oidcSuccessfulWithUserID, oidcWithUserIDToken, oidcWithUserIDChangeDate, oidcWithUserIDSequence, err := sink.SuccessfulOIDCIntent(Instance.ID(), oidcIdpID, "id", "user", expiry)
require.NoError(t, err)
ldapSuccessfulID, ldapToken, ldapChangeDate, ldapSequence, err := sink.SuccessfulLDAPIntent(Instance.ID(), ldapIdpID, "id", "")
require.NoError(t, err)
ldapSuccessfulWithUserID, ldapWithUserToken, ldapWithUserChangeDate, ldapWithUserSequence, err := sink.SuccessfulLDAPIntent(Instance.ID(), ldapIdpID, "id", "user")
require.NoError(t, err)
- samlSuccessfulID, samlToken, samlChangeDate, samlSequence, err := sink.SuccessfulSAMLIntent(Instance.ID(), samlIdpID, "id", "")
+ samlSuccessfulID, samlToken, samlChangeDate, samlSequence, err := sink.SuccessfulSAMLIntent(Instance.ID(), samlIdpID, "id", "", expiry)
require.NoError(t, err)
- samlSuccessfulWithUserID, samlWithUserToken, samlWithUserChangeDate, samlWithUserSequence, err := sink.SuccessfulSAMLIntent(Instance.ID(), samlIdpID, "id", "user")
+ samlSuccessfulWithUserID, samlWithUserToken, samlWithUserChangeDate, samlWithUserSequence, err := sink.SuccessfulSAMLIntent(Instance.ID(), samlIdpID, "id", "user", expiry)
require.NoError(t, err)
type args struct {
ctx context.Context
@@ -2281,6 +2295,28 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
},
wantErr: false,
},
+ {
+ name: "retrieve successful expired intent",
+ args: args{
+ CTX,
+ &user.RetrieveIdentityProviderIntentRequest{
+ IdpIntentId: successfulExpiredID,
+ IdpIntentToken: expiredToken,
+ },
+ },
+ wantErr: true,
+ },
+ {
+ name: "retrieve successful consumed intent",
+ args: args{
+ CTX,
+ &user.RetrieveIdentityProviderIntentRequest{
+ IdpIntentId: successfulConsumedID,
+ IdpIntentToken: consumedToken,
+ },
+ },
+ wantErr: true,
+ },
{
name: "retrieve successful oidc intent",
args: args{
@@ -2466,7 +2502,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Saml{
Saml: &user.IDPSAMLAccessInformation{
- Assertion: []byte(""),
+ Assertion: []byte(fmt.Sprintf(``, expiryFormatted)),
},
},
IdpId: samlIdpID,
@@ -2504,7 +2540,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
IdpInformation: &user.IDPInformation{
Access: &user.IDPInformation_Saml{
Saml: &user.IDPSAMLAccessInformation{
- Assertion: []byte(""),
+ Assertion: []byte(fmt.Sprintf(``, expiryFormatted)),
},
},
IdpId: samlIdpID,
diff --git a/internal/api/grpc/user/v2beta/user.go b/internal/api/grpc/user/v2beta/user.go
index cf6dfa6304..93afbde0aa 100644
--- a/internal/api/grpc/user/v2beta/user.go
+++ b/internal/api/grpc/user/v2beta/user.go
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"io"
+ "time"
"golang.org/x/text/language"
"google.golang.org/protobuf/types/known/structpb"
@@ -399,14 +400,14 @@ func (s *Server) startLDAPIntent(ctx context.Context, idpID string, ldapCredenti
if err != nil {
return nil, err
}
- externalUser, userID, attributes, err := s.ldapLogin(ctx, intentWriteModel.IDPID, ldapCredentials.GetUsername(), ldapCredentials.GetPassword())
+ externalUser, userID, session, err := s.ldapLogin(ctx, intentWriteModel.IDPID, ldapCredentials.GetUsername(), ldapCredentials.GetPassword())
if err != nil {
if err := s.command.FailIDPIntent(ctx, intentWriteModel, err.Error()); err != nil {
return nil, err
}
return nil, err
}
- token, err := s.command.SucceedLDAPIDPIntent(ctx, intentWriteModel, externalUser, userID, attributes)
+ token, err := s.command.SucceedLDAPIDPIntent(ctx, intentWriteModel, externalUser, userID, session)
if err != nil {
return nil, err
}
@@ -444,7 +445,7 @@ func (s *Server) checkLinkedExternalUser(ctx context.Context, idpID, externalUse
return "", nil
}
-func (s *Server) ldapLogin(ctx context.Context, idpID, username, password string) (idp.User, string, map[string][]string, error) {
+func (s *Server) ldapLogin(ctx context.Context, idpID, username, password string) (idp.User, string, *ldap.Session, error) {
provider, err := s.command.GetProvider(ctx, idpID, "", "")
if err != nil {
return nil, "", nil, err
@@ -470,7 +471,7 @@ func (s *Server) ldapLogin(ctx context.Context, idpID, username, password string
for _, item := range session.Entry.Attributes {
attributes[item.Name] = item.Values
}
- return externalUser, userID, attributes, nil
+ return externalUser, userID, session, nil
}
func (s *Server) RetrieveIdentityProviderIntent(ctx context.Context, req *user.RetrieveIdentityProviderIntentRequest) (_ *user.RetrieveIdentityProviderIntentResponse, err error) {
@@ -484,6 +485,9 @@ func (s *Server) RetrieveIdentityProviderIntent(ctx context.Context, req *user.R
if intent.State != domain.IDPIntentStateSucceeded {
return nil, zerrors.ThrowPreconditionFailed(nil, "IDP-nme4gszsvx", "Errors.Intent.NotSucceeded")
}
+ if time.Now().After(intent.ExpiresAt()) {
+ return nil, zerrors.ThrowPreconditionFailed(nil, "IDP-Afb2s", "Errors.Intent.Expired")
+ }
return idpIntentToIDPIntentPb(intent, s.idpAlg)
}
diff --git a/internal/api/idp/idp.go b/internal/api/idp/idp.go
index c3e9586a59..ebf904a395 100644
--- a/internal/api/idp/idp.go
+++ b/internal/api/idp/idp.go
@@ -287,7 +287,7 @@ func (h *Handler) handleACS(w http.ResponseWriter, r *http.Request) {
userID, err := h.checkExternalUser(ctx, intent.IDPID, idpUser.GetID())
logging.WithFields("intent", intent.AggregateID).OnError(err).Error("could not check if idp user already exists")
- token, err := h.commands.SucceedSAMLIDPIntent(ctx, intent, idpUser, userID, session.Assertion)
+ token, err := h.commands.SucceedSAMLIDPIntent(ctx, intent, idpUser, userID, session)
if err != nil {
redirectToFailureURLErr(w, r, intent, zerrors.ThrowInternal(err, "IDP-JdD3g", "Errors.Intent.TokenCreationFailed"))
return
diff --git a/internal/api/idp/idp_test.go b/internal/api/idp/idp_test.go
index 6804a035af..2f64f598a9 100644
--- a/internal/api/idp/idp_test.go
+++ b/internal/api/idp/idp_test.go
@@ -4,6 +4,7 @@ import (
"net/http/httptest"
"net/url"
"testing"
+ "time"
"github.com/stretchr/testify/assert"
@@ -14,11 +15,12 @@ import (
func Test_redirectToSuccessURL(t *testing.T) {
type args struct {
- id string
- userID string
- token string
- failureURL string
- successURL string
+ id string
+ userID string
+ token string
+ failureURL string
+ successURL string
+ maxIdPIntentLifetime time.Duration
}
type res struct {
want string
@@ -59,7 +61,7 @@ func Test_redirectToSuccessURL(t *testing.T) {
req := httptest.NewRequest("GET", "http://example.com", nil)
resp := httptest.NewRecorder()
- wm := command.NewIDPIntentWriteModel(tt.args.id, tt.args.id)
+ wm := command.NewIDPIntentWriteModel(tt.args.id, tt.args.id, tt.args.maxIdPIntentLifetime)
wm.FailureURL, _ = url.Parse(tt.args.failureURL)
wm.SuccessURL, _ = url.Parse(tt.args.successURL)
@@ -71,11 +73,12 @@ func Test_redirectToSuccessURL(t *testing.T) {
func Test_redirectToFailureURL(t *testing.T) {
type args struct {
- id string
- failureURL string
- successURL string
- err string
- desc string
+ id string
+ failureURL string
+ successURL string
+ err string
+ desc string
+ maxIdPIntentLifetime time.Duration
}
type res struct {
want string
@@ -115,7 +118,7 @@ func Test_redirectToFailureURL(t *testing.T) {
req := httptest.NewRequest("GET", "http://example.com", nil)
resp := httptest.NewRecorder()
- wm := command.NewIDPIntentWriteModel(tt.args.id, tt.args.id)
+ wm := command.NewIDPIntentWriteModel(tt.args.id, tt.args.id, tt.args.maxIdPIntentLifetime)
wm.FailureURL, _ = url.Parse(tt.args.failureURL)
wm.SuccessURL, _ = url.Parse(tt.args.successURL)
@@ -127,10 +130,11 @@ func Test_redirectToFailureURL(t *testing.T) {
func Test_redirectToFailureURLErr(t *testing.T) {
type args struct {
- id string
- failureURL string
- successURL string
- err error
+ id string
+ failureURL string
+ successURL string
+ err error
+ maxIdPIntentLifetime time.Duration
}
type res struct {
want string
@@ -158,7 +162,7 @@ func Test_redirectToFailureURLErr(t *testing.T) {
req := httptest.NewRequest("GET", "http://example.com", nil)
resp := httptest.NewRecorder()
- wm := command.NewIDPIntentWriteModel(tt.args.id, tt.args.id)
+ wm := command.NewIDPIntentWriteModel(tt.args.id, tt.args.id, tt.args.maxIdPIntentLifetime)
wm.FailureURL, _ = url.Parse(tt.args.failureURL)
wm.SuccessURL, _ = url.Parse(tt.args.successURL)
diff --git a/internal/api/ui/login/static/resources/themes/scss/styles/button/button_base.scss b/internal/api/ui/login/static/resources/themes/scss/styles/button/button_base.scss
index dd53dceb79..aeeeba541c 100644
--- a/internal/api/ui/login/static/resources/themes/scss/styles/button/button_base.scss
+++ b/internal/api/ui/login/static/resources/themes/scss/styles/button/button_base.scss
@@ -39,7 +39,9 @@ $lgn-icon-button-line-height: 40px !default;
padding: $lgn-button-padding;
border-radius: $lgn-button-border-radius;
- overflow: visible;
+ overflow: hidden;
+ text-overflow: ellipsis;
+
transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
&[disabled] {
diff --git a/internal/auth/repository/eventsourcing/handler/handler.go b/internal/auth/repository/eventsourcing/handler/handler.go
index 0d87ab06bb..74a27a8312 100644
--- a/internal/auth/repository/eventsourcing/handler/handler.go
+++ b/internal/auth/repository/eventsourcing/handler/handler.go
@@ -2,8 +2,12 @@ package handler
import (
"context"
+ "fmt"
"time"
+ "github.com/zitadel/logging"
+
+ "github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/eventstore"
@@ -72,11 +76,13 @@ func Projections() []*handler2.Handler {
}
func ProjectInstance(ctx context.Context) error {
- for _, projection := range projections {
+ for i, projection := range projections {
+ logging.WithFields("name", projection.ProjectionName(), "instance", authz.GetInstance(ctx).InstanceID(), "index", fmt.Sprintf("%d/%d", i, len(projections))).Info("starting auth projection")
_, err := projection.Trigger(ctx)
if err != nil {
return err
}
+ logging.WithFields("name", projection.ProjectionName(), "instance", authz.GetInstance(ctx).InstanceID(), "index", fmt.Sprintf("%d/%d", i, len(projections))).Info("auth projection done")
}
return nil
}
diff --git a/internal/command/command.go b/internal/command/command.go
index b0e67ad52e..64b7b53b67 100644
--- a/internal/command/command.go
+++ b/internal/command/command.go
@@ -81,6 +81,7 @@ type Commands struct {
publicKeyLifetime time.Duration
certificateLifetime time.Duration
defaultSecretGenerators *SecretGenerators
+ maxIdPIntentLifetime time.Duration
samlCertificateAndKeyGenerator func(id string) ([]byte, []byte, error)
webKeyGenerator func(keyID string, alg crypto.EncryptionAlgorithm, genConfig crypto.WebKeyConfig) (encryptedPrivate *crypto.CryptoValue, public *jose.JSONWebKey, err error)
@@ -152,6 +153,7 @@ func StartCommands(
privateKeyLifetime: defaults.KeyConfig.PrivateKeyLifetime,
publicKeyLifetime: defaults.KeyConfig.PublicKeyLifetime,
certificateLifetime: defaults.KeyConfig.CertificateLifetime,
+ maxIdPIntentLifetime: defaults.MaxIdPIntentLifetime,
idpConfigEncryption: idpConfigEncryption,
smtpEncryption: smtpEncryption,
smsEncryption: smsEncryption,
diff --git a/internal/command/idp_intent.go b/internal/command/idp_intent.go
index 3cd9991679..9690117edd 100644
--- a/internal/command/idp_intent.go
+++ b/internal/command/idp_intent.go
@@ -7,7 +7,6 @@ import (
"encoding/xml"
"net/url"
- "github.com/crewjam/saml"
"github.com/crewjam/saml/samlsp"
"github.com/zitadel/oidc/v3/pkg/oidc"
@@ -19,8 +18,10 @@ import (
"github.com/zitadel/zitadel/internal/idp/providers/apple"
"github.com/zitadel/zitadel/internal/idp/providers/azuread"
"github.com/zitadel/zitadel/internal/idp/providers/jwt"
+ "github.com/zitadel/zitadel/internal/idp/providers/ldap"
"github.com/zitadel/zitadel/internal/idp/providers/oauth"
openid "github.com/zitadel/zitadel/internal/idp/providers/oidc"
+ "github.com/zitadel/zitadel/internal/idp/providers/saml"
"github.com/zitadel/zitadel/internal/repository/idpintent"
"github.com/zitadel/zitadel/internal/zerrors"
)
@@ -68,7 +69,7 @@ func (c *Commands) CreateIntent(ctx context.Context, intentID, idpID, successURL
return nil, nil, err
}
}
- writeModel := NewIDPIntentWriteModel(intentID, resourceOwner)
+ writeModel := NewIDPIntentWriteModel(intentID, resourceOwner, c.maxIdPIntentLifetime)
//nolint: staticcheck
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareCreateIntent(writeModel, idpID, successURL, failureURL, idpArguments))
@@ -180,6 +181,7 @@ func (c *Commands) SucceedIDPIntent(ctx context.Context, writeModel *IDPIntentWr
userID,
accessToken,
idToken,
+ idpSession.ExpiresAt(),
)
err = c.pushAppendAndReduce(ctx, writeModel, cmd)
if err != nil {
@@ -188,7 +190,7 @@ func (c *Commands) SucceedIDPIntent(ctx context.Context, writeModel *IDPIntentWr
return token, nil
}
-func (c *Commands) SucceedSAMLIDPIntent(ctx context.Context, writeModel *IDPIntentWriteModel, idpUser idp.User, userID string, assertion *saml.Assertion) (string, error) {
+func (c *Commands) SucceedSAMLIDPIntent(ctx context.Context, writeModel *IDPIntentWriteModel, idpUser idp.User, userID string, session *saml.Session) (string, error) {
token, err := c.generateIntentToken(writeModel.AggregateID)
if err != nil {
return "", err
@@ -197,7 +199,7 @@ func (c *Commands) SucceedSAMLIDPIntent(ctx context.Context, writeModel *IDPInte
if err != nil {
return "", err
}
- assertionData, err := xml.Marshal(assertion)
+ assertionData, err := xml.Marshal(session.Assertion)
if err != nil {
return "", err
}
@@ -213,6 +215,7 @@ func (c *Commands) SucceedSAMLIDPIntent(ctx context.Context, writeModel *IDPInte
idpUser.GetPreferredUsername(),
userID,
assertionEnc,
+ session.ExpiresAt(),
)
err = c.pushAppendAndReduce(ctx, writeModel, cmd)
if err != nil {
@@ -237,7 +240,7 @@ func (c *Commands) generateIntentToken(intentID string) (string, error) {
return base64.RawURLEncoding.EncodeToString(token), nil
}
-func (c *Commands) SucceedLDAPIDPIntent(ctx context.Context, writeModel *IDPIntentWriteModel, idpUser idp.User, userID string, attributes map[string][]string) (string, error) {
+func (c *Commands) SucceedLDAPIDPIntent(ctx context.Context, writeModel *IDPIntentWriteModel, idpUser idp.User, userID string, session *ldap.Session) (string, error) {
token, err := c.generateIntentToken(writeModel.AggregateID)
if err != nil {
return "", err
@@ -246,6 +249,10 @@ func (c *Commands) SucceedLDAPIDPIntent(ctx context.Context, writeModel *IDPInte
if err != nil {
return "", err
}
+ attributes := make(map[string][]string, len(session.Entry.Attributes))
+ for _, item := range session.Entry.Attributes {
+ attributes[item.Name] = item.Values
+ }
cmd := idpintent.NewLDAPSucceededEvent(
ctx,
IDPIntentAggregateFromWriteModel(&writeModel.WriteModel),
@@ -254,6 +261,7 @@ func (c *Commands) SucceedLDAPIDPIntent(ctx context.Context, writeModel *IDPInte
idpUser.GetPreferredUsername(),
userID,
attributes,
+ session.ExpiresAt(),
)
err = c.pushAppendAndReduce(ctx, writeModel, cmd)
if err != nil {
@@ -273,7 +281,7 @@ func (c *Commands) FailIDPIntent(ctx context.Context, writeModel *IDPIntentWrite
}
func (c *Commands) GetIntentWriteModel(ctx context.Context, id, resourceOwner string) (*IDPIntentWriteModel, error) {
- writeModel := NewIDPIntentWriteModel(id, resourceOwner)
+ writeModel := NewIDPIntentWriteModel(id, resourceOwner, c.maxIdPIntentLifetime)
err := c.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
diff --git a/internal/command/idp_intent_model.go b/internal/command/idp_intent_model.go
index c6bc26ab06..07e0821813 100644
--- a/internal/command/idp_intent_model.go
+++ b/internal/command/idp_intent_model.go
@@ -2,6 +2,7 @@ package command
import (
"net/url"
+ "time"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
@@ -29,18 +30,29 @@ type IDPIntentWriteModel struct {
RequestID string
Assertion *crypto.CryptoValue
- State domain.IDPIntentState
+ State domain.IDPIntentState
+ succeededAt time.Time
+ maxIdPIntentLifetime time.Duration
+ expiresAt time.Time
}
-func NewIDPIntentWriteModel(id, resourceOwner string) *IDPIntentWriteModel {
+func NewIDPIntentWriteModel(id, resourceOwner string, maxIdPIntentLifetime time.Duration) *IDPIntentWriteModel {
return &IDPIntentWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: id,
ResourceOwner: resourceOwner,
},
+ maxIdPIntentLifetime: maxIdPIntentLifetime,
}
}
+func (wm *IDPIntentWriteModel) ExpiresAt() time.Time {
+ if wm.expiresAt.IsZero() {
+ return wm.succeededAt.Add(wm.maxIdPIntentLifetime)
+ }
+ return wm.expiresAt
+}
+
func (wm *IDPIntentWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
@@ -56,6 +68,8 @@ func (wm *IDPIntentWriteModel) Reduce() error {
wm.reduceLDAPSucceededEvent(e)
case *idpintent.FailedEvent:
wm.reduceFailedEvent(e)
+ case *idpintent.ConsumedEvent:
+ wm.reduceConsumedEvent(e)
}
}
return wm.WriteModel.Reduce()
@@ -74,6 +88,7 @@ func (wm *IDPIntentWriteModel) Query() *eventstore.SearchQueryBuilder {
idpintent.SAMLRequestEventType,
idpintent.LDAPSucceededEventType,
idpintent.FailedEventType,
+ idpintent.ConsumedEventType,
).
Builder()
}
@@ -93,6 +108,8 @@ func (wm *IDPIntentWriteModel) reduceSAMLSucceededEvent(e *idpintent.SAMLSucceed
wm.IDPUserName = e.IDPUserName
wm.Assertion = e.Assertion
wm.State = domain.IDPIntentStateSucceeded
+ wm.succeededAt = e.CreationDate()
+ wm.expiresAt = e.ExpiresAt
}
func (wm *IDPIntentWriteModel) reduceLDAPSucceededEvent(e *idpintent.LDAPSucceededEvent) {
@@ -102,6 +119,8 @@ func (wm *IDPIntentWriteModel) reduceLDAPSucceededEvent(e *idpintent.LDAPSucceed
wm.IDPUserName = e.IDPUserName
wm.IDPEntryAttributes = e.EntryAttributes
wm.State = domain.IDPIntentStateSucceeded
+ wm.succeededAt = e.CreationDate()
+ wm.expiresAt = e.ExpiresAt
}
func (wm *IDPIntentWriteModel) reduceOAuthSucceededEvent(e *idpintent.SucceededEvent) {
@@ -112,6 +131,8 @@ func (wm *IDPIntentWriteModel) reduceOAuthSucceededEvent(e *idpintent.SucceededE
wm.IDPAccessToken = e.IDPAccessToken
wm.IDPIDToken = e.IDPIDToken
wm.State = domain.IDPIntentStateSucceeded
+ wm.succeededAt = e.CreationDate()
+ wm.expiresAt = e.ExpiresAt
}
func (wm *IDPIntentWriteModel) reduceSAMLRequestEvent(e *idpintent.SAMLRequestEvent) {
@@ -122,6 +143,10 @@ func (wm *IDPIntentWriteModel) reduceFailedEvent(e *idpintent.FailedEvent) {
wm.State = domain.IDPIntentStateFailed
}
+func (wm *IDPIntentWriteModel) reduceConsumedEvent(e *idpintent.ConsumedEvent) {
+ wm.State = domain.IDPIntentStateConsumed
+}
+
func IDPIntentAggregateFromWriteModel(wm *eventstore.WriteModel) *eventstore.Aggregate {
return &eventstore.Aggregate{
Type: idpintent.AggregateType,
diff --git a/internal/command/idp_intent_test.go b/internal/command/idp_intent_test.go
index 2400b9ee35..1be3971e87 100644
--- a/internal/command/idp_intent_test.go
+++ b/internal/command/idp_intent_test.go
@@ -4,8 +4,10 @@ import (
"context"
"net/url"
"testing"
+ "time"
- "github.com/crewjam/saml"
+ crewjam_saml "github.com/crewjam/saml"
+ goldap "github.com/go-ldap/ldap/v3"
"github.com/muhlemmer/gu"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -26,6 +28,7 @@ import (
"github.com/zitadel/zitadel/internal/idp/providers/ldap"
"github.com/zitadel/zitadel/internal/idp/providers/oauth"
openid "github.com/zitadel/zitadel/internal/idp/providers/oidc"
+ "github.com/zitadel/zitadel/internal/idp/providers/saml"
rep_idp "github.com/zitadel/zitadel/internal/repository/idp"
"github.com/zitadel/zitadel/internal/repository/idpintent"
"github.com/zitadel/zitadel/internal/repository/instance"
@@ -867,7 +870,7 @@ func TestCommands_SucceedIDPIntent(t *testing.T) {
},
args{
ctx: context.Background(),
- writeModel: NewIDPIntentWriteModel("id", "ro"),
+ writeModel: NewIDPIntentWriteModel("id", "ro", 0),
},
res{
err: zerrors.ThrowInternal(nil, "id", "encryption failed"),
@@ -888,7 +891,7 @@ func TestCommands_SucceedIDPIntent(t *testing.T) {
},
args{
ctx: context.Background(),
- writeModel: NewIDPIntentWriteModel("id", "ro"),
+ writeModel: NewIDPIntentWriteModel("id", "ro", 0),
idpSession: &oauth.Session{
Tokens: &oidc.Tokens[*oidc.IDTokenClaims]{
Token: &oauth2.Token{
@@ -922,6 +925,7 @@ func TestCommands_SucceedIDPIntent(t *testing.T) {
Crypted: []byte("accessToken"),
},
"idToken",
+ time.Time{},
)
return event
}(),
@@ -930,7 +934,7 @@ func TestCommands_SucceedIDPIntent(t *testing.T) {
},
args{
ctx: context.Background(),
- writeModel: NewIDPIntentWriteModel("id", "instance"),
+ writeModel: NewIDPIntentWriteModel("id", "instance", 0),
idpSession: &openid.Session{
Tokens: &oidc.Tokens[*oidc.IDTokenClaims]{
Token: &oauth2.Token{
@@ -973,7 +977,7 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) {
ctx context.Context
writeModel *IDPIntentWriteModel
idpUser idp.User
- assertion *saml.Assertion
+ session *saml.Session
userID string
}
type res struct {
@@ -998,7 +1002,7 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) {
},
args{
ctx: context.Background(),
- writeModel: NewIDPIntentWriteModel("id", "ro"),
+ writeModel: NewIDPIntentWriteModel("id", "ro", 0),
},
res{
err: zerrors.ThrowInternal(nil, "id", "encryption failed"),
@@ -1023,14 +1027,17 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) {
KeyID: "id",
Crypted: []byte(""),
},
+ time.Time{},
),
),
),
},
args{
ctx: context.Background(),
- writeModel: NewIDPIntentWriteModel("id", "instance"),
- assertion: &saml.Assertion{ID: "id"},
+ writeModel: NewIDPIntentWriteModel("id", "instance", 0),
+ session: &saml.Session{
+ Assertion: &crewjam_saml.Assertion{ID: "id"},
+ },
idpUser: openid.NewUser(&oidc.UserInfo{
Subject: "id",
UserInfoProfile: oidc.UserInfoProfile{
@@ -1061,14 +1068,17 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) {
KeyID: "id",
Crypted: []byte(""),
},
+ time.Time{},
),
),
),
},
args{
ctx: context.Background(),
- writeModel: NewIDPIntentWriteModel("id", "instance"),
- assertion: &saml.Assertion{ID: "id"},
+ writeModel: NewIDPIntentWriteModel("id", "instance", 0),
+ session: &saml.Session{
+ Assertion: &crewjam_saml.Assertion{ID: "id"},
+ },
idpUser: openid.NewUser(&oidc.UserInfo{
Subject: "id",
UserInfoProfile: oidc.UserInfoProfile{
@@ -1088,7 +1098,7 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) {
eventstore: tt.fields.eventstore(t),
idpConfigEncryption: tt.fields.idpConfigEncryption,
}
- got, err := c.SucceedSAMLIDPIntent(tt.args.ctx, tt.args.writeModel, tt.args.idpUser, tt.args.userID, tt.args.assertion)
+ got, err := c.SucceedSAMLIDPIntent(tt.args.ctx, tt.args.writeModel, tt.args.idpUser, tt.args.userID, tt.args.session)
require.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.token, got)
})
@@ -1128,7 +1138,7 @@ func TestCommands_RequestSAMLIDPIntent(t *testing.T) {
},
args{
ctx: context.Background(),
- writeModel: NewIDPIntentWriteModel("id", "instance"),
+ writeModel: NewIDPIntentWriteModel("id", "instance", 0),
request: "request",
},
res{},
@@ -1156,7 +1166,7 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) {
writeModel *IDPIntentWriteModel
idpUser idp.User
userID string
- attributes map[string][]string
+ session *ldap.Session
}
type res struct {
token string
@@ -1180,7 +1190,7 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) {
},
args{
ctx: context.Background(),
- writeModel: NewIDPIntentWriteModel("id", "instance"),
+ writeModel: NewIDPIntentWriteModel("id", "instance", 0),
},
res{
err: zerrors.ThrowInternal(nil, "id", "encryption failed"),
@@ -1200,14 +1210,24 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) {
"username",
"",
map[string][]string{"id": {"id"}},
+ time.Time{},
),
),
),
},
args{
ctx: context.Background(),
- writeModel: NewIDPIntentWriteModel("id", "instance"),
- attributes: map[string][]string{"id": {"id"}},
+ writeModel: NewIDPIntentWriteModel("id", "instance", 0),
+ session: &ldap.Session{
+ Entry: &goldap.Entry{
+ Attributes: []*goldap.EntryAttribute{
+ {
+ Name: "id",
+ Values: []string{"id"},
+ },
+ },
+ },
+ },
idpUser: ldap.NewUser(
"id",
"",
@@ -1235,7 +1255,7 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) {
eventstore: tt.fields.eventstore(t),
idpConfigEncryption: tt.fields.idpConfigEncryption,
}
- got, err := c.SucceedLDAPIDPIntent(tt.args.ctx, tt.args.writeModel, tt.args.idpUser, tt.args.userID, tt.args.attributes)
+ got, err := c.SucceedLDAPIDPIntent(tt.args.ctx, tt.args.writeModel, tt.args.idpUser, tt.args.userID, tt.args.session)
require.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.token, got)
})
@@ -1275,7 +1295,7 @@ func TestCommands_FailIDPIntent(t *testing.T) {
},
args{
ctx: context.Background(),
- writeModel: NewIDPIntentWriteModel("id", "instance"),
+ writeModel: NewIDPIntentWriteModel("id", "instance", 0),
reason: "reason",
},
res{
diff --git a/internal/command/instance_features.go b/internal/command/instance_features.go
index 3e927cc0c5..cb12bff828 100644
--- a/internal/command/instance_features.go
+++ b/internal/command/instance_features.go
@@ -21,7 +21,6 @@ type InstanceFeatures struct {
LegacyIntrospection *bool
UserSchema *bool
TokenExchange *bool
- Actions *bool
ImprovedPerformance []feature.ImprovedPerformanceType
WebKey *bool
DebugOIDCParentError *bool
@@ -39,7 +38,6 @@ func (m *InstanceFeatures) isEmpty() bool {
m.LegacyIntrospection == nil &&
m.UserSchema == nil &&
m.TokenExchange == nil &&
- m.Actions == nil &&
// nil check to allow unset improvements
m.ImprovedPerformance == nil &&
m.WebKey == nil &&
diff --git a/internal/command/instance_features_model.go b/internal/command/instance_features_model.go
index 954c769304..977a46b6c2 100644
--- a/internal/command/instance_features_model.go
+++ b/internal/command/instance_features_model.go
@@ -71,7 +71,6 @@ func (m *InstanceFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
feature_v2.InstanceLegacyIntrospectionEventType,
feature_v2.InstanceUserSchemaEventType,
feature_v2.InstanceTokenExchangeEventType,
- feature_v2.InstanceActionsEventType,
feature_v2.InstanceImprovedPerformanceEventType,
feature_v2.InstanceWebKeyEventType,
feature_v2.InstanceDebugOIDCParentErrorEventType,
@@ -108,9 +107,6 @@ func reduceInstanceFeature(features *InstanceFeatures, key feature.Key, value an
case feature.KeyUserSchema:
v := value.(bool)
features.UserSchema = &v
- case feature.KeyActions:
- v := value.(bool)
- features.Actions = &v
case feature.KeyImprovedPerformance:
v := value.([]feature.ImprovedPerformanceType)
features.ImprovedPerformance = v
@@ -148,7 +144,6 @@ func (wm *InstanceFeaturesWriteModel) setCommands(ctx context.Context, f *Instan
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.LegacyIntrospection, f.LegacyIntrospection, feature_v2.InstanceLegacyIntrospectionEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.TokenExchange, f.TokenExchange, feature_v2.InstanceTokenExchangeEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.UserSchema, f.UserSchema, feature_v2.InstanceUserSchemaEventType)
- cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.Actions, f.Actions, feature_v2.InstanceActionsEventType)
cmds = appendFeatureSliceUpdate(ctx, cmds, aggregate, wm.ImprovedPerformance, f.ImprovedPerformance, feature_v2.InstanceImprovedPerformanceEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.WebKey, f.WebKey, feature_v2.InstanceWebKeyEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.DebugOIDCParentError, f.DebugOIDCParentError, feature_v2.InstanceDebugOIDCParentErrorEventType)
diff --git a/internal/command/instance_features_test.go b/internal/command/instance_features_test.go
index e6b6bb4346..02e8896a0c 100644
--- a/internal/command/instance_features_test.go
+++ b/internal/command/instance_features_test.go
@@ -149,24 +149,6 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
ResourceOwner: "instance1",
},
},
- {
- name: "set Actions",
- eventstore: expectEventstore(
- expectFilter(),
- expectPush(
- feature_v2.NewSetEvent[bool](
- ctx, aggregate,
- feature_v2.InstanceActionsEventType, true,
- ),
- ),
- ),
- args: args{ctx, &InstanceFeatures{
- Actions: gu.Ptr(true),
- }},
- want: &domain.ObjectDetails{
- ResourceOwner: "instance1",
- },
- },
{
name: "push error",
eventstore: expectEventstore(
@@ -204,10 +186,6 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
ctx, aggregate,
feature_v2.InstanceUserSchemaEventType, true,
),
- feature_v2.NewSetEvent[bool](
- ctx, aggregate,
- feature_v2.InstanceActionsEventType, true,
- ),
feature_v2.NewSetEvent[bool](
ctx, aggregate,
feature_v2.InstanceOIDCSingleV1SessionTerminationEventType, true,
@@ -219,7 +197,6 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
TriggerIntrospectionProjections: gu.Ptr(false),
LegacyIntrospection: gu.Ptr(true),
UserSchema: gu.Ptr(true),
- Actions: gu.Ptr(true),
OIDCSingleV1SessionTermination: gu.Ptr(true),
}},
want: &domain.ObjectDetails{
diff --git a/internal/command/session.go b/internal/command/session.go
index d00e541e62..3c06c22967 100644
--- a/internal/command/session.go
+++ b/internal/command/session.go
@@ -17,6 +17,7 @@ import (
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/id"
"github.com/zitadel/zitadel/internal/notification/senders"
+ "github.com/zitadel/zitadel/internal/repository/idpintent"
"github.com/zitadel/zitadel/internal/repository/session"
"github.com/zitadel/zitadel/internal/repository/user"
"github.com/zitadel/zitadel/internal/zerrors"
@@ -32,31 +33,33 @@ type SessionCommands struct {
eventstore *eventstore.Eventstore
eventCommands []eventstore.Command
- hasher *crypto.Hasher
- intentAlg crypto.EncryptionAlgorithm
- totpAlg crypto.EncryptionAlgorithm
- otpAlg crypto.EncryptionAlgorithm
- createCode encryptedCodeWithDefaultFunc
- createPhoneCode encryptedCodeGeneratorWithDefaultFunc
- createToken func(sessionID string) (id string, token string, err error)
- getCodeVerifier func(ctx context.Context, id string) (senders.CodeGenerator, error)
- now func() time.Time
+ hasher *crypto.Hasher
+ intentAlg crypto.EncryptionAlgorithm
+ totpAlg crypto.EncryptionAlgorithm
+ otpAlg crypto.EncryptionAlgorithm
+ createCode encryptedCodeWithDefaultFunc
+ createPhoneCode encryptedCodeGeneratorWithDefaultFunc
+ createToken func(sessionID string) (id string, token string, err error)
+ getCodeVerifier func(ctx context.Context, id string) (senders.CodeGenerator, error)
+ now func() time.Time
+ maxIdPIntentLifetime time.Duration
}
func (c *Commands) NewSessionCommands(cmds []SessionCommand, session *SessionWriteModel) *SessionCommands {
return &SessionCommands{
- sessionCommands: cmds,
- sessionWriteModel: session,
- eventstore: c.eventstore,
- hasher: c.userPasswordHasher,
- intentAlg: c.idpConfigEncryption,
- totpAlg: c.multifactors.OTP.CryptoMFA,
- otpAlg: c.userEncryption,
- createCode: c.newEncryptedCodeWithDefault,
- createPhoneCode: c.newPhoneCode,
- createToken: c.sessionTokenCreator,
- getCodeVerifier: c.phoneCodeVerifierFromConfig,
- now: time.Now,
+ sessionCommands: cmds,
+ sessionWriteModel: session,
+ eventstore: c.eventstore,
+ hasher: c.userPasswordHasher,
+ intentAlg: c.idpConfigEncryption,
+ totpAlg: c.multifactors.OTP.CryptoMFA,
+ otpAlg: c.userEncryption,
+ createCode: c.newEncryptedCodeWithDefault,
+ createPhoneCode: c.newPhoneCode,
+ createToken: c.sessionTokenCreator,
+ getCodeVerifier: c.phoneCodeVerifierFromConfig,
+ now: time.Now,
+ maxIdPIntentLifetime: c.maxIdPIntentLifetime,
}
}
@@ -92,7 +95,7 @@ func CheckIntent(intentID, token string) SessionCommand {
if err := crypto.CheckToken(cmd.intentAlg, token, intentID); err != nil {
return nil, err
}
- cmd.intentWriteModel = NewIDPIntentWriteModel(intentID, "")
+ cmd.intentWriteModel = NewIDPIntentWriteModel(intentID, "", cmd.maxIdPIntentLifetime)
err := cmd.eventstore.FilterToQueryReducer(ctx, cmd.intentWriteModel)
if err != nil {
return nil, err
@@ -100,6 +103,9 @@ func CheckIntent(intentID, token string) SessionCommand {
if cmd.intentWriteModel.State != domain.IDPIntentStateSucceeded {
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Df4bw", "Errors.Intent.NotSucceeded")
}
+ if time.Now().After(cmd.intentWriteModel.ExpiresAt()) {
+ return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-SAf42", "Errors.Intent.Expired")
+ }
if cmd.intentWriteModel.UserID != "" {
if cmd.intentWriteModel.UserID != cmd.sessionWriteModel.UserID {
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-O8xk3w", "Errors.Intent.OtherUser")
@@ -168,6 +174,7 @@ func (s *SessionCommands) PasswordChecked(ctx context.Context, checkedAt time.Ti
func (s *SessionCommands) IntentChecked(ctx context.Context, checkedAt time.Time) {
s.eventCommands = append(s.eventCommands, session.NewIntentCheckedEvent(ctx, s.sessionWriteModel.aggregate, checkedAt))
+ s.eventCommands = append(s.eventCommands, idpintent.NewConsumedEvent(ctx, IDPIntentAggregateFromWriteModel(&s.intentWriteModel.WriteModel)))
}
func (s *SessionCommands) WebAuthNChallenged(ctx context.Context, challenge string, allowedCrentialIDs [][]byte, userVerification domain.UserVerificationRequirement, rpid string) {
diff --git a/internal/command/session_test.go b/internal/command/session_test.go
index 60027d3a05..e65f32fb57 100644
--- a/internal/command/session_test.go
+++ b/internal/command/session_test.go
@@ -695,6 +695,7 @@ func TestCommands_updateSession(t *testing.T) {
"userID2",
nil,
"",
+ time.Now().Add(time.Hour),
),
),
),
@@ -757,6 +758,111 @@ func TestCommands_updateSession(t *testing.T) {
err: zerrors.ThrowPermissionDenied(nil, "CRYPTO-CRYPTO", "Errors.Intent.InvalidToken"),
},
},
+ {
+ "set user, intent token already consumed",
+ fields{
+ eventstore: expectEventstore(
+ expectFilter(
+ eventFromEventPusher(
+ user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate,
+ "username", "", "", "", "", language.English, domain.GenderUnspecified, "", false),
+ ),
+ eventFromEventPusher(
+ idpintent.NewSucceededEvent(context.Background(),
+ &idpintent.NewAggregate("intent", "instance1").Aggregate,
+ nil,
+ "idpUserID",
+ "idpUsername",
+ "userID",
+ nil,
+ "",
+ time.Now().Add(time.Hour),
+ ),
+ ),
+ eventFromEventPusher(
+ idpintent.NewConsumedEvent(context.Background(),
+ &idpintent.NewAggregate("intent", "instance1").Aggregate,
+ ),
+ ),
+ ),
+ ),
+ },
+ args{
+ ctx: authz.NewMockContext("instance1", "", ""),
+ checks: &SessionCommands{
+ sessionWriteModel: NewSessionWriteModel("sessionID", "instance1"),
+ sessionCommands: []SessionCommand{
+ CheckUser("userID", "org1", &language.Afrikaans),
+ CheckIntent("intent", "aW50ZW50"),
+ },
+ createToken: func(sessionID string) (string, string, error) {
+ return "tokenID",
+ "token",
+ nil
+ },
+ intentAlg: decryption(nil),
+ now: func() time.Time {
+ return testNow
+ },
+ },
+ metadata: map[string][]byte{
+ "key": []byte("value"),
+ },
+ },
+ res{
+ err: zerrors.ThrowPreconditionFailed(nil, "COMMAND-Df4bw", "Errors.Intent.NotSucceeded"),
+ },
+ },
+ {
+ "set user, intent token already expired",
+ fields{
+ eventstore: expectEventstore(
+ expectFilter(
+ eventFromEventPusher(
+ user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate,
+ "username", "", "", "", "", language.English, domain.GenderUnspecified, "", false),
+ ),
+ eventFromEventPusher(
+ idpintent.NewSucceededEvent(context.Background(),
+ &idpintent.NewAggregate("intent", "instance1").Aggregate,
+ nil,
+ "idpUserID",
+ "idpUsername",
+ "userID",
+ nil,
+ "",
+ time.Now().Add(-time.Hour),
+ ),
+ ),
+ ),
+ ),
+ },
+ args{
+ ctx: authz.NewMockContext("instance1", "", ""),
+ checks: &SessionCommands{
+ sessionWriteModel: NewSessionWriteModel("sessionID", "instance1"),
+ sessionCommands: []SessionCommand{
+ CheckUser("userID", "org1", &language.Afrikaans),
+ CheckIntent("intent", "aW50ZW50"),
+ },
+ createToken: func(sessionID string) (string, string, error) {
+ return "tokenID",
+ "token",
+ nil
+ },
+ intentAlg: decryption(nil),
+ now: func() time.Time {
+ return testNow
+ },
+ },
+ metadata: map[string][]byte{
+ "key": []byte("value"),
+ },
+ },
+ res{
+ err: zerrors.ThrowPreconditionFailed(nil, "COMMAND-SAf42", "Errors.Intent.Expired"),
+ },
+ },
{
"set user, intent, metadata and token",
fields{
@@ -768,13 +874,14 @@ func TestCommands_updateSession(t *testing.T) {
),
eventFromEventPusher(
idpintent.NewSucceededEvent(context.Background(),
- &idpintent.NewAggregate("id", "instance1").Aggregate,
+ &idpintent.NewAggregate("intent", "instance1").Aggregate,
nil,
"idpUserID",
"idpUsername",
"userID",
nil,
"",
+ time.Now().Add(time.Hour),
),
),
),
@@ -783,6 +890,7 @@ func TestCommands_updateSession(t *testing.T) {
"userID", "org1", testNow, &language.Afrikaans),
session.NewIntentCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
testNow),
+ idpintent.NewConsumedEvent(context.Background(), &idpintent.NewAggregate("intent", "org1").Aggregate),
session.NewMetadataSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
map[string][]byte{"key": []byte("value")}),
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
@@ -842,13 +950,14 @@ func TestCommands_updateSession(t *testing.T) {
),
eventFromEventPusher(
idpintent.NewSucceededEvent(context.Background(),
- &idpintent.NewAggregate("id", "instance1").Aggregate,
+ &idpintent.NewAggregate("intent", "instance1").Aggregate,
nil,
"idpUserID",
"idpUsername",
"",
nil,
"",
+ time.Now().Add(time.Hour),
),
),
),
@@ -866,6 +975,7 @@ func TestCommands_updateSession(t *testing.T) {
"userID", "org1", testNow, &language.Afrikaans),
session.NewIntentCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
testNow),
+ idpintent.NewConsumedEvent(context.Background(), &idpintent.NewAggregate("intent", "org1").Aggregate),
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID"),
),
diff --git a/internal/command/system_features.go b/internal/command/system_features.go
index dc886de318..b317ea93bb 100644
--- a/internal/command/system_features.go
+++ b/internal/command/system_features.go
@@ -15,7 +15,6 @@ type SystemFeatures struct {
LegacyIntrospection *bool
TokenExchange *bool
UserSchema *bool
- Actions *bool
ImprovedPerformance []feature.ImprovedPerformanceType
OIDCSingleV1SessionTermination *bool
DisableUserTokenEvent *bool
@@ -30,7 +29,6 @@ func (m *SystemFeatures) isEmpty() bool {
m.LegacyIntrospection == nil &&
m.UserSchema == nil &&
m.TokenExchange == nil &&
- m.Actions == nil &&
// nil check to allow unset improvements
m.ImprovedPerformance == nil &&
m.OIDCSingleV1SessionTermination == nil &&
diff --git a/internal/command/system_features_model.go b/internal/command/system_features_model.go
index 15fc3e0bf0..28e56f8bd4 100644
--- a/internal/command/system_features_model.go
+++ b/internal/command/system_features_model.go
@@ -64,7 +64,6 @@ func (m *SystemFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
feature_v2.SystemLegacyIntrospectionEventType,
feature_v2.SystemUserSchemaEventType,
feature_v2.SystemTokenExchangeEventType,
- feature_v2.SystemActionsEventType,
feature_v2.SystemImprovedPerformanceEventType,
feature_v2.SystemOIDCSingleV1SessionTerminationEventType,
feature_v2.SystemDisableUserTokenEvent,
@@ -98,9 +97,6 @@ func reduceSystemFeature(features *SystemFeatures, key feature.Key, value any) {
case feature.KeyTokenExchange:
v := value.(bool)
features.TokenExchange = &v
- case feature.KeyActions:
- v := value.(bool)
- features.Actions = &v
case feature.KeyImprovedPerformance:
features.ImprovedPerformance = value.([]feature.ImprovedPerformanceType)
case feature.KeyOIDCSingleV1SessionTermination:
@@ -128,7 +124,6 @@ func (wm *SystemFeaturesWriteModel) setCommands(ctx context.Context, f *SystemFe
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.LegacyIntrospection, f.LegacyIntrospection, feature_v2.SystemLegacyIntrospectionEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.UserSchema, f.UserSchema, feature_v2.SystemUserSchemaEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.TokenExchange, f.TokenExchange, feature_v2.SystemTokenExchangeEventType)
- cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.Actions, f.Actions, feature_v2.SystemActionsEventType)
cmds = appendFeatureSliceUpdate(ctx, cmds, aggregate, wm.ImprovedPerformance, f.ImprovedPerformance, feature_v2.SystemImprovedPerformanceEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.OIDCSingleV1SessionTermination, f.OIDCSingleV1SessionTermination, feature_v2.SystemOIDCSingleV1SessionTerminationEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.DisableUserTokenEvent, f.DisableUserTokenEvent, feature_v2.SystemDisableUserTokenEvent)
diff --git a/internal/command/system_features_test.go b/internal/command/system_features_test.go
index 9c5f4cc2a9..b1b5207b8c 100644
--- a/internal/command/system_features_test.go
+++ b/internal/command/system_features_test.go
@@ -117,24 +117,6 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
ResourceOwner: "SYSTEM",
},
},
- {
- name: "set Actions",
- eventstore: expectEventstore(
- expectFilter(),
- expectPush(
- feature_v2.NewSetEvent[bool](
- context.Background(), aggregate,
- feature_v2.SystemActionsEventType, true,
- ),
- ),
- ),
- args: args{context.Background(), &SystemFeatures{
- Actions: gu.Ptr(true),
- }},
- want: &domain.ObjectDetails{
- ResourceOwner: "SYSTEM",
- },
- },
{
name: "push error",
eventstore: expectEventstore(
@@ -172,10 +154,6 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
context.Background(), aggregate,
feature_v2.SystemUserSchemaEventType, true,
),
- feature_v2.NewSetEvent[bool](
- context.Background(), aggregate,
- feature_v2.SystemActionsEventType, true,
- ),
feature_v2.NewSetEvent[bool](
context.Background(), aggregate,
feature_v2.SystemOIDCSingleV1SessionTerminationEventType, true,
@@ -187,7 +165,6 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
TriggerIntrospectionProjections: gu.Ptr(false),
LegacyIntrospection: gu.Ptr(true),
UserSchema: gu.Ptr(true),
- Actions: gu.Ptr(true),
OIDCSingleV1SessionTermination: gu.Ptr(true),
}},
want: &domain.ObjectDetails{
@@ -233,10 +210,6 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
context.Background(), aggregate,
feature_v2.SystemUserSchemaEventType, true,
),
- feature_v2.NewSetEvent[bool](
- context.Background(), aggregate,
- feature_v2.SystemActionsEventType, false,
- ),
feature_v2.NewSetEvent[bool](
context.Background(), aggregate,
feature_v2.SystemOIDCSingleV1SessionTerminationEventType, false,
@@ -248,7 +221,6 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
TriggerIntrospectionProjections: gu.Ptr(false),
LegacyIntrospection: gu.Ptr(true),
UserSchema: gu.Ptr(true),
- Actions: gu.Ptr(false),
OIDCSingleV1SessionTermination: gu.Ptr(false),
}},
want: &domain.ObjectDetails{
diff --git a/internal/config/systemdefaults/system_defaults.go b/internal/config/systemdefaults/system_defaults.go
index f6d39befe7..827dd61f73 100644
--- a/internal/config/systemdefaults/system_defaults.go
+++ b/internal/config/systemdefaults/system_defaults.go
@@ -7,15 +7,16 @@ import (
)
type SystemDefaults struct {
- SecretGenerators SecretGenerators
- PasswordHasher crypto.HashConfig
- SecretHasher crypto.HashConfig
- Multifactors MultifactorConfig
- DomainVerification DomainVerification
- Notifications Notifications
- KeyConfig KeyConfig
- DefaultQueryLimit uint64
- MaxQueryLimit uint64
+ SecretGenerators SecretGenerators
+ PasswordHasher crypto.HashConfig
+ SecretHasher crypto.HashConfig
+ Multifactors MultifactorConfig
+ DomainVerification DomainVerification
+ Notifications Notifications
+ KeyConfig KeyConfig
+ DefaultQueryLimit uint64
+ MaxQueryLimit uint64
+ MaxIdPIntentLifetime time.Duration
}
type SecretGenerators struct {
diff --git a/internal/domain/idp.go b/internal/domain/idp.go
index e2571f6b0d..bea106298b 100644
--- a/internal/domain/idp.go
+++ b/internal/domain/idp.go
@@ -115,6 +115,7 @@ const (
IDPIntentStateStarted
IDPIntentStateSucceeded
IDPIntentStateFailed
+ IDPIntentStateConsumed
idpIntentStateCount
)
diff --git a/internal/execution/execution.go b/internal/execution/execution.go
index 575c86ecc4..b885858d94 100644
--- a/internal/execution/execution.go
+++ b/internal/execution/execution.go
@@ -82,11 +82,11 @@ func CallTarget(
case domain.TargetTypeCall:
return Call(ctx, target.GetEndpoint(), target.GetTimeout(), info.GetHTTPRequestBody(), target.GetSigningKey())
case domain.TargetTypeAsync:
- go func(target Target, info ContextInfoRequest) {
- if _, err := Call(ctx, target.GetEndpoint(), target.GetTimeout(), info.GetHTTPRequestBody(), target.GetSigningKey()); err != nil {
+ go func(ctx context.Context, target Target, info []byte) {
+ if _, err := Call(ctx, target.GetEndpoint(), target.GetTimeout(), info, target.GetSigningKey()); err != nil {
logging.WithFields("target", target.GetTargetID()).OnError(err).Info(err)
}
- }(target, info)
+ }(context.WithoutCancel(ctx), target, info.GetHTTPRequestBody())
return nil, nil
default:
return nil, zerrors.ThrowInternal(nil, "EXEC-auqnansr2m", "Errors.Execution.Unknown")
diff --git a/internal/execution/execution_test.go b/internal/execution/execution_test.go
index 40731a840a..036b160ab7 100644
--- a/internal/execution/execution_test.go
+++ b/internal/execution/execution_test.go
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "google.golang.org/protobuf/types/known/structpb"
"github.com/zitadel/zitadel/internal/api/grpc/server/middleware"
"github.com/zitadel/zitadel/internal/domain"
@@ -149,8 +150,8 @@ func Test_CallTarget(t *testing.T) {
info: requestContextInfo1,
server: &callTestServer{
method: http.MethodPost,
- expectBody: []byte("{\"request\":{\"request\":\"content1\"}}"),
- respondBody: []byte("{\"request\":\"content2\"}"),
+ expectBody: []byte("{\"request\":{\"content\":\"request1\"}}"),
+ respondBody: []byte("{\"content\":\"request2\"}"),
timeout: time.Second,
statusCode: http.StatusInternalServerError,
},
@@ -170,8 +171,8 @@ func Test_CallTarget(t *testing.T) {
server: &callTestServer{
timeout: time.Second,
method: http.MethodPost,
- expectBody: []byte("{\"request\":{\"request\":\"content1\"}}"),
- respondBody: []byte("{\"request\":\"content2\"}"),
+ expectBody: []byte("{\"request\":{\"content\":\"request1\"}}"),
+ respondBody: []byte("{\"content\":\"request2\"}"),
statusCode: http.StatusInternalServerError,
},
target: &mockTarget{
@@ -191,8 +192,8 @@ func Test_CallTarget(t *testing.T) {
server: &callTestServer{
timeout: time.Second,
method: http.MethodPost,
- expectBody: []byte("{\"request\":{\"request\":\"content1\"}}"),
- respondBody: []byte("{\"request\":\"content2\"}"),
+ expectBody: []byte("{\"request\":{\"content\":\"request1\"}}"),
+ respondBody: []byte("{\"content\":\"request2\"}"),
statusCode: http.StatusOK,
},
target: &mockTarget{
@@ -212,8 +213,8 @@ func Test_CallTarget(t *testing.T) {
server: &callTestServer{
timeout: time.Second,
method: http.MethodPost,
- expectBody: []byte("{\"request\":{\"request\":\"content1\"}}"),
- respondBody: []byte("{\"request\":\"content2\"}"),
+ expectBody: []byte("{\"request\":{\"content\":\"request1\"}}"),
+ respondBody: []byte("{\"content\":\"request2\"}"),
statusCode: http.StatusOK,
signingKey: "signingkey",
},
@@ -235,8 +236,8 @@ func Test_CallTarget(t *testing.T) {
server: &callTestServer{
timeout: time.Second,
method: http.MethodPost,
- expectBody: []byte("{\"request\":{\"request\":\"content1\"}}"),
- respondBody: []byte("{\"request\":\"content2\"}"),
+ expectBody: []byte("{\"request\":{\"content\":\"request1\"}}"),
+ respondBody: []byte("{\"content\":\"request2\"}"),
statusCode: http.StatusInternalServerError,
},
target: &mockTarget{
@@ -256,8 +257,8 @@ func Test_CallTarget(t *testing.T) {
server: &callTestServer{
timeout: time.Second,
method: http.MethodPost,
- expectBody: []byte("{\"request\":{\"request\":\"content1\"}}"),
- respondBody: []byte("{\"request\":\"content2\"}"),
+ expectBody: []byte("{\"request\":{\"content\":\"request1\"}}"),
+ respondBody: []byte("{\"content\":\"request2\"}"),
statusCode: http.StatusOK,
},
target: &mockTarget{
@@ -266,7 +267,7 @@ func Test_CallTarget(t *testing.T) {
},
},
res{
- body: []byte("{\"request\":\"content2\"}"),
+ body: []byte("{\"content\":\"request2\"}"),
},
},
{
@@ -277,8 +278,8 @@ func Test_CallTarget(t *testing.T) {
server: &callTestServer{
timeout: time.Second,
method: http.MethodPost,
- expectBody: []byte("{\"request\":{\"request\":\"content1\"}}"),
- respondBody: []byte("{\"request\":\"content2\"}"),
+ expectBody: []byte("{\"request\":{\"content\":\"request1\"}}"),
+ respondBody: []byte("{\"content\":\"request2\"}"),
statusCode: http.StatusOK,
signingKey: "signingkey",
},
@@ -289,7 +290,7 @@ func Test_CallTarget(t *testing.T) {
},
},
res{
- body: []byte("{\"request\":\"content2\"}"),
+ body: []byte("{\"content\":\"request2\"}"),
},
},
}
@@ -576,13 +577,13 @@ func testCallTargets(ctx context.Context,
}
var requestContextInfo1 = &middleware.ContextInfoRequest{
- Request: &request{
- Request: "content1",
- },
+ Request: middleware.Message{Message: &structpb.Struct{
+ Fields: map[string]*structpb.Value{"content": structpb.NewStringValue("request1")},
+ }},
}
-var requestContextInfoBody1 = []byte("{\"request\":{\"request\":\"content1\"}}")
-var requestContextInfoBody2 = []byte("{\"request\":{\"request\":\"content2\"}}")
+var requestContextInfoBody1 = []byte("{\"request\":{\"content\":\"request1\"}}")
+var requestContextInfoBody2 = []byte("{\"request\":{\"content\":\"request2\"}}")
type request struct {
Request string `json:"request"`
diff --git a/internal/feature/feature.go b/internal/feature/feature.go
index 638917fd68..b5f5a901d4 100644
--- a/internal/feature/feature.go
+++ b/internal/feature/feature.go
@@ -15,7 +15,7 @@ const (
KeyLegacyIntrospection
KeyUserSchema
KeyTokenExchange
- KeyActions
+ KeyActionsDeprecated
KeyImprovedPerformance
KeyWebKey
KeyDebugOIDCParentError
@@ -46,7 +46,6 @@ type Features struct {
LegacyIntrospection bool `json:"legacy_introspection,omitempty"`
UserSchema bool `json:"user_schema,omitempty"`
TokenExchange bool `json:"token_exchange,omitempty"`
- Actions bool `json:"actions,omitempty"`
ImprovedPerformance []ImprovedPerformanceType `json:"improved_performance,omitempty"`
WebKey bool `json:"web_key,omitempty"`
DebugOIDCParentError bool `json:"debug_oidc_parent_error,omitempty"`
@@ -58,10 +57,12 @@ type Features struct {
ConsoleUseV2UserApi bool `json:"console_use_v2_user_api,omitempty"`
}
+/* Note: do not generate the stringer or enumer for this type, is it breaks existing events */
+
type ImprovedPerformanceType int32
const (
- ImprovedPerformanceTypeUnknown = iota
+ ImprovedPerformanceTypeUnspecified ImprovedPerformanceType = iota
ImprovedPerformanceTypeOrgByID
ImprovedPerformanceTypeProjectGrant
ImprovedPerformanceTypeProject
diff --git a/internal/feature/key_enumer.go b/internal/feature/key_enumer.go
index 5a37b96270..a47b3eb4d9 100644
--- a/internal/feature/key_enumer.go
+++ b/internal/feature/key_enumer.go
@@ -7,11 +7,11 @@ import (
"strings"
)
-const _KeyName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactionsimproved_performanceweb_keydebug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api"
+const _KeyName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactions_deprecatedimproved_performanceweb_keydebug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api"
-var _KeyIndex = [...]uint16{0, 11, 28, 61, 81, 92, 106, 113, 133, 140, 163, 197, 221, 247, 255, 274, 297}
+var _KeyIndex = [...]uint16{0, 11, 28, 61, 81, 92, 106, 124, 144, 151, 174, 208, 232, 258, 266, 285, 308}
-const _KeyLowerName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactionsimproved_performanceweb_keydebug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api"
+const _KeyLowerName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactions_deprecatedimproved_performanceweb_keydebug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api"
func (i Key) String() string {
if i < 0 || i >= Key(len(_KeyIndex)-1) {
@@ -30,7 +30,7 @@ func _KeyNoOp() {
_ = x[KeyLegacyIntrospection-(3)]
_ = x[KeyUserSchema-(4)]
_ = x[KeyTokenExchange-(5)]
- _ = x[KeyActions-(6)]
+ _ = x[KeyActionsDeprecated-(6)]
_ = x[KeyImprovedPerformance-(7)]
_ = x[KeyWebKey-(8)]
_ = x[KeyDebugOIDCParentError-(9)]
@@ -42,7 +42,7 @@ func _KeyNoOp() {
_ = x[KeyConsoleUseV2UserApi-(15)]
}
-var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyTriggerIntrospectionProjections, KeyLegacyIntrospection, KeyUserSchema, KeyTokenExchange, KeyActions, KeyImprovedPerformance, KeyWebKey, KeyDebugOIDCParentError, KeyOIDCSingleV1SessionTermination, KeyDisableUserTokenEvent, KeyEnableBackChannelLogout, KeyLoginV2, KeyPermissionCheckV2, KeyConsoleUseV2UserApi}
+var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyTriggerIntrospectionProjections, KeyLegacyIntrospection, KeyUserSchema, KeyTokenExchange, KeyActionsDeprecated, KeyImprovedPerformance, KeyWebKey, KeyDebugOIDCParentError, KeyOIDCSingleV1SessionTermination, KeyDisableUserTokenEvent, KeyEnableBackChannelLogout, KeyLoginV2, KeyPermissionCheckV2, KeyConsoleUseV2UserApi}
var _KeyNameToValueMap = map[string]Key{
_KeyName[0:11]: KeyUnspecified,
@@ -57,26 +57,26 @@ var _KeyNameToValueMap = map[string]Key{
_KeyLowerName[81:92]: KeyUserSchema,
_KeyName[92:106]: KeyTokenExchange,
_KeyLowerName[92:106]: KeyTokenExchange,
- _KeyName[106:113]: KeyActions,
- _KeyLowerName[106:113]: KeyActions,
- _KeyName[113:133]: KeyImprovedPerformance,
- _KeyLowerName[113:133]: KeyImprovedPerformance,
- _KeyName[133:140]: KeyWebKey,
- _KeyLowerName[133:140]: KeyWebKey,
- _KeyName[140:163]: KeyDebugOIDCParentError,
- _KeyLowerName[140:163]: KeyDebugOIDCParentError,
- _KeyName[163:197]: KeyOIDCSingleV1SessionTermination,
- _KeyLowerName[163:197]: KeyOIDCSingleV1SessionTermination,
- _KeyName[197:221]: KeyDisableUserTokenEvent,
- _KeyLowerName[197:221]: KeyDisableUserTokenEvent,
- _KeyName[221:247]: KeyEnableBackChannelLogout,
- _KeyLowerName[221:247]: KeyEnableBackChannelLogout,
- _KeyName[247:255]: KeyLoginV2,
- _KeyLowerName[247:255]: KeyLoginV2,
- _KeyName[255:274]: KeyPermissionCheckV2,
- _KeyLowerName[255:274]: KeyPermissionCheckV2,
- _KeyName[274:297]: KeyConsoleUseV2UserApi,
- _KeyLowerName[274:297]: KeyConsoleUseV2UserApi,
+ _KeyName[106:124]: KeyActionsDeprecated,
+ _KeyLowerName[106:124]: KeyActionsDeprecated,
+ _KeyName[124:144]: KeyImprovedPerformance,
+ _KeyLowerName[124:144]: KeyImprovedPerformance,
+ _KeyName[144:151]: KeyWebKey,
+ _KeyLowerName[144:151]: KeyWebKey,
+ _KeyName[151:174]: KeyDebugOIDCParentError,
+ _KeyLowerName[151:174]: KeyDebugOIDCParentError,
+ _KeyName[174:208]: KeyOIDCSingleV1SessionTermination,
+ _KeyLowerName[174:208]: KeyOIDCSingleV1SessionTermination,
+ _KeyName[208:232]: KeyDisableUserTokenEvent,
+ _KeyLowerName[208:232]: KeyDisableUserTokenEvent,
+ _KeyName[232:258]: KeyEnableBackChannelLogout,
+ _KeyLowerName[232:258]: KeyEnableBackChannelLogout,
+ _KeyName[258:266]: KeyLoginV2,
+ _KeyLowerName[258:266]: KeyLoginV2,
+ _KeyName[266:285]: KeyPermissionCheckV2,
+ _KeyLowerName[266:285]: KeyPermissionCheckV2,
+ _KeyName[285:308]: KeyConsoleUseV2UserApi,
+ _KeyLowerName[285:308]: KeyConsoleUseV2UserApi,
}
var _KeyNames = []string{
@@ -86,16 +86,16 @@ var _KeyNames = []string{
_KeyName[61:81],
_KeyName[81:92],
_KeyName[92:106],
- _KeyName[106:113],
- _KeyName[113:133],
- _KeyName[133:140],
- _KeyName[140:163],
- _KeyName[163:197],
- _KeyName[197:221],
- _KeyName[221:247],
- _KeyName[247:255],
- _KeyName[255:274],
- _KeyName[274:297],
+ _KeyName[106:124],
+ _KeyName[124:144],
+ _KeyName[144:151],
+ _KeyName[151:174],
+ _KeyName[174:208],
+ _KeyName[208:232],
+ _KeyName[232:258],
+ _KeyName[258:266],
+ _KeyName[266:285],
+ _KeyName[285:308],
}
// KeyString retrieves an enum value from the enum constants string name.
diff --git a/internal/idp/providers/apple/session.go b/internal/idp/providers/apple/session.go
index eee68fa2a5..9395d84b2b 100644
--- a/internal/idp/providers/apple/session.go
+++ b/internal/idp/providers/apple/session.go
@@ -10,6 +10,8 @@ import (
"github.com/zitadel/zitadel/internal/idp/providers/oidc"
)
+var _ idp.Session = (*Session)(nil)
+
// Session extends the [oidc.Session] with the formValues returned from the callback.
// This enables to parse the user (name and email), which Apple only returns as form params on registration
type Session struct {
diff --git a/internal/idp/providers/azuread/session.go b/internal/idp/providers/azuread/session.go
index 4b0a6fb844..169784fb58 100644
--- a/internal/idp/providers/azuread/session.go
+++ b/internal/idp/providers/azuread/session.go
@@ -3,6 +3,7 @@ package azuread
import (
"context"
"net/http"
+ "time"
"github.com/zitadel/oidc/v3/pkg/client/rp"
httphelper "github.com/zitadel/oidc/v3/pkg/http"
@@ -12,6 +13,8 @@ import (
"github.com/zitadel/zitadel/internal/idp/providers/oauth"
)
+var _ idp.Session = (*Session)(nil)
+
// Session extends the [oauth.Session] to be able to handle the id_token and to implement the [idp.SessionSupportsMigration] functionality
type Session struct {
*Provider
@@ -79,6 +82,13 @@ func (s *Session) FetchUser(ctx context.Context) (user idp.User, err error) {
return user, nil
}
+func (s *Session) ExpiresAt() time.Time {
+ if s.OAuthSession == nil {
+ return time.Time{}
+ }
+ return s.OAuthSession.ExpiresAt()
+}
+
// Tokens returns the [oidc.Tokens] of the underlying [oauth.Session].
func (s *Session) Tokens() *oidc.Tokens[*oidc.IDTokenClaims] {
return s.oauth().Tokens
diff --git a/internal/idp/providers/jwt/session.go b/internal/idp/providers/jwt/session.go
index 6df08a6998..5138812f3c 100644
--- a/internal/idp/providers/jwt/session.go
+++ b/internal/idp/providers/jwt/session.go
@@ -57,6 +57,13 @@ func (s *Session) FetchUser(ctx context.Context) (user idp.User, err error) {
return &User{s.Tokens.IDTokenClaims}, nil
}
+func (s *Session) ExpiresAt() time.Time {
+ if s.Tokens == nil || s.Tokens.IDTokenClaims == nil {
+ return time.Time{}
+ }
+ return s.Tokens.IDTokenClaims.GetExpiration()
+}
+
func (s *Session) validateToken(ctx context.Context, token string) (*oidc.IDTokenClaims, error) {
logging.Debug("begin token validation")
// TODO: be able to specify them in the template: https://github.com/zitadel/zitadel/issues/5322
diff --git a/internal/idp/providers/ldap/session.go b/internal/idp/providers/ldap/session.go
index 0a6a87ba3d..1679e35b61 100644
--- a/internal/idp/providers/ldap/session.go
+++ b/internal/idp/providers/ldap/session.go
@@ -96,6 +96,10 @@ func (s *Session) FetchUser(_ context.Context) (_ idp.User, err error) {
)
}
+func (s *Session) ExpiresAt() time.Time {
+ return time.Time{} // falls back to the default expiration time
+}
+
func tryBind(
server string,
startTLS bool,
diff --git a/internal/idp/providers/oauth/session.go b/internal/idp/providers/oauth/session.go
index 247a7f8710..c9e175d1cf 100644
--- a/internal/idp/providers/oauth/session.go
+++ b/internal/idp/providers/oauth/session.go
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"net/http"
+ "time"
"github.com/zitadel/oidc/v3/pkg/client/rp"
httphelper "github.com/zitadel/oidc/v3/pkg/http"
@@ -69,6 +70,13 @@ func (s *Session) FetchUser(ctx context.Context) (_ idp.User, err error) {
return user, nil
}
+func (s *Session) ExpiresAt() time.Time {
+ if s.Tokens == nil {
+ return time.Time{}
+ }
+ return s.Tokens.Expiry
+}
+
func (s *Session) authorize(ctx context.Context) (err error) {
if s.Code == "" {
return ErrCodeMissing
diff --git a/internal/idp/providers/oidc/session.go b/internal/idp/providers/oidc/session.go
index b17a3b0a0b..430a14e5bb 100644
--- a/internal/idp/providers/oidc/session.go
+++ b/internal/idp/providers/oidc/session.go
@@ -3,6 +3,7 @@ package oidc
import (
"context"
"errors"
+ "time"
"github.com/zitadel/oidc/v3/pkg/client/rp"
"github.com/zitadel/oidc/v3/pkg/oidc"
@@ -72,6 +73,13 @@ func (s *Session) FetchUser(ctx context.Context) (user idp.User, err error) {
return u, nil
}
+func (s *Session) ExpiresAt() time.Time {
+ if s.Tokens == nil {
+ return time.Time{}
+ }
+ return s.Tokens.Expiry
+}
+
func (s *Session) Authorize(ctx context.Context) (err error) {
if s.Code == "" {
return ErrCodeMissing
diff --git a/internal/idp/providers/saml/session.go b/internal/idp/providers/saml/session.go
index b0748d33a3..e2a1655a26 100644
--- a/internal/idp/providers/saml/session.go
+++ b/internal/idp/providers/saml/session.go
@@ -6,6 +6,7 @@ import (
"errors"
"net/http"
"net/url"
+ "time"
"github.com/crewjam/saml"
"github.com/crewjam/saml/samlsp"
@@ -107,6 +108,13 @@ func (s *Session) FetchUser(ctx context.Context) (user idp.User, err error) {
return userMapper, nil
}
+func (s *Session) ExpiresAt() time.Time {
+ if s.Assertion == nil || s.Assertion.Conditions == nil {
+ return time.Time{}
+ }
+ return s.Assertion.Conditions.NotOnOrAfter
+}
+
func (s *Session) transientMappingID() (string, error) {
for _, statement := range s.Assertion.AttributeStatements {
for _, attribute := range statement.Attributes {
diff --git a/internal/idp/session.go b/internal/idp/session.go
index ab54bcabaa..fc593eb820 100644
--- a/internal/idp/session.go
+++ b/internal/idp/session.go
@@ -2,6 +2,7 @@ package idp
import (
"context"
+ "time"
)
// Session is the minimal implementation for a session of a 3rd party authentication [Provider]
@@ -9,6 +10,7 @@ type Session interface {
GetAuth(ctx context.Context) (content string, redirect bool)
PersistentParameters() map[string]any
FetchUser(ctx context.Context) (User, error)
+ ExpiresAt() time.Time
}
// SessionSupportsMigration is an optional extension to the Session interface.
diff --git a/internal/integration/action.go b/internal/integration/action.go
index b8f69c5788..e849b5c21c 100644
--- a/internal/integration/action.go
+++ b/internal/integration/action.go
@@ -8,6 +8,9 @@ import (
"reflect"
"sync"
"time"
+
+ "google.golang.org/protobuf/encoding/protojson"
+ "google.golang.org/protobuf/proto"
)
type server struct {
@@ -100,3 +103,61 @@ func TestServerCall(
server.server = httptest.NewServer(http.HandlerFunc(handler))
return server.URL(), server.Close, server.Called, server.ResetCalled
}
+
+func TestServerCallProto(
+ reqBody interface{},
+ sleep time.Duration,
+ statusCode int,
+ respBody proto.Message,
+) (url string, closeF func(), calledF func() int, resetCalledF func()) {
+ server := &server{
+ called: 0,
+ }
+
+ handler := func(w http.ResponseWriter, r *http.Request) {
+ server.Increase()
+ if reqBody != nil {
+ data, err := json.Marshal(reqBody)
+ if err != nil {
+ http.Error(w, "error, marshall: "+err.Error(), http.StatusInternalServerError)
+ return
+ }
+ sentBody, err := io.ReadAll(r.Body)
+ if err != nil {
+ http.Error(w, "error, read body: "+err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if !reflect.DeepEqual(data, sentBody) {
+ http.Error(w, "error, equal:\n"+string(data)+"\nsent:\n"+string(sentBody), http.StatusInternalServerError)
+ return
+ }
+ }
+ if statusCode != http.StatusOK {
+ http.Error(w, "error, statusCode", statusCode)
+ return
+ }
+
+ time.Sleep(sleep)
+
+ if respBody != nil {
+ w.Header().Set("Content-Type", "application/json")
+ resp, err := protojson.Marshal(respBody)
+ if err != nil {
+ http.Error(w, "error", http.StatusInternalServerError)
+ return
+ }
+ if _, err := io.Writer.Write(w, resp); err != nil {
+ http.Error(w, "error", http.StatusInternalServerError)
+ return
+ }
+ } else {
+ if _, err := io.WriteString(w, "finished successfully"); err != nil {
+ http.Error(w, "error", http.StatusInternalServerError)
+ return
+ }
+ }
+ }
+
+ server.server = httptest.NewServer(http.HandlerFunc(handler))
+ return server.URL(), server.Close, server.Called, server.ResetCalled
+}
diff --git a/internal/integration/client.go b/internal/integration/client.go
index 2cb8fa3641..f1bcfb41bd 100644
--- a/internal/integration/client.go
+++ b/internal/integration/client.go
@@ -672,6 +672,23 @@ func (i *Instance) CreatePasswordSession(t *testing.T, ctx context.Context, user
createResp.GetDetails().GetChangeDate().AsTime(), createResp.GetDetails().GetChangeDate().AsTime()
}
+func (i *Instance) CreateIntentSession(t *testing.T, ctx context.Context, userID, intentID, intentToken string) (id, token string, start, change time.Time) {
+ createResp, err := i.Client.SessionV2.CreateSession(ctx, &session.CreateSessionRequest{
+ Checks: &session.Checks{
+ User: &session.CheckUser{
+ Search: &session.CheckUser_UserId{UserId: userID},
+ },
+ IdpIntent: &session.CheckIDPIntent{
+ IdpIntentId: intentID,
+ IdpIntentToken: intentToken,
+ },
+ },
+ })
+ require.NoError(t, err)
+ return createResp.GetSessionId(), createResp.GetSessionToken(),
+ createResp.GetDetails().GetChangeDate().AsTime(), createResp.GetDetails().GetChangeDate().AsTime()
+}
+
func (i *Instance) CreateProjectGrant(ctx context.Context, projectID, grantedOrgID string) *mgmt.AddProjectGrantResponse {
resp, err := i.Client.Mgmt.AddProjectGrant(ctx, &mgmt.AddProjectGrantRequest{
GrantedOrgId: grantedOrgID,
@@ -763,7 +780,7 @@ func (i *Instance) DeleteExecution(ctx context.Context, t *testing.T, cond *acti
require.NoError(t, err)
}
-func (i *Instance) SetExecution(ctx context.Context, t *testing.T, cond *action.Condition, targets []*action.ExecutionTargetType) *action.SetExecutionResponse {
+func (i *Instance) SetExecution(ctx context.Context, t *testing.T, cond *action.Condition, targets []string) *action.SetExecutionResponse {
target, err := i.Client.ActionV2beta.SetExecution(ctx, &action.SetExecutionRequest{
Condition: cond,
Targets: targets,
diff --git a/internal/integration/sink/server.go b/internal/integration/sink/server.go
index 633ebf424f..8abb31a63e 100644
--- a/internal/integration/sink/server.go
+++ b/internal/integration/sink/server.go
@@ -17,6 +17,7 @@ import (
crewjam_saml "github.com/crewjam/saml"
"github.com/go-chi/chi/v5"
+ goldap "github.com/go-ldap/ldap/v3"
"github.com/gorilla/websocket"
"github.com/sirupsen/logrus"
"github.com/zitadel/logging"
@@ -48,7 +49,7 @@ func CallURL(ch Channel) string {
return u.String()
}
-func SuccessfulOAuthIntent(instanceID, idpID, idpUserID, userID string) (string, string, time.Time, uint64, error) {
+func SuccessfulOAuthIntent(instanceID, idpID, idpUserID, userID string, expiry time.Time) (string, string, time.Time, uint64, error) {
u := url.URL{
Scheme: "http",
Host: host,
@@ -59,6 +60,7 @@ func SuccessfulOAuthIntent(instanceID, idpID, idpUserID, userID string) (string,
IDPID: idpID,
IDPUserID: idpUserID,
UserID: userID,
+ Expiry: expiry,
})
if err != nil {
return "", "", time.Time{}, uint64(0), err
@@ -66,7 +68,7 @@ func SuccessfulOAuthIntent(instanceID, idpID, idpUserID, userID string) (string,
return resp.IntentID, resp.Token, resp.ChangeDate, resp.Sequence, nil
}
-func SuccessfulOIDCIntent(instanceID, idpID, idpUserID, userID string) (string, string, time.Time, uint64, error) {
+func SuccessfulOIDCIntent(instanceID, idpID, idpUserID, userID string, expiry time.Time) (string, string, time.Time, uint64, error) {
u := url.URL{
Scheme: "http",
Host: host,
@@ -77,6 +79,7 @@ func SuccessfulOIDCIntent(instanceID, idpID, idpUserID, userID string) (string,
IDPID: idpID,
IDPUserID: idpUserID,
UserID: userID,
+ Expiry: expiry,
})
if err != nil {
return "", "", time.Time{}, uint64(0), err
@@ -84,7 +87,7 @@ func SuccessfulOIDCIntent(instanceID, idpID, idpUserID, userID string) (string,
return resp.IntentID, resp.Token, resp.ChangeDate, resp.Sequence, nil
}
-func SuccessfulSAMLIntent(instanceID, idpID, idpUserID, userID string) (string, string, time.Time, uint64, error) {
+func SuccessfulSAMLIntent(instanceID, idpID, idpUserID, userID string, expiry time.Time) (string, string, time.Time, uint64, error) {
u := url.URL{
Scheme: "http",
Host: host,
@@ -95,6 +98,7 @@ func SuccessfulSAMLIntent(instanceID, idpID, idpUserID, userID string) (string,
IDPID: idpID,
IDPUserID: idpUserID,
UserID: userID,
+ Expiry: expiry,
})
if err != nil {
return "", "", time.Time{}, uint64(0), err
@@ -282,10 +286,11 @@ func readLoop(ws *websocket.Conn) (done chan error) {
}
type SuccessfulIntentRequest struct {
- InstanceID string `json:"instance_id"`
- IDPID string `json:"idp_id"`
- IDPUserID string `json:"idp_user_id"`
- UserID string `json:"user_id"`
+ InstanceID string `json:"instance_id"`
+ IDPID string `json:"idp_id"`
+ IDPUserID string `json:"idp_user_id"`
+ UserID string `json:"user_id"`
+ Expiry time.Time `json:"expiry"`
}
type SuccessfulIntentResponse struct {
IntentID string `json:"intent_id"`
@@ -376,6 +381,7 @@ func createSuccessfulOAuthIntent(ctx context.Context, cmd *command.Commands, req
Tokens: &oidc.Tokens[*oidc.IDTokenClaims]{
Token: &oauth2.Token{
AccessToken: "accessToken",
+ Expiry: req.Expiry,
},
IDToken: "idToken",
},
@@ -407,6 +413,7 @@ func createSuccessfulOIDCIntent(ctx context.Context, cmd *command.Commands, req
Tokens: &oidc.Tokens[*oidc.IDTokenClaims]{
Token: &oauth2.Token{
AccessToken: "accessToken",
+ Expiry: req.Expiry,
},
IDToken: "idToken",
},
@@ -431,9 +438,16 @@ func createSuccessfulSAMLIntent(ctx context.Context, cmd *command.Commands, req
ID: req.IDPUserID,
Attributes: map[string][]string{"attribute1": {"value1"}},
}
- assertion := &crewjam_saml.Assertion{ID: "id"}
+ session := &saml.Session{
+ Assertion: &crewjam_saml.Assertion{
+ ID: "id",
+ Conditions: &crewjam_saml.Conditions{
+ NotOnOrAfter: req.Expiry,
+ },
+ },
+ }
- token, err := cmd.SucceedSAMLIDPIntent(ctx, writeModel, idpUser, req.UserID, assertion)
+ token, err := cmd.SucceedSAMLIDPIntent(ctx, writeModel, idpUser, req.UserID, session)
if err != nil {
return nil, err
}
@@ -465,8 +479,14 @@ func createSuccessfulLDAPIntent(ctx context.Context, cmd *command.Commands, req
"",
"",
)
- attributes := map[string][]string{"id": {req.IDPUserID}, "username": {username}, "language": {lang.String()}}
- token, err := cmd.SucceedLDAPIDPIntent(ctx, writeModel, idpUser, req.UserID, attributes)
+ session := &ldap.Session{Entry: &goldap.Entry{
+ Attributes: []*goldap.EntryAttribute{
+ {Name: "id", Values: []string{req.IDPUserID}},
+ {Name: "username", Values: []string{username}},
+ {Name: "language", Values: []string{lang.String()}},
+ },
+ }}
+ token, err := cmd.SucceedLDAPIDPIntent(ctx, writeModel, idpUser, req.UserID, session)
if err != nil {
return nil, err
}
diff --git a/internal/notification/projections.go b/internal/notification/projections.go
index a2d4d4140e..9b6b975fa1 100644
--- a/internal/notification/projections.go
+++ b/internal/notification/projections.go
@@ -2,8 +2,12 @@ package notification
import (
"context"
+ "fmt"
"time"
+ "github.com/zitadel/logging"
+
+ "github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/eventstore"
@@ -68,11 +72,13 @@ func Start(ctx context.Context) {
}
func ProjectInstance(ctx context.Context) error {
- for _, projection := range projections {
+ for i, projection := range projections {
+ logging.WithFields("name", projection.ProjectionName(), "instance", authz.GetInstance(ctx).InstanceID(), "index", fmt.Sprintf("%d/%d", i, len(projections))).Info("starting notification projection")
_, err := projection.Trigger(ctx)
if err != nil {
return err
}
+ logging.WithFields("name", projection.ProjectionName(), "instance", authz.GetInstance(ctx).InstanceID(), "index", fmt.Sprintf("%d/%d", i, len(projections))).Info("notification projection done")
}
return nil
}
diff --git a/internal/query/execution.go b/internal/query/execution.go
index 0a2a989918..4739a5839e 100644
--- a/internal/query/execution.go
+++ b/internal/query/execution.go
@@ -1,11 +1,13 @@
package query
import (
+ "cmp"
"context"
"database/sql"
_ "embed"
"encoding/json"
"errors"
+ "slices"
"time"
sq "github.com/Masterminds/squirrel"
@@ -301,13 +303,15 @@ func executionTargetsUnmarshal(data []byte) ([]*exec.Target, error) {
}
targets := make([]*exec.Target, len(executionTargets))
- // position starts with 1
- for _, item := range executionTargets {
+ slices.SortFunc(executionTargets, func(a, b *executionTarget) int {
+ return cmp.Compare(a.Position, b.Position)
+ })
+ for i, item := range executionTargets {
if item.Target != "" {
- targets[item.Position-1] = &exec.Target{Type: domain.ExecutionTargetTypeTarget, Target: item.Target}
+ targets[i] = &exec.Target{Type: domain.ExecutionTargetTypeTarget, Target: item.Target}
}
if item.Include != "" {
- targets[item.Position-1] = &exec.Target{Type: domain.ExecutionTargetTypeInclude, Target: item.Include}
+ targets[i] = &exec.Target{Type: domain.ExecutionTargetTypeInclude, Target: item.Include}
}
}
return targets, nil
diff --git a/internal/query/execution_targets.sql b/internal/query/execution_targets.sql
index 32257f4a1f..a6e6dd6caa 100644
--- a/internal/query/execution_targets.sql
+++ b/internal/query/execution_targets.sql
@@ -1,11 +1,15 @@
-SELECT instance_id,
- execution_id,
+SELECT et.instance_id,
+ et.execution_id,
JSONB_AGG(
JSON_OBJECT(
- 'position' : position,
- 'include' : include,
- 'target' : target_id
- )
- ) as targets
-FROM projections.executions1_targets
-GROUP BY instance_id, execution_id
\ No newline at end of file
+ 'position' : et.position,
+ 'include' : et.include,
+ 'target' : et.target_id
+ )
+ ) as targets
+FROM projections.executions1_targets AS et
+ INNER JOIN projections.targets2 AS t
+ ON et.instance_id = t.instance_id
+ AND et.target_id IS NOT NULL
+ AND et.target_id = t.id
+GROUP BY et.instance_id, et.execution_id
\ No newline at end of file
diff --git a/internal/query/execution_test.go b/internal/query/execution_test.go
index eaaac1e9ba..64f9a4849f 100644
--- a/internal/query/execution_test.go
+++ b/internal/query/execution_test.go
@@ -22,9 +22,10 @@ var (
` COUNT(*) OVER ()` +
` FROM projections.executions1` +
` JOIN (` +
- `SELECT instance_id, execution_id, JSONB_AGG( JSON_OBJECT( 'position' : position, 'include' : include, 'target' : target_id ) ) as targets` +
- ` FROM projections.executions1_targets` +
- ` GROUP BY instance_id, execution_id` +
+ `SELECT et.instance_id, et.execution_id, JSONB_AGG( JSON_OBJECT( 'position' : et.position, 'include' : et.include, 'target' : et.target_id ) ) as targets` +
+ ` FROM projections.executions1_targets AS et` +
+ ` INNER JOIN projections.targets2 AS t ON et.instance_id = t.instance_id AND et.target_id IS NOT NULL AND et.target_id = t.id` +
+ ` GROUP BY et.instance_id, et.execution_id` +
`)` +
` AS execution_targets` +
` ON execution_targets.instance_id = projections.executions1.instance_id` +
@@ -45,9 +46,10 @@ var (
` execution_targets.targets` +
` FROM projections.executions1` +
` JOIN (` +
- `SELECT instance_id, execution_id, JSONB_AGG( JSON_OBJECT( 'position' : position, 'include' : include, 'target' : target_id ) ) as targets` +
- ` FROM projections.executions1_targets` +
- ` GROUP BY instance_id, execution_id` +
+ `SELECT et.instance_id, et.execution_id, JSONB_AGG( JSON_OBJECT( 'position' : et.position, 'include' : et.include, 'target' : et.target_id ) ) as targets` +
+ ` FROM projections.executions1_targets AS et` +
+ ` INNER JOIN projections.targets2 AS t ON et.instance_id = t.instance_id AND et.target_id IS NOT NULL AND et.target_id = t.id` +
+ ` GROUP BY et.instance_id, et.execution_id` +
`)` +
` AS execution_targets` +
` ON execution_targets.instance_id = projections.executions1.instance_id` +
@@ -179,6 +181,63 @@ func Test_ExecutionPrepares(t *testing.T) {
},
},
},
+ {
+ name: "prepareExecutionsQuery multiple result, removed target, position missing",
+ prepare: prepareExecutionsQuery,
+ want: want{
+ sqlExpectations: mockQueries(
+ regexp.QuoteMeta(prepareExecutionsStmt),
+ prepareExecutionsCols,
+ [][]driver.Value{
+ {
+ "ro",
+ "id-1",
+ testNow,
+ testNow,
+ []byte(`[{"position" : 1, "target" : "target"}, {"position" : 3, "include" : "include"}]`),
+ },
+ {
+ "ro",
+ "id-2",
+ testNow,
+ testNow,
+ []byte(`[{"position" : 2, "target" : "target"}, {"position" : 1, "include" : "include"}]`),
+ },
+ },
+ ),
+ },
+ object: &Executions{
+ SearchResponse: SearchResponse{
+ Count: 2,
+ },
+ Executions: []*Execution{
+ {
+ ObjectDetails: domain.ObjectDetails{
+ ID: "id-1",
+ EventDate: testNow,
+ CreationDate: testNow,
+ ResourceOwner: "ro",
+ },
+ Targets: []*exec.Target{
+ {Type: domain.ExecutionTargetTypeTarget, Target: "target"},
+ {Type: domain.ExecutionTargetTypeInclude, Target: "include"},
+ },
+ },
+ {
+ ObjectDetails: domain.ObjectDetails{
+ ID: "id-2",
+ EventDate: testNow,
+ CreationDate: testNow,
+ ResourceOwner: "ro",
+ },
+ Targets: []*exec.Target{
+ {Type: domain.ExecutionTargetTypeInclude, Target: "include"},
+ {Type: domain.ExecutionTargetTypeTarget, Target: "target"},
+ },
+ },
+ },
+ },
+ },
{
name: "prepareExecutionsQuery sql err",
prepare: prepareExecutionsQuery,
diff --git a/internal/query/instance_features.go b/internal/query/instance_features.go
index 911edaa606..4ec40dc9d5 100644
--- a/internal/query/instance_features.go
+++ b/internal/query/instance_features.go
@@ -14,7 +14,6 @@ type InstanceFeatures struct {
LegacyIntrospection FeatureSource[bool]
UserSchema FeatureSource[bool]
TokenExchange FeatureSource[bool]
- Actions FeatureSource[bool]
ImprovedPerformance FeatureSource[[]feature.ImprovedPerformanceType]
WebKey FeatureSource[bool]
DebugOIDCParentError FeatureSource[bool]
diff --git a/internal/query/instance_features_model.go b/internal/query/instance_features_model.go
index 11deb30f34..6a0abbb58c 100644
--- a/internal/query/instance_features_model.go
+++ b/internal/query/instance_features_model.go
@@ -67,7 +67,6 @@ func (m *InstanceFeaturesReadModel) Query() *eventstore.SearchQueryBuilder {
feature_v2.InstanceLegacyIntrospectionEventType,
feature_v2.InstanceUserSchemaEventType,
feature_v2.InstanceTokenExchangeEventType,
- feature_v2.InstanceActionsEventType,
feature_v2.InstanceImprovedPerformanceEventType,
feature_v2.InstanceWebKeyEventType,
feature_v2.InstanceDebugOIDCParentErrorEventType,
@@ -98,7 +97,6 @@ func (m *InstanceFeaturesReadModel) populateFromSystem() bool {
m.instance.LegacyIntrospection = m.system.LegacyIntrospection
m.instance.UserSchema = m.system.UserSchema
m.instance.TokenExchange = m.system.TokenExchange
- m.instance.Actions = m.system.Actions
m.instance.ImprovedPerformance = m.system.ImprovedPerformance
m.instance.OIDCSingleV1SessionTermination = m.system.OIDCSingleV1SessionTermination
m.instance.DisableUserTokenEvent = m.system.DisableUserTokenEvent
@@ -113,7 +111,8 @@ func reduceInstanceFeatureSet[T any](features *InstanceFeatures, event *feature_
return err
}
switch key {
- case feature.KeyUnspecified:
+ case feature.KeyUnspecified,
+ feature.KeyActionsDeprecated:
return nil
case feature.KeyLoginDefaultOrg:
features.LoginDefaultOrg.set(level, event.Value)
@@ -125,8 +124,6 @@ func reduceInstanceFeatureSet[T any](features *InstanceFeatures, event *feature_
features.UserSchema.set(level, event.Value)
case feature.KeyTokenExchange:
features.TokenExchange.set(level, event.Value)
- case feature.KeyActions:
- features.Actions.set(level, event.Value)
case feature.KeyImprovedPerformance:
features.ImprovedPerformance.set(level, event.Value)
case feature.KeyWebKey:
diff --git a/internal/query/instance_features_test.go b/internal/query/instance_features_test.go
index 903c2872a9..d80a3b05fc 100644
--- a/internal/query/instance_features_test.go
+++ b/internal/query/instance_features_test.go
@@ -105,10 +105,6 @@ func TestQueries_GetInstanceFeatures(t *testing.T) {
ctx, aggregate,
feature_v2.InstanceUserSchemaEventType, false,
)),
- eventFromEventPusher(feature_v2.NewSetEvent(
- ctx, aggregate,
- feature_v2.InstanceActionsEventType, false,
- )),
),
),
args: args{true},
@@ -132,10 +128,6 @@ func TestQueries_GetInstanceFeatures(t *testing.T) {
Level: feature.LevelInstance,
Value: false,
},
- Actions: FeatureSource[bool]{
- Level: feature.LevelInstance,
- Value: false,
- },
},
},
{
@@ -162,10 +154,6 @@ func TestQueries_GetInstanceFeatures(t *testing.T) {
ctx, aggregate,
feature_v2.InstanceUserSchemaEventType, false,
)),
- eventFromEventPusher(feature_v2.NewSetEvent(
- ctx, aggregate,
- feature_v2.InstanceActionsEventType, false,
- )),
eventFromEventPusher(feature_v2.NewResetEvent(
ctx, aggregate,
feature_v2.InstanceResetEventType,
@@ -197,10 +185,6 @@ func TestQueries_GetInstanceFeatures(t *testing.T) {
Level: feature.LevelUnspecified,
Value: false,
},
- Actions: FeatureSource[bool]{
- Level: feature.LevelUnspecified,
- Value: false,
- },
},
},
{
@@ -223,10 +207,6 @@ func TestQueries_GetInstanceFeatures(t *testing.T) {
ctx, aggregate,
feature_v2.InstanceUserSchemaEventType, false,
)),
- eventFromEventPusher(feature_v2.NewSetEvent(
- ctx, aggregate,
- feature_v2.InstanceActionsEventType, false,
- )),
eventFromEventPusher(feature_v2.NewResetEvent(
ctx, aggregate,
feature_v2.InstanceResetEventType,
@@ -258,10 +238,6 @@ func TestQueries_GetInstanceFeatures(t *testing.T) {
Level: feature.LevelUnspecified,
Value: false,
},
- Actions: FeatureSource[bool]{
- Level: feature.LevelUnspecified,
- Value: false,
- },
},
},
}
diff --git a/internal/query/projection/execution.go b/internal/query/projection/execution.go
index 9001fcd3ba..1bd7f2e7f5 100644
--- a/internal/query/projection/execution.go
+++ b/internal/query/projection/execution.go
@@ -9,6 +9,7 @@ import (
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
exec "github.com/zitadel/zitadel/internal/repository/execution"
"github.com/zitadel/zitadel/internal/repository/instance"
+ "github.com/zitadel/zitadel/internal/repository/target"
)
const (
@@ -78,6 +79,15 @@ func (p *executionProjection) Reducers() []handler.AggregateReducer {
},
},
},
+ {
+ Aggregate: target.AggregateType,
+ EventReducers: []handler.EventReducer{
+ {
+ Event: target.RemovedEventType,
+ Reduce: p.reduceTargetRemoved,
+ },
+ },
+ },
{
Aggregate: instance.AggregateType,
EventReducers: []handler.EventReducer{
@@ -152,6 +162,21 @@ func (p *executionProjection) reduceExecutionSet(event eventstore.Event) (*handl
return handler.NewMultiStatement(e, stmts...), nil
}
+func (p *executionProjection) reduceTargetRemoved(event eventstore.Event) (*handler.Statement, error) {
+ e, err := assertEvent[*target.RemovedEvent](event)
+ if err != nil {
+ return nil, err
+ }
+ return handler.NewDeleteStatement(
+ e,
+ []handler.Condition{
+ handler.NewCond(ExecutionTargetInstanceIDCol, e.Aggregate().InstanceID),
+ handler.NewCond(ExecutionTargetTargetIDCol, e.Aggregate().ID),
+ },
+ handler.WithTableSuffix(ExecutionTargetSuffix),
+ ), nil
+}
+
func (p *executionProjection) reduceExecutionRemoved(event eventstore.Event) (*handler.Statement, error) {
e, err := assertEvent[*exec.RemovedEvent](event)
if err != nil {
diff --git a/internal/query/projection/execution_test.go b/internal/query/projection/execution_test.go
index 27d6e89258..aecae6905a 100644
--- a/internal/query/projection/execution_test.go
+++ b/internal/query/projection/execution_test.go
@@ -7,6 +7,7 @@ import (
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
exec "github.com/zitadel/zitadel/internal/repository/execution"
"github.com/zitadel/zitadel/internal/repository/instance"
+ "github.com/zitadel/zitadel/internal/repository/target"
"github.com/zitadel/zitadel/internal/zerrors"
)
@@ -79,6 +80,35 @@ func TestExecutionProjection_reduces(t *testing.T) {
},
},
},
+ {
+ name: "reduceTargetRemoved",
+ args: args{
+ event: getEvent(
+ testEvent(
+ target.RemovedEventType,
+ target.AggregateType,
+ []byte(`{}`),
+ ),
+ eventstore.GenericEventMapper[target.RemovedEvent],
+ ),
+ },
+ reduce: (&executionProjection{}).reduceTargetRemoved,
+ want: wantReduce{
+ aggregateType: eventstore.AggregateType("target"),
+ sequence: 15,
+ executer: &testExecuter{
+ executions: []execution{
+ {
+ expectedStmt: "DELETE FROM projections.executions1_targets WHERE (instance_id = $1) AND (target_id = $2)",
+ expectedArgs: []interface{}{
+ "instance-id",
+ "agg-id",
+ },
+ },
+ },
+ },
+ },
+ },
{
name: "reduceExecutionRemoved",
args: args{
diff --git a/internal/query/projection/instance_features.go b/internal/query/projection/instance_features.go
index 798af6693c..34100a0d66 100644
--- a/internal/query/projection/instance_features.go
+++ b/internal/query/projection/instance_features.go
@@ -80,10 +80,6 @@ func (*instanceFeatureProjection) Reducers() []handler.AggregateReducer {
Event: feature_v2.InstanceTokenExchangeEventType,
Reduce: reduceInstanceSetFeature[bool],
},
- {
- Event: feature_v2.InstanceActionsEventType,
- Reduce: reduceInstanceSetFeature[bool],
- },
{
Event: feature_v2.InstanceImprovedPerformanceEventType,
Reduce: reduceInstanceSetFeature[[]feature.ImprovedPerformanceType],
diff --git a/internal/query/projection/projection.go b/internal/query/projection/projection.go
index f4e3bbe0d4..07953a27e8 100644
--- a/internal/query/projection/projection.go
+++ b/internal/query/projection/projection.go
@@ -2,6 +2,9 @@ package projection
import (
"context"
+ "fmt"
+
+ "github.com/zitadel/logging"
internal_authz "github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto"
@@ -90,6 +93,7 @@ var (
)
type projection interface {
+ ProjectionName() string
Start(ctx context.Context)
Init(ctx context.Context) error
Trigger(ctx context.Context, opts ...handler.TriggerOpt) (_ context.Context, err error)
@@ -206,21 +210,25 @@ func Start(ctx context.Context) {
}
func ProjectInstance(ctx context.Context) error {
- for _, projection := range projections {
+ for i, projection := range projections {
+ logging.WithFields("name", projection.ProjectionName(), "instance", internal_authz.GetInstance(ctx).InstanceID(), "index", fmt.Sprintf("%d/%d", i, len(projections))).Info("starting projection")
_, err := projection.Trigger(ctx)
if err != nil {
return err
}
+ logging.WithFields("name", projection.ProjectionName(), "instance", internal_authz.GetInstance(ctx).InstanceID()).Info("projection done")
}
return nil
}
func ProjectInstanceFields(ctx context.Context) error {
- for _, fieldProjection := range fields {
+ for i, fieldProjection := range fields {
+ logging.WithFields("name", fieldProjection.ProjectionName(), "instance", internal_authz.GetInstance(ctx).InstanceID(), "index", fmt.Sprintf("%d/%d", i, len(fields))).Info("starting fields projection")
err := fieldProjection.Trigger(ctx)
if err != nil {
return err
}
+ logging.WithFields("name", fieldProjection.ProjectionName(), "instance", internal_authz.GetInstance(ctx).InstanceID()).Info("fields projection done")
}
return nil
}
diff --git a/internal/query/projection/session.go b/internal/query/projection/session.go
index 196a352190..53eba54efb 100644
--- a/internal/query/projection/session.go
+++ b/internal/query/projection/session.go
@@ -87,6 +87,7 @@ func (*sessionProjection) Init() *old_handler.Check {
SessionColumnUserAgentFingerprintID+"_idx",
[]string{SessionColumnUserAgentFingerprintID},
)),
+ handler.WithIndex(handler.NewIndex(SessionColumnUserID+"_idx", []string{SessionColumnUserID})),
),
)
}
diff --git a/internal/query/projection/system_features.go b/internal/query/projection/system_features.go
index f6f0a36d56..de54054e78 100644
--- a/internal/query/projection/system_features.go
+++ b/internal/query/projection/system_features.go
@@ -72,10 +72,6 @@ func (*systemFeatureProjection) Reducers() []handler.AggregateReducer {
Event: feature_v2.SystemTokenExchangeEventType,
Reduce: reduceSystemSetFeature[bool],
},
- {
- Event: feature_v2.SystemActionsEventType,
- Reduce: reduceSystemSetFeature[bool],
- },
{
Event: feature_v2.SystemImprovedPerformanceEventType,
Reduce: reduceSystemSetFeature[[]feature.ImprovedPerformanceType],
diff --git a/internal/query/system_features.go b/internal/query/system_features.go
index 31ad402d12..dcbbb7d6fe 100644
--- a/internal/query/system_features.go
+++ b/internal/query/system_features.go
@@ -25,7 +25,6 @@ type SystemFeatures struct {
LegacyIntrospection FeatureSource[bool]
UserSchema FeatureSource[bool]
TokenExchange FeatureSource[bool]
- Actions FeatureSource[bool]
ImprovedPerformance FeatureSource[[]feature.ImprovedPerformanceType]
OIDCSingleV1SessionTermination FeatureSource[bool]
DisableUserTokenEvent FeatureSource[bool]
diff --git a/internal/query/system_features_model.go b/internal/query/system_features_model.go
index 217154e3ed..69e1f35968 100644
--- a/internal/query/system_features_model.go
+++ b/internal/query/system_features_model.go
@@ -60,7 +60,6 @@ func (m *SystemFeaturesReadModel) Query() *eventstore.SearchQueryBuilder {
feature_v2.SystemLegacyIntrospectionEventType,
feature_v2.SystemUserSchemaEventType,
feature_v2.SystemTokenExchangeEventType,
- feature_v2.SystemActionsEventType,
feature_v2.SystemImprovedPerformanceEventType,
feature_v2.SystemOIDCSingleV1SessionTerminationEventType,
feature_v2.SystemDisableUserTokenEvent,
@@ -82,7 +81,8 @@ func reduceSystemFeatureSet[T any](features *SystemFeatures, event *feature_v2.S
return err
}
switch key {
- case feature.KeyUnspecified:
+ case feature.KeyUnspecified,
+ feature.KeyActionsDeprecated:
return nil
case feature.KeyLoginDefaultOrg:
features.LoginDefaultOrg.set(level, event.Value)
@@ -94,8 +94,6 @@ func reduceSystemFeatureSet[T any](features *SystemFeatures, event *feature_v2.S
features.UserSchema.set(level, event.Value)
case feature.KeyTokenExchange:
features.TokenExchange.set(level, event.Value)
- case feature.KeyActions:
- features.Actions.set(level, event.Value)
case feature.KeyImprovedPerformance:
features.ImprovedPerformance.set(level, event.Value)
case feature.KeyOIDCSingleV1SessionTermination:
diff --git a/internal/query/system_features_test.go b/internal/query/system_features_test.go
index fcd0f812f5..5a58ac23d7 100644
--- a/internal/query/system_features_test.go
+++ b/internal/query/system_features_test.go
@@ -61,10 +61,6 @@ func TestQueries_GetSystemFeatures(t *testing.T) {
context.Background(), aggregate,
feature_v2.SystemUserSchemaEventType, false,
)),
- eventFromEventPusher(feature_v2.NewSetEvent(
- context.Background(), aggregate,
- feature_v2.SystemActionsEventType, true,
- )),
),
),
want: &SystemFeatures{
@@ -87,10 +83,6 @@ func TestQueries_GetSystemFeatures(t *testing.T) {
Level: feature.LevelSystem,
Value: false,
},
- Actions: FeatureSource[bool]{
- Level: feature.LevelSystem,
- Value: true,
- },
},
},
{
@@ -113,10 +105,6 @@ func TestQueries_GetSystemFeatures(t *testing.T) {
context.Background(), aggregate,
feature_v2.SystemUserSchemaEventType, false,
)),
- eventFromEventPusher(feature_v2.NewSetEvent(
- context.Background(), aggregate,
- feature_v2.SystemActionsEventType, false,
- )),
eventFromEventPusher(feature_v2.NewResetEvent(
context.Background(), aggregate,
feature_v2.SystemResetEventType,
@@ -147,10 +135,6 @@ func TestQueries_GetSystemFeatures(t *testing.T) {
Level: feature.LevelUnspecified,
Value: false,
},
- Actions: FeatureSource[bool]{
- Level: feature.LevelUnspecified,
- Value: false,
- },
},
},
{
@@ -173,10 +157,6 @@ func TestQueries_GetSystemFeatures(t *testing.T) {
context.Background(), aggregate,
feature_v2.SystemUserSchemaEventType, false,
)),
- eventFromEventPusher(feature_v2.NewSetEvent(
- context.Background(), aggregate,
- feature_v2.SystemActionsEventType, false,
- )),
eventFromEventPusher(feature_v2.NewResetEvent(
context.Background(), aggregate,
feature_v2.SystemResetEventType,
@@ -207,10 +187,6 @@ func TestQueries_GetSystemFeatures(t *testing.T) {
Level: feature.LevelUnspecified,
Value: false,
},
- Actions: FeatureSource[bool]{
- Level: feature.LevelUnspecified,
- Value: false,
- },
},
},
}
diff --git a/internal/repository/execution/queue.go b/internal/repository/execution/queue.go
index 28f8edbf31..ed3a6ce4a0 100644
--- a/internal/repository/execution/queue.go
+++ b/internal/repository/execution/queue.go
@@ -41,16 +41,16 @@ func ContextInfoFromRequest(e *Request) *ContextInfoEvent {
}
type ContextInfoEvent struct {
- AggregateID string `json:"aggregateID,omitempty"`
- AggregateType string `json:"aggregateType,omitempty"`
- ResourceOwner string `json:"resourceOwner,omitempty"`
- InstanceID string `json:"instanceID,omitempty"`
- Version string `json:"version,omitempty"`
- Sequence uint64 `json:"sequence,omitempty"`
- EventType string `json:"event_type,omitempty"`
- CreatedAt string `json:"created_at,omitempty"`
- UserID string `json:"userID,omitempty"`
- EventPayload []byte `json:"event_payload,omitempty"`
+ AggregateID string `json:"aggregateID,omitempty"`
+ AggregateType string `json:"aggregateType,omitempty"`
+ ResourceOwner string `json:"resourceOwner,omitempty"`
+ InstanceID string `json:"instanceID,omitempty"`
+ Version string `json:"version,omitempty"`
+ Sequence uint64 `json:"sequence,omitempty"`
+ EventType string `json:"event_type,omitempty"`
+ CreatedAt string `json:"created_at,omitempty"`
+ UserID string `json:"userID,omitempty"`
+ EventPayload json.RawMessage `json:"event_payload,omitempty"`
}
func (c *ContextInfoEvent) GetHTTPRequestBody() []byte {
diff --git a/internal/repository/feature/feature_v2/eventstore.go b/internal/repository/feature/feature_v2/eventstore.go
index e8d0da1ab0..00618f56c2 100644
--- a/internal/repository/feature/feature_v2/eventstore.go
+++ b/internal/repository/feature/feature_v2/eventstore.go
@@ -12,7 +12,6 @@ func init() {
eventstore.RegisterFilterEventMapper(AggregateType, SystemLegacyIntrospectionEventType, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, SystemUserSchemaEventType, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, SystemTokenExchangeEventType, eventstore.GenericEventMapper[SetEvent[bool]])
- eventstore.RegisterFilterEventMapper(AggregateType, SystemActionsEventType, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, SystemImprovedPerformanceEventType, eventstore.GenericEventMapper[SetEvent[[]feature.ImprovedPerformanceType]])
eventstore.RegisterFilterEventMapper(AggregateType, SystemOIDCSingleV1SessionTerminationEventType, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, SystemDisableUserTokenEvent, eventstore.GenericEventMapper[SetEvent[bool]])
@@ -26,7 +25,6 @@ func init() {
eventstore.RegisterFilterEventMapper(AggregateType, InstanceLegacyIntrospectionEventType, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, InstanceUserSchemaEventType, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, InstanceTokenExchangeEventType, eventstore.GenericEventMapper[SetEvent[bool]])
- eventstore.RegisterFilterEventMapper(AggregateType, InstanceActionsEventType, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, InstanceImprovedPerformanceEventType, eventstore.GenericEventMapper[SetEvent[[]feature.ImprovedPerformanceType]])
eventstore.RegisterFilterEventMapper(AggregateType, InstanceWebKeyEventType, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, InstanceDebugOIDCParentErrorEventType, eventstore.GenericEventMapper[SetEvent[bool]])
diff --git a/internal/repository/feature/feature_v2/feature.go b/internal/repository/feature/feature_v2/feature.go
index 008986824b..d5e8941df2 100644
--- a/internal/repository/feature/feature_v2/feature.go
+++ b/internal/repository/feature/feature_v2/feature.go
@@ -17,7 +17,6 @@ var (
SystemLegacyIntrospectionEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyLegacyIntrospection)
SystemUserSchemaEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyUserSchema)
SystemTokenExchangeEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyTokenExchange)
- SystemActionsEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyActions)
SystemImprovedPerformanceEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyImprovedPerformance)
SystemOIDCSingleV1SessionTerminationEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyOIDCSingleV1SessionTermination)
SystemDisableUserTokenEvent = setEventTypeFromFeature(feature.LevelSystem, feature.KeyDisableUserTokenEvent)
@@ -31,7 +30,6 @@ var (
InstanceLegacyIntrospectionEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyLegacyIntrospection)
InstanceUserSchemaEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyUserSchema)
InstanceTokenExchangeEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyTokenExchange)
- InstanceActionsEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyActions)
InstanceImprovedPerformanceEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyImprovedPerformance)
InstanceWebKeyEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyWebKey)
InstanceDebugOIDCParentErrorEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyDebugOIDCParentError)
diff --git a/internal/repository/idpintent/eventstore.go b/internal/repository/idpintent/eventstore.go
index ea94803973..6bec32c735 100644
--- a/internal/repository/idpintent/eventstore.go
+++ b/internal/repository/idpintent/eventstore.go
@@ -11,4 +11,5 @@ func init() {
eventstore.RegisterFilterEventMapper(AggregateType, SAMLRequestEventType, SAMLRequestEventMapper)
eventstore.RegisterFilterEventMapper(AggregateType, LDAPSucceededEventType, LDAPSucceededEventMapper)
eventstore.RegisterFilterEventMapper(AggregateType, FailedEventType, FailedEventMapper)
+ eventstore.RegisterFilterEventMapper(AggregateType, ConsumedEventType, eventstore.GenericEventMapper[ConsumedEvent])
}
diff --git a/internal/repository/idpintent/intent.go b/internal/repository/idpintent/intent.go
index 27e6391f95..e4ee28cae9 100644
--- a/internal/repository/idpintent/intent.go
+++ b/internal/repository/idpintent/intent.go
@@ -3,6 +3,7 @@ package idpintent
import (
"context"
"net/url"
+ "time"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/eventstore"
@@ -16,6 +17,7 @@ const (
SAMLRequestEventType = instanceEventTypePrefix + "saml.requested"
LDAPSucceededEventType = instanceEventTypePrefix + "ldap.succeeded"
FailedEventType = instanceEventTypePrefix + "failed"
+ ConsumedEventType = instanceEventTypePrefix + "consumed"
)
type StartedEvent struct {
@@ -79,6 +81,7 @@ type SucceededEvent struct {
IDPAccessToken *crypto.CryptoValue `json:"idpAccessToken,omitempty"`
IDPIDToken string `json:"idpIdToken,omitempty"`
+ ExpiresAt time.Time `json:"expiresAt,omitempty"`
}
func NewSucceededEvent(
@@ -90,6 +93,7 @@ func NewSucceededEvent(
userID string,
idpAccessToken *crypto.CryptoValue,
idpIDToken string,
+ expiresAt time.Time,
) *SucceededEvent {
return &SucceededEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
@@ -103,6 +107,7 @@ func NewSucceededEvent(
UserID: userID,
IDPAccessToken: idpAccessToken,
IDPIDToken: idpIDToken,
+ ExpiresAt: expiresAt,
}
}
@@ -136,6 +141,7 @@ type SAMLSucceededEvent struct {
UserID string `json:"userId,omitempty"`
Assertion *crypto.CryptoValue `json:"assertion,omitempty"`
+ ExpiresAt time.Time `json:"expiresAt,omitempty"`
}
func NewSAMLSucceededEvent(
@@ -146,6 +152,7 @@ func NewSAMLSucceededEvent(
idpUserName,
userID string,
assertion *crypto.CryptoValue,
+ expiresAt time.Time,
) *SAMLSucceededEvent {
return &SAMLSucceededEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
@@ -158,6 +165,7 @@ func NewSAMLSucceededEvent(
IDPUserName: idpUserName,
UserID: userID,
Assertion: assertion,
+ ExpiresAt: expiresAt,
}
}
@@ -233,6 +241,7 @@ type LDAPSucceededEvent struct {
UserID string `json:"userId,omitempty"`
EntryAttributes map[string][]string `json:"user,omitempty"`
+ ExpiresAt time.Time `json:"expiresAt,omitempty"`
}
func NewLDAPSucceededEvent(
@@ -243,6 +252,7 @@ func NewLDAPSucceededEvent(
idpUserName,
userID string,
attributes map[string][]string,
+ expiresAt time.Time,
) *LDAPSucceededEvent {
return &LDAPSucceededEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
@@ -255,6 +265,7 @@ func NewLDAPSucceededEvent(
IDPUserName: idpUserName,
UserID: userID,
EntryAttributes: attributes,
+ ExpiresAt: expiresAt,
}
}
@@ -320,3 +331,32 @@ func FailedEventMapper(event eventstore.Event) (eventstore.Event, error) {
return e, nil
}
+
+type ConsumedEvent struct {
+ eventstore.BaseEvent `json:"-"`
+}
+
+func NewConsumedEvent(
+ ctx context.Context,
+ aggregate *eventstore.Aggregate,
+) *ConsumedEvent {
+ return &ConsumedEvent{
+ BaseEvent: *eventstore.NewBaseEventForPush(
+ ctx,
+ aggregate,
+ ConsumedEventType,
+ ),
+ }
+}
+
+func (e *ConsumedEvent) Payload() interface{} {
+ return e
+}
+
+func (e *ConsumedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
+ return nil
+}
+
+func (e *ConsumedEvent) SetBaseEvent(base *eventstore.BaseEvent) {
+ e.BaseEvent = *base
+}
diff --git a/internal/static/i18n/bg.yaml b/internal/static/i18n/bg.yaml
index d7dc18898b..8254b82b45 100644
--- a/internal/static/i18n/bg.yaml
+++ b/internal/static/i18n/bg.yaml
@@ -554,6 +554,7 @@ Errors:
StateMissing: В заявката липсва параметър състояние
NotStarted: Намерението не е стартирано или вече е прекратено
NotSucceeded: Намерението не е успешно
+ Expired: Намерението е изтекло
TokenCreationFailed: Неуспешно създаване на токен
InvalidToken: Знакът за намерение е невалиден
OtherUser: Намерение, предназначено за друг потребител
diff --git a/internal/static/i18n/cs.yaml b/internal/static/i18n/cs.yaml
index 80db4952f9..bb4172fbff 100644
--- a/internal/static/i18n/cs.yaml
+++ b/internal/static/i18n/cs.yaml
@@ -534,6 +534,7 @@ Errors:
StateMissing: V požadavku chybí parametr stavu
NotStarted: Záměr nebyl zahájen nebo již byl ukončen
NotSucceeded: Záměr nebyl úspěšný
+ Expired: Záměr vypršel
TokenCreationFailed: Vytvoření tokenu selhalo
InvalidToken: Token záměru je neplatný
OtherUser: Záměr určený pro jiného uživatele
diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml
index dcb3ac5c71..a24ce7c933 100644
--- a/internal/static/i18n/de.yaml
+++ b/internal/static/i18n/de.yaml
@@ -536,6 +536,7 @@ Errors:
StateMissing: State parameter fehlt im Request
NotStarted: Intent wurde nicht gestartet oder wurde bereits beendet
NotSucceeded: Intent war nicht erfolgreich
+ Expired: Intent ist abgelaufen
TokenCreationFailed: Tokenerstellung schlug fehl
InvalidToken: Intent Token ist ungültig
OtherUser: Intent ist für anderen Benutzer gedacht
diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml
index bd8d26d727..e8f2781de1 100644
--- a/internal/static/i18n/en.yaml
+++ b/internal/static/i18n/en.yaml
@@ -537,6 +537,7 @@ Errors:
StateMissing: State parameter is missing in the request
NotStarted: Intent is not started or was already terminated
NotSucceeded: Intent has not succeeded
+ Expired: Intent has expired
TokenCreationFailed: Token creation failed
InvalidToken: Intent Token is invalid
OtherUser: Intent meant for another user
diff --git a/internal/static/i18n/es.yaml b/internal/static/i18n/es.yaml
index 9f11b63964..b91d055f70 100644
--- a/internal/static/i18n/es.yaml
+++ b/internal/static/i18n/es.yaml
@@ -536,6 +536,7 @@ Errors:
StateMissing: Falta un parámetro de estado en la solicitud
NotStarted: La intención no se ha iniciado o ya ha finalizado
NotSucceeded: Intento fallido
+ Expired: La intención ha expirado
TokenCreationFailed: Fallo en la creación del token
InvalidToken: El token de la intención no es válido
OtherUser: Destinado a otro usuario
diff --git a/internal/static/i18n/fr.yaml b/internal/static/i18n/fr.yaml
index ff8393befc..98f2bee9a0 100644
--- a/internal/static/i18n/fr.yaml
+++ b/internal/static/i18n/fr.yaml
@@ -536,6 +536,7 @@ Errors:
StateMissing: Paramètre d'état manquant dans la requête
NotStarted: Intent n'a pas démarré ou s'est déjà terminé
NotSucceeded: l'intention n'a pas abouti
+ Expired: L'intention a expiré
TokenCreationFailed: La création du token a échoué
InvalidToken: Le jeton d'intention n'est pas valide
OtherUser: Intention destinée à un autre utilisateur
diff --git a/internal/static/i18n/hu.yaml b/internal/static/i18n/hu.yaml
index b17c6a1225..5becd6e606 100644
--- a/internal/static/i18n/hu.yaml
+++ b/internal/static/i18n/hu.yaml
@@ -536,6 +536,7 @@ Errors:
StateMissing: A kérésből hiányzik a State paraméter
NotStarted: Az intent nem indult el, vagy már befejeződött
NotSucceeded: Az intent nem sikerült
+ Expired: A kérésből lejárt
TokenCreationFailed: A token létrehozása nem sikerült
InvalidToken: Az Intent Token érvénytelen
OtherUser: Az intent egy másik felhasználónak szól
diff --git a/internal/static/i18n/id.yaml b/internal/static/i18n/id.yaml
index 56a454e71d..0108d7618b 100644
--- a/internal/static/i18n/id.yaml
+++ b/internal/static/i18n/id.yaml
@@ -536,6 +536,7 @@ Errors:
StateMissing: Parameter status tidak ada dalam permintaan
NotStarted: Niat belum dimulai atau sudah dihentikan
NotSucceeded: Niatnya belum berhasil
+ Expired: Kode sudah habis masa berlakunya
TokenCreationFailed: Pembuatan token gagal
InvalidToken: Token Niat tidak valid
OtherUser: Maksudnya ditujukan untuk pengguna lain
diff --git a/internal/static/i18n/it.yaml b/internal/static/i18n/it.yaml
index 6713abf2e1..750c48471a 100644
--- a/internal/static/i18n/it.yaml
+++ b/internal/static/i18n/it.yaml
@@ -536,6 +536,7 @@ Errors:
StateMissing: parametro di stato mancante nella richiesta
NotStarted: l'intento non è stato avviato o è già stato terminato
NotSucceeded: l'intento non è andato a buon fine
+ Expired: L'intento è scaduto
TokenCreationFailed: creazione del token fallita
InvalidToken: Il token dell'intento non è valido
OtherUser: Intento destinato a un altro utente
diff --git a/internal/static/i18n/ja.yaml b/internal/static/i18n/ja.yaml
index f57d0f6661..fcd7920999 100644
--- a/internal/static/i18n/ja.yaml
+++ b/internal/static/i18n/ja.yaml
@@ -537,6 +537,7 @@ Errors:
StateMissing: リクエストに State パラメータがありません
NotStarted: インテントが開始されなかったか、既に終了している
NotSucceeded: インテントが成功しなかった
+ Expired: 意図の有効期限が切れました
TokenCreationFailed: トークンの作成に失敗しました
InvalidToken: インテントのトークンが無効である
OtherUser: 他のユーザーを意図している
diff --git a/internal/static/i18n/ko.yaml b/internal/static/i18n/ko.yaml
index d238142e01..d83af62235 100644
--- a/internal/static/i18n/ko.yaml
+++ b/internal/static/i18n/ko.yaml
@@ -537,6 +537,7 @@ Errors:
StateMissing: 요청에 상태 매개변수가 누락되었습니다
NotStarted: 의도가 시작되지 않았거나 이미 종료되었습니다
NotSucceeded: 의도가 성공하지 않았습니다
+ Expired: 의도의 유효 기간이 만료되었습니다
TokenCreationFailed: 토큰 생성 실패
InvalidToken: 의도 토큰이 유효하지 않습니다
OtherUser: 다른 사용자를 위한 의도입니다
diff --git a/internal/static/i18n/mk.yaml b/internal/static/i18n/mk.yaml
index 898ed67360..7126925279 100644
--- a/internal/static/i18n/mk.yaml
+++ b/internal/static/i18n/mk.yaml
@@ -535,6 +535,7 @@ Errors:
StateMissing: Параметарот State недостасува во барањето
NotStarted: Намерата не е започната или веќе завршена
NotSucceeded: Намерата не е успешна
+ Expired: Намерата е истечена
TokenCreationFailed: Неуспешно креирање на токен
InvalidToken: Токенот за намера е невалиден
OtherUser: Намерата е за друг корисник
diff --git a/internal/static/i18n/nl.yaml b/internal/static/i18n/nl.yaml
index 882c58a4f2..a398e4b770 100644
--- a/internal/static/i18n/nl.yaml
+++ b/internal/static/i18n/nl.yaml
@@ -536,6 +536,7 @@ Errors:
StateMissing: Staat parameter ontbreekt in het verzoek
NotStarted: Intentie is niet gestart of was al beëindigd
NotSucceeded: Intentie is niet geslaagd
+ Expired: Intentie is verlopen
TokenCreationFailed: Token aanmaken mislukt
InvalidToken: Intentie Token is ongeldig
OtherUser: Intentie bedoeld voor een andere gebruiker
diff --git a/internal/static/i18n/pl.yaml b/internal/static/i18n/pl.yaml
index 13125bc2a9..049a189930 100644
--- a/internal/static/i18n/pl.yaml
+++ b/internal/static/i18n/pl.yaml
@@ -536,6 +536,7 @@ Errors:
StateMissing: Brak parametru stanu w żądaniu
NotStarted: Intencja nie została rozpoczęta lub już się zakończyła
NotSucceeded: intencja nie powiodła się
+ Expired: Intencja wygasła
TokenCreationFailed: Tworzenie tokena nie powiodło się
InvalidToken: Token intencji jest nieprawidłowy
OtherUser: Intencja przeznaczona dla innego użytkownika
diff --git a/internal/static/i18n/pt.yaml b/internal/static/i18n/pt.yaml
index 4ab3573c2b..09a5fc02c5 100644
--- a/internal/static/i18n/pt.yaml
+++ b/internal/static/i18n/pt.yaml
@@ -535,6 +535,7 @@ Errors:
StateMissing: O parâmetro de estado está faltando na solicitação
NotStarted: A intenção não foi iniciada ou já foi encerrada
NotSucceeded: A intenção não teve sucesso
+ Expired: A intenção expirou
TokenCreationFailed: Falha na criação do token
InvalidToken: O token da intenção é inválido
OtherUser: Intenção destinada a outro usuário
diff --git a/internal/static/i18n/ro.yaml b/internal/static/i18n/ro.yaml
index 48790da9e5..9010e57032 100644
--- a/internal/static/i18n/ro.yaml
+++ b/internal/static/i18n/ro.yaml
@@ -537,6 +537,7 @@ Errors:
StateMissing: Parametrul de stare lipsește în cerere
NotStarted: Intenția nu este pornită sau a fost deja terminată
NotSucceeded: Intenția nu a reușit
+ Expired: Intenția a expirat
TokenCreationFailed: Crearea token-ului a eșuat
InvalidToken: Token-ul intenției este invalid
OtherUser: Intenția este destinată altui utilizator
diff --git a/internal/static/i18n/ru.yaml b/internal/static/i18n/ru.yaml
index 64a8ef8013..38b2847637 100644
--- a/internal/static/i18n/ru.yaml
+++ b/internal/static/i18n/ru.yaml
@@ -525,6 +525,7 @@ Errors:
StateMissing: В запросе отсутствует параметр State
NotStarted: Намерение не начато или уже прекращено
NotSucceeded: Намерение не увенчалось успехом
+ Epired: Намерение истекло
TokenCreationFailed: Не удалось создать токен
InvalidToken: Маркер намерения недействителен
OtherUser: Намерение, предназначенное для другого пользователя
diff --git a/internal/static/i18n/sv.yaml b/internal/static/i18n/sv.yaml
index 2c292976d3..ed4b863886 100644
--- a/internal/static/i18n/sv.yaml
+++ b/internal/static/i18n/sv.yaml
@@ -536,6 +536,7 @@ Errors:
StateMissing: State-parameter saknas i begäran
NotStarted: Avsikten har inte startat eller har redan avslutats
NotSucceeded: Avsikten har inte lyckats
+ Expired: Avsikten har gått ut
TokenCreationFailed: Token-skapande misslyckades
InvalidToken: Avsiktstoken är ogiltig
OtherUser: Avsikten är avsedd för en annan användare
diff --git a/internal/static/i18n/zh.yaml b/internal/static/i18n/zh.yaml
index d4b36df7ff..03aa168a50 100644
--- a/internal/static/i18n/zh.yaml
+++ b/internal/static/i18n/zh.yaml
@@ -536,6 +536,7 @@ Errors:
StateMissing: 请求中缺少状态参数
NotStarted: 意图没有开始或已经结束
NotSucceeded: 意图不成功
+ Expired: 意图已过期
TokenCreationFailed: 令牌创建失败
InvalidToken: 意图令牌是无效的
OtherUser: 意图是为另一个用户准备的
diff --git a/proto/buf.yaml b/proto/buf.yaml
index f8cf192a95..31bc7b4ccc 100644
--- a/proto/buf.yaml
+++ b/proto/buf.yaml
@@ -7,6 +7,10 @@ deps:
breaking:
use:
- FILE
+ - FIELD_NO_DELETE_UNLESS_NAME_RESERVED
+ - FIELD_NO_DELETE_UNLESS_NUMBER_RESERVED
+ except:
+ - FIELD_NO_DELETE
ignore_unstable_packages: true
lint:
use:
diff --git a/proto/zitadel/action/v2beta/action_service.proto b/proto/zitadel/action/v2beta/action_service.proto
index d1eebfa344..f225905225 100644
--- a/proto/zitadel/action/v2beta/action_service.proto
+++ b/proto/zitadel/action/v2beta/action_service.proto
@@ -670,8 +670,8 @@ message ListTargetsResponse {
message SetExecutionRequest {
// Condition defining when the execution should be used.
Condition condition = 1;
- // Ordered list of targets/includes called during the execution.
- repeated ExecutionTargetType targets = 2;
+ // Ordered list of targets called during the execution.
+ repeated string targets = 2;
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
example: "{\"condition\":{\"request\":{\"method\":\"zitadel.session.v2.SessionService/ListSessions\"}},\"targets\":[{\"target\":\"69629026806489455\"}]}";
};
diff --git a/proto/zitadel/action/v2beta/execution.proto b/proto/zitadel/action/v2beta/execution.proto
index 4f4ad1ce88..e93470e5dc 100644
--- a/proto/zitadel/action/v2beta/execution.proto
+++ b/proto/zitadel/action/v2beta/execution.proto
@@ -29,18 +29,8 @@ message Execution {
example: "\"2025-01-23T10:34:18.051Z\"";
}
];
- // Ordered list of targets/includes called during the execution.
- repeated ExecutionTargetType targets = 4;
-}
-
-message ExecutionTargetType {
- oneof type {
- option (validate.required) = true;
- // Unique identifier of existing target to call.
- string target = 1;
- // Unique identifier of existing execution to include targets of.
- Condition include = 2;
- }
+ // Ordered list of targets called during the execution.
+ repeated string targets = 4;
}
message Condition {
diff --git a/proto/zitadel/action/v2beta/query.proto b/proto/zitadel/action/v2beta/query.proto
index 564db8bc9f..fe4f72f294 100644
--- a/proto/zitadel/action/v2beta/query.proto
+++ b/proto/zitadel/action/v2beta/query.proto
@@ -19,7 +19,6 @@ message ExecutionSearchFilter {
InConditionsFilter in_conditions_filter = 1;
ExecutionTypeFilter execution_type_filter = 2;
TargetFilter target_filter = 3;
- IncludeFilter include_filter = 4;
}
}
@@ -43,16 +42,6 @@ message TargetFilter {
];
}
-message IncludeFilter {
- // Defines the include to query for.
- Condition include = 1 [
- (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
- description: "the id of the include"
- example: "\"request.zitadel.session.v2.SessionService\"";
- }
- ];
-}
-
enum TargetFieldName {
TARGET_FIELD_NAME_UNSPECIFIED = 0;
TARGET_FIELD_NAME_ID = 1;
diff --git a/proto/zitadel/feature/v2/instance.proto b/proto/zitadel/feature/v2/instance.proto
index efd7f83e4c..fe8d3f7a39 100644
--- a/proto/zitadel/feature/v2/instance.proto
+++ b/proto/zitadel/feature/v2/instance.proto
@@ -11,6 +11,8 @@ import "zitadel/feature/v2/feature.proto";
option go_package = "github.com/zitadel/zitadel/pkg/grpc/feature/v2;feature";
message SetInstanceFeaturesRequest{
+ reserved 6;
+ reserved "actions";
optional bool login_default_org = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true";
@@ -43,12 +45,6 @@ message SetInstanceFeaturesRequest{
description: "Enable the experimental `urn:ietf:params:oauth:grant-type:token-exchange` grant type for the OIDC token endpoint. Token exchange can be used to request tokens with a lesser scope or impersonate other users. See the security policy to allow impersonation on an instance.";
}
];
- optional bool actions = 6 [
- (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
- example: "true";
- description: "Actions allow to manage data executions and targets. If the flag is enabled, you'll be able to use the new API and its features. Note that it is still in an early stage.";
- }
- ];
repeated ImprovedPerformance improved_performance = 7 [
(validate.rules).repeated.unique = true,
@@ -135,6 +131,8 @@ message GetInstanceFeaturesRequest {
}
message GetInstanceFeaturesResponse {
+ reserved 7;
+ reserved "actions";
zitadel.object.v2.Details details = 1;
FeatureFlag login_default_org = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@@ -171,13 +169,6 @@ message GetInstanceFeaturesResponse {
}
];
- FeatureFlag actions = 7 [
- (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
- example: "true";
- description: "Actions v2 allow to manage data executions and targets. If the flag is enabled, you'll be able to use the new API and its features. Note that it is still in an early stage.";
- }
- ];
-
ImprovedPerformanceFeatureFlag improved_performance = 8 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "[1]";
diff --git a/proto/zitadel/feature/v2/system.proto b/proto/zitadel/feature/v2/system.proto
index c734905fb2..d222e2a90c 100644
--- a/proto/zitadel/feature/v2/system.proto
+++ b/proto/zitadel/feature/v2/system.proto
@@ -11,6 +11,8 @@ import "zitadel/feature/v2/feature.proto";
option go_package = "github.com/zitadel/zitadel/pkg/grpc/feature/v2;feature";
message SetSystemFeaturesRequest{
+ reserved 6;
+ reserved "actions";
optional bool login_default_org = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true";
@@ -46,13 +48,6 @@ message SetSystemFeaturesRequest{
}
];
- optional bool actions = 6 [
- (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
- example: "true";
- description: "Actions allow to manage data executions and targets. If the flag is enabled, you'll be able to use the new API and its features. Note that it is still in an early stage.";
- }
- ];
-
repeated ImprovedPerformance improved_performance = 7 [
(validate.rules).repeated.unique = true,
(validate.rules).repeated.items.enum = {defined_only: true, not_in: [0]},
@@ -110,6 +105,8 @@ message ResetSystemFeaturesResponse {
message GetSystemFeaturesRequest {}
message GetSystemFeaturesResponse {
+ reserved 7;
+ reserved "actions";
zitadel.object.v2.Details details = 1;
FeatureFlag login_default_org = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@@ -146,13 +143,6 @@ message GetSystemFeaturesResponse {
}
];
- FeatureFlag actions = 7 [
- (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
- example: "true";
- description: "Actions v2 allow to manage data executions and targets. If the flag is enabled, you'll be able to use the new API and its features. Note that it is still in an early stage.";
- }
- ];
-
ImprovedPerformanceFeatureFlag improved_performance = 8 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "[1]";
diff --git a/proto/zitadel/feature/v2beta/instance.proto b/proto/zitadel/feature/v2beta/instance.proto
index 865a1d2308..7717dd7556 100644
--- a/proto/zitadel/feature/v2beta/instance.proto
+++ b/proto/zitadel/feature/v2beta/instance.proto
@@ -11,6 +11,8 @@ import "zitadel/feature/v2beta/feature.proto";
option go_package = "github.com/zitadel/zitadel/pkg/grpc/feature/v2beta;feature";
message SetInstanceFeaturesRequest{
+ reserved 6;
+ reserved "actions";
optional bool login_default_org = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true";
@@ -43,12 +45,6 @@ message SetInstanceFeaturesRequest{
description: "Enable the experimental `urn:ietf:params:oauth:grant-type:token-exchange` grant type for the OIDC token endpoint. Token exchange can be used to request tokens with a lesser scope or impersonate other users. See the security policy to allow impersonation on an instance.";
}
];
- optional bool actions = 6 [
- (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
- example: "true";
- description: "Actions allow to manage data executions and targets. If the flag is enabled, you'll be able to use the new API and its features. Note that it is still in an early stage.";
- }
- ];
repeated ImprovedPerformance improved_performance = 7 [
(validate.rules).repeated.unique = true,
@@ -101,6 +97,8 @@ message GetInstanceFeaturesRequest {
}
message GetInstanceFeaturesResponse {
+ reserved 7;
+ reserved "actions";
zitadel.object.v2beta.Details details = 1;
FeatureFlag login_default_org = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@@ -137,13 +135,6 @@ message GetInstanceFeaturesResponse {
}
];
- FeatureFlag actions = 7 [
- (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
- example: "true";
- description: "Actions v2 allow to manage data executions and targets. If the flag is enabled, you'll be able to use the new API and its features. Note that it is still in an early stage.";
- }
- ];
-
ImprovedPerformanceFeatureFlag improved_performance = 8 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "[1]";
diff --git a/proto/zitadel/feature/v2beta/system.proto b/proto/zitadel/feature/v2beta/system.proto
index 98b37ad893..624e68ec79 100644
--- a/proto/zitadel/feature/v2beta/system.proto
+++ b/proto/zitadel/feature/v2beta/system.proto
@@ -11,6 +11,8 @@ import "zitadel/feature/v2beta/feature.proto";
option go_package = "github.com/zitadel/zitadel/pkg/grpc/feature/v2beta;feature";
message SetSystemFeaturesRequest{
+ reserved 6;
+ reserved "actions";
optional bool login_default_org = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true";
@@ -46,13 +48,6 @@ message SetSystemFeaturesRequest{
}
];
- optional bool actions = 6 [
- (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
- example: "true";
- description: "Actions allow to manage data executions and targets. If the flag is enabled, you'll be able to use the new API and its features. Note that it is still in an early stage.";
- }
- ];
-
repeated ImprovedPerformance improved_performance = 7 [
(validate.rules).repeated.unique = true,
(validate.rules).repeated.items.enum = {defined_only: true, not_in: [0]},
@@ -83,6 +78,8 @@ message ResetSystemFeaturesResponse {
message GetSystemFeaturesRequest {}
message GetSystemFeaturesResponse {
+ reserved 7;
+ reserved "actions";
zitadel.object.v2beta.Details details = 1;
FeatureFlag login_default_org = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@@ -119,13 +116,6 @@ message GetSystemFeaturesResponse {
}
];
- FeatureFlag actions = 7 [
- (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
- example: "true";
- description: "Actions v2 allow to manage data executions and targets. If the flag is enabled, you'll be able to use the new API and its features. Note that it is still in an early stage.";
- }
- ];
-
ImprovedPerformanceFeatureFlag improved_performance = 8 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "[1]";
|