mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-04 23:45:07 +00:00
feat(oidc): end session by id_token_hint and without cookie (#8542)
# Which Problems Are Solved The end_session_endpoint currently always requires the userAgent cookie to be able to terminate session created through the hosted login UI. Only tokens issued through the Login V2 can be used to directly terminate a specific session and without the need of a cookie. This PR adds the possibility to terminate a single V1 session or all V1 sessions belonging to the same user agent without the need of the userAgent cookie by providing an id_token as `id_token_hint` which contains the id of a V1 session as `sid`. # How the Problems Are Solved - #8525 added the `sid` claim for id_tokens issued through the login UI - The `sid` can now be checked for the `V1_` prefix and queries for either the userAgentID and depending on the `OIDCSingleV1SessionTermination` flag all userIDs of active session from the same user agent id - The `OIDCSingleV1SessionTermination` flag is added with default value false to keep the existing behavior of terminating all sessions even in case of providing an id_token_hint # Additional Changes - pass `context.Context` into session view functions for querying the database with that context # Additional Context - relates to #8499 - closes #8501
This commit is contained in:
parent
c26a07210c
commit
382a97c30f
@ -348,6 +348,61 @@
|
||||
</div>
|
||||
<cnsl-info-section class="feature-info">{{ 'SETTING.FEATURES.ACTIONS_DESCRIPTION' | translate }}</cnsl-info-section>
|
||||
</div>
|
||||
|
||||
<div class="feature-row" *ngIf="toggleStates.oidcSingleV1SessionTermination">
|
||||
<span>{{ 'SETTING.FEATURES.OIDCSINGLEV1SESSIONTERMINATION' | translate }}</span>
|
||||
<div class="row">
|
||||
<mat-button-toggle-group
|
||||
class="theme-toggle"
|
||||
class="buttongroup"
|
||||
[(ngModel)]="toggleStates.oidcSingleV1SessionTermination.state"
|
||||
(change)="validateAndSave()"
|
||||
name="displayview"
|
||||
aria-label="Display View"
|
||||
>
|
||||
<mat-button-toggle [value]="ToggleState.INHERITED">
|
||||
<div class="toggle-row">
|
||||
<span>{{ 'SETTING.FEATURES.STATES.INHERITED' | translate }}</span>
|
||||
<i
|
||||
class="info-i las la-question-circle"
|
||||
matTooltip="{{ 'SETTING.FEATURES.INHERITED_DESCRIPTION' | translate }}"
|
||||
></i>
|
||||
<div
|
||||
*ngIf="
|
||||
!!featureData.oidcSingleV1SessionTermination?.enabled &&
|
||||
(featureData.oidcSingleV1SessionTermination?.source === Source.SOURCE_SYSTEM ||
|
||||
featureData.oidcSingleV1SessionTermination?.source === Source.SOURCE_UNSPECIFIED)
|
||||
"
|
||||
class="current-dot enabled"
|
||||
matTooltip="{{ 'SETTING.FEATURES.INHERITEDINDICATOR_DESCRIPTION.ENABLED' | translate }}"
|
||||
></div>
|
||||
<div
|
||||
*ngIf="
|
||||
!featureData.oidcSingleV1SessionTermination?.enabled &&
|
||||
(featureData.oidcSingleV1SessionTermination?.source === Source.SOURCE_SYSTEM ||
|
||||
featureData.oidcSingleV1SessionTermination?.source === Source.SOURCE_UNSPECIFIED)
|
||||
"
|
||||
class="current-dot disabled"
|
||||
matTooltip="{{ 'SETTING.FEATURES.INHERITEDINDICATOR_DESCRIPTION.DISABLED' | translate }}"
|
||||
></div>
|
||||
</div>
|
||||
</mat-button-toggle>
|
||||
<mat-button-toggle [value]="ToggleState.DISABLED">
|
||||
<div class="toggle-row">
|
||||
<span> {{ 'SETTING.FEATURES.STATES.DISABLED' | translate }}</span>
|
||||
</div>
|
||||
</mat-button-toggle>
|
||||
<mat-button-toggle [value]="ToggleState.ENABLED">
|
||||
<div class="toggle-row">
|
||||
<span> {{ 'SETTING.FEATURES.STATES.ENABLED' | translate }}</span>
|
||||
</div>
|
||||
</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
</div>
|
||||
<cnsl-info-section class="feature-info">{{
|
||||
'SETTING.FEATURES.OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION' | translate
|
||||
}}</cnsl-info-section>
|
||||
</div>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
</div>
|
||||
|
@ -38,6 +38,7 @@ type ToggleStates = {
|
||||
userSchema?: FeatureState;
|
||||
oidcTokenExchange?: FeatureState;
|
||||
actions?: FeatureState;
|
||||
oidcSingleV1SessionTermination?: FeatureState;
|
||||
};
|
||||
|
||||
@Component({
|
||||
@ -135,6 +136,12 @@ export class FeaturesComponent implements OnDestroy {
|
||||
req.setActions(this.toggleStates?.actions?.state === ToggleState.ENABLED);
|
||||
changed = true;
|
||||
}
|
||||
if (this.toggleStates?.oidcSingleV1SessionTermination?.state !== ToggleState.INHERITED) {
|
||||
req.setOidcSingleV1SessionTermination(
|
||||
this.toggleStates?.oidcSingleV1SessionTermination?.state === ToggleState.ENABLED,
|
||||
);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this.featureService
|
||||
@ -215,6 +222,16 @@ export class FeaturesComponent implements OnDestroy {
|
||||
? ToggleState.ENABLED
|
||||
: ToggleState.DISABLED,
|
||||
},
|
||||
oidcSingleV1SessionTermination: {
|
||||
source: this.featureData.oidcSingleV1SessionTermination?.source || Source.SOURCE_SYSTEM,
|
||||
state:
|
||||
this.featureData.oidcSingleV1SessionTermination?.source === Source.SOURCE_SYSTEM ||
|
||||
this.featureData.oidcSingleV1SessionTermination?.source === Source.SOURCE_UNSPECIFIED
|
||||
? ToggleState.INHERITED
|
||||
: !!this.featureData.oidcSingleV1SessionTermination?.enabled
|
||||
? ToggleState.ENABLED
|
||||
: ToggleState.DISABLED,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -232,24 +249,4 @@ export class FeaturesComponent implements OnDestroy {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public saveFeatures(): void {
|
||||
if (this.featureData) {
|
||||
const req = new SetInstanceFeaturesRequest();
|
||||
req.setLoginDefaultOrg(!!this.featureData.loginDefaultOrg?.enabled);
|
||||
req.setOidcLegacyIntrospection(!!this.featureData.oidcLegacyIntrospection?.enabled);
|
||||
req.setOidcTokenExchange(!!this.featureData.oidcTokenExchange?.enabled);
|
||||
req.setOidcTriggerIntrospectionProjections(!!this.featureData.oidcTriggerIntrospectionProjections?.enabled);
|
||||
req.setUserSchema(!!this.featureData.userSchema?.enabled);
|
||||
|
||||
this.featureService
|
||||
.setInstanceFeatures(req)
|
||||
.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1468,6 +1468,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "Потребителските схеми позволяват управление на данните за схемите на потребителите. Ако е активиран флагът, ще можете да използвате новото API и неговите функции.",
|
||||
"ACTIONS": "Действия",
|
||||
"ACTIONS_DESCRIPTION": "Действия v2 позволяват управление на выполнения на данни и цели. Ако флагът е активиран, ще можете да използвате новия API и неговите функции.",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 Завършване на сесия",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "Ако флагът е активиран, ще можете да прекратите единична сесия от UI за вход, като предоставите id_token с `sid` претенция като id_token_hint на крайната точка на end_session. Имайте предвид, че в момента всички сесии от същия потребителски агент (браузър) се прекратяват в UI за вход. Сесиите, управлявани чрез API на сесията, вече позволяват прекратяването на единични сесии.",
|
||||
"STATES": {
|
||||
"INHERITED": "Наследено",
|
||||
"ENABLED": "Активирано",
|
||||
|
@ -1469,6 +1469,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "Schémata uživatelů umožňují spravovat datová schémata uživatelů. Pokud je příznak povolen, budete moci používat nové API a jeho funkce.",
|
||||
"ACTIONS": "Akce",
|
||||
"ACTIONS_DESCRIPTION": "Akce v2 umožňují správu datových provedení a cílů. Pokud je tento příznak povolen, budete moci používat nové API a jeho funkce.",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 ukončení relace",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "Pokud je příznak aktivován, budete moci ukončit jedinou relaci z rozhraní pro přihlášení zadáním id_token s nárokem `sid` jako id_token_hint na koncovém bodu end_session. Poznamenejte si, že v současné době jsou v rozhraní pro přihlášení ukončeny všechny relace ze stejného uživatelského agenta (prohlížeče). Relace spravované prostřednictvím rozhraní API relace již umožňují ukončení jednotlivých relací.",
|
||||
"STATES": {
|
||||
"INHERITED": "Děděno",
|
||||
"ENABLED": "Povoleno",
|
||||
|
@ -1469,6 +1469,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "Benutzerschemata ermöglichen das Verwalten von Datenschemata von Benutzern. Wenn die Flagge aktiviert ist, können Sie die neue API und ihre Funktionen verwenden.",
|
||||
"ACTIONS": "Aktionen",
|
||||
"ACTIONS_DESCRIPTION": "Aktionen v2 ermöglichen die Verwaltung von Datenausführungen und Zielen. Wenn das Flag aktiviert ist, können Sie die neue API und ihre Funktionen verwenden.",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 Sitzungsbeendigung",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "Wenn das Flag aktiviert ist, können Sie eine einzelne Sitzung über die Login-Benutzeroberfläche beenden, indem Sie einen id_token mit einem `sid` Claim als id_token_hint am Endpunkt end_session übergeben. Beachten Sie, dass derzeit alle Sitzungen desselben Benutzeragenten (Browser) in der Login-Benutzeroberfläche beendet werden. Sitzungen, die über die Session API verwaltet werden, ermöglichen bereits die Beendigung einzelner Sitzungen.",
|
||||
"STATES": {
|
||||
"INHERITED": "Erben",
|
||||
"ENABLED": "Aktiviert",
|
||||
|
@ -1469,6 +1469,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "User Schemas allow to manage data schemas of user. If the flag is enabled, you'll be able to use the new API and its features.",
|
||||
"ACTIONS": "Actions",
|
||||
"ACTIONS_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.",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 Session Termination",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "If the flag is enabled, you'll be able to terminate a single session from the login UI by providing an id_token with a `sid` claim as id_token_hint on the end_session endpoint. Note that currently all sessions from the same user agent (browser) are terminated in the login UI. Sessions managed through the Session API already allow the termination of single sessions.",
|
||||
"STATES": {
|
||||
"INHERITED": "Inherit",
|
||||
"ENABLED": "Enabled",
|
||||
|
@ -1470,6 +1470,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "Los esquemas de usuario permiten gestionar los esquemas de datos de los usuarios. Si se activa la bandera, podrás utilizar la nueva API y sus funciones.",
|
||||
"ACTIONS": "Acciones",
|
||||
"ACTIONS_DESCRIPTION": "Acciones v2 permite administrar las ejecuciones y objetivos de datos. Si la bandera está habilitada, podrá utilizar la nueva API y sus funciones.",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 Terminación de sesión",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "Si la bandera está habilitada, podrá terminar una sesión única desde la interfaz de usuario de inicio de sesión proporcionando un id_token con una reclamación `sid` como id_token_hint en el punto final de end_session. Tenga en cuenta que actualmente se terminan todas las sesiones del mismo agente de usuario (navegador) en la interfaz de usuario de inicio de sesión. Las sesiones administradas a través de la API de sesión ya permiten la terminación de sesiones individuales.",
|
||||
"STATES": {
|
||||
"INHERITED": "Heredado",
|
||||
"ENABLED": "Habilitado",
|
||||
|
@ -1469,6 +1469,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "Les schémas utilisateur permettent de gérer les schémas de données des utilisateurs. Si le drapeau est activé, vous pourrez utiliser la nouvelle API et ses fonctionnalités.",
|
||||
"ACTIONS": "Actions",
|
||||
"ACTIONS_DESCRIPTION": "Les actions v2 permettent de gérer les exécutions et les cibles de données. Si l'indicateur est activé, vous pourrez utiliser la nouvelle API et ses fonctionnalités.",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 Fin de session",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "Si l'indicateur est activé, vous pourrez terminer une seule session à partir de l'interface utilisateur de connexion en fournissant un id_token avec une revendication `sid` en tant que id_token_hint sur le point de terminaison end_session. Notez que toutes les sessions du même agent utilisateur (navigateur) sont actuellement terminées dans l'interface utilisateur de connexion. Les sessions gérées via l'API de session permettent déjà la terminaison de sessions individuelles.",
|
||||
"STATES": {
|
||||
"INHERITED": "Hérité",
|
||||
"ENABLED": "Activé",
|
||||
|
@ -1469,6 +1469,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "Gli schemi utente consentono di gestire gli schemi di dati degli utenti. Se la flag è attivata, sarà possibile utilizzare la nuova API e le sue funzionalità.",
|
||||
"ACTIONS": "Azioni",
|
||||
"ACTIONS_DESCRIPTION": "Le azioni v2 consentono di gestire le esecuzioni e gli obiettivi dei dati. Se l'indicatore è abilitato, potrai utilizzare la nuova API e le sue funzionalità.",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 Terminazione sessione",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "Se il flag è abilitato, sarai in grado di terminare una singola sessione dall'interfaccia utente di accesso fornendo un id_token con una richiesta `sid` come id_token_hint nel punto finale di end_session. Tieni presente che attualmente tutte le sessioni dello stesso agente utente (browser) vengono terminate nell'interfaccia utente di accesso. Le sessioni gestite tramite l'API di sessione consentono già la terminazione di singole sessioni.",
|
||||
"STATES": {
|
||||
"INHERITED": "Predefinito",
|
||||
"ENABLED": "Abilitato",
|
||||
|
@ -1469,6 +1469,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "ユーザー スキーマを使用すると、ユーザーのデータスキーマを管理できます。フラグが有効になっている場合、新しい APIとその機能を使用できます。",
|
||||
"ACTIONS": "アクション",
|
||||
"ACTIONS_DESCRIPTION": "Actions v2は、データの実行とターゲットを管理できます。フラグが有効になっている場合、新しい APIとその機能を使用できます。",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 セッション終了",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "フラグが有効になっている場合、id_token を `sid` クレームと共に id_token_hint として end_session エンドポイントに提供することで、ログイン UI から単一のセッションを終了できるようになります。 現在、同じユーザー エージェント (ブラウザ) からのすべてのセッションがログイン UI で終了することに注意してください。 セッション API を通じて管理されるセッションは、すでに単一のセッションの終了を許可しています。",
|
||||
"STATES": {
|
||||
"INHERITED": "継承",
|
||||
"ENABLED": "有効",
|
||||
|
@ -1470,6 +1470,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "Корисничките шеми овозможуваат управување со податоци шеми на корисникот. Ако знамето е овозможено, ќе можете да го користите новиот API и неговите функции.",
|
||||
"ACTIONS": "Акции",
|
||||
"ACTIONS_DESCRIPTION": "Акциите v2 овозможуваат управување со извршување на податоци и цели. Ако знамето е овозможено, ќе можете да го користите новиот API и неговите функции.",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 Завршување на сесија",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "Ако ознаката е активирана, ќе можете да ја завршите единечна сесија од корисничкиот интерфејс за најава, со обезбедување id_token со `sid` побарување како id_token_hint на крајната точка на end_session. Имајте предвид дека во моментов сите сесии од истиот кориснички агент (прелистувач) се завршуваат во корисничкиот интерфејс за најава. Сесиите управувани преку API на сесија веќе дозволуваат завршување на единечни сесии.",
|
||||
"STATES": {
|
||||
"INHERITED": "Наследи",
|
||||
"ENABLED": "Овозможено",
|
||||
|
@ -1468,6 +1468,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "Met gebruikerschema's kunt u de dataschema's van gebruikers beheren. Als de vlag is ingeschakeld, kunt u de nieuwe API en zijn functies gebruiken.",
|
||||
"ACTIONS": "Acties",
|
||||
"ACTIONS_DESCRIPTION": "Actions v2 maken het mogelijk om data-uitvoeringen en doelen te beheren. Als de vlag is ingeschakeld, kunt u de nieuwe API en zijn functies gebruiken.",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 Sessiebeëindiging",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "Als het vlagje is ingeschakeld, kunt u een enkele sessie beëindigen via de login-gebruikersinterface door een id_token met een `sid`-claim als id_token_hint op het eindpunt end_session te verstrekken. Houd er rekening mee dat momenteel alle sessies van dezelfde gebruikersagent (browser) worden beëindigd in de login-gebruikersinterface. Sessies die worden beheerd via de Session API staan al toe om individuele sessies te beëindigen.",
|
||||
"STATES": {
|
||||
"INHERITED": "Overgenomen",
|
||||
"ENABLED": "Ingeschakeld",
|
||||
|
@ -1468,6 +1468,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "Schematy użytkowników umożliwiają zarządzanie schematami danych użytkowników. Jeśli flaga jest włączona, będziesz mógł korzystać z nowego interfejsu API i jego funkcji.",
|
||||
"ACTIONS": "Akcje",
|
||||
"ACTIONS_DESCRIPTION": "Akcje v2 umożliwiają zarządzanie wykonaniami danych i celami. Jeżeli flaga jest włączona, będziesz mógł korzystać z nowego interfejsu API i jego funkcji.",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 Zakończenie sesji",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "Jeśli flaga jest włączona, będziesz mógł zakończyć pojedynczą sesję z interfejsu użytkownika logowania, podając id_token z roszczeniem `sid` jako id_token_hint w punkcie końcowym end_session. Należy pamiętać, że obecnie wszystkie sesje z tego samego agenta użytkownika (przeglądarki) są kończone w interfejsie użytkownika logowania. Sesje zarządzane za pomocą interfejsu API sesji już pozwalają na zakończenie pojedynczych sesji.",
|
||||
"STATES": {
|
||||
"INHERITED": "Dziedziczony",
|
||||
"ENABLED": "Włączony",
|
||||
|
@ -1470,6 +1470,8 @@
|
||||
"USERSCHEMAS_DESCRIPTION": "Esquemas de Usuário permitem gerenciar esquemas de dados do usuário. Se o sinalizador estiver ativado, você poderá usar a nova API e seus recursos.",
|
||||
"ACTIONS": "Ações",
|
||||
"ACTIONS_DESCRIPTION": "Actions v2 permitem gerenciar execuções e destinos de dados. Se a flag estiver habilitada, você poderá usar a nova API e seus recursos.",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 Término de sessão",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "Se a bandeira estiver habilitada, você poderá encerrar uma sessão única da interface do usuário de login fornecendo um id_token com uma reivindicação `sid como id_token_hint no ponto final de end_session. Observe que atualmente todas as sessões do mesmo agente de usuário (navegador) são encerradas na interface do usuário de login. As sessões gerenciadas por meio da API de sessão já permitem o encerramento de sessões individuais.",
|
||||
"STATES": {
|
||||
"INHERITED": "Herdade",
|
||||
"ENABLED": "Habilitado",
|
||||
|
@ -1521,6 +1521,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "Схемы пользователей позволяют управлять схемами данных пользователей. Если флаг включен, вы сможете использовать новый API и его функции.",
|
||||
"ACTIONS": "Действия",
|
||||
"ACTIONS_DESCRIPTION": "Actions v2 позволяют управлять выполнением данных и целевыми объектами. Если флаг включен, вы сможете использовать новый API и его функции.",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 Окончание сеанса",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "Если флаг включен, вы сможете завершить отдельный сеанс из интерфейса пользователя входа, предоставив id_token с претензией `sid` в качестве id_token_hint на конечной точке end_session. Обратите внимание, что в настоящее время все сеансы одного и того же пользовательского агента (браузера) завершаются в интерфейсе пользователя входа. Сеансы, управляемые через API сеанса, уже позволяют завершать отдельные сеансы.",
|
||||
"STATES": {
|
||||
"INHERITED": "Наследовать",
|
||||
"ENABLED": "Включено",
|
||||
|
@ -1473,6 +1473,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "Användarscheman tillåter att hantera datascheman för användare. Om flaggan är aktiverad kommer du att kunna använda det nya API:et och dess funktioner.",
|
||||
"ACTIONS": "Åtgärder",
|
||||
"ACTIONS_DESCRIPTION": "Åtgärder v2 tillåter att hantera dataexekveringar och mål. Om flaggan är aktiverad kommer du att kunna använda det nya API:et och dess funktioner.",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 Session avslutning",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "Om flaggan är aktiverad, kan du avsluta en enskild session från inloggningsgränssnittet genom att ange en id_token med ett `sid`-krav som id_token_hint på slutpunkten end_session. Observera att för närvarande alla sessioner från samma användaragent (webbläsare) avslutas i inloggningsgränssnittet. Sessioner som hanteras via Session API tillåter redan avslutning av enskilda sessioner.",
|
||||
"STATES": {
|
||||
"INHERITED": "Ärv",
|
||||
"ENABLED": "Aktiverad",
|
||||
|
@ -1469,6 +1469,8 @@
|
||||
"USERSCHEMA_DESCRIPTION": "用户架构允许管理用户的数据架构。如果启用此标志,您将可以使用新的 API 及其功能。",
|
||||
"ACTIONS": "操作",
|
||||
"ACTIONS_DESCRIPTION": "Actions v2 可以管理数据执行和目标。如果启用此标志,您将可以使用新的 API 及其功能。",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION": "OIDC Single V1 终止会话",
|
||||
"OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "如果启用了标志,您可以通过在 end_session 端点上提供带有 `sid` 声明的 id_token 作为 id_token_hint 来从登录 UI 终止单个会话。 请注意,目前所有来自同一用户代理(浏览器)的会话都在登录 UI 中终止。 通过会话 API 管理的会话已经允许终止单个会话。",
|
||||
"STATES": {
|
||||
"INHERITED": "继承",
|
||||
"ENABLED": "已启用",
|
||||
|
@ -17,6 +17,7 @@ func systemFeaturesToCommand(req *feature_pb.SetSystemFeaturesRequest) *command.
|
||||
Actions: req.Actions,
|
||||
TokenExchange: req.OidcTokenExchange,
|
||||
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
|
||||
OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination,
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +31,7 @@ func systemFeaturesToPb(f *query.SystemFeatures) *feature_pb.GetSystemFeaturesRe
|
||||
OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange),
|
||||
Actions: featureSourceToFlagPb(&f.Actions),
|
||||
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
|
||||
OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination),
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,6 +46,7 @@ func instanceFeaturesToCommand(req *feature_pb.SetInstanceFeaturesRequest) *comm
|
||||
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
|
||||
WebKey: req.WebKey,
|
||||
DebugOIDCParentError: req.DebugOidcParentError,
|
||||
OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination,
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,6 +62,7 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat
|
||||
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
|
||||
WebKey: featureSourceToFlagPb(&f.WebKey),
|
||||
DebugOidcParentError: featureSourceToFlagPb(&f.DebugOIDCParentError),
|
||||
OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ func Test_systemFeaturesToCommand(t *testing.T) {
|
||||
Actions: gu.Ptr(true),
|
||||
OidcTokenExchange: gu.Ptr(true),
|
||||
ImprovedPerformance: nil,
|
||||
OidcSingleV1SessionTermination: gu.Ptr(true),
|
||||
}
|
||||
want := &command.SystemFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
@ -34,6 +35,7 @@ func Test_systemFeaturesToCommand(t *testing.T) {
|
||||
Actions: gu.Ptr(true),
|
||||
TokenExchange: gu.Ptr(true),
|
||||
ImprovedPerformance: nil,
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(true),
|
||||
}
|
||||
got := systemFeaturesToCommand(arg)
|
||||
assert.Equal(t, want, got)
|
||||
@ -74,6 +76,10 @@ func Test_systemFeaturesToPb(t *testing.T) {
|
||||
Level: feature.LevelSystem,
|
||||
Value: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgByID},
|
||||
},
|
||||
OIDCSingleV1SessionTermination: query.FeatureSource[bool]{
|
||||
Level: feature.LevelSystem,
|
||||
Value: true,
|
||||
},
|
||||
}
|
||||
want := &feature_pb.GetSystemFeaturesResponse{
|
||||
Details: &object.Details{
|
||||
@ -109,6 +115,10 @@ func Test_systemFeaturesToPb(t *testing.T) {
|
||||
ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID},
|
||||
Source: feature_pb.Source_SOURCE_SYSTEM,
|
||||
},
|
||||
OidcSingleV1SessionTermination: &feature_pb.FeatureFlag{
|
||||
Enabled: true,
|
||||
Source: feature_pb.Source_SOURCE_SYSTEM,
|
||||
},
|
||||
}
|
||||
got := systemFeaturesToPb(arg)
|
||||
assert.Equal(t, want, got)
|
||||
@ -124,6 +134,8 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
|
||||
Actions: gu.Ptr(true),
|
||||
ImprovedPerformance: nil,
|
||||
WebKey: gu.Ptr(true),
|
||||
DebugOidcParentError: gu.Ptr(true),
|
||||
OidcSingleV1SessionTermination: gu.Ptr(true),
|
||||
}
|
||||
want := &command.InstanceFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
@ -134,6 +146,8 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
|
||||
Actions: gu.Ptr(true),
|
||||
ImprovedPerformance: nil,
|
||||
WebKey: gu.Ptr(true),
|
||||
DebugOIDCParentError: gu.Ptr(true),
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(true),
|
||||
}
|
||||
got := instanceFeaturesToCommand(arg)
|
||||
assert.Equal(t, want, got)
|
||||
@ -178,6 +192,10 @@ func Test_instanceFeaturesToPb(t *testing.T) {
|
||||
Level: feature.LevelInstance,
|
||||
Value: true,
|
||||
},
|
||||
OIDCSingleV1SessionTermination: query.FeatureSource[bool]{
|
||||
Level: feature.LevelInstance,
|
||||
Value: true,
|
||||
},
|
||||
}
|
||||
want := &feature_pb.GetInstanceFeaturesResponse{
|
||||
Details: &object.Details{
|
||||
@ -221,6 +239,10 @@ func Test_instanceFeaturesToPb(t *testing.T) {
|
||||
Enabled: false,
|
||||
Source: feature_pb.Source_SOURCE_UNSPECIFIED,
|
||||
},
|
||||
OidcSingleV1SessionTermination: &feature_pb.FeatureFlag{
|
||||
Enabled: true,
|
||||
Source: feature_pb.Source_SOURCE_INSTANCE,
|
||||
},
|
||||
}
|
||||
got := instanceFeaturesToPb(arg)
|
||||
assert.Equal(t, want, got)
|
||||
|
@ -17,6 +17,7 @@ func systemFeaturesToCommand(req *feature_pb.SetSystemFeaturesRequest) *command.
|
||||
Actions: req.Actions,
|
||||
TokenExchange: req.OidcTokenExchange,
|
||||
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
|
||||
OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination,
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +31,7 @@ func systemFeaturesToPb(f *query.SystemFeatures) *feature_pb.GetSystemFeaturesRe
|
||||
OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange),
|
||||
Actions: featureSourceToFlagPb(&f.Actions),
|
||||
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
|
||||
OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination),
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,6 +46,7 @@ func instanceFeaturesToCommand(req *feature_pb.SetInstanceFeaturesRequest) *comm
|
||||
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
|
||||
WebKey: req.WebKey,
|
||||
DebugOIDCParentError: req.DebugOidcParentError,
|
||||
OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination,
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,6 +62,7 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat
|
||||
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
|
||||
WebKey: featureSourceToFlagPb(&f.WebKey),
|
||||
DebugOidcParentError: featureSourceToFlagPb(&f.DebugOIDCParentError),
|
||||
OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ func Test_systemFeaturesToCommand(t *testing.T) {
|
||||
Actions: gu.Ptr(true),
|
||||
OidcTokenExchange: gu.Ptr(true),
|
||||
ImprovedPerformance: nil,
|
||||
OidcSingleV1SessionTermination: gu.Ptr(true),
|
||||
}
|
||||
want := &command.SystemFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
@ -34,6 +35,7 @@ func Test_systemFeaturesToCommand(t *testing.T) {
|
||||
Actions: gu.Ptr(true),
|
||||
TokenExchange: gu.Ptr(true),
|
||||
ImprovedPerformance: nil,
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(true),
|
||||
}
|
||||
got := systemFeaturesToCommand(arg)
|
||||
assert.Equal(t, want, got)
|
||||
@ -74,6 +76,10 @@ func Test_systemFeaturesToPb(t *testing.T) {
|
||||
Level: feature.LevelSystem,
|
||||
Value: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgByID},
|
||||
},
|
||||
OIDCSingleV1SessionTermination: query.FeatureSource[bool]{
|
||||
Level: feature.LevelSystem,
|
||||
Value: true,
|
||||
},
|
||||
}
|
||||
want := &feature_pb.GetSystemFeaturesResponse{
|
||||
Details: &object.Details{
|
||||
@ -109,6 +115,10 @@ func Test_systemFeaturesToPb(t *testing.T) {
|
||||
ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID},
|
||||
Source: feature_pb.Source_SOURCE_SYSTEM,
|
||||
},
|
||||
OidcSingleV1SessionTermination: &feature_pb.FeatureFlag{
|
||||
Enabled: true,
|
||||
Source: feature_pb.Source_SOURCE_SYSTEM,
|
||||
},
|
||||
}
|
||||
got := systemFeaturesToPb(arg)
|
||||
assert.Equal(t, want, got)
|
||||
@ -124,6 +134,7 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
|
||||
Actions: gu.Ptr(true),
|
||||
ImprovedPerformance: nil,
|
||||
WebKey: gu.Ptr(true),
|
||||
OidcSingleV1SessionTermination: gu.Ptr(true),
|
||||
}
|
||||
want := &command.InstanceFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
@ -134,6 +145,7 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
|
||||
Actions: gu.Ptr(true),
|
||||
ImprovedPerformance: nil,
|
||||
WebKey: gu.Ptr(true),
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(true),
|
||||
}
|
||||
got := instanceFeaturesToCommand(arg)
|
||||
assert.Equal(t, want, got)
|
||||
@ -178,6 +190,10 @@ func Test_instanceFeaturesToPb(t *testing.T) {
|
||||
Level: feature.LevelInstance,
|
||||
Value: true,
|
||||
},
|
||||
OIDCSingleV1SessionTermination: query.FeatureSource[bool]{
|
||||
Level: feature.LevelInstance,
|
||||
Value: true,
|
||||
},
|
||||
}
|
||||
want := &feature_pb.GetInstanceFeaturesResponse{
|
||||
Details: &object.Details{
|
||||
@ -221,6 +237,10 @@ func Test_instanceFeaturesToPb(t *testing.T) {
|
||||
Enabled: false,
|
||||
Source: feature_pb.Source_SOURCE_UNSPECIFIED,
|
||||
},
|
||||
OidcSingleV1SessionTermination: &feature_pb.FeatureFlag{
|
||||
Enabled: true,
|
||||
Source: feature_pb.Source_SOURCE_INSTANCE,
|
||||
},
|
||||
}
|
||||
got := instanceFeaturesToPb(arg)
|
||||
assert.Equal(t, want, got)
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
http_utils "github.com/zitadel/zitadel/internal/api/http"
|
||||
"github.com/zitadel/zitadel/internal/api/http/middleware"
|
||||
"github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/handler"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
@ -245,11 +246,20 @@ func (o *OPStorage) TerminateSessionFromRequest(ctx context.Context, endSessionR
|
||||
}
|
||||
|
||||
// If there is no login client header and no id_token_hint or the id_token_hint does not have a session ID,
|
||||
// do a v1 Terminate session.
|
||||
// do a v1 Terminate session (which terminates all sessions of the user agent, identified by cookie).
|
||||
if endSessionRequest.IDTokenHintClaims == nil || endSessionRequest.IDTokenHintClaims.SessionID == "" {
|
||||
return endSessionRequest.RedirectURI, o.TerminateSession(ctx, endSessionRequest.UserID, endSessionRequest.ClientID)
|
||||
}
|
||||
|
||||
// If the sessionID is prefixed by V1, we also terminate a v1 session.
|
||||
if strings.HasPrefix(endSessionRequest.IDTokenHintClaims.SessionID, handler.IDPrefixV1) {
|
||||
err = o.terminateV1Session(ctx, endSessionRequest.UserID, endSessionRequest.IDTokenHintClaims.SessionID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return endSessionRequest.RedirectURI, nil
|
||||
}
|
||||
|
||||
// terminate the v2 session of the id_token_hint
|
||||
_, err = o.command.TerminateSessionWithoutTokenCheck(ctx, endSessionRequest.IDTokenHintClaims.SessionID)
|
||||
if err != nil {
|
||||
@ -258,6 +268,30 @@ func (o *OPStorage) TerminateSessionFromRequest(ctx context.Context, endSessionR
|
||||
return endSessionRequest.RedirectURI, nil
|
||||
}
|
||||
|
||||
// terminateV1Session terminates "v1" sessions created through the login UI.
|
||||
// Depending on the flag, we either terminate a single session or all of the user agent
|
||||
func (o *OPStorage) terminateV1Session(ctx context.Context, userID, sessionID string) error {
|
||||
ctx = authz.SetCtxData(ctx, authz.CtxData{UserID: userID})
|
||||
// if the flag is active we only terminate the specific session
|
||||
if authz.GetFeatures(ctx).OIDCSingleV1SessionTermination {
|
||||
userAgentID, err := o.repo.UserAgentIDBySessionID(ctx, sessionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.command.HumansSignOut(ctx, userAgentID, []string{userID})
|
||||
}
|
||||
// otherwise we search for all active sessions within the same user agent of the current session id
|
||||
userAgentID, userIDs, err := o.repo.ActiveUserIDsBySessionID(ctx, sessionID)
|
||||
if err != nil {
|
||||
logging.WithError(err).Error("error retrieving user sessions")
|
||||
return err
|
||||
}
|
||||
if len(userIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return o.command.HumansSignOut(ctx, userAgentID, userIDs)
|
||||
}
|
||||
|
||||
func (o *OPStorage) RevokeToken(ctx context.Context, token, userID, clientID string) (err *oidc.Error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() {
|
||||
|
@ -74,8 +74,8 @@ type privacyPolicyProvider interface {
|
||||
}
|
||||
|
||||
type userSessionViewProvider interface {
|
||||
UserSessionByIDs(string, string, string) (*user_view_model.UserSessionView, error)
|
||||
UserSessionsByAgentID(string, string) ([]*user_view_model.UserSessionView, error)
|
||||
UserSessionByIDs(context.Context, string, string, string) (*user_view_model.UserSessionView, error)
|
||||
UserSessionsByAgentID(context.Context, string, string) ([]*user_view_model.UserSessionView, error)
|
||||
GetLatestUserSessionSequence(ctx context.Context, instanceID string) (*query.CurrentState, error)
|
||||
}
|
||||
|
||||
@ -1533,7 +1533,7 @@ func userSessionsByUserAgentID(ctx context.Context, provider userSessionViewProv
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
session, err := provider.UserSessionsByAgentID(agentID, instanceID)
|
||||
session, err := provider.UserSessionsByAgentID(ctx, agentID, instanceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1573,7 +1573,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
|
||||
OnError(err).
|
||||
Errorf("could not get current sequence for userSessionByIDs")
|
||||
|
||||
session, err := provider.UserSessionByIDs(agentID, user.ID, instanceID)
|
||||
session, err := provider.UserSessionByIDs(ctx, agentID, user.ID, instanceID)
|
||||
if err != nil {
|
||||
if !zerrors.IsNotFound(err) {
|
||||
return nil, err
|
||||
|
@ -34,11 +34,11 @@ var (
|
||||
|
||||
type mockViewNoUserSession struct{}
|
||||
|
||||
func (m *mockViewNoUserSession) UserSessionByIDs(string, string, string) (*user_view_model.UserSessionView, error) {
|
||||
func (m *mockViewNoUserSession) UserSessionByIDs(context.Context, string, string, string) (*user_view_model.UserSessionView, error) {
|
||||
return nil, zerrors.ThrowNotFound(nil, "id", "user session not found")
|
||||
}
|
||||
|
||||
func (m *mockViewNoUserSession) UserSessionsByAgentID(string, string) ([]*user_view_model.UserSessionView, error) {
|
||||
func (m *mockViewNoUserSession) UserSessionsByAgentID(context.Context, string, string) ([]*user_view_model.UserSessionView, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -48,11 +48,11 @@ func (m *mockViewNoUserSession) GetLatestUserSessionSequence(ctx context.Context
|
||||
|
||||
type mockViewErrUserSession struct{}
|
||||
|
||||
func (m *mockViewErrUserSession) UserSessionByIDs(string, string, string) (*user_view_model.UserSessionView, error) {
|
||||
func (m *mockViewErrUserSession) UserSessionByIDs(context.Context, string, string, string) (*user_view_model.UserSessionView, error) {
|
||||
return nil, zerrors.ThrowInternal(nil, "id", "internal error")
|
||||
}
|
||||
|
||||
func (m *mockViewErrUserSession) UserSessionsByAgentID(string, string) ([]*user_view_model.UserSessionView, error) {
|
||||
func (m *mockViewErrUserSession) UserSessionsByAgentID(context.Context, string, string) ([]*user_view_model.UserSessionView, error) {
|
||||
return nil, zerrors.ThrowInternal(nil, "id", "internal error")
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ type mockUser struct {
|
||||
SessionState domain.UserSessionState
|
||||
}
|
||||
|
||||
func (m *mockViewUserSession) UserSessionByIDs(string, string, string) (*user_view_model.UserSessionView, error) {
|
||||
func (m *mockViewUserSession) UserSessionByIDs(context.Context, string, string, string) (*user_view_model.UserSessionView, error) {
|
||||
return &user_view_model.UserSessionView{
|
||||
ExternalLoginVerification: sql.NullTime{Time: m.ExternalLoginVerification},
|
||||
PasswordlessVerification: sql.NullTime{Time: m.PasswordlessVerification},
|
||||
@ -86,7 +86,7 @@ func (m *mockViewUserSession) UserSessionByIDs(string, string, string) (*user_vi
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mockViewUserSession) UserSessionsByAgentID(string, string) ([]*user_view_model.UserSessionView, error) {
|
||||
func (m *mockViewUserSession) UserSessionsByAgentID(context.Context, string, string) ([]*user_view_model.UserSessionView, error) {
|
||||
sessions := make([]*user_view_model.UserSessionView, len(m.Users))
|
||||
for i, user := range m.Users {
|
||||
sessions[i] = &user_view_model.UserSessionView{
|
||||
|
@ -28,7 +28,7 @@ func (repo *UserRepo) Health(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (repo *UserRepo) UserSessionUserIDsByAgentID(ctx context.Context, agentID string) ([]string, error) {
|
||||
userSessions, err := repo.View.UserSessionsByAgentID(agentID, authz.GetInstance(ctx).InstanceID())
|
||||
userSessions, err := repo.View.UserSessionsByAgentID(ctx, agentID, authz.GetInstance(ctx).InstanceID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -41,6 +41,14 @@ func (repo *UserRepo) UserSessionUserIDsByAgentID(ctx context.Context, agentID s
|
||||
return userIDs, nil
|
||||
}
|
||||
|
||||
func (repo *UserRepo) UserAgentIDBySessionID(ctx context.Context, sessionID string) (string, error) {
|
||||
return repo.View.UserAgentIDBySessionID(ctx, sessionID, authz.GetInstance(ctx).InstanceID())
|
||||
}
|
||||
|
||||
func (repo *UserRepo) ActiveUserIDsBySessionID(ctx context.Context, sessionID string) (userAgentID string, userIDs []string, err error) {
|
||||
return repo.View.ActiveUserIDsBySessionID(ctx, sessionID, authz.GetInstance(ctx).InstanceID())
|
||||
}
|
||||
|
||||
func (repo *UserRepo) UserEventsByID(ctx context.Context, id string, changeDate time.Time, eventTypes []eventstore.EventType) ([]eventstore.Event, error) {
|
||||
query, err := usr_view.UserByIDQuery(id, authz.GetInstance(ctx).InstanceID(), changeDate, eventTypes)
|
||||
if err != nil {
|
||||
|
@ -14,7 +14,7 @@ type UserSessionRepo struct {
|
||||
}
|
||||
|
||||
func (repo *UserSessionRepo) GetMyUserSessions(ctx context.Context) ([]*usr_model.UserSessionView, error) {
|
||||
userSessions, err := repo.View.UserSessionsByAgentID(authz.GetCtxData(ctx).AgentID, authz.GetInstance(ctx).InstanceID())
|
||||
userSessions, err := repo.View.UserSessionsByAgentID(ctx, authz.GetCtxData(ctx).AgentID, authz.GetInstance(ctx).InstanceID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -12,12 +12,20 @@ const (
|
||||
userSessionTable = "auth.user_sessions"
|
||||
)
|
||||
|
||||
func (v *View) UserSessionByIDs(agentID, userID, instanceID string) (*model.UserSessionView, error) {
|
||||
return view.UserSessionByIDs(v.client, agentID, userID, instanceID)
|
||||
func (v *View) UserSessionByIDs(ctx context.Context, agentID, userID, instanceID string) (*model.UserSessionView, error) {
|
||||
return view.UserSessionByIDs(ctx, v.client, agentID, userID, instanceID)
|
||||
}
|
||||
|
||||
func (v *View) UserSessionsByAgentID(agentID, instanceID string) ([]*model.UserSessionView, error) {
|
||||
return view.UserSessionsByAgentID(v.client, agentID, instanceID)
|
||||
func (v *View) UserSessionsByAgentID(ctx context.Context, agentID, instanceID string) ([]*model.UserSessionView, error) {
|
||||
return view.UserSessionsByAgentID(ctx, v.client, agentID, instanceID)
|
||||
}
|
||||
|
||||
func (v *View) UserAgentIDBySessionID(ctx context.Context, sessionID, instanceID string) (string, error) {
|
||||
return view.UserAgentIDBySessionID(ctx, v.client, sessionID, instanceID)
|
||||
}
|
||||
|
||||
func (v *View) ActiveUserIDsBySessionID(ctx context.Context, sessionID, instanceID string) (userAgentID string, userIDs []string, err error) {
|
||||
return view.ActiveUserIDsBySessionID(ctx, v.client, sessionID, instanceID)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestUserSessionSequence(ctx context.Context, instanceID string) (_ *query.CurrentState, err error) {
|
||||
|
@ -6,4 +6,6 @@ import (
|
||||
|
||||
type UserRepository interface {
|
||||
UserSessionUserIDsByAgentID(ctx context.Context, agentID string) ([]string, error)
|
||||
UserAgentIDBySessionID(ctx context.Context, sessionID string) (string, error)
|
||||
ActiveUserIDsBySessionID(ctx context.Context, sessionID string) (userAgentID string, userIDs []string, err error)
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ type InstanceFeatures struct {
|
||||
ImprovedPerformance []feature.ImprovedPerformanceType
|
||||
WebKey *bool
|
||||
DebugOIDCParentError *bool
|
||||
OIDCSingleV1SessionTermination *bool
|
||||
}
|
||||
|
||||
func (m *InstanceFeatures) isEmpty() bool {
|
||||
@ -37,7 +38,8 @@ func (m *InstanceFeatures) isEmpty() bool {
|
||||
// nil check to allow unset improvements
|
||||
m.ImprovedPerformance == nil &&
|
||||
m.WebKey == nil &&
|
||||
m.DebugOIDCParentError == nil
|
||||
m.DebugOIDCParentError == nil &&
|
||||
m.OIDCSingleV1SessionTermination == nil
|
||||
}
|
||||
|
||||
func (c *Commands) SetInstanceFeatures(ctx context.Context, f *InstanceFeatures) (*domain.ObjectDetails, error) {
|
||||
|
@ -69,6 +69,7 @@ func (m *InstanceFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.InstanceImprovedPerformanceEventType,
|
||||
feature_v2.InstanceWebKeyEventType,
|
||||
feature_v2.InstanceDebugOIDCParentErrorEventType,
|
||||
feature_v2.InstanceOIDCSingleV1SessionTerminationEventType,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
@ -108,6 +109,9 @@ func reduceInstanceFeature(features *InstanceFeatures, key feature.Key, value an
|
||||
case feature.KeyDebugOIDCParentError:
|
||||
v := value.(bool)
|
||||
features.DebugOIDCParentError = &v
|
||||
case feature.KeyOIDCSingleV1SessionTermination:
|
||||
v := value.(bool)
|
||||
features.OIDCSingleV1SessionTermination = &v
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,5 +127,6 @@ func (wm *InstanceFeaturesWriteModel) setCommands(ctx context.Context, f *Instan
|
||||
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)
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.OIDCSingleV1SessionTermination, f.OIDCSingleV1SessionTermination, feature_v2.InstanceOIDCSingleV1SessionTerminationEventType)
|
||||
return cmds
|
||||
}
|
||||
|
@ -208,6 +208,10 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceActionsEventType, true,
|
||||
),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceOIDCSingleV1SessionTerminationEventType, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
args: args{ctx, &InstanceFeatures{
|
||||
@ -216,6 +220,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
LegacyIntrospection: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
Actions: gu.Ptr(true),
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(true),
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
@ -246,6 +251,10 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceLegacyIntrospectionEventType, true,
|
||||
)),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
context.Background(), aggregate,
|
||||
feature_v2.InstanceOIDCSingleV1SessionTerminationEventType, false,
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
feature_v2.NewSetEvent[bool](
|
||||
@ -262,6 +271,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
TriggerIntrospectionProjections: gu.Ptr(false),
|
||||
LegacyIntrospection: gu.Ptr(true),
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(false),
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
|
@ -17,6 +17,7 @@ type SystemFeatures struct {
|
||||
UserSchema *bool
|
||||
Actions *bool
|
||||
ImprovedPerformance []feature.ImprovedPerformanceType
|
||||
OIDCSingleV1SessionTermination *bool
|
||||
}
|
||||
|
||||
func (m *SystemFeatures) isEmpty() bool {
|
||||
@ -27,7 +28,8 @@ func (m *SystemFeatures) isEmpty() bool {
|
||||
m.TokenExchange == nil &&
|
||||
m.Actions == nil &&
|
||||
// nil check to allow unset improvements
|
||||
m.ImprovedPerformance == nil
|
||||
m.ImprovedPerformance == nil &&
|
||||
m.OIDCSingleV1SessionTermination == nil
|
||||
}
|
||||
|
||||
func (c *Commands) SetSystemFeatures(ctx context.Context, f *SystemFeatures) (*domain.ObjectDetails, error) {
|
||||
|
@ -60,6 +60,7 @@ func (m *SystemFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.SystemTokenExchangeEventType,
|
||||
feature_v2.SystemActionsEventType,
|
||||
feature_v2.SystemImprovedPerformanceEventType,
|
||||
feature_v2.SystemOIDCSingleV1SessionTerminationEventType,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
@ -92,6 +93,9 @@ func reduceSystemFeature(features *SystemFeatures, key feature.Key, value any) {
|
||||
features.Actions = &v
|
||||
case feature.KeyImprovedPerformance:
|
||||
features.ImprovedPerformance = value.([]feature.ImprovedPerformanceType)
|
||||
case feature.KeyOIDCSingleV1SessionTermination:
|
||||
v := value.(bool)
|
||||
features.OIDCSingleV1SessionTermination = &v
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,6 +109,7 @@ func (wm *SystemFeaturesWriteModel) setCommands(ctx context.Context, f *SystemFe
|
||||
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)
|
||||
return cmds
|
||||
}
|
||||
|
||||
|
@ -176,6 +176,10 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemActionsEventType, true,
|
||||
),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemOIDCSingleV1SessionTerminationEventType, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
args: args{context.Background(), &SystemFeatures{
|
||||
@ -184,6 +188,7 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
LegacyIntrospection: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
Actions: gu.Ptr(true),
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(true),
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "SYSTEM",
|
||||
@ -232,6 +237,10 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemActionsEventType, false,
|
||||
),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemOIDCSingleV1SessionTerminationEventType, false,
|
||||
),
|
||||
),
|
||||
),
|
||||
args: args{context.Background(), &SystemFeatures{
|
||||
@ -240,6 +249,7 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
LegacyIntrospection: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
Actions: gu.Ptr(false),
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(false),
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "SYSTEM",
|
||||
|
@ -16,6 +16,7 @@ const (
|
||||
KeyImprovedPerformance
|
||||
KeyWebKey
|
||||
KeyDebugOIDCParentError
|
||||
KeyOIDCSingleV1SessionTermination
|
||||
)
|
||||
|
||||
//go:generate enumer -type Level -transform snake -trimprefix Level
|
||||
@ -41,6 +42,7 @@ type Features struct {
|
||||
ImprovedPerformance []ImprovedPerformanceType `json:"improved_performance,omitempty"`
|
||||
WebKey bool `json:"web_key,omitempty"`
|
||||
DebugOIDCParentError bool `json:"debug_oidc_parent_error,omitempty"`
|
||||
OIDCSingleV1SessionTermination bool `json:"terminate_single_v1_session,omitempty"`
|
||||
}
|
||||
|
||||
type ImprovedPerformanceType int32
|
||||
|
@ -7,11 +7,11 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const _KeyName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactionsimproved_performanceweb_keydebug_oidc_parent_error"
|
||||
const _KeyName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactionsimproved_performanceweb_keydebug_oidc_parent_errorterminate_single_v1_session"
|
||||
|
||||
var _KeyIndex = [...]uint8{0, 11, 28, 61, 81, 92, 106, 113, 133, 140, 163}
|
||||
var _KeyIndex = [...]uint8{0, 11, 28, 61, 81, 92, 106, 113, 133, 140, 163, 190}
|
||||
|
||||
const _KeyLowerName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactionsimproved_performanceweb_keydebug_oidc_parent_error"
|
||||
const _KeyLowerName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactionsimproved_performanceweb_keydebug_oidc_parent_errorterminate_single_v1_session"
|
||||
|
||||
func (i Key) String() string {
|
||||
if i < 0 || i >= Key(len(_KeyIndex)-1) {
|
||||
@ -34,9 +34,10 @@ func _KeyNoOp() {
|
||||
_ = x[KeyImprovedPerformance-(7)]
|
||||
_ = x[KeyWebKey-(8)]
|
||||
_ = x[KeyDebugOIDCParentError-(9)]
|
||||
_ = x[KeyOIDCSingleV1SessionTermination-(10)]
|
||||
}
|
||||
|
||||
var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyTriggerIntrospectionProjections, KeyLegacyIntrospection, KeyUserSchema, KeyTokenExchange, KeyActions, KeyImprovedPerformance, KeyWebKey, KeyDebugOIDCParentError}
|
||||
var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyTriggerIntrospectionProjections, KeyLegacyIntrospection, KeyUserSchema, KeyTokenExchange, KeyActions, KeyImprovedPerformance, KeyWebKey, KeyDebugOIDCParentError, KeyOIDCSingleV1SessionTermination}
|
||||
|
||||
var _KeyNameToValueMap = map[string]Key{
|
||||
_KeyName[0:11]: KeyUnspecified,
|
||||
@ -59,6 +60,8 @@ var _KeyNameToValueMap = map[string]Key{
|
||||
_KeyLowerName[133:140]: KeyWebKey,
|
||||
_KeyName[140:163]: KeyDebugOIDCParentError,
|
||||
_KeyLowerName[140:163]: KeyDebugOIDCParentError,
|
||||
_KeyName[163:190]: KeyOIDCSingleV1SessionTermination,
|
||||
_KeyLowerName[163:190]: KeyOIDCSingleV1SessionTermination,
|
||||
}
|
||||
|
||||
var _KeyNames = []string{
|
||||
@ -72,6 +75,7 @@ var _KeyNames = []string{
|
||||
_KeyName[113:133],
|
||||
_KeyName[133:140],
|
||||
_KeyName[140:163],
|
||||
_KeyName[163:190],
|
||||
}
|
||||
|
||||
// KeyString retrieves an enum value from the enum constants string name.
|
||||
|
@ -18,6 +18,7 @@ type InstanceFeatures struct {
|
||||
ImprovedPerformance FeatureSource[[]feature.ImprovedPerformanceType]
|
||||
WebKey FeatureSource[bool]
|
||||
DebugOIDCParentError FeatureSource[bool]
|
||||
OIDCSingleV1SessionTermination FeatureSource[bool]
|
||||
}
|
||||
|
||||
func (q *Queries) GetInstanceFeatures(ctx context.Context, cascade bool) (_ *InstanceFeatures, err error) {
|
||||
|
@ -69,6 +69,7 @@ func (m *InstanceFeaturesReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.InstanceImprovedPerformanceEventType,
|
||||
feature_v2.InstanceWebKeyEventType,
|
||||
feature_v2.InstanceDebugOIDCParentErrorEventType,
|
||||
feature_v2.InstanceOIDCSingleV1SessionTerminationEventType,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
@ -92,6 +93,7 @@ func (m *InstanceFeaturesReadModel) populateFromSystem() bool {
|
||||
m.instance.TokenExchange = m.system.TokenExchange
|
||||
m.instance.Actions = m.system.Actions
|
||||
m.instance.ImprovedPerformance = m.system.ImprovedPerformance
|
||||
m.instance.OIDCSingleV1SessionTermination = m.system.OIDCSingleV1SessionTermination
|
||||
return true
|
||||
}
|
||||
|
||||
@ -121,6 +123,8 @@ func reduceInstanceFeatureSet[T any](features *InstanceFeatures, event *feature_
|
||||
features.WebKey.set(level, event.Value)
|
||||
case feature.KeyDebugOIDCParentError:
|
||||
features.DebugOIDCParentError.set(level, event.Value)
|
||||
case feature.KeyOIDCSingleV1SessionTermination:
|
||||
features.OIDCSingleV1SessionTermination.set(level, event.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -96,6 +96,10 @@ func (*instanceFeatureProjection) Reducers() []handler.AggregateReducer {
|
||||
Event: feature_v2.InstanceDebugOIDCParentErrorEventType,
|
||||
Reduce: reduceInstanceSetFeature[bool],
|
||||
},
|
||||
{
|
||||
Event: feature_v2.InstanceOIDCSingleV1SessionTerminationEventType,
|
||||
Reduce: reduceInstanceSetFeature[bool],
|
||||
},
|
||||
{
|
||||
Event: instance.InstanceRemovedEventType,
|
||||
Reduce: reduceInstanceRemovedHelper(InstanceDomainInstanceIDCol),
|
||||
|
@ -27,6 +27,7 @@ type SystemFeatures struct {
|
||||
TokenExchange FeatureSource[bool]
|
||||
Actions FeatureSource[bool]
|
||||
ImprovedPerformance FeatureSource[[]feature.ImprovedPerformanceType]
|
||||
OIDCSingleV1SessionTermination FeatureSource[bool]
|
||||
}
|
||||
|
||||
func (q *Queries) GetSystemFeatures(ctx context.Context) (_ *SystemFeatures, err error) {
|
||||
|
@ -57,6 +57,7 @@ func (m *SystemFeaturesReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.SystemTokenExchangeEventType,
|
||||
feature_v2.SystemActionsEventType,
|
||||
feature_v2.SystemImprovedPerformanceEventType,
|
||||
feature_v2.SystemOIDCSingleV1SessionTerminationEventType,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
@ -88,6 +89,8 @@ func reduceSystemFeatureSet[T any](features *SystemFeatures, event *feature_v2.S
|
||||
features.Actions.set(level, event.Value)
|
||||
case feature.KeyImprovedPerformance:
|
||||
features.ImprovedPerformance.set(level, event.Value)
|
||||
case feature.KeyOIDCSingleV1SessionTermination:
|
||||
features.OIDCSingleV1SessionTermination.set(level, event.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ func init() {
|
||||
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, InstanceOIDCSingleV1SessionTerminationEventType, eventstore.GenericEventMapper[SetEvent[bool]])
|
||||
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, InstanceResetEventType, eventstore.GenericEventMapper[ResetEvent])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, InstanceLoginDefaultOrgEventType, eventstore.GenericEventMapper[SetEvent[bool]])
|
||||
@ -25,4 +26,5 @@ func init() {
|
||||
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]])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, InstanceOIDCSingleV1SessionTerminationEventType, eventstore.GenericEventMapper[SetEvent[bool]])
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ var (
|
||||
SystemTokenExchangeEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyTokenExchange)
|
||||
SystemActionsEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyActions)
|
||||
SystemImprovedPerformanceEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyImprovedPerformance)
|
||||
SystemOIDCSingleV1SessionTerminationEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyOIDCSingleV1SessionTermination)
|
||||
|
||||
InstanceResetEventType = resetEventTypeFromFeature(feature.LevelInstance)
|
||||
InstanceLoginDefaultOrgEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyLoginDefaultOrg)
|
||||
@ -30,6 +31,7 @@ var (
|
||||
InstanceImprovedPerformanceEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyImprovedPerformance)
|
||||
InstanceWebKeyEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyWebKey)
|
||||
InstanceDebugOIDCParentErrorEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyDebugOIDCParentError)
|
||||
InstanceOIDCSingleV1SessionTerminationEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyOIDCSingleV1SessionTermination)
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -0,0 +1,11 @@
|
||||
SELECT
|
||||
s.user_agent_id,
|
||||
s.user_id
|
||||
FROM auth.user_sessions s
|
||||
JOIN auth.user_sessions s2
|
||||
ON s.instance_id = s2.instance_id
|
||||
AND s.user_agent_id = s2.user_agent_id
|
||||
WHERE
|
||||
s2.id = $1
|
||||
AND s.instance_id = $2
|
||||
AND s.state = 0;
|
@ -63,6 +63,11 @@ type UserSessionView struct {
|
||||
ID sql.NullString `json:"id" gorm:"-"`
|
||||
}
|
||||
|
||||
type ActiveUserAgentUserIDs struct {
|
||||
UserAgentID string
|
||||
UserIDs []string
|
||||
}
|
||||
|
||||
type userAgentIDPayload struct {
|
||||
ID string `json:"userAgentID"`
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
SELECT
|
||||
s.user_agent_id
|
||||
FROM auth.user_sessions s
|
||||
WHERE
|
||||
s.id = $1
|
||||
AND s.instance_id = $2
|
||||
LIMIT 1;
|
@ -1,6 +1,7 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"errors"
|
||||
@ -16,8 +17,15 @@ var userSessionByIDQuery string
|
||||
//go:embed user_sessions_by_user_agent.sql
|
||||
var userSessionsByUserAgentQuery string
|
||||
|
||||
func UserSessionByIDs(db *database.DB, agentID, userID, instanceID string) (userSession *model.UserSessionView, err error) {
|
||||
err = db.QueryRow(
|
||||
//go:embed user_agent_by_user_session_id.sql
|
||||
var userAgentByUserSessionIDQuery string
|
||||
|
||||
//go:embed active_user_ids_by_session_id.sql
|
||||
var activeUserIDsBySessionIDQuery string
|
||||
|
||||
func UserSessionByIDs(ctx context.Context, db *database.DB, agentID, userID, instanceID string) (userSession *model.UserSessionView, err error) {
|
||||
err = db.QueryRowContext(
|
||||
ctx,
|
||||
func(row *sql.Row) error {
|
||||
userSession, err = scanUserSession(row)
|
||||
return err
|
||||
@ -29,8 +37,10 @@ func UserSessionByIDs(db *database.DB, agentID, userID, instanceID string) (user
|
||||
)
|
||||
return userSession, err
|
||||
}
|
||||
func UserSessionsByAgentID(db *database.DB, agentID, instanceID string) (userSessions []*model.UserSessionView, err error) {
|
||||
err = db.Query(
|
||||
|
||||
func UserSessionsByAgentID(ctx context.Context, db *database.DB, agentID, instanceID string) (userSessions []*model.UserSessionView, err error) {
|
||||
err = db.QueryContext(
|
||||
ctx,
|
||||
func(rows *sql.Rows) error {
|
||||
userSessions, err = scanUserSessions(rows)
|
||||
return err
|
||||
@ -42,6 +52,51 @@ func UserSessionsByAgentID(db *database.DB, agentID, instanceID string) (userSes
|
||||
return userSessions, err
|
||||
}
|
||||
|
||||
func UserAgentIDBySessionID(ctx context.Context, db *database.DB, sessionID, instanceID string) (userAgentID string, err error) {
|
||||
err = db.QueryRowContext(
|
||||
ctx,
|
||||
func(row *sql.Row) error {
|
||||
return row.Scan(&userAgentID)
|
||||
},
|
||||
userAgentByUserSessionIDQuery,
|
||||
sessionID,
|
||||
instanceID,
|
||||
)
|
||||
return userAgentID, err
|
||||
}
|
||||
|
||||
// ActiveUserIDsBySessionID returns all userIDs with an active session on the same user agent (its id is also returned) based on a sessionID
|
||||
func ActiveUserIDsBySessionID(ctx context.Context, db *database.DB, sessionID, instanceID string) (userAgentID string, userIDs []string, err error) {
|
||||
err = db.QueryContext(
|
||||
ctx,
|
||||
func(rows *sql.Rows) error {
|
||||
userAgentID, userIDs, err = scanActiveUserAgentUserIDs(rows)
|
||||
return err
|
||||
},
|
||||
activeUserIDsBySessionIDQuery,
|
||||
sessionID,
|
||||
instanceID,
|
||||
)
|
||||
return userAgentID, userIDs, err
|
||||
}
|
||||
|
||||
func scanActiveUserAgentUserIDs(rows *sql.Rows) (userAgentID string, userIDs []string, err error) {
|
||||
for rows.Next() {
|
||||
var userID string
|
||||
err := rows.Scan(
|
||||
&userAgentID,
|
||||
&userID)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
userIDs = append(userIDs, userID)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return "", nil, zerrors.ThrowInternal(err, "VIEW-Sbrws", "Errors.Query.CloseRows")
|
||||
}
|
||||
return userAgentID, userIDs, nil
|
||||
}
|
||||
|
||||
func scanUserSession(row *sql.Row) (*model.UserSessionView, error) {
|
||||
session := new(model.UserSessionView)
|
||||
err := row.Scan(
|
||||
|
@ -72,6 +72,13 @@ message SetInstanceFeaturesRequest{
|
||||
description: "Return parent errors to OIDC clients for debugging purposes. Parent errors may contain sensitive data or unwanted details about the system status of zitadel. Only enable if really needed.";
|
||||
}
|
||||
];
|
||||
|
||||
optional bool oidc_single_v1_session_termination = 10 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "true";
|
||||
description: "If the flag is enabled, you'll be able to terminate a single session from the login UI by providing an id_token with a `sid` claim as id_token_hint on the end_session endpoint. Note that currently all sessions from the same user agent (browser) are terminated in the login UI. Sessions managed through the Session API already allow the termination of single sessions.";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message SetInstanceFeaturesResponse {
|
||||
@ -157,4 +164,11 @@ message GetInstanceFeaturesResponse {
|
||||
description: "Return parent errors to OIDC clients for debugging purposes. Parent errors may contain sensitive data or unwanted details about the system status of zitadel. Only enable if really needed.";
|
||||
}
|
||||
];
|
||||
|
||||
FeatureFlag oidc_single_v1_session_termination = 11 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "true";
|
||||
description: "If the flag is enabled, you'll be able to terminate a single session from the login UI by providing an id_token with a `sid` claim as id_token_hint on the end_session endpoint. Note that currently all sessions from the same user agent (browser) are terminated in the login UI. Sessions managed through the Session API already allow the termination of single sessions.";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
@ -61,6 +61,13 @@ message SetSystemFeaturesRequest{
|
||||
description: "Improves performance of specified execution paths.";
|
||||
}
|
||||
];
|
||||
|
||||
optional bool oidc_single_v1_session_termination = 8 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "true";
|
||||
description: "If the flag is enabled, you'll be able to terminate a single session from the login UI by providing an id_token with a `sid` claim as id_token_hint on the end_session endpoint. Note that currently all sessions from the same user agent (browser) are terminated in the login UI. Sessions managed through the Session API already allow the termination of single sessions.";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message SetSystemFeaturesResponse {
|
||||
@ -125,4 +132,11 @@ message GetSystemFeaturesResponse {
|
||||
description: "Improves performance of specified execution paths.";
|
||||
}
|
||||
];
|
||||
|
||||
FeatureFlag oidc_single_v1_session_termination = 9 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "true";
|
||||
description: "If the flag is enabled, you'll be able to terminate a single session from the login UI by providing an id_token with a `sid` claim as id_token_hint on the end_session endpoint. Note that currently all sessions from the same user agent (browser) are terminated in the login UI. Sessions managed through the Session API already allow the termination of single sessions.";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
@ -72,6 +72,13 @@ message SetInstanceFeaturesRequest{
|
||||
description: "Return parent errors to OIDC clients for debugging purposes. Parent errors may contain sensitive data or unwanted details about the system status of zitadel. Only enable if really needed.";
|
||||
}
|
||||
];
|
||||
|
||||
optional bool oidc_single_v1_session_termination = 10 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "true";
|
||||
description: "If the flag is enabled, you'll be able to terminate a single session from the login UI by providing an id_token with a `sid` claim as id_token_hint on the end_session endpoint. Note that currently all sessions from the same user agent (browser) are terminated in the login UI. Sessions managed through the Session API already allow the termination of single sessions.";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message SetInstanceFeaturesResponse {
|
||||
@ -157,4 +164,11 @@ message GetInstanceFeaturesResponse {
|
||||
description: "Return parent errors to OIDC clients for debugging purposes. Parent errors may contain sensitive data or unwanted details about the system status of zitadel. Only enable if really needed.";
|
||||
}
|
||||
];
|
||||
|
||||
FeatureFlag oidc_single_v1_session_termination = 11 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "true";
|
||||
description: "If the flag is enabled, you'll be able to terminate a single session from the login UI by providing an id_token with a `sid` claim as id_token_hint on the end_session endpoint. Note that currently all sessions from the same user agent (browser) are terminated in the login UI. Sessions managed through the Session API already allow the termination of single sessions.";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
@ -61,6 +61,13 @@ message SetSystemFeaturesRequest{
|
||||
description: "Improves performance of specified execution paths.";
|
||||
}
|
||||
];
|
||||
|
||||
optional bool oidc_single_v1_session_termination = 8 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "true";
|
||||
description: "If the flag is enabled, you'll be able to terminate a single session from the login UI by providing an id_token with a `sid` claim as id_token_hint on the end_session endpoint. Note that currently all sessions from the same user agent (browser) are terminated in the login UI. Sessions managed through the Session API already allow the termination of single sessions.";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message SetSystemFeaturesResponse {
|
||||
@ -125,4 +132,11 @@ message GetSystemFeaturesResponse {
|
||||
description: "Improves performance of specified execution paths.";
|
||||
}
|
||||
];
|
||||
|
||||
FeatureFlag oidc_single_v1_session_termination = 9 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "true";
|
||||
description: "If the flag is enabled, you'll be able to terminate a single session from the login UI by providing an id_token with a `sid` claim as id_token_hint on the end_session endpoint. Note that currently all sessions from the same user agent (browser) are terminated in the login UI. Sessions managed through the Session API already allow the termination of single sessions.";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user