mirror of
https://github.com/zitadel/zitadel.git
synced 2025-07-19 13:38:34 +00:00
Merge branch 'next' into next-rc
This commit is contained in:
commit
a5f7dc8c30
10
README.md
10
README.md
@ -80,6 +80,11 @@ See all guides [here](https://zitadel.com/docs/self-hosting/deploy/overview)
|
|||||||
### Setup ZITADEL Cloud (SaaS)
|
### Setup ZITADEL Cloud (SaaS)
|
||||||
|
|
||||||
If you want to experience a hands-free ZITADEL, you should use [ZITADEL Cloud](https://zitadel.com).
|
If you want to experience a hands-free ZITADEL, you should use [ZITADEL Cloud](https://zitadel.com).
|
||||||
|
Available data regions are:
|
||||||
|
* 🇺🇸 United States
|
||||||
|
* 🇪🇺 European Union
|
||||||
|
* 🇦🇺 Australia
|
||||||
|
* 🇨🇭 Switzerland
|
||||||
|
|
||||||
ZITADEL Cloud comes with a free tier, providing you with all the same features as the open-source version.
|
ZITADEL Cloud comes with a free tier, providing you with all the same features as the open-source version.
|
||||||
Learn more about the [pay-as-you-go pricing](https://zitadel.com/pricing).
|
Learn more about the [pay-as-you-go pricing](https://zitadel.com/pricing).
|
||||||
@ -149,11 +154,12 @@ Deployment
|
|||||||
- [Zero Downtime Updates](https://zitadel.com/docs/concepts/architecture/solution#zero-downtime-updates)
|
- [Zero Downtime Updates](https://zitadel.com/docs/concepts/architecture/solution#zero-downtime-updates)
|
||||||
- [High scalability](https://zitadel.com/docs/self-hosting/manage/production)
|
- [High scalability](https://zitadel.com/docs/self-hosting/manage/production)
|
||||||
|
|
||||||
Track upcoming features on our [roadmap](https://zitadel.com/roadmap).
|
Track upcoming features on our [roadmap](https://zitadel.com/roadmap) and follow our [changelog](https://zitadel.com/changelog) for recent updates.
|
||||||
|
|
||||||
## How To Contribute
|
## How To Contribute
|
||||||
|
|
||||||
Find details about how you can contribute in our [Contribution Guide](./CONTRIBUTING.md)
|
Find details about how you can contribute in our [Contribution Guide](./CONTRIBUTING.md).
|
||||||
|
Join our [Discord Chat](https://zitadel.com/chat) to get help.
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
|
@ -3,8 +3,6 @@ package mirror
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/v2/projection"
|
"github.com/zitadel/zitadel/internal/v2/projection"
|
||||||
"github.com/zitadel/zitadel/internal/v2/readmodel"
|
"github.com/zitadel/zitadel/internal/v2/readmodel"
|
||||||
@ -32,12 +30,12 @@ func queryLastSuccessfulMigration(ctx context.Context, destinationES *eventstore
|
|||||||
return lastSuccess, nil
|
return lastSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeMigrationStart(ctx context.Context, sourceES *eventstore.EventStore, id string, destination string) (_ decimal.Decimal, err error) {
|
func writeMigrationStart(ctx context.Context, sourceES *eventstore.EventStore, id string, destination string) (_ float64, err error) {
|
||||||
var cmd *eventstore.Command
|
var cmd *eventstore.Command
|
||||||
if len(instanceIDs) > 0 {
|
if len(instanceIDs) > 0 {
|
||||||
cmd, err = mirror_event.NewStartedInstancesCommand(destination, instanceIDs)
|
cmd, err = mirror_event.NewStartedInstancesCommand(destination, instanceIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return decimal.Decimal{}, err
|
return 0, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cmd = mirror_event.NewStartedSystemCommand(destination)
|
cmd = mirror_event.NewStartedSystemCommand(destination)
|
||||||
@ -60,12 +58,12 @@ func writeMigrationStart(ctx context.Context, sourceES *eventstore.EventStore, i
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return decimal.Decimal{}, err
|
return 0, err
|
||||||
}
|
}
|
||||||
return position.Position, nil
|
return position.Position, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeMigrationSucceeded(ctx context.Context, destinationES *eventstore.EventStore, id, source string, position decimal.Decimal) error {
|
func writeMigrationSucceeded(ctx context.Context, destinationES *eventstore.EventStore, id, source string, position float64) error {
|
||||||
return destinationES.Push(
|
return destinationES.Push(
|
||||||
ctx,
|
ctx,
|
||||||
eventstore.NewPushIntent(
|
eventstore.NewPushIntent(
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/stdlib"
|
"github.com/jackc/pgx/v5/stdlib"
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
@ -181,7 +180,7 @@ func copyEvents(ctx context.Context, source, dest *db.DB, bulkSize uint32) {
|
|||||||
logging.WithFields("took", time.Since(start), "count", eventCount).Info("events migrated")
|
logging.WithFields("took", time.Since(start), "count", eventCount).Info("events migrated")
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeCopyEventsDone(ctx context.Context, es *eventstore.EventStore, id, source string, position decimal.Decimal, errs <-chan error) {
|
func writeCopyEventsDone(ctx context.Context, es *eventstore.EventStore, id, source string, position float64, errs <-chan error) {
|
||||||
joinedErrs := make([]error, 0, len(errs))
|
joinedErrs := make([]error, 0, len(errs))
|
||||||
for err := range errs {
|
for err := range errs {
|
||||||
joinedErrs = append(joinedErrs, err)
|
joinedErrs = append(joinedErrs, err)
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<th class="availability" mat-header-cell *matHeaderCellDef>
|
<th class="availability" mat-header-cell *matHeaderCellDef>
|
||||||
<span>{{ 'SMTP.LIST.ACTIVATED' | translate }}</span>
|
<span>{{ 'SMTP.LIST.ACTIVATED' | translate }}</span>
|
||||||
</th>
|
</th>
|
||||||
<td class="pointer availability" mat-cell *matCellDef="let config">
|
<td class="availability" [ngClass]="{ pointer: config.senderAddress }" mat-cell *matCellDef="let config">
|
||||||
<i
|
<i
|
||||||
matTooltip="{{ 'SMTP.LIST.ACTIVATED' | translate }}"
|
matTooltip="{{ 'SMTP.LIST.ACTIVATED' | translate }}"
|
||||||
*ngIf="isActive(config.state)"
|
*ngIf="isActive(config.state)"
|
||||||
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
<ng-container matColumnDef="description">
|
<ng-container matColumnDef="description">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'SETTING.SMTP.DESCRIPTION' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'SETTING.SMTP.DESCRIPTION' | translate }}</th>
|
||||||
<td class="pointer" mat-cell *matCellDef="let config">
|
<td [ngClass]="{ pointer: config.senderAddress }" mat-cell *matCellDef="let config">
|
||||||
<span>{{ config?.description }}</span>
|
<span>{{ config?.description }}</span>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@ -45,22 +45,22 @@
|
|||||||
<th class="availability" mat-header-cell *matHeaderCellDef>
|
<th class="availability" mat-header-cell *matHeaderCellDef>
|
||||||
<span>TLS</span>
|
<span>TLS</span>
|
||||||
</th>
|
</th>
|
||||||
<td class="pointer availability" mat-cell *matCellDef="let config">
|
<td class="availability" [ngClass]="{ pointer: config.senderAddress }" mat-cell *matCellDef="let config">
|
||||||
<i *ngIf="config.tls" class="las la-lock"></i>
|
<i *ngIf="config.tls" class="las la-lock"></i>
|
||||||
<i *ngIf="!config.tls" class="las la-unlock"></i>
|
<i *ngIf="config.senderAddress && !config.tls" class="las la-unlock"></i>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="host">
|
<ng-container matColumnDef="host">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'SETTING.SMTP.HOSTANDPORT' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'SETTING.SMTP.HOSTANDPORT' | translate }}</th>
|
||||||
<td class="pointer" mat-cell *matCellDef="let config">
|
<td [ngClass]="{ pointer: config.senderAddress }" mat-cell *matCellDef="let config">
|
||||||
<span>{{ config?.host }}</span>
|
<span>{{ config?.host }}</span>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="senderAddress">
|
<ng-container matColumnDef="senderAddress">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'SETTING.SMTP.SENDERADDRESS' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'SETTING.SMTP.SENDERADDRESS' | translate }}</th>
|
||||||
<td class="pointer" mat-cell *matCellDef="let config">
|
<td [ngClass]="{ pointer: config.senderAddress }" mat-cell *matCellDef="let config">
|
||||||
<span>{{ config?.senderAddress }}</span>
|
<span>{{ config?.senderAddress }}</span>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@ -95,7 +95,7 @@
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
actions
|
actions
|
||||||
[disabled]="(['iam.write'] | hasRole | async) === false"
|
[disabled]="(['iam.write'] | hasRole | async) === false || !config?.senderAddress"
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
color="primary"
|
color="primary"
|
||||||
matTooltip="{{ 'SMTP.LIST.TEST' | translate }}"
|
matTooltip="{{ 'SMTP.LIST.TEST' | translate }}"
|
||||||
|
@ -205,6 +205,9 @@ export class SMTPTableComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public navigateToProvider(row: SMTPConfig.AsObject) {
|
public navigateToProvider(row: SMTPConfig.AsObject) {
|
||||||
|
if (!row.senderAddress) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.router.navigate(this.routerLinkForRow(row));
|
this.router.navigate(this.routerLinkForRow(row));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,10 +154,25 @@
|
|||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="state">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.GRANT.STATE' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let grant">
|
||||||
|
<span
|
||||||
|
class="state"
|
||||||
|
[ngClass]="{
|
||||||
|
active: grant.state === UserGrantState.USER_GRANT_STATE_ACTIVE,
|
||||||
|
inactive: grant.state === UserGrantState.USER_GRANT_STATE_INACTIVE,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ 'USER.DATA.STATE' + grant.state | translate }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="actions" stickyEnd>
|
<ng-container matColumnDef="actions" stickyEnd>
|
||||||
<th mat-header-cell *matHeaderCellDef class="user-tr-actions"></th>
|
<th mat-header-cell *matHeaderCellDef class="user-tr-actions"></th>
|
||||||
<td mat-cell class="user-tr-actions" *matCellDef="let grant; let i = index">
|
<td mat-cell class="user-tr-actions" *matCellDef="let grant; let i = index">
|
||||||
<cnsl-table-actions [hasActions]="true">
|
<cnsl-table-actions [hasActions]="!context.includes('user')">
|
||||||
<button
|
<button
|
||||||
actions
|
actions
|
||||||
matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"
|
matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"
|
||||||
|
@ -8,7 +8,13 @@ import { tap } from 'rxjs/operators';
|
|||||||
import { enterAnimations } from 'src/app/animations';
|
import { enterAnimations } from 'src/app/animations';
|
||||||
import { UserGrant as AuthUserGrant } from 'src/app/proto/generated/zitadel/auth_pb';
|
import { UserGrant as AuthUserGrant } from 'src/app/proto/generated/zitadel/auth_pb';
|
||||||
import { Role } from 'src/app/proto/generated/zitadel/project_pb';
|
import { Role } from 'src/app/proto/generated/zitadel/project_pb';
|
||||||
import { Type, UserGrant as MgmtUserGrant, UserGrantQuery, UserGrant } from 'src/app/proto/generated/zitadel/user_pb';
|
import {
|
||||||
|
Type,
|
||||||
|
UserGrant as MgmtUserGrant,
|
||||||
|
UserGrant,
|
||||||
|
UserGrantQuery,
|
||||||
|
UserGrantState,
|
||||||
|
} from 'src/app/proto/generated/zitadel/user_pb';
|
||||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
@ -66,6 +72,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
|||||||
public UserGrantContext: any = UserGrantContext;
|
public UserGrantContext: any = UserGrantContext;
|
||||||
public Type: any = Type;
|
public Type: any = Type;
|
||||||
public ActionKeysType: any = ActionKeysType;
|
public ActionKeysType: any = ActionKeysType;
|
||||||
|
public UserGrantState: any = UserGrantState;
|
||||||
@Input() public type: Type | undefined = undefined;
|
@Input() public type: Type | undefined = undefined;
|
||||||
|
|
||||||
public filterOpen: boolean = false;
|
public filterOpen: boolean = false;
|
||||||
@ -86,6 +93,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
|||||||
'type',
|
'type',
|
||||||
'creationDate',
|
'creationDate',
|
||||||
'changeDate',
|
'changeDate',
|
||||||
|
'state',
|
||||||
'roleNamesList',
|
'roleNamesList',
|
||||||
'actions',
|
'actions',
|
||||||
];
|
];
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
'type',
|
'type',
|
||||||
'creationDate',
|
'creationDate',
|
||||||
'changeDate',
|
'changeDate',
|
||||||
|
'state',
|
||||||
'roleNamesList',
|
'roleNamesList',
|
||||||
'actions',
|
'actions',
|
||||||
]"
|
]"
|
||||||
|
@ -42,7 +42,16 @@
|
|||||||
[context]="UserGrantContext.GRANTED_PROJECT"
|
[context]="UserGrantContext.GRANTED_PROJECT"
|
||||||
[projectId]="projectId"
|
[projectId]="projectId"
|
||||||
[grantId]="grantId"
|
[grantId]="grantId"
|
||||||
[displayedColumns]="['select', 'user', 'projectId', 'creationDate', 'changeDate', 'roleNamesList', 'actions']"
|
[displayedColumns]="[
|
||||||
|
'select',
|
||||||
|
'user',
|
||||||
|
'projectId',
|
||||||
|
'creationDate',
|
||||||
|
'changeDate',
|
||||||
|
'state',
|
||||||
|
'roleNamesList',
|
||||||
|
'actions',
|
||||||
|
]"
|
||||||
[disableWrite]="(['user.grant.write$', 'user.grant.write:' + grantId] | hasRole | async) === false"
|
[disableWrite]="(['user.grant.write$', 'user.grant.write:' + grantId] | hasRole | async) === false"
|
||||||
[disableDelete]="(['user.grant.delete$', 'user.grant.delete:' + grantId] | hasRole | async) === false"
|
[disableDelete]="(['user.grant.delete$', 'user.grant.delete:' + grantId] | hasRole | async) === false"
|
||||||
[refreshOnPreviousRoutes]="['/grant-create/project/{{projectId}}/grant/{{grantId}}']"
|
[refreshOnPreviousRoutes]="['/grant-create/project/{{projectId}}/grant/{{grantId}}']"
|
||||||
|
@ -187,7 +187,7 @@
|
|||||||
<cnsl-user-grants
|
<cnsl-user-grants
|
||||||
[context]="UserGrantContext.OWNED_PROJECT"
|
[context]="UserGrantContext.OWNED_PROJECT"
|
||||||
[projectId]="projectId"
|
[projectId]="projectId"
|
||||||
[displayedColumns]="['select', 'user', 'creationDate', 'changeDate', 'roleNamesList', 'actions']"
|
[displayedColumns]="['select', 'user', 'creationDate', 'changeDate', 'state', 'roleNamesList', 'actions']"
|
||||||
[refreshOnPreviousRoutes]="['/grant-create/project/' + projectId]"
|
[refreshOnPreviousRoutes]="['/grant-create/project/' + projectId]"
|
||||||
[disableWrite]="(['user.grant.write$', 'user.grant.write:' + projectId] | hasRole | async) === false"
|
[disableWrite]="(['user.grant.write$', 'user.grant.write:' + projectId] | hasRole | async) === false"
|
||||||
[disableDelete]="(['user.grant.delete$', 'user.grant.delete:' + projectId] | hasRole | async) === false"
|
[disableDelete]="(['user.grant.delete$', 'user.grant.delete:' + projectId] | hasRole | async) === false"
|
||||||
|
@ -136,7 +136,16 @@
|
|||||||
<cnsl-user-grants
|
<cnsl-user-grants
|
||||||
[userId]="user.id"
|
[userId]="user.id"
|
||||||
[context]="USERGRANTCONTEXT"
|
[context]="USERGRANTCONTEXT"
|
||||||
[displayedColumns]="['org', 'projectId', 'type', 'creationDate', 'changeDate', 'roleNamesList', 'actions']"
|
[displayedColumns]="[
|
||||||
|
'org',
|
||||||
|
'projectId',
|
||||||
|
'type',
|
||||||
|
'creationDate',
|
||||||
|
'changeDate',
|
||||||
|
'state',
|
||||||
|
'roleNamesList',
|
||||||
|
'actions',
|
||||||
|
]"
|
||||||
[disableWrite]="(['user.grant.write$'] | hasRole | async) === false"
|
[disableWrite]="(['user.grant.write$'] | hasRole | async) === false"
|
||||||
[disableDelete]="(['user.grant.delete$'] | hasRole | async) === false"
|
[disableDelete]="(['user.grant.delete$'] | hasRole | async) === false"
|
||||||
>
|
>
|
||||||
|
@ -222,7 +222,7 @@
|
|||||||
<cnsl-user-grants
|
<cnsl-user-grants
|
||||||
[userId]="user.id"
|
[userId]="user.id"
|
||||||
[context]="USERGRANTCONTEXT"
|
[context]="USERGRANTCONTEXT"
|
||||||
[displayedColumns]="['select', 'projectId', 'creationDate', 'changeDate', 'roleNamesList', 'actions']"
|
[displayedColumns]="['select', 'projectId', 'creationDate', 'changeDate', 'state', 'roleNamesList', 'actions']"
|
||||||
[disableWrite]="(['user.grant.write$'] | hasRole | async) === false"
|
[disableWrite]="(['user.grant.write$'] | hasRole | async) === false"
|
||||||
[disableDelete]="(['user.grant.delete$'] | hasRole | async) === false"
|
[disableDelete]="(['user.grant.delete$'] | hasRole | async) === false"
|
||||||
>
|
>
|
||||||
|
@ -12,8 +12,6 @@ The typescript repository contains all TypeScript and JavaScript packages and ap
|
|||||||
- `@zitadel/proto`: Typescript implementation of Protocol Buffers, suitable for web browsers and Node.js.
|
- `@zitadel/proto`: Typescript implementation of Protocol Buffers, suitable for web browsers and Node.js.
|
||||||
- `@zitadel/client`: Core components for establishing a client connection
|
- `@zitadel/client`: Core components for establishing a client connection
|
||||||
- `@zitadel/node`: Core components for establishing a server connection
|
- `@zitadel/node`: Core components for establishing a server connection
|
||||||
- `@zitadel/react`: Shared React Utilities and components built with Tailwind CSS
|
|
||||||
- `@zitadel/next`: Shared Next.js Utilities
|
|
||||||
- `@zitadel/tsconfig`: shared `tsconfig.json`s used throughout the monorepo
|
- `@zitadel/tsconfig`: shared `tsconfig.json`s used throughout the monorepo
|
||||||
- `eslint-config-zitadel`: ESLint preset
|
- `eslint-config-zitadel`: ESLint preset
|
||||||
|
|
||||||
@ -47,10 +45,13 @@ It does so by redirecting to `/login`.
|
|||||||
The login is then able to load an [AuthRequest](/docs/apis/resources/oidc_service_v2/oidc-service-get-auth-request#get-oidc-auth-request-details).
|
The login is then able to load an [AuthRequest](/docs/apis/resources/oidc_service_v2/oidc-service-get-auth-request#get-oidc-auth-request-details).
|
||||||
|
|
||||||
The Auth Request defines how users proceed to authenticate. If no special prompts or scopes are set, the login brings up the `/loginname` page.
|
The Auth Request defines how users proceed to authenticate. If no special prompts or scopes are set, the login brings up the `/loginname` page.
|
||||||
The /loginname page allows to enter loginname or email of a user. User discovery is implemented at /api/loginname and if the user is found, they will be redirected to the available authentication method page.
|
The /loginname page allows to enter loginname or email of a user which is then used to search for a user.
|
||||||
Right after the user is found, a session is created and set as cookie.
|
If the user is found, a session is created and set as cookie, then the user is redirected to the available authentication method page.
|
||||||
This cookie is then hydrated with more information once the users continues.
|
While the users continues and provides more information, the cookie is hydrated with this information until a final state is reached.
|
||||||
|
|
||||||
|
The communication from the browser to the server is done by [NextJS Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations).
|
||||||
The OIDC Auth request is always passed in the url to have a context to the ongoing authentication flow.
|
The OIDC Auth request is always passed in the url to have a context to the ongoing authentication flow.
|
||||||
|
|
||||||
If enough user information is retrieved and the user is authenticated according to the policies, the flow is finalized by [requesting a the callback url](/docs/apis/resources/oidc_service_v2/oidc-service-create-callback) for the auth request and the user is redirected back to the application.
|
If enough user information is retrieved and the user is authenticated according to the policies, the flow is finalized by [requesting a the callback url](/docs/apis/resources/oidc_service_v2/oidc-service-create-callback) for the auth request and the user is redirected back to the application.
|
||||||
The application can then request a token calling the /token endpoint of the login which is proxied to the ZITADEL API.
|
The application can then request a token calling the /token endpoint of the login which is proxied to the ZITADEL API.
|
||||||
|
|
||||||
@ -91,9 +92,9 @@ The application can then request a token calling the /token endpoint of the logi
|
|||||||
- [x] Passkey
|
- [x] Passkey
|
||||||
- [x] IDPs
|
- [x] IDPs
|
||||||
- [x] Google
|
- [x] Google
|
||||||
- [ ] GitHub
|
- [x] GitHub
|
||||||
- [x] GitLab
|
- [x] GitLab
|
||||||
- [ ] Azure
|
- [x] Azure
|
||||||
- [ ] Apple
|
- [ ] Apple
|
||||||
- Register
|
- Register
|
||||||
- [x] Email Password
|
- [x] Email Password
|
||||||
@ -113,7 +114,7 @@ Authenticated users are directly able to self service their account.
|
|||||||
- [x] OTP via email
|
- [x] OTP via email
|
||||||
- [x] OTP via SMS
|
- [x] OTP via SMS
|
||||||
- [x] Setup Multifactor Passkey (U2F)
|
- [x] Setup Multifactor Passkey (U2F)
|
||||||
- [ ] Validate Account (email verification)
|
- [x] Validate Account (email verification)
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
@ -122,10 +123,18 @@ In order to run the new login app, make sure to follow the instructions in the [
|
|||||||
#### How to setup domains for OIDC
|
#### How to setup domains for OIDC
|
||||||
|
|
||||||
When setting up the new login app for OIDC, the domain must be registered on your instance and use https.
|
When setting up the new login app for OIDC, the domain must be registered on your instance and use https.
|
||||||
If you are using a self hosted instance, [install](/docs/apis/resources/system/system-service-add-domain) your domain on your instance using the system service.
|
To register your login domain on your instance, [add](/docs/apis/resources/admin/admin-service-add-instance-trusted-domain) your domain on your instances trusted domains.
|
||||||
|
|
||||||
If you want to use the login with your cloud instance, you can purchase your domain on zitadel.com, install it on your domain following [our guide](/docs/guides/manage/cloud/instances#add-custom-domain).
|
#### OIDC Proxy
|
||||||
After your domain has been verified, you can reconfigure your DNS settings in order to deploy the login on your own.
|
|
||||||
|
When setting up the new login app for OIDC, ensure it meets the following requirements:
|
||||||
|
|
||||||
|
- The OIDC Proxy is deployed and running on HTTPS
|
||||||
|
- The OIDC Proxy sets `x-zitadel-login-client` which is the user ID of the service account
|
||||||
|
- The OIDC Proxy sets `x-zitadel-public-host` which is the host, your login is deployed to `ex. login.example.com`.
|
||||||
|
- The OIDC Proxy sets `x-zitadel-instance-host` which is the host of your instance `ex. test-hdujwl.zitadel.cloud`.
|
||||||
|
|
||||||
|
You can review an example implementation of a middlware [here](https://github.com/zitadel/typescript/blob/main/apps/login/src/middleware.ts).
|
||||||
|
|
||||||
#### Deploy to Vercel
|
#### Deploy to Vercel
|
||||||
|
|
||||||
@ -135,5 +144,6 @@ To deploy your own version on Vercel, navigate to your instance and create a ser
|
|||||||
Copy its id from the overview and set it as `ZITADEL_SERVICE_USER_ID`.
|
Copy its id from the overview and set it as `ZITADEL_SERVICE_USER_ID`.
|
||||||
Then create a personal access token (PAT), copy and set it as `ZITADEL_SERVICE_USER_TOKEN`, then navigate to Default settings and make sure it gets `IAM_OWNER` permissions.
|
Then create a personal access token (PAT), copy and set it as `ZITADEL_SERVICE_USER_TOKEN`, then navigate to Default settings and make sure it gets `IAM_OWNER` permissions.
|
||||||
Finally set your instance url as `ZITADEL_API_URL`. Make sure to set it without trailing slash.
|
Finally set your instance url as `ZITADEL_API_URL`. Make sure to set it without trailing slash.
|
||||||
|
Also ensure your login domain is registered on your instance by adding it as a [trusted domain](/docs/apis/resources/admin/admin-service-add-instance-trusted-domain).
|
||||||
|
|
||||||

|

|
||||||
|
74
docs/docs/guides/manage/customize/notifications.md
Normal file
74
docs/docs/guides/manage/customize/notifications.md
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
title: SMS, SMTP and HTTP Provider for Notifications
|
||||||
|
---
|
||||||
|
|
||||||
|
All Notifications send as SMS and Email are customizable as that you can define your own providers,
|
||||||
|
which then send the notifications out. These providers can also be defined as an HTTP type,
|
||||||
|
and the text and content, which is used to send the SMS's and Emails will get send to a Webhook as JSON.
|
||||||
|
|
||||||
|
With this everything can be customized or even custom logic can be implemented to use a not yet supported provider by ZITADEL.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
There is a default provider configured in ZITADEL Cloud, both for SMS's and Emails, but this default providers can be changed through the respective API's.
|
||||||
|
|
||||||
|
This API's are provided on an instance level:
|
||||||
|
- [SMS Providers](/apis/resources/admin/sms-provider)
|
||||||
|
- [Email Providers](/apis/resources/admin/email-provider)
|
||||||
|
|
||||||
|
To use a non-default provider just add, and then activate. There can only be 1 provider be activated at the same time.
|
||||||
|
|
||||||
|
## Resulting messages
|
||||||
|
|
||||||
|
In case of the Twilio and SMTP providers, the messages will be sent as before, in case of the HTTP providers the content of the messages is the same but as a HTTP call.
|
||||||
|
|
||||||
|
Here an example of the body of an Email sent via HTTP provider:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"contextInfo": {
|
||||||
|
"eventType": "user.human.initialization.code.added",
|
||||||
|
"provider": {
|
||||||
|
"id": "285181292935381355",
|
||||||
|
"description": "test"
|
||||||
|
},
|
||||||
|
"recipientEmailAddress": "example@zitadel.com"
|
||||||
|
},
|
||||||
|
"templateData": {
|
||||||
|
"title": "Zitadel - Initialize User",
|
||||||
|
"preHeader": "Initialize User",
|
||||||
|
"subject": "Initialize User",
|
||||||
|
"greeting": "Hello GivenName FamilyName,",
|
||||||
|
"text": "This user was created in Zitadel. Use the username Username to login. Please click the button below to finish the initialization process. (Code 0M53RF) If you didn't ask for this mail, please ignore it.",
|
||||||
|
"url": "http://example.zitadel.com/ui/login/user/init?authRequestID=\u0026code=0M53RF\u0026loginname=Username\u0026orgID=275353657317327214\u0026passwordset=false\u0026userID=285181014567813483",
|
||||||
|
"buttonText": "Finish initialization",
|
||||||
|
"primaryColor": "#5469d4",
|
||||||
|
"backgroundColor": "#fafafa",
|
||||||
|
"fontColor": "#000000",
|
||||||
|
"fontFamily": "-apple-system, BlinkMacSystemFont, Segoe UI, Lato, Arial, Helvetica, sans-serif",
|
||||||
|
"footerText": "InitCode.Footer"
|
||||||
|
},
|
||||||
|
"args": {
|
||||||
|
"changeDate": "2024-09-16T10:58:50.73237+02:00",
|
||||||
|
"code": "0M53RF",
|
||||||
|
"creationDate": "2024-09-16T10:58:50.73237+02:00",
|
||||||
|
"displayName": "GivenName FamilyName",
|
||||||
|
"firstName": "GivenName",
|
||||||
|
"lastEmail": "example@zitadel.com",
|
||||||
|
"lastName": "FamilyName",
|
||||||
|
"lastPhone": "+41791234567",
|
||||||
|
"loginNames": [
|
||||||
|
"Username"
|
||||||
|
],
|
||||||
|
"nickName": "",
|
||||||
|
"preferredLoginName": "Username",
|
||||||
|
"userName": "Username",
|
||||||
|
"verifiedEmail": "example@zitadel.com",
|
||||||
|
"verifiedPhone": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
There are 3 elements to this message:
|
||||||
|
- contextInfo, with information on why this message is sent like the Event, which Email or SMS provider is used and which recipient should receive this message
|
||||||
|
- templateData, with all texts and format information which can be used with a template to produce the desired message
|
||||||
|
- args, with the information provided to the user which can be used in the message to customize
|
@ -0,0 +1,37 @@
|
|||||||
|
If you operate Zitadel behind a Reverse Proxy or Ingress inside a Kubernetes cluster,
|
||||||
|
you may encounter an Error like `upstream sent too big header while reading response header from upstream`
|
||||||
|
in your NGINX Logs and receive a 403 Error when accessing NGINX.
|
||||||
|
|
||||||
|
you can solve it by increasing the grpc buffer size in your nginx config:
|
||||||
|
|
||||||
|
### Ingress NGINX
|
||||||
|
```yaml
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/modsecurity-snippet: |
|
||||||
|
SecRuleRemoveById 949110
|
||||||
|
nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
|
||||||
|
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||||
|
grpc_set_header Host $host;
|
||||||
|
more_clear_input_headers "Host" "X-Forwarded-Host";
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Forwarded-Host $http_x_forwarded_host;
|
||||||
|
# highlight-next-line
|
||||||
|
nginx.ingress.kubernetes.io/server-snippet: "grpc_buffer_size 8k;"
|
||||||
|
```
|
||||||
|
### NGINX Config
|
||||||
|
```nginx
|
||||||
|
http {
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
http2 on;
|
||||||
|
location / {
|
||||||
|
grpc_pass grpc://zitadel-disabled-tls:8080;
|
||||||
|
grpc_set_header Host $host:$server_port;
|
||||||
|
# highlight-next-line
|
||||||
|
grpc_buffer_size 8k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
@ -3,8 +3,14 @@ title: Troubleshoot ZITADEL
|
|||||||
---
|
---
|
||||||
|
|
||||||
import InstanceNotFound from '/docs/self-hosting/deploy/troubleshooting/_instance_not_found.mdx';
|
import InstanceNotFound from '/docs/self-hosting/deploy/troubleshooting/_instance_not_found.mdx';
|
||||||
|
import UpstreamHeader from '/docs/self-hosting/deploy/troubleshooting/_upstream_header.mdx'
|
||||||
|
|
||||||
## Instance not found
|
## Instance not found
|
||||||
|
|
||||||
|
|
||||||
<InstanceNotFound/>
|
<InstanceNotFound/>
|
||||||
|
|
||||||
|
|
||||||
|
## upstream sent too big header while reading response header from upstream
|
||||||
|
|
||||||
|
<UpstreamHeader/>
|
@ -158,6 +158,7 @@ module.exports = {
|
|||||||
"guides/manage/customize/texts",
|
"guides/manage/customize/texts",
|
||||||
"guides/manage/customize/behavior",
|
"guides/manage/customize/behavior",
|
||||||
"guides/manage/customize/restrictions",
|
"guides/manage/customize/restrictions",
|
||||||
|
"guides/manage/customize/notifications",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
BIN
docs/static/img/typescript-login-architecture.png
vendored
BIN
docs/static/img/typescript-login-architecture.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 69 KiB |
2
go.mod
2
go.mod
@ -38,7 +38,6 @@ require (
|
|||||||
github.com/h2non/gock v1.2.0
|
github.com/h2non/gock v1.2.0
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||||
github.com/improbable-eng/grpc-web v0.15.0
|
github.com/improbable-eng/grpc-web v0.15.0
|
||||||
github.com/jackc/pgx-shopspring-decimal v0.0.0-20220624020537-1d36b5a1853e
|
|
||||||
github.com/jackc/pgx/v5 v5.6.0
|
github.com/jackc/pgx/v5 v5.6.0
|
||||||
github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52
|
github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52
|
||||||
github.com/jinzhu/gorm v1.9.16
|
github.com/jinzhu/gorm v1.9.16
|
||||||
@ -55,7 +54,6 @@ require (
|
|||||||
github.com/rakyll/statik v0.1.7
|
github.com/rakyll/statik v0.1.7
|
||||||
github.com/rs/cors v1.11.0
|
github.com/rs/cors v1.11.0
|
||||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
|
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
|
||||||
github.com/shopspring/decimal v1.4.0
|
|
||||||
github.com/sony/sonyflake v1.2.0
|
github.com/sony/sonyflake v1.2.0
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.19.0
|
||||||
|
4
go.sum
4
go.sum
@ -405,8 +405,6 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
|||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
|
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
github.com/jackc/pgx-shopspring-decimal v0.0.0-20220624020537-1d36b5a1853e h1:i3gQ/Zo7sk4LUVbsAjTNeC4gIjoPNIZVzs4EXstssV4=
|
|
||||||
github.com/jackc/pgx-shopspring-decimal v0.0.0-20220624020537-1d36b5a1853e/go.mod h1:zUHglCZ4mpDUPgIwqEKoba6+tcUQzRdb1+DPTuYe9pI=
|
|
||||||
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||||
@ -651,8 +649,6 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0
|
|||||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
|
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
|
||||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
|
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
|
||||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
@ -76,7 +76,7 @@ func (s *SystemTokenVerifierFromConfig) VerifySystemToken(ctx context.Context, t
|
|||||||
|
|
||||||
type systemJWTStorage struct {
|
type systemJWTStorage struct {
|
||||||
keys map[string]*SystemAPIUser
|
keys map[string]*SystemAPIUser
|
||||||
mutex sync.Mutex
|
mutex sync.RWMutex
|
||||||
cachedKeys map[string]*rsa.PublicKey
|
cachedKeys map[string]*rsa.PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +98,9 @@ func (s *SystemAPIUser) readKey() (*rsa.PublicKey, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *systemJWTStorage) GetKeyByIDAndClientID(_ context.Context, _, userID string) (*jose.JSONWebKey, error) {
|
func (s *systemJWTStorage) GetKeyByIDAndClientID(_ context.Context, _, userID string) (*jose.JSONWebKey, error) {
|
||||||
|
s.mutex.RLock()
|
||||||
cachedKey, ok := s.cachedKeys[userID]
|
cachedKey, ok := s.cachedKeys[userID]
|
||||||
|
s.mutex.RUnlock()
|
||||||
if ok {
|
if ok {
|
||||||
return &jose.JSONWebKey{KeyID: userID, Key: cachedKey}, nil
|
return &jose.JSONWebKey{KeyID: userID, Key: cachedKey}, nil
|
||||||
}
|
}
|
||||||
|
@ -20,17 +20,20 @@ func listSMTPConfigsToModel(req *admin_pb.ListSMTPConfigsRequest) (*query.SMTPCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SMTPConfigToProviderPb(config *query.SMTPConfig) *settings_pb.SMTPConfig {
|
func SMTPConfigToProviderPb(config *query.SMTPConfig) *settings_pb.SMTPConfig {
|
||||||
return &settings_pb.SMTPConfig{
|
ret := &settings_pb.SMTPConfig{
|
||||||
Details: object.ToViewDetailsPb(config.Sequence, config.CreationDate, config.ChangeDate, config.ResourceOwner),
|
Details: object.ToViewDetailsPb(config.Sequence, config.CreationDate, config.ChangeDate, config.ResourceOwner),
|
||||||
Id: config.ID,
|
Id: config.ID,
|
||||||
Description: config.Description,
|
Description: config.Description,
|
||||||
Tls: config.SMTPConfig.TLS,
|
State: SMTPConfigStateToPb(config.State),
|
||||||
Host: config.SMTPConfig.Host,
|
|
||||||
User: config.SMTPConfig.User,
|
|
||||||
State: SMTPConfigStateToPb(config.State),
|
|
||||||
SenderAddress: config.SMTPConfig.SenderAddress,
|
|
||||||
SenderName: config.SMTPConfig.SenderName,
|
|
||||||
}
|
}
|
||||||
|
if config.SMTPConfig != nil {
|
||||||
|
ret.Tls = config.SMTPConfig.TLS
|
||||||
|
ret.Host = config.SMTPConfig.Host
|
||||||
|
ret.User = config.SMTPConfig.User
|
||||||
|
ret.SenderAddress = config.SMTPConfig.SenderAddress
|
||||||
|
ret.SenderName = config.SMTPConfig.SenderName
|
||||||
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func SMTPConfigsToPb(configs []*query.SMTPConfig) []*settings_pb.SMTPConfig {
|
func SMTPConfigsToPb(configs []*query.SMTPConfig) []*settings_pb.SMTPConfig {
|
||||||
|
@ -55,5 +55,6 @@ func UserGrantToPb(grant *query.UserGrant) *auth_pb.UserGrant {
|
|||||||
ProjectGrantId: grant.GrantID,
|
ProjectGrantId: grant.GrantID,
|
||||||
RoleKeys: grant.Roles,
|
RoleKeys: grant.Roles,
|
||||||
UserType: user.TypeToPb(grant.UserType),
|
UserType: user.TypeToPb(grant.UserType),
|
||||||
|
State: user.UserGrantStateToPb(grant.State),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ func UserGrantToPb(assetPrefix string, grant *query.UserGrant) *user_pb.UserGran
|
|||||||
return &user_pb.UserGrant{
|
return &user_pb.UserGrant{
|
||||||
Id: grant.ID,
|
Id: grant.ID,
|
||||||
UserId: grant.UserID,
|
UserId: grant.UserID,
|
||||||
State: user_pb.UserGrantState_USER_GRANT_STATE_ACTIVE,
|
State: UserGrantStateToPb(grant.State),
|
||||||
RoleKeys: grant.Roles,
|
RoleKeys: grant.Roles,
|
||||||
ProjectId: grant.ProjectID,
|
ProjectId: grant.ProjectID,
|
||||||
OrgId: grant.ResourceOwner,
|
OrgId: grant.ResourceOwner,
|
||||||
@ -51,6 +51,21 @@ func UserGrantToPb(assetPrefix string, grant *query.UserGrant) *user_pb.UserGran
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UserGrantStateToPb(state domain.UserGrantState) user_pb.UserGrantState {
|
||||||
|
switch state {
|
||||||
|
case domain.UserGrantStateActive:
|
||||||
|
return user_pb.UserGrantState_USER_GRANT_STATE_ACTIVE
|
||||||
|
case domain.UserGrantStateInactive:
|
||||||
|
return user_pb.UserGrantState_USER_GRANT_STATE_INACTIVE
|
||||||
|
case domain.UserGrantStateRemoved,
|
||||||
|
domain.UserGrantStateUnspecified:
|
||||||
|
// these states should never occur here and are mainly listed for linting purposes
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
return user_pb.UserGrantState_USER_GRANT_STATE_UNSPECIFIED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func UserGrantQueriesToQuery(ctx context.Context, queries []*user_pb.UserGrantQuery) (q []query.SearchQuery, err error) {
|
func UserGrantQueriesToQuery(ctx context.Context, queries []*user_pb.UserGrantQuery) (q []query.SearchQuery, err error) {
|
||||||
q = make([]query.SearchQuery, len(queries))
|
q = make([]query.SearchQuery, len(queries))
|
||||||
for i, query := range queries {
|
for i, query := range queries {
|
||||||
|
@ -50,13 +50,10 @@ func (o *OPStorage) GetClientByClientID(ctx context.Context, id string) (_ op.Cl
|
|||||||
err = oidcError(err)
|
err = oidcError(err)
|
||||||
span.EndWithError(err)
|
span.EndWithError(err)
|
||||||
}()
|
}()
|
||||||
client, err := o.query.GetOIDCClientByID(ctx, id, false)
|
client, err := o.query.ActiveOIDCClientByID(ctx, id, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if client.State != domain.AppStateActive {
|
|
||||||
return nil, zerrors.ThrowPreconditionFailed(nil, "OIDC-sdaGg", "client is not active")
|
|
||||||
}
|
|
||||||
return ClientFromBusiness(client, o.defaultLoginURL, o.defaultLoginURLV2), nil
|
return ClientFromBusiness(client, o.defaultLoginURL, o.defaultLoginURLV2), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,6 +330,9 @@ func (o *OPStorage) setUserinfo(ctx context.Context, userInfo *oidc.UserInfo, us
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if user.State != domain.UserStateActive {
|
||||||
|
return zerrors.ThrowUnauthenticated(nil, "OIDC-S3tha", "Errors.Users.NotActive")
|
||||||
|
}
|
||||||
var allRoles bool
|
var allRoles bool
|
||||||
roles := make([]string, 0)
|
roles := make([]string, 0)
|
||||||
for _, scope := range scopes {
|
for _, scope := range scopes {
|
||||||
@ -802,19 +802,24 @@ func (o *OPStorage) assertRoles(ctx context.Context, userID, applicationID strin
|
|||||||
if projectID != "" {
|
if projectID != "" {
|
||||||
roleAudience = append(roleAudience, projectID)
|
roleAudience = append(roleAudience, projectID)
|
||||||
}
|
}
|
||||||
queries := make([]query.SearchQuery, 0, 2)
|
|
||||||
projectQuery, err := query.NewUserGrantProjectIDsSearchQuery(roleAudience)
|
projectQuery, err := query.NewUserGrantProjectIDsSearchQuery(roleAudience)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
queries = append(queries, projectQuery)
|
|
||||||
userIDQuery, err := query.NewUserGrantUserIDSearchQuery(userID)
|
userIDQuery, err := query.NewUserGrantUserIDSearchQuery(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
queries = append(queries, userIDQuery)
|
activeQuery, err := query.NewUserGrantStateQuery(domain.UserGrantStateActive)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
grants, err := o.query.UserGrants(ctx, &query.UserGrantsQueries{
|
grants, err := o.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||||
Queries: queries,
|
Queries: []query.SearchQuery{
|
||||||
|
projectQuery,
|
||||||
|
userIDQuery,
|
||||||
|
activeQuery,
|
||||||
|
},
|
||||||
}, true)
|
}, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@ -979,16 +984,13 @@ func (s *Server) VerifyClient(ctx context.Context, r *op.Request[op.ClientCreden
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
client, err := s.query.GetOIDCClientByID(ctx, clientID, assertion)
|
client, err := s.query.ActiveOIDCClientByID(ctx, clientID, assertion)
|
||||||
if zerrors.IsNotFound(err) {
|
if zerrors.IsNotFound(err) {
|
||||||
return nil, oidc.ErrInvalidClient().WithParent(err).WithReturnParentToClient(authz.GetFeatures(ctx).DebugOIDCParentError).WithDescription("client not found")
|
return nil, oidc.ErrInvalidClient().WithParent(err).WithReturnParentToClient(authz.GetFeatures(ctx).DebugOIDCParentError).WithDescription("no active client not found")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err // defaults to server error
|
return nil, err // defaults to server error
|
||||||
}
|
}
|
||||||
if client.State != domain.AppStateActive {
|
|
||||||
return nil, oidc.ErrInvalidClient().WithDescription("client is not active")
|
|
||||||
}
|
|
||||||
if client.Settings == nil {
|
if client.Settings == nil {
|
||||||
client.Settings = &query.OIDCSettings{
|
client.Settings = &query.OIDCSettings{
|
||||||
AccessTokenLifetime: s.defaultAccessTokenLifetime,
|
AccessTokenLifetime: s.defaultAccessTokenLifetime,
|
||||||
|
@ -196,9 +196,13 @@ func TestServer_VerifyClient(t *testing.T) {
|
|||||||
sessionID, sessionToken, startTime, changeTime := Instance.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
sessionID, sessionToken, startTime, changeTime := Instance.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||||
project, err := Instance.CreateProject(CTX)
|
project, err := Instance.CreateProject(CTX)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
projectInactive, err := Instance.CreateProject(CTX)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
inactiveClient, err := Instance.CreateOIDCInactivateClient(CTX, redirectURI, logoutRedirectURI, project.GetId())
|
inactiveClient, err := Instance.CreateOIDCInactivateClient(CTX, redirectURI, logoutRedirectURI, project.GetId())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
inactiveProjectClient, err := Instance.CreateOIDCInactivateProjectClient(CTX, redirectURI, logoutRedirectURI, projectInactive.GetId())
|
||||||
|
require.NoError(t, err)
|
||||||
nativeClient, err := Instance.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId(), false)
|
nativeClient, err := Instance.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId(), false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
basicWebClient, err := Instance.CreateOIDCWebClientBasic(CTX, redirectURI, logoutRedirectURI, project.GetId())
|
basicWebClient, err := Instance.CreateOIDCWebClientBasic(CTX, redirectURI, logoutRedirectURI, project.GetId())
|
||||||
@ -240,6 +244,14 @@ func TestServer_VerifyClient(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "client inactive (project) error",
|
||||||
|
client: clientDetails{
|
||||||
|
authReqClientID: nativeClient.GetClientId(),
|
||||||
|
clientID: inactiveProjectClient.GetClientId(),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "native client success",
|
name: "native client success",
|
||||||
client: clientDetails{
|
client: clientDetails{
|
||||||
|
@ -24,6 +24,9 @@ func TestServer_ClientCredentialsExchange(t *testing.T) {
|
|||||||
machine, name, clientID, clientSecret, err := Instance.CreateOIDCCredentialsClient(CTX)
|
machine, name, clientID, clientSecret, err := Instance.CreateOIDCCredentialsClient(CTX)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, _, clientIDInactive, clientSecretInactive, err := Instance.CreateOIDCCredentialsClientInactive(CTX)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
type claims struct {
|
type claims struct {
|
||||||
name string
|
name string
|
||||||
username string
|
username string
|
||||||
@ -71,6 +74,13 @@ func TestServer_ClientCredentialsExchange(t *testing.T) {
|
|||||||
scope: []string{oidc.ScopeOpenID},
|
scope: []string{oidc.ScopeOpenID},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "inactive machine user error",
|
||||||
|
clientID: clientIDInactive,
|
||||||
|
clientSecret: clientSecretInactive,
|
||||||
|
scope: []string{oidc.ScopeOpenID},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "wrong secret error",
|
name: "wrong secret error",
|
||||||
clientID: clientID,
|
clientID: clientID,
|
||||||
|
@ -213,7 +213,7 @@ func (s *Server) clientFromCredentials(ctx context.Context, cc *op.ClientCredent
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
client, err = s.query.GetIntrospectionClientByID(ctx, clientID, assertion)
|
client, err = s.query.ActiveIntrospectionClientByID(ctx, clientID, assertion)
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, oidc.ErrUnauthorizedClient().WithParent(err).WithReturnParentToClient(authz.GetFeatures(ctx).DebugOIDCParentError)
|
return nil, oidc.ErrUnauthorizedClient().WithParent(err).WithReturnParentToClient(authz.GetFeatures(ctx).DebugOIDCParentError)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/go-jose/go-jose/v4"
|
"github.com/go-jose/go-jose/v4"
|
||||||
"github.com/jonboulle/clockwork"
|
"github.com/jonboulle/clockwork"
|
||||||
"github.com/muhlemmer/gu"
|
"github.com/muhlemmer/gu"
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
"github.com/zitadel/oidc/v3/pkg/op"
|
"github.com/zitadel/oidc/v3/pkg/op"
|
||||||
|
|
||||||
@ -351,14 +350,14 @@ func (o *OPStorage) getSigningKey(ctx context.Context) (op.SigningKey, error) {
|
|||||||
if len(keys.Keys) > 0 {
|
if len(keys.Keys) > 0 {
|
||||||
return o.privateKeyToSigningKey(selectSigningKey(keys.Keys))
|
return o.privateKeyToSigningKey(selectSigningKey(keys.Keys))
|
||||||
}
|
}
|
||||||
var position decimal.Decimal
|
var position float64
|
||||||
if keys.State != nil {
|
if keys.State != nil {
|
||||||
position = keys.State.Position
|
position = keys.State.Position
|
||||||
}
|
}
|
||||||
return nil, o.refreshSigningKey(ctx, o.signingKeyAlgorithm, position)
|
return nil, o.refreshSigningKey(ctx, o.signingKeyAlgorithm, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OPStorage) refreshSigningKey(ctx context.Context, algorithm string, position decimal.Decimal) error {
|
func (o *OPStorage) refreshSigningKey(ctx context.Context, algorithm string, position float64) error {
|
||||||
ok, err := o.ensureIsLatestKey(ctx, position)
|
ok, err := o.ensureIsLatestKey(ctx, position)
|
||||||
if err != nil || !ok {
|
if err != nil || !ok {
|
||||||
return zerrors.ThrowInternal(err, "OIDC-ASfh3", "cannot ensure that projection is up to date")
|
return zerrors.ThrowInternal(err, "OIDC-ASfh3", "cannot ensure that projection is up to date")
|
||||||
@ -370,12 +369,12 @@ func (o *OPStorage) refreshSigningKey(ctx context.Context, algorithm string, pos
|
|||||||
return zerrors.ThrowInternal(nil, "OIDC-Df1bh", "")
|
return zerrors.ThrowInternal(nil, "OIDC-Df1bh", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OPStorage) ensureIsLatestKey(ctx context.Context, position decimal.Decimal) (bool, error) {
|
func (o *OPStorage) ensureIsLatestKey(ctx context.Context, position float64) (bool, error) {
|
||||||
maxSequence, err := o.getMaxKeySequence(ctx)
|
maxSequence, err := o.getMaxKeySequence(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("error retrieving new events: %w", err)
|
return false, fmt.Errorf("error retrieving new events: %w", err)
|
||||||
}
|
}
|
||||||
return position.GreaterThanOrEqual(maxSequence), nil
|
return position >= maxSequence, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OPStorage) privateKeyToSigningKey(key query.PrivateKey) (_ op.SigningKey, err error) {
|
func (o *OPStorage) privateKeyToSigningKey(key query.PrivateKey) (_ op.SigningKey, err error) {
|
||||||
@ -413,9 +412,9 @@ func (o *OPStorage) lockAndGenerateSigningKeyPair(ctx context.Context, algorithm
|
|||||||
return o.command.GenerateSigningKeyPair(setOIDCCtx(ctx), algorithm)
|
return o.command.GenerateSigningKeyPair(setOIDCCtx(ctx), algorithm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OPStorage) getMaxKeySequence(ctx context.Context) (decimal.Decimal, error) {
|
func (o *OPStorage) getMaxKeySequence(ctx context.Context) (float64, error) {
|
||||||
return o.eventstore.LatestPosition(ctx,
|
return o.eventstore.LatestSequence(ctx,
|
||||||
eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxPosition).
|
eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxSequence).
|
||||||
ResourceOwner(authz.GetInstance(ctx).InstanceID()).
|
ResourceOwner(authz.GetInstance(ctx).InstanceID()).
|
||||||
AwaitOpenTransactions().
|
AwaitOpenTransactions().
|
||||||
AllowTimeTravel().
|
AllowTimeTravel().
|
||||||
|
@ -66,7 +66,10 @@ func (s *Server) UserInfo(ctx context.Context, r *op.Request[oidc.UserInfoReques
|
|||||||
false,
|
false,
|
||||||
)(ctx, true, domain.TriggerTypePreUserinfoCreation)
|
)(ctx, true, domain.TriggerTypePreUserinfoCreation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
if !zerrors.IsNotFound(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, op.NewStatusError(oidc.ErrAccessDenied().WithDescription("no active user").WithParent(err).WithReturnParentToClient(authz.GetFeatures(ctx).DebugOIDCParentError), http.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
return op.NewResponse(userInfo), nil
|
return op.NewResponse(userInfo), nil
|
||||||
}
|
}
|
||||||
@ -163,11 +166,11 @@ func prepareRoles(ctx context.Context, scope []string, projectID string, project
|
|||||||
}
|
}
|
||||||
|
|
||||||
func userInfoToOIDC(user *query.OIDCUserInfo, userInfoAssertion bool, scope []string, assetPrefix string) *oidc.UserInfo {
|
func userInfoToOIDC(user *query.OIDCUserInfo, userInfoAssertion bool, scope []string, assetPrefix string) *oidc.UserInfo {
|
||||||
out := new(oidc.UserInfo)
|
out := &oidc.UserInfo{
|
||||||
|
Subject: user.User.ID,
|
||||||
|
}
|
||||||
for _, s := range scope {
|
for _, s := range scope {
|
||||||
switch s {
|
switch s {
|
||||||
case oidc.ScopeOpenID:
|
|
||||||
out.Subject = user.User.ID
|
|
||||||
case oidc.ScopeEmail:
|
case oidc.ScopeEmail:
|
||||||
if !userInfoAssertion {
|
if !userInfoAssertion {
|
||||||
continue
|
continue
|
||||||
|
@ -280,14 +280,18 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
user: humanUserInfo,
|
user: humanUserInfo,
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{},
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "human1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "machine, empty",
|
name: "machine, empty",
|
||||||
args: args{
|
args: args{
|
||||||
user: machineUserInfo,
|
user: machineUserInfo,
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{},
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "machine1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "human, scope openid",
|
name: "human, scope openid",
|
||||||
@ -317,6 +321,7 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
scope: []string{oidc.ScopeEmail},
|
scope: []string{oidc.ScopeEmail},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "human1",
|
||||||
UserInfoEmail: oidc.UserInfoEmail{
|
UserInfoEmail: oidc.UserInfoEmail{
|
||||||
Email: "foo@bar.com",
|
Email: "foo@bar.com",
|
||||||
EmailVerified: true,
|
EmailVerified: true,
|
||||||
@ -329,7 +334,9 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
user: humanUserInfo,
|
user: humanUserInfo,
|
||||||
scope: []string{oidc.ScopeEmail},
|
scope: []string{oidc.ScopeEmail},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{},
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "human1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "machine, scope email, profileInfoAssertion",
|
name: "machine, scope email, profileInfoAssertion",
|
||||||
@ -338,7 +345,7 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
scope: []string{oidc.ScopeEmail},
|
scope: []string{oidc.ScopeEmail},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{
|
want: &oidc.UserInfo{
|
||||||
UserInfoEmail: oidc.UserInfoEmail{},
|
Subject: "machine1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -349,6 +356,7 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
scope: []string{oidc.ScopeProfile},
|
scope: []string{oidc.ScopeProfile},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "human1",
|
||||||
UserInfoProfile: oidc.UserInfoProfile{
|
UserInfoProfile: oidc.UserInfoProfile{
|
||||||
Name: "xxx",
|
Name: "xxx",
|
||||||
GivenName: "user",
|
GivenName: "user",
|
||||||
@ -370,6 +378,7 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
scope: []string{oidc.ScopeProfile},
|
scope: []string{oidc.ScopeProfile},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "machine1",
|
||||||
UserInfoProfile: oidc.UserInfoProfile{
|
UserInfoProfile: oidc.UserInfoProfile{
|
||||||
Name: "machine",
|
Name: "machine",
|
||||||
UpdatedAt: oidc.FromTime(time.Unix(567, 890)),
|
UpdatedAt: oidc.FromTime(time.Unix(567, 890)),
|
||||||
@ -383,7 +392,9 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
user: machineUserInfo,
|
user: machineUserInfo,
|
||||||
scope: []string{oidc.ScopeProfile},
|
scope: []string{oidc.ScopeProfile},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{},
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "machine1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "human, scope phone, profileInfoAssertion",
|
name: "human, scope phone, profileInfoAssertion",
|
||||||
@ -393,6 +404,7 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
scope: []string{oidc.ScopePhone},
|
scope: []string{oidc.ScopePhone},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "human1",
|
||||||
UserInfoPhone: oidc.UserInfoPhone{
|
UserInfoPhone: oidc.UserInfoPhone{
|
||||||
PhoneNumber: "+31123456789",
|
PhoneNumber: "+31123456789",
|
||||||
PhoneNumberVerified: true,
|
PhoneNumberVerified: true,
|
||||||
@ -405,7 +417,9 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
user: humanUserInfo,
|
user: humanUserInfo,
|
||||||
scope: []string{oidc.ScopePhone},
|
scope: []string{oidc.ScopePhone},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{},
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "human1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "machine, scope phone",
|
name: "machine, scope phone",
|
||||||
@ -414,6 +428,7 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
scope: []string{oidc.ScopePhone},
|
scope: []string{oidc.ScopePhone},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "machine1",
|
||||||
UserInfoPhone: oidc.UserInfoPhone{},
|
UserInfoPhone: oidc.UserInfoPhone{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -424,6 +439,8 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
scope: []string{ScopeUserMetaData},
|
scope: []string{ScopeUserMetaData},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "human1",
|
||||||
|
UserInfoEmail: oidc.UserInfoEmail{},
|
||||||
Claims: map[string]any{
|
Claims: map[string]any{
|
||||||
ClaimUserMetaData: map[string]string{
|
ClaimUserMetaData: map[string]string{
|
||||||
"key1": base64.RawURLEncoding.EncodeToString([]byte{1, 2, 3}),
|
"key1": base64.RawURLEncoding.EncodeToString([]byte{1, 2, 3}),
|
||||||
@ -438,7 +455,9 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
user: machineUserInfo,
|
user: machineUserInfo,
|
||||||
scope: []string{ScopeUserMetaData},
|
scope: []string{ScopeUserMetaData},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{},
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "machine1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "machine, scope resource owner",
|
name: "machine, scope resource owner",
|
||||||
@ -447,6 +466,7 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
scope: []string{ScopeResourceOwner},
|
scope: []string{ScopeResourceOwner},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "machine1",
|
||||||
Claims: map[string]any{
|
Claims: map[string]any{
|
||||||
ClaimResourceOwnerID: "orgID",
|
ClaimResourceOwnerID: "orgID",
|
||||||
ClaimResourceOwnerName: "orgName",
|
ClaimResourceOwnerName: "orgName",
|
||||||
@ -461,6 +481,7 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
scope: []string{domain.OrgDomainPrimaryScope + "foo.com"},
|
scope: []string{domain.OrgDomainPrimaryScope + "foo.com"},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "human1",
|
||||||
Claims: map[string]any{
|
Claims: map[string]any{
|
||||||
domain.OrgDomainPrimaryClaim: "foo.com",
|
domain.OrgDomainPrimaryClaim: "foo.com",
|
||||||
},
|
},
|
||||||
@ -473,6 +494,7 @@ func Test_userInfoToOIDC(t *testing.T) {
|
|||||||
scope: []string{domain.OrgIDScope + "orgID"},
|
scope: []string{domain.OrgIDScope + "orgID"},
|
||||||
},
|
},
|
||||||
want: &oidc.UserInfo{
|
want: &oidc.UserInfo{
|
||||||
|
Subject: "machine1",
|
||||||
Claims: map[string]any{
|
Claims: map[string]any{
|
||||||
domain.OrgIDClaim: "orgID",
|
domain.OrgIDClaim: "orgID",
|
||||||
ClaimResourceOwnerID: "orgID",
|
ClaimResourceOwnerID: "orgID",
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-jose/go-jose/v4"
|
"github.com/go-jose/go-jose/v4"
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
"github.com/zitadel/saml/pkg/provider/key"
|
"github.com/zitadel/saml/pkg/provider/key"
|
||||||
|
|
||||||
@ -77,7 +76,7 @@ func (p *Storage) getCertificateAndKey(ctx context.Context, usage crypto.KeyUsag
|
|||||||
return p.certificateToCertificateAndKey(selectCertificate(certs.Certificates))
|
return p.certificateToCertificateAndKey(selectCertificate(certs.Certificates))
|
||||||
}
|
}
|
||||||
|
|
||||||
var position decimal.Decimal
|
var position float64
|
||||||
if certs.State != nil {
|
if certs.State != nil {
|
||||||
position = certs.State.Position
|
position = certs.State.Position
|
||||||
}
|
}
|
||||||
@ -88,7 +87,7 @@ func (p *Storage) getCertificateAndKey(ctx context.Context, usage crypto.KeyUsag
|
|||||||
func (p *Storage) refreshCertificate(
|
func (p *Storage) refreshCertificate(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
usage crypto.KeyUsage,
|
usage crypto.KeyUsage,
|
||||||
position decimal.Decimal,
|
position float64,
|
||||||
) error {
|
) error {
|
||||||
ok, err := p.ensureIsLatestCertificate(ctx, position)
|
ok, err := p.ensureIsLatestCertificate(ctx, position)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -104,12 +103,12 @@ func (p *Storage) refreshCertificate(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Storage) ensureIsLatestCertificate(ctx context.Context, position decimal.Decimal) (bool, error) {
|
func (p *Storage) ensureIsLatestCertificate(ctx context.Context, position float64) (bool, error) {
|
||||||
maxSequence, err := p.getMaxKeySequence(ctx)
|
maxSequence, err := p.getMaxKeySequence(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("error retrieving new events: %w", err)
|
return false, fmt.Errorf("error retrieving new events: %w", err)
|
||||||
}
|
}
|
||||||
return position.GreaterThanOrEqual(maxSequence), nil
|
return position >= maxSequence, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Storage) lockAndGenerateCertificateAndKey(ctx context.Context, usage crypto.KeyUsage) error {
|
func (p *Storage) lockAndGenerateCertificateAndKey(ctx context.Context, usage crypto.KeyUsage) error {
|
||||||
@ -152,9 +151,9 @@ func (p *Storage) lockAndGenerateCertificateAndKey(ctx context.Context, usage cr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Storage) getMaxKeySequence(ctx context.Context) (decimal.Decimal, error) {
|
func (p *Storage) getMaxKeySequence(ctx context.Context) (float64, error) {
|
||||||
return p.eventstore.LatestPosition(ctx,
|
return p.eventstore.LatestSequence(ctx,
|
||||||
eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxPosition).
|
eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxSequence).
|
||||||
ResourceOwner(authz.GetInstance(ctx).InstanceID()).
|
ResourceOwner(authz.GetInstance(ctx).InstanceID()).
|
||||||
AwaitOpenTransactions().
|
AwaitOpenTransactions().
|
||||||
AddQuery().
|
AddQuery().
|
||||||
|
@ -55,13 +55,10 @@ type Storage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Storage) GetEntityByID(ctx context.Context, entityID string) (*serviceprovider.ServiceProvider, error) {
|
func (p *Storage) GetEntityByID(ctx context.Context, entityID string) (*serviceprovider.ServiceProvider, error) {
|
||||||
app, err := p.query.AppBySAMLEntityID(ctx, entityID)
|
app, err := p.query.ActiveAppBySAMLEntityID(ctx, entityID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if app.State != domain.AppStateActive {
|
|
||||||
return nil, zerrors.ThrowPreconditionFailed(nil, "SAML-sdaGg", "app is not active")
|
|
||||||
}
|
|
||||||
return serviceprovider.NewServiceProvider(
|
return serviceprovider.NewServiceProvider(
|
||||||
app.ID,
|
app.ID,
|
||||||
&serviceprovider.Config{
|
&serviceprovider.Config{
|
||||||
@ -72,13 +69,10 @@ func (p *Storage) GetEntityByID(ctx context.Context, entityID string) (*servicep
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Storage) GetEntityIDByAppID(ctx context.Context, appID string) (string, error) {
|
func (p *Storage) GetEntityIDByAppID(ctx context.Context, appID string) (string, error) {
|
||||||
app, err := p.query.AppByID(ctx, appID)
|
app, err := p.query.AppByID(ctx, appID, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if app.State != domain.AppStateActive {
|
|
||||||
return "", zerrors.ThrowPreconditionFailed(nil, "SAML-sdaGg", "app is not active")
|
|
||||||
}
|
|
||||||
return app.SAMLConfig.EntityID, nil
|
return app.SAMLConfig.EntityID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,6 +131,9 @@ func (p *Storage) SetUserinfoWithUserID(ctx context.Context, applicationID strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if user.State != domain.UserStateActive {
|
||||||
|
return zerrors.ThrowPreconditionFailed(nil, "SAML-S3gFd", "Errors.User.NotActive")
|
||||||
|
}
|
||||||
|
|
||||||
userGrants, err := p.getGrants(ctx, userID, applicationID)
|
userGrants, err := p.getGrants(ctx, userID, applicationID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -163,6 +160,9 @@ func (p *Storage) SetUserinfoWithLoginName(ctx context.Context, userinfo models.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if user.State != domain.UserStateActive {
|
||||||
|
return zerrors.ThrowPreconditionFailed(nil, "SAML-FJ262", "Errors.User.NotActive")
|
||||||
|
}
|
||||||
|
|
||||||
setUserinfo(user, userinfo, attributes, map[string]*customAttribute{})
|
setUserinfo(user, userinfo, attributes, map[string]*customAttribute{})
|
||||||
return nil
|
return nil
|
||||||
@ -330,10 +330,15 @@ func (p *Storage) getGrants(ctx context.Context, userID, applicationID string) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
activeQuery, err := query.NewUserGrantStateQuery(domain.UserGrantStateActive)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return p.query.UserGrants(ctx, &query.UserGrantsQueries{
|
return p.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||||
Queries: []query.SearchQuery{
|
Queries: []query.SearchQuery{
|
||||||
projectQuery,
|
projectQuery,
|
||||||
userIDQuery,
|
userIDQuery,
|
||||||
|
activeQuery,
|
||||||
},
|
},
|
||||||
}, true)
|
}, true)
|
||||||
}
|
}
|
||||||
|
@ -1092,7 +1092,7 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
|
|||||||
}
|
}
|
||||||
if !user.IsEmailVerified {
|
if !user.IsEmailVerified {
|
||||||
steps = append(steps, &domain.VerifyEMailStep{
|
steps = append(steps, &domain.VerifyEMailStep{
|
||||||
InitPassword: !user.PasswordSet,
|
InitPassword: !user.PasswordSet && len(idps.Links) == 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if user.UsernameChangeRequired {
|
if user.UsernameChangeRequired {
|
||||||
|
@ -1058,6 +1058,74 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
|||||||
[]domain.NextStep{&domain.VerifyInviteStep{}},
|
[]domain.NextStep{&domain.VerifyInviteStep{}},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"password not set (email not verified), verify email with password step",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewUserSession{},
|
||||||
|
userViewProvider: &mockViewUser{
|
||||||
|
PasswordInitRequired: true,
|
||||||
|
},
|
||||||
|
userEventProvider: &mockEventUser{},
|
||||||
|
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||||
|
policy: &query.LockoutPolicy{
|
||||||
|
ShowFailures: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||||
|
idpUserLinksProvider: &mockIDPUserLinks{},
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
&domain.AuthRequest{
|
||||||
|
UserID: "UserID",
|
||||||
|
LoginPolicy: &domain.LoginPolicy{
|
||||||
|
AllowUsernamePassword: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
[]domain.NextStep{&domain.VerifyEMailStep{InitPassword: true}},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"password not set, but idp, email not verified, verify email step",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewUserSession{
|
||||||
|
ExternalLoginVerification: testNow.Add(-5 * time.Minute),
|
||||||
|
},
|
||||||
|
userViewProvider: &mockViewUser{},
|
||||||
|
userEventProvider: &mockEventUser{},
|
||||||
|
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||||
|
policy: &query.LockoutPolicy{
|
||||||
|
ShowFailures: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||||
|
idpUserLinksProvider: &mockIDPUserLinks{
|
||||||
|
[]*query.IDPUserLink{
|
||||||
|
{
|
||||||
|
IDPID: "idpID",
|
||||||
|
UserID: "userID",
|
||||||
|
IDPName: "idpName",
|
||||||
|
ProvidedUserID: "providedUserID",
|
||||||
|
ProvidedUsername: "providedUsername",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
&domain.AuthRequest{
|
||||||
|
UserID: "UserID",
|
||||||
|
LoginPolicy: &domain.LoginPolicy{
|
||||||
|
AllowUsernamePassword: true,
|
||||||
|
ExternalLoginCheckLifetime: 10 * 24 * time.Hour,
|
||||||
|
},
|
||||||
|
SelectedIDPConfigID: "idpID",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
[]domain.NextStep{&domain.VerifyEMailStep{}},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"password not set (email not verified), init password step",
|
"password not set (email not verified), init password step",
|
||||||
fields{
|
fields{
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
sd "github.com/zitadel/zitadel/internal/config/systemdefaults"
|
sd "github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
"github.com/zitadel/zitadel/internal/database"
|
"github.com/zitadel/zitadel/internal/database"
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
eventstore2 "github.com/zitadel/zitadel/internal/eventstore"
|
eventstore2 "github.com/zitadel/zitadel/internal/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/id"
|
"github.com/zitadel/zitadel/internal/id"
|
||||||
"github.com/zitadel/zitadel/internal/query"
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
@ -119,7 +120,11 @@ func (q queryViewWrapper) UserGrantsByProjectAndUserID(ctx context.Context, proj
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
queries := &query.UserGrantsQueries{Queries: []query.SearchQuery{userGrantUserID, userGrantProjectID}}
|
activeQuery, err := query.NewUserGrantStateQuery(domain.UserGrantStateActive)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
queries := &query.UserGrantsQueries{Queries: []query.SearchQuery{userGrantUserID, userGrantProjectID, activeQuery}}
|
||||||
grants, err := q.Queries.UserGrants(ctx, queries, true)
|
grants, err := q.Queries.UserGrants(ctx, queries, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -144,7 +144,7 @@ func (c *Commands) CreateOIDCSessionFromDeviceAuth(ctx context.Context, deviceCo
|
|||||||
return nil, DeviceAuthStateError(deviceAuthModel.State)
|
return nil, DeviceAuthStateError(deviceAuthModel.State)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd, err := c.newOIDCSessionAddEvents(ctx, deviceAuthModel.UserOrgID)
|
cmd, err := c.newOIDCSessionAddEvents(ctx, deviceAuthModel.UserID, deviceAuthModel.UserOrgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ func TestCommands_ApproveDeviceAuth(t *testing.T) {
|
|||||||
pushErr := errors.New("pushErr")
|
pushErr := errors.New("pushErr")
|
||||||
|
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@ -149,7 +149,7 @@ func TestCommands_ApproveDeviceAuth(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "not found error",
|
name: "not found error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -169,7 +169,7 @@ func TestCommands_ApproveDeviceAuth(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "push error",
|
name: "push error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(eventFromEventPusherWithInstanceID(
|
expectFilter(eventFromEventPusherWithInstanceID(
|
||||||
"instance1",
|
"instance1",
|
||||||
deviceauth.NewAddedEvent(
|
deviceauth.NewAddedEvent(
|
||||||
@ -211,7 +211,7 @@ func TestCommands_ApproveDeviceAuth(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "success",
|
name: "success",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(eventFromEventPusherWithInstanceID(
|
expectFilter(eventFromEventPusherWithInstanceID(
|
||||||
"instance1",
|
"instance1",
|
||||||
deviceauth.NewAddedEvent(
|
deviceauth.NewAddedEvent(
|
||||||
@ -256,7 +256,7 @@ func TestCommands_ApproveDeviceAuth(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
c := &Commands{
|
c := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
}
|
}
|
||||||
gotDetails, err := c.ApproveDeviceAuth(tt.args.ctx, tt.args.id, tt.args.userID, tt.args.userOrgID, tt.args.authMethods, tt.args.authTime, tt.args.preferredLanguage, tt.args.userAgent, tt.args.sessionID)
|
gotDetails, err := c.ApproveDeviceAuth(tt.args.ctx, tt.args.id, tt.args.userID, tt.args.userOrgID, tt.args.authMethods, tt.args.authTime, tt.args.preferredLanguage, tt.args.userAgent, tt.args.sessionID)
|
||||||
require.ErrorIs(t, err, tt.wantErr)
|
require.ErrorIs(t, err, tt.wantErr)
|
||||||
@ -271,7 +271,7 @@ func TestCommands_CancelDeviceAuth(t *testing.T) {
|
|||||||
pushErr := errors.New("pushErr")
|
pushErr := errors.New("pushErr")
|
||||||
|
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@ -288,7 +288,7 @@ func TestCommands_CancelDeviceAuth(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "not found error",
|
name: "not found error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -298,7 +298,7 @@ func TestCommands_CancelDeviceAuth(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "push error",
|
name: "push error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(eventFromEventPusherWithInstanceID(
|
expectFilter(eventFromEventPusherWithInstanceID(
|
||||||
"instance1",
|
"instance1",
|
||||||
deviceauth.NewAddedEvent(
|
deviceauth.NewAddedEvent(
|
||||||
@ -323,7 +323,7 @@ func TestCommands_CancelDeviceAuth(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "success/denied",
|
name: "success/denied",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(eventFromEventPusherWithInstanceID(
|
expectFilter(eventFromEventPusherWithInstanceID(
|
||||||
"instance1",
|
"instance1",
|
||||||
deviceauth.NewAddedEvent(
|
deviceauth.NewAddedEvent(
|
||||||
@ -350,7 +350,7 @@ func TestCommands_CancelDeviceAuth(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "success/expired",
|
name: "success/expired",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(eventFromEventPusherWithInstanceID(
|
expectFilter(eventFromEventPusherWithInstanceID(
|
||||||
"instance1",
|
"instance1",
|
||||||
deviceauth.NewAddedEvent(
|
deviceauth.NewAddedEvent(
|
||||||
@ -378,7 +378,7 @@ func TestCommands_CancelDeviceAuth(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
c := &Commands{
|
c := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
}
|
}
|
||||||
gotDetails, err := c.CancelDeviceAuth(tt.args.ctx, tt.args.id, tt.args.reason)
|
gotDetails, err := c.CancelDeviceAuth(tt.args.ctx, tt.args.id, tt.args.reason)
|
||||||
require.ErrorIs(t, err, tt.wantErr)
|
require.ErrorIs(t, err, tt.wantErr)
|
||||||
@ -586,6 +586,69 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantErr: DeviceAuthStateError(domain.DeviceAuthStateDone),
|
wantErr: DeviceAuthStateError(domain.DeviceAuthStateDone),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "user not active",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusherWithInstanceID(
|
||||||
|
"instance1",
|
||||||
|
deviceauth.NewAddedEvent(
|
||||||
|
ctx,
|
||||||
|
deviceauth.NewAggregate("123", "instance1"),
|
||||||
|
"clientID", "123", "456", time.Now().Add(-time.Minute),
|
||||||
|
[]string{"openid", "offline_access"},
|
||||||
|
[]string{"audience"}, false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusherWithInstanceID(
|
||||||
|
"instance1",
|
||||||
|
deviceauth.NewApprovedEvent(ctx,
|
||||||
|
deviceauth.NewAggregate("123", "instance1"),
|
||||||
|
"userID", "org1",
|
||||||
|
[]domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
|
||||||
|
testNow, &language.Afrikaans, &domain.UserAgent{
|
||||||
|
FingerprintID: gu.Ptr("fp1"),
|
||||||
|
IP: net.ParseIP("1.2.3.4"),
|
||||||
|
Description: gu.Ptr("firefox"),
|
||||||
|
Header: http.Header{"foo": []string{"bar"}},
|
||||||
|
},
|
||||||
|
"sessionID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
ctx,
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.English,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
user.NewUserDeactivatedEvent(
|
||||||
|
ctx,
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
idGenerator: mock.NewIDGeneratorExpectIDs(t),
|
||||||
|
defaultAccessTokenLifetime: time.Hour,
|
||||||
|
defaultRefreshTokenLifetime: 7 * 24 * time.Hour,
|
||||||
|
defaultRefreshTokenIdleLifetime: 24 * time.Hour,
|
||||||
|
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx,
|
||||||
|
"123",
|
||||||
|
},
|
||||||
|
wantErr: zerrors.ThrowPreconditionFailed(nil, "OIDCS-kj3g2", "Errors.User.NotActive"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "approved, success",
|
name: "approved, success",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
@ -617,6 +680,21 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
ctx,
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.English,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
expectFilter(), // token lifetime
|
expectFilter(), // token lifetime
|
||||||
expectPush(
|
expectPush(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -699,6 +777,21 @@ func TestCommands_CreateOIDCSessionFromDeviceAuth(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
ctx,
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.English,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
expectFilter(), // token lifetime
|
expectFilter(), // token lifetime
|
||||||
expectPush(
|
expectPush(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
|
@ -104,6 +104,7 @@ func (wm *IAMSMTPConfigWriteModel) Reduce() error {
|
|||||||
wm.reduceSMTPConfigRemovedEvent(e)
|
wm.reduceSMTPConfigRemovedEvent(e)
|
||||||
case *instance.SMTPConfigActivatedEvent:
|
case *instance.SMTPConfigActivatedEvent:
|
||||||
if wm.ID != e.ID {
|
if wm.ID != e.ID {
|
||||||
|
wm.State = domain.SMTPConfigStateInactive
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
wm.State = domain.SMTPConfigStateActive
|
wm.State = domain.SMTPConfigStateActive
|
||||||
|
@ -80,7 +80,7 @@ func (c *Commands) CreateOIDCSessionFromAuthRequest(ctx context.Context, authReq
|
|||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd, err := c.newOIDCSessionAddEvents(ctx, sessionModel.UserResourceOwner)
|
cmd, err := c.newOIDCSessionAddEvents(ctx, sessionModel.UserID, sessionModel.UserResourceOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ func (c *Commands) CreateOIDCSession(ctx context.Context,
|
|||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
cmd, err := c.newOIDCSessionAddEvents(ctx, resourceOwner)
|
cmd, err := c.newOIDCSessionAddEvents(ctx, userID, resourceOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -265,7 +265,14 @@ func (c *Commands) RevokeOIDCSessionToken(ctx context.Context, token, clientID s
|
|||||||
return c.pushAppendAndReduce(ctx, writeModel, oidcsession.NewAccessTokenRevokedEvent(ctx, writeModel.aggregate))
|
return c.pushAppendAndReduce(ctx, writeModel, oidcsession.NewAccessTokenRevokedEvent(ctx, writeModel.aggregate))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) newOIDCSessionAddEvents(ctx context.Context, resourceOwner string, pending ...eventstore.Command) (*OIDCSessionEvents, error) {
|
func (c *Commands) newOIDCSessionAddEvents(ctx context.Context, userID, resourceOwner string, pending ...eventstore.Command) (*OIDCSessionEvents, error) {
|
||||||
|
userStateModel, err := c.userStateWriteModel(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !userStateModel.UserState.IsEnabled() {
|
||||||
|
return nil, zerrors.ThrowPreconditionFailed(nil, "OIDCS-kj3g2", "Errors.User.NotActive")
|
||||||
|
}
|
||||||
accessTokenLifetime, refreshTokenLifeTime, refreshTokenIdleLifetime, err := c.tokenTokenLifetimes(ctx)
|
accessTokenLifetime, refreshTokenLifeTime, refreshTokenIdleLifetime, err := c.tokenTokenLifetimes(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -281,6 +288,7 @@ func (c *Commands) newOIDCSessionAddEvents(ctx context.Context, resourceOwner st
|
|||||||
encryptionAlg: c.keyAlgorithm,
|
encryptionAlg: c.keyAlgorithm,
|
||||||
events: pending,
|
events: pending,
|
||||||
oidcSessionWriteModel: NewOIDCSessionWriteModel(sessionID, resourceOwner),
|
oidcSessionWriteModel: NewOIDCSessionWriteModel(sessionID, resourceOwner),
|
||||||
|
userStateModel: userStateModel,
|
||||||
accessTokenLifetime: accessTokenLifetime,
|
accessTokenLifetime: accessTokenLifetime,
|
||||||
refreshTokenLifeTime: refreshTokenLifeTime,
|
refreshTokenLifeTime: refreshTokenLifeTime,
|
||||||
refreshTokenIdleLifetime: refreshTokenIdleLifetime,
|
refreshTokenIdleLifetime: refreshTokenIdleLifetime,
|
||||||
@ -321,6 +329,13 @@ func (c *Commands) newOIDCSessionUpdateEvents(ctx context.Context, refreshToken
|
|||||||
if err = sessionWriteModel.CheckRefreshToken(refreshTokenID); err != nil {
|
if err = sessionWriteModel.CheckRefreshToken(refreshTokenID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
userStateWriteModel, err := c.userStateWriteModel(ctx, sessionWriteModel.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !userStateWriteModel.UserState.IsEnabled() {
|
||||||
|
return nil, zerrors.ThrowPreconditionFailed(nil, "OIDCS-J39h2", "Errors.User.NotActive")
|
||||||
|
}
|
||||||
accessTokenLifetime, refreshTokenLifeTime, refreshTokenIdleLifetime, err := c.tokenTokenLifetimes(ctx)
|
accessTokenLifetime, refreshTokenLifeTime, refreshTokenIdleLifetime, err := c.tokenTokenLifetimes(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -342,6 +357,7 @@ type OIDCSessionEvents struct {
|
|||||||
encryptionAlg crypto.EncryptionAlgorithm
|
encryptionAlg crypto.EncryptionAlgorithm
|
||||||
events []eventstore.Command
|
events []eventstore.Command
|
||||||
oidcSessionWriteModel *OIDCSessionWriteModel
|
oidcSessionWriteModel *OIDCSessionWriteModel
|
||||||
|
userStateModel *UserV2WriteModel
|
||||||
|
|
||||||
accessTokenLifetime time.Duration
|
accessTokenLifetime time.Duration
|
||||||
refreshTokenLifeTime time.Duration
|
refreshTokenLifeTime time.Duration
|
||||||
|
@ -205,6 +205,103 @@ func TestCommands_CreateOIDCSessionFromAuthRequest(t *testing.T) {
|
|||||||
err: zerrors.ThrowPreconditionFailed(nil, "COMMAND-Flk38", "Errors.Session.NotExisting"),
|
err: zerrors.ThrowPreconditionFailed(nil, "COMMAND-Flk38", "Errors.Session.NotExisting"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"user not active",
|
||||||
|
fields{
|
||||||
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
authrequest.NewAddedEvent(context.Background(), &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate,
|
||||||
|
"loginClient",
|
||||||
|
"clientID",
|
||||||
|
"redirectURI",
|
||||||
|
"state",
|
||||||
|
"nonce",
|
||||||
|
[]string{"openid", "offline_access"},
|
||||||
|
[]string{"audience"},
|
||||||
|
domain.OIDCResponseTypeCode,
|
||||||
|
domain.OIDCResponseModeQuery,
|
||||||
|
&domain.OIDCCodeChallenge{
|
||||||
|
Challenge: "challenge",
|
||||||
|
Method: domain.CodeChallengeMethodS256,
|
||||||
|
},
|
||||||
|
[]domain.Prompt{domain.PromptNone},
|
||||||
|
[]string{"en", "de"},
|
||||||
|
gu.Ptr(time.Duration(0)),
|
||||||
|
gu.Ptr("loginHint"),
|
||||||
|
gu.Ptr("hintUserID"),
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
authrequest.NewCodeAddedEvent(context.Background(), &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
authrequest.NewSessionLinkedEvent(context.Background(), &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate,
|
||||||
|
"sessionID",
|
||||||
|
"userID",
|
||||||
|
testNow,
|
||||||
|
[]domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
session.NewAddedEvent(context.Background(),
|
||||||
|
&session.NewAggregate("sessionID", "instance1").Aggregate,
|
||||||
|
&domain.UserAgent{
|
||||||
|
FingerprintID: gu.Ptr("fp1"),
|
||||||
|
IP: net.ParseIP("1.2.3.4"),
|
||||||
|
Description: gu.Ptr("firefox"),
|
||||||
|
Header: http.Header{"foo": []string{"bar"}},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate,
|
||||||
|
"userID", "org1", testNow, &language.Afrikaans),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
session.NewPasswordCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate,
|
||||||
|
testNow),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.Afrikaans,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
user.NewUserDeactivatedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
idGenerator: mock.NewIDGeneratorExpectIDs(t),
|
||||||
|
defaultAccessTokenLifetime: time.Hour,
|
||||||
|
defaultRefreshTokenLifetime: 7 * 24 * time.Hour,
|
||||||
|
defaultRefreshTokenIdleLifetime: 24 * time.Hour,
|
||||||
|
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
ctx: authz.WithInstanceID(context.Background(), "instanceID"),
|
||||||
|
authRequestID: "V2_authRequestID",
|
||||||
|
complianceCheck: mockAuthRequestComplianceChecker(nil),
|
||||||
|
needRefreshToken: true,
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: zerrors.ThrowPreconditionFailed(nil, "OIDCS-kj3g2", "Errors.User.NotActive"),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"add successful",
|
"add successful",
|
||||||
fields{
|
fields{
|
||||||
@ -266,6 +363,21 @@ func TestCommands_CreateOIDCSessionFromAuthRequest(t *testing.T) {
|
|||||||
testNow),
|
testNow),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.Afrikaans,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
expectFilter(), // token lifetime
|
expectFilter(), // token lifetime
|
||||||
expectPush(
|
expectPush(
|
||||||
authrequest.NewCodeExchangedEvent(context.Background(), &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate),
|
authrequest.NewCodeExchangedEvent(context.Background(), &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate),
|
||||||
@ -382,6 +494,21 @@ func TestCommands_CreateOIDCSessionFromAuthRequest(t *testing.T) {
|
|||||||
testNow),
|
testNow),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.Afrikaans,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
expectFilter(), // token lifetime
|
expectFilter(), // token lifetime
|
||||||
expectPush(
|
expectPush(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -521,10 +648,81 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantErr: io.ErrClosedPipe,
|
wantErr: io.ErrClosedPipe,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "not active user",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.Afrikaans,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
user.NewUserDeactivatedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
idGenerator: mock.NewIDGeneratorExpectIDs(t),
|
||||||
|
defaultAccessTokenLifetime: time.Hour,
|
||||||
|
defaultRefreshTokenLifetime: 7 * 24 * time.Hour,
|
||||||
|
defaultRefreshTokenIdleLifetime: 24 * time.Hour,
|
||||||
|
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "userID",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
clientID: "clientID",
|
||||||
|
audience: []string{"audience"},
|
||||||
|
scope: []string{"openid", "offline_access"},
|
||||||
|
authMethods: []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
|
||||||
|
authTime: testNow,
|
||||||
|
nonce: "nonce",
|
||||||
|
preferredLanguage: &language.Afrikaans,
|
||||||
|
userAgent: &domain.UserAgent{
|
||||||
|
FingerprintID: gu.Ptr("fp1"),
|
||||||
|
IP: net.ParseIP("1.2.3.4"),
|
||||||
|
Description: gu.Ptr("firefox"),
|
||||||
|
Header: http.Header{"foo": []string{"bar"}},
|
||||||
|
},
|
||||||
|
reason: domain.TokenReasonAuthRequest,
|
||||||
|
actor: &domain.TokenActor{
|
||||||
|
UserID: "user2",
|
||||||
|
Issuer: "foo.com",
|
||||||
|
},
|
||||||
|
needRefreshToken: false,
|
||||||
|
},
|
||||||
|
wantErr: zerrors.ThrowPreconditionFailed(nil, "OIDCS-kj3g2", "Errors.User.NotActive"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "without refresh token",
|
name: "without refresh token",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: expectEventstore(
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.Afrikaans,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
expectFilter(), // token lifetime
|
expectFilter(), // token lifetime
|
||||||
expectPush(
|
expectPush(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -606,6 +804,21 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
|||||||
name: "with refresh token",
|
name: "with refresh token",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: expectEventstore(
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.Afrikaans,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
expectFilter(), // token lifetime
|
expectFilter(), // token lifetime
|
||||||
expectPush(
|
expectPush(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -689,6 +902,21 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
|||||||
name: "with sessionID",
|
name: "with sessionID",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: expectEventstore(
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.Afrikaans,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
expectFilter(), // token lifetime
|
expectFilter(), // token lifetime
|
||||||
expectPush(
|
expectPush(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -772,6 +1000,21 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
|||||||
name: "impersonation not allowed",
|
name: "impersonation not allowed",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: expectEventstore(
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.Afrikaans,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
expectFilter(), // token lifetime
|
expectFilter(), // token lifetime
|
||||||
),
|
),
|
||||||
idGenerator: mock.NewIDGeneratorExpectIDs(t, "oidcSessionID"),
|
idGenerator: mock.NewIDGeneratorExpectIDs(t, "oidcSessionID"),
|
||||||
@ -813,6 +1056,21 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
|||||||
name: "impersonation allowed",
|
name: "impersonation allowed",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: expectEventstore(
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.Afrikaans,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
expectFilter(), // token lifetime
|
expectFilter(), // token lifetime
|
||||||
expectPush(
|
expectPush(
|
||||||
user.NewUserImpersonatedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, "clientID", &domain.TokenActor{
|
user.NewUserImpersonatedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, "clientID", &domain.TokenActor{
|
||||||
@ -1067,6 +1325,63 @@ func TestCommands_ExchangeOIDCSessionRefreshAndAccessToken(t *testing.T) {
|
|||||||
err: zerrors.ThrowPreconditionFailed(nil, "OIDCS-3jt2w", "Errors.OIDCSession.RefreshTokenInvalid"),
|
err: zerrors.ThrowPreconditionFailed(nil, "OIDCS-3jt2w", "Errors.OIDCSession.RefreshTokenInvalid"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"user not active",
|
||||||
|
fields{
|
||||||
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusherWithCreationDateNow(
|
||||||
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
|
"userID", "org1", "sessionID", "clientID", []string{"audience"}, []string{"openid", "profile", "offline_access"},
|
||||||
|
[]domain.UserAuthMethodType{domain.UserAuthMethodTypePassword}, testNow, "nonce", &language.Afrikaans,
|
||||||
|
&domain.UserAgent{FingerprintID: gu.Ptr("browserFP")},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusherWithCreationDateNow(
|
||||||
|
oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
|
"at_accessTokenID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil),
|
||||||
|
),
|
||||||
|
eventFromEventPusherWithCreationDateNow(
|
||||||
|
oidcsession.NewRefreshTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
|
"rt_refreshTokenID", 7*24*time.Hour, 24*time.Hour),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.Afrikaans,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
user.NewUserDeactivatedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
idGenerator: mock.NewIDGeneratorExpectIDs(t),
|
||||||
|
defaultAccessTokenLifetime: time.Hour,
|
||||||
|
defaultRefreshTokenLifetime: 7 * 24 * time.Hour,
|
||||||
|
defaultRefreshTokenIdleLifetime: 24 * time.Hour,
|
||||||
|
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
ctx: authz.WithInstanceID(context.Background(), "instanceID"),
|
||||||
|
refreshToken: "VjJfb2lkY1Nlc3Npb25JRC1ydF9yZWZyZXNoVG9rZW5JRDp1c2VySUQ", //V2_oidcSessionID:rt_refreshTokenID:userID
|
||||||
|
scope: []string{"openid", "offline_access"},
|
||||||
|
complianceCheck: mockRefreshTokenComplianceChecker(nil),
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: zerrors.ThrowPreconditionFailed(nil, "OIDCS-J39h2", "Errors.User.NotActive"),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"refresh successful",
|
"refresh successful",
|
||||||
fields{
|
fields{
|
||||||
@ -1088,6 +1403,21 @@ func TestCommands_ExchangeOIDCSessionRefreshAndAccessToken(t *testing.T) {
|
|||||||
"rt_refreshTokenID", 7*24*time.Hour, 24*time.Hour),
|
"rt_refreshTokenID", 7*24*time.Hour, 24*time.Hour),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
expectFilter(
|
||||||
|
user.NewHumanAddedEvent(
|
||||||
|
context.Background(),
|
||||||
|
&user.NewAggregate("userID", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.Afrikaans,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
expectFilter(), // token lifetime
|
expectFilter(), // token lifetime
|
||||||
expectPush(
|
expectPush(
|
||||||
oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -1153,7 +1483,7 @@ func TestCommands_ExchangeOIDCSessionRefreshAndAccessToken(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommands_OIDCSessionByRefreshToken(t *testing.T) {
|
func TestCommands_OIDCSessionByRefreshToken(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
idGenerator id.Generator
|
idGenerator id.Generator
|
||||||
defaultAccessTokenLifetime time.Duration
|
defaultAccessTokenLifetime time.Duration
|
||||||
defaultRefreshTokenLifetime time.Duration
|
defaultRefreshTokenLifetime time.Duration
|
||||||
@ -1177,7 +1507,7 @@ func TestCommands_OIDCSessionByRefreshToken(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"invalid refresh token format error",
|
"invalid refresh token format error",
|
||||||
fields{
|
fields{
|
||||||
eventstore: eventstoreExpect(t),
|
eventstore: expectEventstore(),
|
||||||
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
},
|
},
|
||||||
args{
|
args{
|
||||||
@ -1191,7 +1521,7 @@ func TestCommands_OIDCSessionByRefreshToken(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"inactive session error",
|
"inactive session error",
|
||||||
fields{
|
fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
@ -1207,7 +1537,7 @@ func TestCommands_OIDCSessionByRefreshToken(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"invalid refresh token error",
|
"invalid refresh token error",
|
||||||
fields{
|
fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -1235,7 +1565,7 @@ func TestCommands_OIDCSessionByRefreshToken(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"expired refresh token error",
|
"expired refresh token error",
|
||||||
fields{
|
fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -1267,7 +1597,7 @@ func TestCommands_OIDCSessionByRefreshToken(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"get successful",
|
"get successful",
|
||||||
fields{
|
fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusherWithCreationDateNow(
|
eventFromEventPusherWithCreationDateNow(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -1316,7 +1646,7 @@ func TestCommands_OIDCSessionByRefreshToken(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
c := &Commands{
|
c := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
idGenerator: tt.fields.idGenerator,
|
idGenerator: tt.fields.idGenerator,
|
||||||
defaultAccessTokenLifetime: tt.fields.defaultAccessTokenLifetime,
|
defaultAccessTokenLifetime: tt.fields.defaultAccessTokenLifetime,
|
||||||
defaultRefreshTokenLifetime: tt.fields.defaultRefreshTokenLifetime,
|
defaultRefreshTokenLifetime: tt.fields.defaultRefreshTokenLifetime,
|
||||||
@ -1348,7 +1678,7 @@ func TestCommands_OIDCSessionByRefreshToken(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommands_RevokeOIDCSessionToken(t *testing.T) {
|
func TestCommands_RevokeOIDCSessionToken(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
keyAlgorithm crypto.EncryptionAlgorithm
|
keyAlgorithm crypto.EncryptionAlgorithm
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
@ -1368,7 +1698,7 @@ func TestCommands_RevokeOIDCSessionToken(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"invalid token",
|
"invalid token",
|
||||||
fields{
|
fields{
|
||||||
eventstore: eventstoreExpect(t),
|
eventstore: expectEventstore(),
|
||||||
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
},
|
},
|
||||||
args{
|
args{
|
||||||
@ -1382,7 +1712,7 @@ func TestCommands_RevokeOIDCSessionToken(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"refresh_token inactive",
|
"refresh_token inactive",
|
||||||
fields{
|
fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -1407,7 +1737,7 @@ func TestCommands_RevokeOIDCSessionToken(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"refresh_token invalid client",
|
"refresh_token invalid client",
|
||||||
fields{
|
fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -1432,7 +1762,7 @@ func TestCommands_RevokeOIDCSessionToken(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"refresh_token revoked",
|
"refresh_token revoked",
|
||||||
fields{
|
fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -1468,7 +1798,7 @@ func TestCommands_RevokeOIDCSessionToken(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"access_token inactive session",
|
"access_token inactive session",
|
||||||
fields{
|
fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -1493,7 +1823,7 @@ func TestCommands_RevokeOIDCSessionToken(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"access_token invalid client",
|
"access_token invalid client",
|
||||||
fields{
|
fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -1518,7 +1848,7 @@ func TestCommands_RevokeOIDCSessionToken(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"access_token revoked",
|
"access_token revoked",
|
||||||
fields{
|
fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||||
@ -1555,7 +1885,7 @@ func TestCommands_RevokeOIDCSessionToken(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
c := &Commands{
|
c := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
keyAlgorithm: tt.fields.keyAlgorithm,
|
keyAlgorithm: tt.fields.keyAlgorithm,
|
||||||
}
|
}
|
||||||
err := c.RevokeOIDCSessionToken(tt.args.ctx, tt.args.token, tt.args.clientID)
|
err := c.RevokeOIDCSessionToken(tt.args.ctx, tt.args.token, tt.args.clientID)
|
||||||
|
@ -93,6 +93,7 @@ func (wm *IAMSMSConfigWriteModel) Reduce() error {
|
|||||||
}
|
}
|
||||||
case *instance.SMSConfigTwilioActivatedEvent:
|
case *instance.SMSConfigTwilioActivatedEvent:
|
||||||
if wm.ID != e.ID {
|
if wm.ID != e.ID {
|
||||||
|
wm.State = domain.SMSConfigStateInactive
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
wm.State = domain.SMSConfigStateActive
|
wm.State = domain.SMSConfigStateActive
|
||||||
@ -110,6 +111,7 @@ func (wm *IAMSMSConfigWriteModel) Reduce() error {
|
|||||||
wm.State = domain.SMSConfigStateRemoved
|
wm.State = domain.SMSConfigStateRemoved
|
||||||
case *instance.SMSConfigActivatedEvent:
|
case *instance.SMSConfigActivatedEvent:
|
||||||
if wm.ID != e.ID {
|
if wm.ID != e.ID {
|
||||||
|
wm.State = domain.SMSConfigStateInactive
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
wm.State = domain.SMSConfigStateActive
|
wm.State = domain.SMSConfigStateActive
|
||||||
|
@ -7,8 +7,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
pgxdecimal "github.com/jackc/pgx-shopspring-decimal"
|
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
"github.com/jackc/pgx/v5/stdlib"
|
"github.com/jackc/pgx/v5/stdlib"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
@ -84,11 +82,6 @@ func (c *Config) Connect(useAdmin bool, pusherRatio, spoolerRatio float64, purpo
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
config.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
|
|
||||||
pgxdecimal.Register(conn.TypeMap())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if connConfig.MaxOpenConns != 0 {
|
if connConfig.MaxOpenConns != 0 {
|
||||||
config.MaxConns = int32(connConfig.MaxOpenConns)
|
config.MaxConns = int32(connConfig.MaxOpenConns)
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
pgxdecimal "github.com/jackc/pgx-shopspring-decimal"
|
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
"github.com/jackc/pgx/v5/stdlib"
|
"github.com/jackc/pgx/v5/stdlib"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
@ -84,10 +82,6 @@ func (c *Config) Connect(useAdmin bool, pusherRatio, spoolerRatio float64, purpo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
config.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
|
|
||||||
pgxdecimal.Register(conn.TypeMap())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if connConfig.MaxOpenConns != 0 {
|
if connConfig.MaxOpenConns != 0 {
|
||||||
config.MaxConns = int32(connConfig.MaxOpenConns)
|
config.MaxConns = int32(connConfig.MaxOpenConns)
|
||||||
|
@ -5,8 +5,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -46,7 +44,7 @@ type Event interface {
|
|||||||
// CreatedAt is the time the event was created at
|
// CreatedAt is the time the event was created at
|
||||||
CreatedAt() time.Time
|
CreatedAt() time.Time
|
||||||
// Position is the global position of the event
|
// Position is the global position of the event
|
||||||
Position() decimal.Decimal
|
Position() float64
|
||||||
|
|
||||||
// Unmarshal parses the payload and stores the result
|
// Unmarshal parses the payload and stores the result
|
||||||
// in the value pointed to by ptr. If ptr is nil or not a pointer,
|
// in the value pointed to by ptr. If ptr is nil or not a pointer,
|
||||||
|
@ -5,8 +5,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
"github.com/zitadel/zitadel/internal/api/service"
|
"github.com/zitadel/zitadel/internal/api/service"
|
||||||
)
|
)
|
||||||
@ -23,7 +21,7 @@ type BaseEvent struct {
|
|||||||
Agg *Aggregate
|
Agg *Aggregate
|
||||||
|
|
||||||
Seq uint64
|
Seq uint64
|
||||||
Pos decimal.Decimal
|
Pos float64
|
||||||
Creation time.Time
|
Creation time.Time
|
||||||
previousAggregateSequence uint64
|
previousAggregateSequence uint64
|
||||||
previousAggregateTypeSequence uint64
|
previousAggregateTypeSequence uint64
|
||||||
@ -36,7 +34,7 @@ type BaseEvent struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Position implements Event.
|
// Position implements Event.
|
||||||
func (e *BaseEvent) Position() decimal.Decimal {
|
func (e *BaseEvent) Position() float64 {
|
||||||
return e.Pos
|
return e.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgconn"
|
"github.com/jackc/pgx/v5/pgconn"
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
@ -218,11 +217,11 @@ func (es *Eventstore) FilterToReducer(ctx context.Context, searchQuery *SearchQu
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// LatestPosition filters the latest position for the given search query
|
// LatestSequence filters the latest sequence for the given search query
|
||||||
func (es *Eventstore) LatestPosition(ctx context.Context, queryFactory *SearchQueryBuilder) (decimal.Decimal, error) {
|
func (es *Eventstore) LatestSequence(ctx context.Context, queryFactory *SearchQueryBuilder) (float64, error) {
|
||||||
queryFactory.InstanceID(authz.GetInstance(ctx).InstanceID())
|
queryFactory.InstanceID(authz.GetInstance(ctx).InstanceID())
|
||||||
|
|
||||||
return es.querier.LatestPosition(ctx, queryFactory)
|
return es.querier.LatestSequence(ctx, queryFactory)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstanceIDs returns the instance ids found by the search query
|
// InstanceIDs returns the instance ids found by the search query
|
||||||
@ -267,8 +266,8 @@ type Querier interface {
|
|||||||
Health(ctx context.Context) error
|
Health(ctx context.Context) error
|
||||||
// FilterToReducer calls r for every event returned from the storage
|
// FilterToReducer calls r for every event returned from the storage
|
||||||
FilterToReducer(ctx context.Context, searchQuery *SearchQueryBuilder, r Reducer) error
|
FilterToReducer(ctx context.Context, searchQuery *SearchQueryBuilder, r Reducer) error
|
||||||
// LatestPosition returns the latest position found by the search query
|
// LatestSequence returns the latest sequence found by the search query
|
||||||
LatestPosition(ctx context.Context, queryFactory *SearchQueryBuilder) (decimal.Decimal, error)
|
LatestSequence(ctx context.Context, queryFactory *SearchQueryBuilder) (float64, error)
|
||||||
// InstanceIDs returns the instance ids found by the search query
|
// InstanceIDs returns the instance ids found by the search query
|
||||||
InstanceIDs(ctx context.Context, queryFactory *SearchQueryBuilder) ([]string, error)
|
InstanceIDs(ctx context.Context, queryFactory *SearchQueryBuilder) ([]string, error)
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -100,7 +98,7 @@ func TestCRDB_Filter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCRDB_LatestPosition(t *testing.T) {
|
func TestCRDB_LatestSequence(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
searchQuery *eventstore.SearchQueryBuilder
|
searchQuery *eventstore.SearchQueryBuilder
|
||||||
}
|
}
|
||||||
@ -108,7 +106,7 @@ func TestCRDB_LatestPosition(t *testing.T) {
|
|||||||
existingEvents []eventstore.Command
|
existingEvents []eventstore.Command
|
||||||
}
|
}
|
||||||
type res struct {
|
type res struct {
|
||||||
position decimal.Decimal
|
sequence float64
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -120,7 +118,7 @@ func TestCRDB_LatestPosition(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "aggregate type filter no sequence",
|
name: "aggregate type filter no sequence",
|
||||||
args: args{
|
args: args{
|
||||||
searchQuery: eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxPosition).
|
searchQuery: eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxSequence).
|
||||||
AddQuery().
|
AddQuery().
|
||||||
AggregateTypes("not found").
|
AggregateTypes("not found").
|
||||||
Builder(),
|
Builder(),
|
||||||
@ -137,7 +135,7 @@ func TestCRDB_LatestPosition(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "aggregate type filter sequence",
|
name: "aggregate type filter sequence",
|
||||||
args: args{
|
args: args{
|
||||||
searchQuery: eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxPosition).
|
searchQuery: eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxSequence).
|
||||||
AddQuery().
|
AddQuery().
|
||||||
AggregateTypes(eventstore.AggregateType(t.Name())).
|
AggregateTypes(eventstore.AggregateType(t.Name())).
|
||||||
Builder(),
|
Builder(),
|
||||||
@ -171,12 +169,12 @@ func TestCRDB_LatestPosition(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
position, err := db.LatestPosition(context.Background(), tt.args.searchQuery)
|
sequence, err := db.LatestSequence(context.Background(), tt.args.searchQuery)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("CRDB.query() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("CRDB.query() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
if tt.res.position.GreaterThan(position) {
|
if tt.res.sequence > sequence {
|
||||||
t.Errorf("CRDB.query() expected sequence: %v got %v", tt.res.position, position)
|
t.Errorf("CRDB.query() expected sequence: %v got %v", tt.res.sequence, sequence)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgconn"
|
"github.com/jackc/pgx/v5/pgconn"
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
"github.com/zitadel/zitadel/internal/api/service"
|
"github.com/zitadel/zitadel/internal/api/service"
|
||||||
@ -391,7 +390,7 @@ func (repo *testPusher) Push(ctx context.Context, commands ...Command) (events [
|
|||||||
|
|
||||||
type testQuerier struct {
|
type testQuerier struct {
|
||||||
events []Event
|
events []Event
|
||||||
sequence decimal.Decimal
|
sequence float64
|
||||||
instances []string
|
instances []string
|
||||||
err error
|
err error
|
||||||
t *testing.T
|
t *testing.T
|
||||||
@ -424,9 +423,9 @@ func (repo *testQuerier) FilterToReducer(ctx context.Context, searchQuery *Searc
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *testQuerier) LatestPosition(ctx context.Context, queryFactory *SearchQueryBuilder) (decimal.Decimal, error) {
|
func (repo *testQuerier) LatestSequence(ctx context.Context, queryFactory *SearchQueryBuilder) (float64, error) {
|
||||||
if repo.err != nil {
|
if repo.err != nil {
|
||||||
return decimal.Decimal{}, repo.err
|
return 0, repo.err
|
||||||
}
|
}
|
||||||
return repo.sequence, nil
|
return repo.sequence, nil
|
||||||
}
|
}
|
||||||
@ -1056,7 +1055,7 @@ func TestEventstore_FilterEvents(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEventstore_LatestPosition(t *testing.T) {
|
func TestEventstore_LatestSequence(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
query *SearchQueryBuilder
|
query *SearchQueryBuilder
|
||||||
}
|
}
|
||||||
@ -1076,7 +1075,7 @@ func TestEventstore_LatestPosition(t *testing.T) {
|
|||||||
name: "no events",
|
name: "no events",
|
||||||
args: args{
|
args: args{
|
||||||
query: &SearchQueryBuilder{
|
query: &SearchQueryBuilder{
|
||||||
columns: ColumnsMaxPosition,
|
columns: ColumnsMaxSequence,
|
||||||
queries: []*SearchQuery{
|
queries: []*SearchQuery{
|
||||||
{
|
{
|
||||||
builder: &SearchQueryBuilder{},
|
builder: &SearchQueryBuilder{},
|
||||||
@ -1099,7 +1098,7 @@ func TestEventstore_LatestPosition(t *testing.T) {
|
|||||||
name: "repo error",
|
name: "repo error",
|
||||||
args: args{
|
args: args{
|
||||||
query: &SearchQueryBuilder{
|
query: &SearchQueryBuilder{
|
||||||
columns: ColumnsMaxPosition,
|
columns: ColumnsMaxSequence,
|
||||||
queries: []*SearchQuery{
|
queries: []*SearchQuery{
|
||||||
{
|
{
|
||||||
builder: &SearchQueryBuilder{},
|
builder: &SearchQueryBuilder{},
|
||||||
@ -1122,7 +1121,7 @@ func TestEventstore_LatestPosition(t *testing.T) {
|
|||||||
name: "found events",
|
name: "found events",
|
||||||
args: args{
|
args: args{
|
||||||
query: &SearchQueryBuilder{
|
query: &SearchQueryBuilder{
|
||||||
columns: ColumnsMaxPosition,
|
columns: ColumnsMaxSequence,
|
||||||
queries: []*SearchQuery{
|
queries: []*SearchQuery{
|
||||||
{
|
{
|
||||||
builder: &SearchQueryBuilder{},
|
builder: &SearchQueryBuilder{},
|
||||||
@ -1148,7 +1147,7 @@ func TestEventstore_LatestPosition(t *testing.T) {
|
|||||||
querier: tt.fields.repo,
|
querier: tt.fields.repo,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := es.LatestPosition(context.Background(), tt.args.query)
|
_, err := es.LatestSequence(context.Background(), tt.args.query)
|
||||||
if (err != nil) != tt.res.wantErr {
|
if (err != nil) != tt.res.wantErr {
|
||||||
t.Errorf("Eventstore.aggregatesToEvents() error = %v, wantErr %v", err, tt.res.wantErr)
|
t.Errorf("Eventstore.aggregatesToEvents() error = %v, wantErr %v", err, tt.res.wantErr)
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgconn"
|
"github.com/jackc/pgx/v5/pgconn"
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
)
|
)
|
||||||
@ -124,7 +123,7 @@ func (h *FieldHandler) processEvents(ctx context.Context, config *triggerConfig)
|
|||||||
return additionalIteration, err
|
return additionalIteration, err
|
||||||
}
|
}
|
||||||
// stop execution if currentState.eventTimestamp >= config.maxCreatedAt
|
// stop execution if currentState.eventTimestamp >= config.maxCreatedAt
|
||||||
if !config.maxPosition.IsZero() && currentState.position.GreaterThanOrEqual(config.maxPosition) {
|
if config.maxPosition != 0 && currentState.position >= config.maxPosition {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +156,7 @@ func (h *FieldHandler) fetchEvents(ctx context.Context, tx *sql.Tx, currentState
|
|||||||
|
|
||||||
idx, offset := skipPreviouslyReducedEvents(events, currentState)
|
idx, offset := skipPreviouslyReducedEvents(events, currentState)
|
||||||
|
|
||||||
if currentState.position.Equal(events[len(events)-1].Position()) {
|
if currentState.position == events[len(events)-1].Position() {
|
||||||
offset += currentState.offset
|
offset += currentState.offset
|
||||||
}
|
}
|
||||||
currentState.position = events[len(events)-1].Position()
|
currentState.position = events[len(events)-1].Position()
|
||||||
@ -187,9 +186,9 @@ func (h *FieldHandler) fetchEvents(ctx context.Context, tx *sql.Tx, currentState
|
|||||||
}
|
}
|
||||||
|
|
||||||
func skipPreviouslyReducedEvents(events []eventstore.Event, currentState *state) (index int, offset uint32) {
|
func skipPreviouslyReducedEvents(events []eventstore.Event, currentState *state) (index int, offset uint32) {
|
||||||
var position decimal.Decimal
|
var position float64
|
||||||
for i, event := range events {
|
for i, event := range events {
|
||||||
if !event.Position().Equal(position) {
|
if event.Position() != position {
|
||||||
offset = 0
|
offset = 0
|
||||||
position = event.Position()
|
position = event.Position()
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"slices"
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgconn"
|
"github.com/jackc/pgx/v5/pgconn"
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
@ -379,7 +379,7 @@ func (h *Handler) existingInstances(ctx context.Context) ([]string, error) {
|
|||||||
|
|
||||||
type triggerConfig struct {
|
type triggerConfig struct {
|
||||||
awaitRunning bool
|
awaitRunning bool
|
||||||
maxPosition decimal.Decimal
|
maxPosition float64
|
||||||
}
|
}
|
||||||
|
|
||||||
type TriggerOpt func(conf *triggerConfig)
|
type TriggerOpt func(conf *triggerConfig)
|
||||||
@ -390,7 +390,7 @@ func WithAwaitRunning() TriggerOpt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithMaxPosition(position decimal.Decimal) TriggerOpt {
|
func WithMaxPosition(position float64) TriggerOpt {
|
||||||
return func(conf *triggerConfig) {
|
return func(conf *triggerConfig) {
|
||||||
conf.maxPosition = position
|
conf.maxPosition = position
|
||||||
}
|
}
|
||||||
@ -500,7 +500,7 @@ func (h *Handler) processEvents(ctx context.Context, config *triggerConfig) (add
|
|||||||
return additionalIteration, err
|
return additionalIteration, err
|
||||||
}
|
}
|
||||||
// stop execution if currentState.eventTimestamp >= config.maxCreatedAt
|
// stop execution if currentState.eventTimestamp >= config.maxCreatedAt
|
||||||
if !config.maxPosition.Equal(decimal.Decimal{}) && currentState.position.GreaterThanOrEqual(config.maxPosition) {
|
if config.maxPosition != 0 && currentState.position >= config.maxPosition {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,7 +576,7 @@ func (h *Handler) generateStatements(ctx context.Context, tx *sql.Tx, currentSta
|
|||||||
|
|
||||||
func skipPreviouslyReducedStatements(statements []*Statement, currentState *state) int {
|
func skipPreviouslyReducedStatements(statements []*Statement, currentState *state) int {
|
||||||
for i, statement := range statements {
|
for i, statement := range statements {
|
||||||
if statement.Position.Equal(currentState.position) &&
|
if statement.Position == currentState.position &&
|
||||||
statement.AggregateID == currentState.aggregateID &&
|
statement.AggregateID == currentState.aggregateID &&
|
||||||
statement.AggregateType == currentState.aggregateType &&
|
statement.AggregateType == currentState.aggregateType &&
|
||||||
statement.Sequence == currentState.sequence {
|
statement.Sequence == currentState.sequence {
|
||||||
@ -609,14 +609,14 @@ func (h *Handler) executeStatement(ctx context.Context, tx *sql.Tx, currentState
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.ExecContext(ctx, "SAVEPOINT exec")
|
_, err = tx.Exec("SAVEPOINT exec")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.log().WithError(err).Debug("create savepoint failed")
|
h.log().WithError(err).Debug("create savepoint failed")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var shouldContinue bool
|
var shouldContinue bool
|
||||||
defer func() {
|
defer func() {
|
||||||
_, errSave := tx.ExecContext(ctx, "RELEASE SAVEPOINT exec")
|
_, errSave := tx.Exec("RELEASE SAVEPOINT exec")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = errSave
|
err = errSave
|
||||||
}
|
}
|
||||||
@ -644,8 +644,9 @@ func (h *Handler) eventQuery(currentState *state) *eventstore.SearchQueryBuilder
|
|||||||
OrderAsc().
|
OrderAsc().
|
||||||
InstanceID(currentState.instanceID)
|
InstanceID(currentState.instanceID)
|
||||||
|
|
||||||
if currentState.position.GreaterThan(decimal.Decimal{}) {
|
if currentState.position > 0 {
|
||||||
builder = builder.PositionGreaterEqual(currentState.position)
|
// decrease position by 10 because builder.PositionAfter filters for position > and we need position >=
|
||||||
|
builder = builder.PositionAfter(math.Float64frombits(math.Float64bits(currentState.position) - 10))
|
||||||
if currentState.offset > 0 {
|
if currentState.offset > 0 {
|
||||||
builder = builder.Offset(currentState.offset)
|
builder = builder.Offset(currentState.offset)
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
@ -16,7 +14,7 @@ import (
|
|||||||
|
|
||||||
type state struct {
|
type state struct {
|
||||||
instanceID string
|
instanceID string
|
||||||
position decimal.Decimal
|
position float64
|
||||||
eventTimestamp time.Time
|
eventTimestamp time.Time
|
||||||
aggregateType eventstore.AggregateType
|
aggregateType eventstore.AggregateType
|
||||||
aggregateID string
|
aggregateID string
|
||||||
@ -47,7 +45,7 @@ func (h *Handler) currentState(ctx context.Context, tx *sql.Tx, config *triggerC
|
|||||||
aggregateType = new(sql.NullString)
|
aggregateType = new(sql.NullString)
|
||||||
sequence = new(sql.NullInt64)
|
sequence = new(sql.NullInt64)
|
||||||
timestamp = new(sql.NullTime)
|
timestamp = new(sql.NullTime)
|
||||||
position = new(decimal.NullDecimal)
|
position = new(sql.NullFloat64)
|
||||||
offset = new(sql.NullInt64)
|
offset = new(sql.NullInt64)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -77,7 +75,7 @@ func (h *Handler) currentState(ctx context.Context, tx *sql.Tx, config *triggerC
|
|||||||
currentState.aggregateType = eventstore.AggregateType(aggregateType.String)
|
currentState.aggregateType = eventstore.AggregateType(aggregateType.String)
|
||||||
currentState.sequence = uint64(sequence.Int64)
|
currentState.sequence = uint64(sequence.Int64)
|
||||||
currentState.eventTimestamp = timestamp.Time
|
currentState.eventTimestamp = timestamp.Time
|
||||||
currentState.position = position.Decimal
|
currentState.position = position.Float64
|
||||||
// psql does not provide unsigned numbers so we work around it
|
// psql does not provide unsigned numbers so we work around it
|
||||||
currentState.offset = uint32(offset.Int64)
|
currentState.offset = uint32(offset.Int64)
|
||||||
return currentState, nil
|
return currentState, nil
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgconn"
|
"github.com/jackc/pgx/v5/pgconn"
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
"github.com/zitadel/zitadel/internal/database/mock"
|
"github.com/zitadel/zitadel/internal/database/mock"
|
||||||
@ -167,7 +166,7 @@ func TestHandler_updateLastUpdated(t *testing.T) {
|
|||||||
updatedState: &state{
|
updatedState: &state{
|
||||||
instanceID: "instance",
|
instanceID: "instance",
|
||||||
eventTimestamp: time.Now(),
|
eventTimestamp: time.Now(),
|
||||||
position: decimal.NewFromInt(42),
|
position: 42,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isErr: func(t *testing.T, err error) {
|
isErr: func(t *testing.T, err error) {
|
||||||
@ -193,7 +192,7 @@ func TestHandler_updateLastUpdated(t *testing.T) {
|
|||||||
updatedState: &state{
|
updatedState: &state{
|
||||||
instanceID: "instance",
|
instanceID: "instance",
|
||||||
eventTimestamp: time.Now(),
|
eventTimestamp: time.Now(),
|
||||||
position: decimal.NewFromInt(42),
|
position: 42,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isErr: func(t *testing.T, err error) {
|
isErr: func(t *testing.T, err error) {
|
||||||
@ -218,7 +217,7 @@ func TestHandler_updateLastUpdated(t *testing.T) {
|
|||||||
eventstore.AggregateType("aggregate type"),
|
eventstore.AggregateType("aggregate type"),
|
||||||
uint64(42),
|
uint64(42),
|
||||||
mock.AnyType[time.Time]{},
|
mock.AnyType[time.Time]{},
|
||||||
decimal.NewFromInt(42),
|
float64(42),
|
||||||
uint32(0),
|
uint32(0),
|
||||||
),
|
),
|
||||||
mock.WithExecRowsAffected(1),
|
mock.WithExecRowsAffected(1),
|
||||||
@ -229,7 +228,7 @@ func TestHandler_updateLastUpdated(t *testing.T) {
|
|||||||
updatedState: &state{
|
updatedState: &state{
|
||||||
instanceID: "instance",
|
instanceID: "instance",
|
||||||
eventTimestamp: time.Now(),
|
eventTimestamp: time.Now(),
|
||||||
position: decimal.NewFromInt(42),
|
position: 42,
|
||||||
aggregateType: "aggregate type",
|
aggregateType: "aggregate type",
|
||||||
aggregateID: "aggregate id",
|
aggregateID: "aggregate id",
|
||||||
sequence: 42,
|
sequence: 42,
|
||||||
@ -398,7 +397,7 @@ func TestHandler_currentState(t *testing.T) {
|
|||||||
"aggregate type",
|
"aggregate type",
|
||||||
int64(42),
|
int64(42),
|
||||||
testTime,
|
testTime,
|
||||||
decimal.NewFromInt(42).String(),
|
float64(42),
|
||||||
uint16(10),
|
uint16(10),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -413,7 +412,7 @@ func TestHandler_currentState(t *testing.T) {
|
|||||||
currentState: &state{
|
currentState: &state{
|
||||||
instanceID: "instance",
|
instanceID: "instance",
|
||||||
eventTimestamp: testTime,
|
eventTimestamp: testTime,
|
||||||
position: decimal.NewFromInt(42),
|
position: 42,
|
||||||
aggregateType: "aggregate type",
|
aggregateType: "aggregate type",
|
||||||
aggregateID: "aggregate id",
|
aggregateID: "aggregate id",
|
||||||
sequence: 42,
|
sequence: 42,
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
|
|
||||||
@ -84,7 +83,7 @@ type Statement struct {
|
|||||||
AggregateType eventstore.AggregateType
|
AggregateType eventstore.AggregateType
|
||||||
AggregateID string
|
AggregateID string
|
||||||
Sequence uint64
|
Sequence uint64
|
||||||
Position decimal.Decimal
|
Position float64
|
||||||
CreationDate time.Time
|
CreationDate time.Time
|
||||||
InstanceID string
|
InstanceID string
|
||||||
|
|
||||||
|
@ -2,16 +2,13 @@ package eventstore_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cockroachdb/cockroach-go/v2/testserver"
|
"github.com/cockroachdb/cockroach-go/v2/testserver"
|
||||||
pgxdecimal "github.com/jackc/pgx-shopspring-decimal"
|
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
|
||||||
"github.com/jackc/pgx/v5/stdlib"
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/cmd/initialise"
|
"github.com/zitadel/zitadel/cmd/initialise"
|
||||||
@ -42,19 +39,10 @@ func TestMain(m *testing.M) {
|
|||||||
testCRDBClient = &database.DB{
|
testCRDBClient = &database.DB{
|
||||||
Database: new(testDB),
|
Database: new(testDB),
|
||||||
}
|
}
|
||||||
config, err := pgxpool.ParseConfig(ts.PGURL().String())
|
testCRDBClient.DB, err = sql.Open("postgres", ts.PGURL().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.WithFields("error", err).Fatal("unable to parse db config")
|
logging.WithFields("error", err).Fatal("unable to connect to db")
|
||||||
}
|
}
|
||||||
config.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
|
|
||||||
pgxdecimal.Register(conn.TypeMap())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
pool, err := pgxpool.NewWithConfig(context.Background(), config)
|
|
||||||
if err != nil {
|
|
||||||
logging.WithFields("error", err).Fatal("unable to create db pool")
|
|
||||||
}
|
|
||||||
testCRDBClient.DB = stdlib.OpenDBFromPool(pool)
|
|
||||||
if err = testCRDBClient.Ping(); err != nil {
|
if err = testCRDBClient.Ping(); err != nil {
|
||||||
logging.WithFields("error", err).Fatal("unable to ping db")
|
logging.WithFields("error", err).Fatal("unable to ping db")
|
||||||
}
|
}
|
||||||
@ -115,19 +103,10 @@ func initDB(db *database.DB) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func connectLocalhost() (*database.DB, error) {
|
func connectLocalhost() (*database.DB, error) {
|
||||||
config, err := pgxpool.ParseConfig("postgresql://root@localhost:26257/defaultdb?sslmode=disable")
|
client, err := sql.Open("pgx", "postgresql://root@localhost:26257/defaultdb?sslmode=disable")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
config.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
|
|
||||||
pgxdecimal.Register(conn.TypeMap())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
pool, err := pgxpool.NewWithConfig(context.Background(), config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
client := stdlib.OpenDBFromPool(pool)
|
|
||||||
if err = client.Ping(); err != nil {
|
if err = client.Ping(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,19 @@
|
|||||||
package eventstore
|
package eventstore
|
||||||
|
|
||||||
import (
|
import "time"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ReadModel is the minimum representation of a read model.
|
// ReadModel is the minimum representation of a read model.
|
||||||
// It implements a basic reducer
|
// It implements a basic reducer
|
||||||
// it might be saved in a database or in memory
|
// it might be saved in a database or in memory
|
||||||
type ReadModel struct {
|
type ReadModel struct {
|
||||||
AggregateID string `json:"-"`
|
AggregateID string `json:"-"`
|
||||||
ProcessedSequence uint64 `json:"-"`
|
ProcessedSequence uint64 `json:"-"`
|
||||||
CreationDate time.Time `json:"-"`
|
CreationDate time.Time `json:"-"`
|
||||||
ChangeDate time.Time `json:"-"`
|
ChangeDate time.Time `json:"-"`
|
||||||
Events []Event `json:"-"`
|
Events []Event `json:"-"`
|
||||||
ResourceOwner string `json:"-"`
|
ResourceOwner string `json:"-"`
|
||||||
InstanceID string `json:"-"`
|
InstanceID string `json:"-"`
|
||||||
Position decimal.Decimal `json:"-"`
|
Position float64 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendEvents adds all the events to the read model.
|
// AppendEvents adds all the events to the read model.
|
||||||
|
@ -5,8 +5,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,7 +18,7 @@ type Event struct {
|
|||||||
// Seq is the sequence of the event
|
// Seq is the sequence of the event
|
||||||
Seq uint64
|
Seq uint64
|
||||||
// Pos is the global sequence of the event multiple events can have the same sequence
|
// Pos is the global sequence of the event multiple events can have the same sequence
|
||||||
Pos decimal.Decimal
|
Pos float64
|
||||||
|
|
||||||
//CreationDate is the time the event is created
|
//CreationDate is the time the event is created
|
||||||
// it's used for human readability.
|
// it's used for human readability.
|
||||||
@ -93,7 +91,7 @@ func (e *Event) Sequence() uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Position implements [eventstore.Event]
|
// Position implements [eventstore.Event]
|
||||||
func (e *Event) Position() decimal.Decimal {
|
func (e *Event) Position() float64 {
|
||||||
return e.Pos
|
return e.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
context "context"
|
context "context"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
decimal "github.com/shopspring/decimal"
|
|
||||||
eventstore "github.com/zitadel/zitadel/internal/eventstore"
|
eventstore "github.com/zitadel/zitadel/internal/eventstore"
|
||||||
gomock "go.uber.org/mock/gomock"
|
gomock "go.uber.org/mock/gomock"
|
||||||
)
|
)
|
||||||
@ -84,19 +83,19 @@ func (mr *MockQuerierMockRecorder) InstanceIDs(arg0, arg1 any) *gomock.Call {
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceIDs", reflect.TypeOf((*MockQuerier)(nil).InstanceIDs), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceIDs", reflect.TypeOf((*MockQuerier)(nil).InstanceIDs), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LatestPosition mocks base method.
|
// LatestSequence mocks base method.
|
||||||
func (m *MockQuerier) LatestPosition(arg0 context.Context, arg1 *eventstore.SearchQueryBuilder) (decimal.Decimal, error) {
|
func (m *MockQuerier) LatestSequence(arg0 context.Context, arg1 *eventstore.SearchQueryBuilder) (float64, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "LatestPosition", arg0, arg1)
|
ret := m.ctrl.Call(m, "LatestSequence", arg0, arg1)
|
||||||
ret0, _ := ret[0].(decimal.Decimal)
|
ret0, _ := ret[0].(float64)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// LatestPosition indicates an expected call of LatestPosition.
|
// LatestSequence indicates an expected call of LatestSequence.
|
||||||
func (mr *MockQuerierMockRecorder) LatestPosition(arg0, arg1 any) *gomock.Call {
|
func (mr *MockQuerierMockRecorder) LatestSequence(arg0, arg1 any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestPosition", reflect.TypeOf((*MockQuerier)(nil).LatestPosition), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestSequence", reflect.TypeOf((*MockQuerier)(nil).LatestSequence), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockPusher is a mock of Pusher interface.
|
// MockPusher is a mock of Pusher interface.
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"go.uber.org/mock/gomock"
|
"go.uber.org/mock/gomock"
|
||||||
|
|
||||||
@ -187,8 +186,8 @@ func (e *mockEvent) Sequence() uint64 {
|
|||||||
return e.sequence
|
return e.sequence
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *mockEvent) Position() decimal.Decimal {
|
func (e *mockEvent) Position() float64 {
|
||||||
return decimal.Decimal{}
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *mockEvent) CreatedAt() time.Time {
|
func (e *mockEvent) CreatedAt() time.Time {
|
||||||
|
@ -55,8 +55,6 @@ const (
|
|||||||
//OperationNotIn checks if a stored value does not match one of the passed value list
|
//OperationNotIn checks if a stored value does not match one of the passed value list
|
||||||
OperationNotIn
|
OperationNotIn
|
||||||
|
|
||||||
OperationGreaterEqual
|
|
||||||
|
|
||||||
operationCount
|
operationCount
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -234,10 +232,10 @@ func instanceIDsFilter(builder *eventstore.SearchQueryBuilder, query *SearchQuer
|
|||||||
}
|
}
|
||||||
|
|
||||||
func positionAfterFilter(builder *eventstore.SearchQueryBuilder, query *SearchQuery) *Filter {
|
func positionAfterFilter(builder *eventstore.SearchQueryBuilder, query *SearchQuery) *Filter {
|
||||||
if builder.GetPositionAfter().IsZero() {
|
if builder.GetPositionAfter() == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
query.Position = NewFilter(FieldPosition, builder.GetPositionAfter(), OperationGreaterEqual)
|
query.Position = NewFilter(FieldPosition, builder.GetPositionAfter(), OperationGreater)
|
||||||
return query.Position
|
return query.Position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"github.com/cockroachdb/cockroach-go/v2/crdb"
|
"github.com/cockroachdb/cockroach-go/v2/crdb"
|
||||||
"github.com/jackc/pgx/v5/pgconn"
|
"github.com/jackc/pgx/v5/pgconn"
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
@ -266,11 +265,11 @@ func (crdb *CRDB) FilterToReducer(ctx context.Context, searchQuery *eventstore.S
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// LatestPosition returns the latest position found by the search query
|
// LatestSequence returns the latest sequence found by the search query
|
||||||
func (db *CRDB) LatestPosition(ctx context.Context, searchQuery *eventstore.SearchQueryBuilder) (decimal.Decimal, error) {
|
func (db *CRDB) LatestSequence(ctx context.Context, searchQuery *eventstore.SearchQueryBuilder) (float64, error) {
|
||||||
var position decimal.Decimal
|
var position sql.NullFloat64
|
||||||
err := query(ctx, db, searchQuery, &position, false)
|
err := query(ctx, db, searchQuery, &position, false)
|
||||||
return position, err
|
return position.Float64, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstanceIDs returns the instance ids found by the search query
|
// InstanceIDs returns the instance ids found by the search query
|
||||||
@ -337,7 +336,7 @@ func (db *CRDB) eventQuery(useV1 bool) string {
|
|||||||
" FROM eventstore.events2"
|
" FROM eventstore.events2"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *CRDB) maxPositionQuery(useV1 bool) string {
|
func (db *CRDB) maxSequenceQuery(useV1 bool) string {
|
||||||
if useV1 {
|
if useV1 {
|
||||||
return `SELECT event_sequence FROM eventstore.events`
|
return `SELECT event_sequence FROM eventstore.events`
|
||||||
}
|
}
|
||||||
@ -415,8 +414,6 @@ func (db *CRDB) operation(operation repository.Operation) string {
|
|||||||
return "="
|
return "="
|
||||||
case repository.OperationGreater:
|
case repository.OperationGreater:
|
||||||
return ">"
|
return ">"
|
||||||
case repository.OperationGreaterEqual:
|
|
||||||
return ">="
|
|
||||||
case repository.OperationLess:
|
case repository.OperationLess:
|
||||||
return "<"
|
return "<"
|
||||||
case repository.OperationJSONContains:
|
case repository.OperationJSONContains:
|
||||||
|
@ -4,8 +4,6 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||||
)
|
)
|
||||||
@ -314,7 +312,7 @@ func generateEvent(t *testing.T, aggregateID string, opts ...func(*repository.Ev
|
|||||||
ResourceOwner: sql.NullString{String: "ro", Valid: true},
|
ResourceOwner: sql.NullString{String: "ro", Valid: true},
|
||||||
Typ: "test.created",
|
Typ: "test.created",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
Pos: decimal.NewFromInt(42),
|
Pos: 42,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/call"
|
"github.com/zitadel/zitadel/internal/api/call"
|
||||||
@ -26,7 +25,7 @@ type querier interface {
|
|||||||
conditionFormat(repository.Operation) string
|
conditionFormat(repository.Operation) string
|
||||||
placeholder(query string) string
|
placeholder(query string) string
|
||||||
eventQuery(useV1 bool) string
|
eventQuery(useV1 bool) string
|
||||||
maxPositionQuery(useV1 bool) string
|
maxSequenceQuery(useV1 bool) string
|
||||||
instanceIDsQuery(useV1 bool) string
|
instanceIDsQuery(useV1 bool) string
|
||||||
db() *database.DB
|
db() *database.DB
|
||||||
orderByEventSequence(desc, shouldOrderBySequence, useV1 bool) string
|
orderByEventSequence(desc, shouldOrderBySequence, useV1 bool) string
|
||||||
@ -75,7 +74,7 @@ func query(ctx context.Context, criteria querier, searchQuery *eventstore.Search
|
|||||||
|
|
||||||
// instead of using the max function of the database (which doesn't work for postgres)
|
// instead of using the max function of the database (which doesn't work for postgres)
|
||||||
// we select the most recent row
|
// we select the most recent row
|
||||||
if q.Columns == eventstore.ColumnsMaxPosition {
|
if q.Columns == eventstore.ColumnsMaxSequence {
|
||||||
q.Limit = 1
|
q.Limit = 1
|
||||||
q.Desc = true
|
q.Desc = true
|
||||||
}
|
}
|
||||||
@ -92,7 +91,7 @@ func query(ctx context.Context, criteria querier, searchQuery *eventstore.Search
|
|||||||
|
|
||||||
switch q.Columns {
|
switch q.Columns {
|
||||||
case eventstore.ColumnsEvent,
|
case eventstore.ColumnsEvent,
|
||||||
eventstore.ColumnsMaxPosition:
|
eventstore.ColumnsMaxSequence:
|
||||||
query += criteria.orderByEventSequence(q.Desc, shouldOrderBySequence, useV1)
|
query += criteria.orderByEventSequence(q.Desc, shouldOrderBySequence, useV1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,8 +135,8 @@ func query(ctx context.Context, criteria querier, searchQuery *eventstore.Search
|
|||||||
|
|
||||||
func prepareColumns(criteria querier, columns eventstore.Columns, useV1 bool) (string, func(s scan, dest interface{}) error) {
|
func prepareColumns(criteria querier, columns eventstore.Columns, useV1 bool) (string, func(s scan, dest interface{}) error) {
|
||||||
switch columns {
|
switch columns {
|
||||||
case eventstore.ColumnsMaxPosition:
|
case eventstore.ColumnsMaxSequence:
|
||||||
return criteria.maxPositionQuery(useV1), maxPositionScanner
|
return criteria.maxSequenceQuery(useV1), maxSequenceScanner
|
||||||
case eventstore.ColumnsInstanceIDs:
|
case eventstore.ColumnsInstanceIDs:
|
||||||
return criteria.instanceIDsQuery(useV1), instanceIDsScanner
|
return criteria.instanceIDsQuery(useV1), instanceIDsScanner
|
||||||
case eventstore.ColumnsEvent:
|
case eventstore.ColumnsEvent:
|
||||||
@ -155,15 +154,13 @@ func prepareTimeTravel(ctx context.Context, criteria querier, allow bool) string
|
|||||||
return criteria.Timetravel(took)
|
return criteria.Timetravel(took)
|
||||||
}
|
}
|
||||||
|
|
||||||
func maxPositionScanner(row scan, dest interface{}) (err error) {
|
func maxSequenceScanner(row scan, dest interface{}) (err error) {
|
||||||
position, ok := dest.(*decimal.Decimal)
|
position, ok := dest.(*sql.NullFloat64)
|
||||||
if !ok {
|
if !ok {
|
||||||
return zerrors.ThrowInvalidArgumentf(nil, "SQL-NBjA9", "type must be decimal.Decimal got: %T", dest)
|
return zerrors.ThrowInvalidArgumentf(nil, "SQL-NBjA9", "type must be sql.NullInt64 got: %T", dest)
|
||||||
}
|
}
|
||||||
var res decimal.NullDecimal
|
err = row(position)
|
||||||
err = row(&res)
|
|
||||||
if err == nil || errors.Is(err, sql.ErrNoRows) {
|
if err == nil || errors.Is(err, sql.ErrNoRows) {
|
||||||
*position = res.Decimal
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return zerrors.ThrowInternal(err, "SQL-bN5xg", "something went wrong")
|
return zerrors.ThrowInternal(err, "SQL-bN5xg", "something went wrong")
|
||||||
@ -192,7 +189,7 @@ func eventsScanner(useV1 bool) func(scanner scan, dest interface{}) (err error)
|
|||||||
return zerrors.ThrowInvalidArgumentf(nil, "SQL-4GP6F", "events scanner: invalid type %T", dest)
|
return zerrors.ThrowInvalidArgumentf(nil, "SQL-4GP6F", "events scanner: invalid type %T", dest)
|
||||||
}
|
}
|
||||||
event := new(repository.Event)
|
event := new(repository.Event)
|
||||||
position := new(decimal.NullDecimal)
|
position := new(sql.NullFloat64)
|
||||||
|
|
||||||
if useV1 {
|
if useV1 {
|
||||||
err = scanner(
|
err = scanner(
|
||||||
@ -229,7 +226,7 @@ func eventsScanner(useV1 bool) func(scanner scan, dest interface{}) (err error)
|
|||||||
logging.New().WithError(err).Warn("unable to scan row")
|
logging.New().WithError(err).Warn("unable to scan row")
|
||||||
return zerrors.ThrowInternal(err, "SQL-M0dsf", "unable to scan row")
|
return zerrors.ThrowInternal(err, "SQL-M0dsf", "unable to scan row")
|
||||||
}
|
}
|
||||||
event.Pos = position.Decimal
|
event.Pos = position.Float64
|
||||||
return reduce(event)
|
return reduce(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/DATA-DOG/go-sqlmock"
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/database"
|
"github.com/zitadel/zitadel/internal/database"
|
||||||
@ -110,36 +109,36 @@ func Test_prepareColumns(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "max column",
|
name: "max column",
|
||||||
args: args{
|
args: args{
|
||||||
columns: eventstore.ColumnsMaxPosition,
|
columns: eventstore.ColumnsMaxSequence,
|
||||||
dest: new(decimal.Decimal),
|
dest: new(sql.NullFloat64),
|
||||||
useV1: true,
|
useV1: true,
|
||||||
},
|
},
|
||||||
res: res{
|
res: res{
|
||||||
query: `SELECT event_sequence FROM eventstore.events`,
|
query: `SELECT event_sequence FROM eventstore.events`,
|
||||||
expected: decimal.NewFromInt(42),
|
expected: sql.NullFloat64{Float64: 43, Valid: true},
|
||||||
},
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
dbRow: []interface{}{decimal.NewNullDecimal(decimal.NewFromInt(42))},
|
dbRow: []interface{}{sql.NullFloat64{Float64: 43, Valid: true}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "max column v2",
|
name: "max column v2",
|
||||||
args: args{
|
args: args{
|
||||||
columns: eventstore.ColumnsMaxPosition,
|
columns: eventstore.ColumnsMaxSequence,
|
||||||
dest: new(decimal.Decimal),
|
dest: new(sql.NullFloat64),
|
||||||
},
|
},
|
||||||
res: res{
|
res: res{
|
||||||
query: `SELECT "position" FROM eventstore.events2`,
|
query: `SELECT "position" FROM eventstore.events2`,
|
||||||
expected: decimal.NewFromInt(42),
|
expected: sql.NullFloat64{Float64: 43, Valid: true},
|
||||||
},
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
dbRow: []interface{}{decimal.NewNullDecimal(decimal.NewFromInt(42))},
|
dbRow: []interface{}{sql.NullFloat64{Float64: 43, Valid: true}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "max sequence wrong dest type",
|
name: "max sequence wrong dest type",
|
||||||
args: args{
|
args: args{
|
||||||
columns: eventstore.ColumnsMaxPosition,
|
columns: eventstore.ColumnsMaxSequence,
|
||||||
dest: new(uint64),
|
dest: new(uint64),
|
||||||
},
|
},
|
||||||
res: res{
|
res: res{
|
||||||
@ -179,11 +178,11 @@ func Test_prepareColumns(t *testing.T) {
|
|||||||
res: res{
|
res: res{
|
||||||
query: `SELECT created_at, event_type, "sequence", "position", payload, creator, "owner", instance_id, aggregate_type, aggregate_id, revision FROM eventstore.events2`,
|
query: `SELECT created_at, event_type, "sequence", "position", payload, creator, "owner", instance_id, aggregate_type, aggregate_id, revision FROM eventstore.events2`,
|
||||||
expected: []eventstore.Event{
|
expected: []eventstore.Event{
|
||||||
&repository.Event{AggregateID: "hodor", AggregateType: "user", Seq: 5, Pos: decimal.NewFromInt(42), Data: nil, Version: "v1"},
|
&repository.Event{AggregateID: "hodor", AggregateType: "user", Seq: 5, Pos: 42, Data: nil, Version: "v1"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
dbRow: []interface{}{time.Time{}, eventstore.EventType(""), uint64(5), decimal.NewNullDecimal(decimal.NewFromInt(42)), sql.RawBytes(nil), "", sql.NullString{}, "", eventstore.AggregateType("user"), "hodor", uint8(1)},
|
dbRow: []interface{}{time.Time{}, eventstore.EventType(""), uint64(5), sql.NullFloat64{Float64: 42, Valid: true}, sql.RawBytes(nil), "", sql.NullString{}, "", eventstore.AggregateType("user"), "hodor", uint8(1)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -198,11 +197,11 @@ func Test_prepareColumns(t *testing.T) {
|
|||||||
res: res{
|
res: res{
|
||||||
query: `SELECT created_at, event_type, "sequence", "position", payload, creator, "owner", instance_id, aggregate_type, aggregate_id, revision FROM eventstore.events2`,
|
query: `SELECT created_at, event_type, "sequence", "position", payload, creator, "owner", instance_id, aggregate_type, aggregate_id, revision FROM eventstore.events2`,
|
||||||
expected: []eventstore.Event{
|
expected: []eventstore.Event{
|
||||||
&repository.Event{AggregateID: "hodor", AggregateType: "user", Seq: 5, Pos: decimal.Decimal{}, Data: nil, Version: "v1"},
|
&repository.Event{AggregateID: "hodor", AggregateType: "user", Seq: 5, Pos: 0, Data: nil, Version: "v1"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
dbRow: []interface{}{time.Time{}, eventstore.EventType(""), uint64(5), decimal.NullDecimal{}, sql.RawBytes(nil), "", sql.NullString{}, "", eventstore.AggregateType("user"), "hodor", uint8(1)},
|
dbRow: []interface{}{time.Time{}, eventstore.EventType(""), uint64(5), sql.NullFloat64{Float64: 0, Valid: false}, sql.RawBytes(nil), "", sql.NullString{}, "", eventstore.AggregateType("user"), "hodor", uint8(1)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5,8 +5,6 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
)
|
)
|
||||||
@ -25,7 +23,7 @@ type SearchQueryBuilder struct {
|
|||||||
queries []*SearchQuery
|
queries []*SearchQuery
|
||||||
tx *sql.Tx
|
tx *sql.Tx
|
||||||
allowTimeTravel bool
|
allowTimeTravel bool
|
||||||
positionGreaterEqual decimal.Decimal
|
positionAfter float64
|
||||||
awaitOpenTransactions bool
|
awaitOpenTransactions bool
|
||||||
creationDateAfter time.Time
|
creationDateAfter time.Time
|
||||||
creationDateBefore time.Time
|
creationDateBefore time.Time
|
||||||
@ -76,8 +74,8 @@ func (b *SearchQueryBuilder) GetAllowTimeTravel() bool {
|
|||||||
return b.allowTimeTravel
|
return b.allowTimeTravel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b SearchQueryBuilder) GetPositionAfter() decimal.Decimal {
|
func (b SearchQueryBuilder) GetPositionAfter() float64 {
|
||||||
return b.positionGreaterEqual
|
return b.positionAfter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b SearchQueryBuilder) GetAwaitOpenTransactions() bool {
|
func (b SearchQueryBuilder) GetAwaitOpenTransactions() bool {
|
||||||
@ -133,8 +131,8 @@ type Columns int8
|
|||||||
const (
|
const (
|
||||||
//ColumnsEvent represents all fields of an event
|
//ColumnsEvent represents all fields of an event
|
||||||
ColumnsEvent = iota + 1
|
ColumnsEvent = iota + 1
|
||||||
// ColumnsMaxPosition represents the latest sequence of the filtered events
|
// ColumnsMaxSequence represents the latest sequence of the filtered events
|
||||||
ColumnsMaxPosition
|
ColumnsMaxSequence
|
||||||
// ColumnsInstanceIDs represents the instance ids of the filtered events
|
// ColumnsInstanceIDs represents the instance ids of the filtered events
|
||||||
ColumnsInstanceIDs
|
ColumnsInstanceIDs
|
||||||
|
|
||||||
@ -269,8 +267,8 @@ func (builder *SearchQueryBuilder) AllowTimeTravel() *SearchQueryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PositionAfter filters for events which happened after the specified time
|
// PositionAfter filters for events which happened after the specified time
|
||||||
func (builder *SearchQueryBuilder) PositionGreaterEqual(position decimal.Decimal) *SearchQueryBuilder {
|
func (builder *SearchQueryBuilder) PositionAfter(position float64) *SearchQueryBuilder {
|
||||||
builder.positionGreaterEqual = position
|
builder.positionAfter = position
|
||||||
return builder
|
return builder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,10 +116,10 @@ func TestSearchQuerybuilderSetters(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "set columns",
|
name: "set columns",
|
||||||
args: args{
|
args: args{
|
||||||
setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{testSetColumns(ColumnsMaxPosition)},
|
setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{testSetColumns(ColumnsMaxSequence)},
|
||||||
},
|
},
|
||||||
res: &SearchQueryBuilder{
|
res: &SearchQueryBuilder{
|
||||||
columns: ColumnsMaxPosition,
|
columns: ColumnsMaxSequence,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5,8 +5,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
)
|
)
|
||||||
@ -22,7 +20,7 @@ var _ eventstore.Event = (*Event)(nil)
|
|||||||
type Event struct {
|
type Event struct {
|
||||||
ID string
|
ID string
|
||||||
Seq uint64
|
Seq uint64
|
||||||
Pos decimal.Decimal
|
Pos float64
|
||||||
CreationDate time.Time
|
CreationDate time.Time
|
||||||
Typ eventstore.EventType
|
Typ eventstore.EventType
|
||||||
PreviousSequence uint64
|
PreviousSequence uint64
|
||||||
@ -82,7 +80,7 @@ func (e *Event) Sequence() uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Position implements [eventstore.Event]
|
// Position implements [eventstore.Event]
|
||||||
func (e *Event) Position() decimal.Decimal {
|
func (e *Event) Position() float64 {
|
||||||
return e.Pos
|
return e.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
@ -22,7 +21,7 @@ type event struct {
|
|||||||
typ eventstore.EventType
|
typ eventstore.EventType
|
||||||
createdAt time.Time
|
createdAt time.Time
|
||||||
sequence uint64
|
sequence uint64
|
||||||
position decimal.Decimal
|
position float64
|
||||||
payload Payload
|
payload Payload
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,8 +84,8 @@ func (e *event) Sequence() uint64 {
|
|||||||
return e.sequence
|
return e.sequence
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position implements [eventstore.Event]
|
// Sequence implements [eventstore.Event]
|
||||||
func (e *event) Position() decimal.Decimal {
|
func (e *event) Position() float64 {
|
||||||
return e.position
|
return e.position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/zitadel/zitadel/pkg/grpc/authn"
|
"github.com/zitadel/zitadel/pkg/grpc/authn"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/management"
|
"github.com/zitadel/zitadel/pkg/grpc/management"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/user"
|
"github.com/zitadel/zitadel/pkg/grpc/user"
|
||||||
|
user_v2 "github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i *Instance) CreateOIDCClient(ctx context.Context, redirectURI, logoutRedirectURI, projectID string, appType app.OIDCAppType, authMethod app.OIDCAuthMethodType, devMode bool, grantTypes ...app.OIDCGrantType) (*management.AddOIDCAppResponse, error) {
|
func (i *Instance) CreateOIDCClient(ctx context.Context, redirectURI, logoutRedirectURI, projectID string, appType app.OIDCAppType, authMethod app.OIDCAuthMethodType, devMode bool, grantTypes ...app.OIDCGrantType) (*management.AddOIDCAppResponse, error) {
|
||||||
@ -107,6 +108,20 @@ func (i *Instance) CreateOIDCInactivateClient(ctx context.Context, redirectURI,
|
|||||||
return client, err
|
return client, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Instance) CreateOIDCInactivateProjectClient(ctx context.Context, redirectURI, logoutRedirectURI, projectID string) (*management.AddOIDCAppResponse, error) {
|
||||||
|
client, err := i.CreateOIDCNativeClient(ctx, redirectURI, logoutRedirectURI, projectID, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = i.Client.Mgmt.DeactivateProject(ctx, &management.DeactivateProjectRequest{
|
||||||
|
Id: projectID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return client, err
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Instance) CreateOIDCImplicitFlowClient(ctx context.Context, redirectURI string) (*management.AddOIDCAppResponse, error) {
|
func (i *Instance) CreateOIDCImplicitFlowClient(ctx context.Context, redirectURI string) (*management.AddOIDCAppResponse, error) {
|
||||||
project, err := i.Client.Mgmt.AddProject(ctx, &management.AddProjectRequest{
|
project, err := i.Client.Mgmt.AddProject(ctx, &management.AddProjectRequest{
|
||||||
Name: fmt.Sprintf("project-%d", time.Now().UnixNano()),
|
Name: fmt.Sprintf("project-%d", time.Now().UnixNano()),
|
||||||
@ -341,6 +356,31 @@ func (i *Instance) CreateOIDCCredentialsClient(ctx context.Context) (machine *ma
|
|||||||
return machine, name, secret.GetClientId(), secret.GetClientSecret(), nil
|
return machine, name, secret.GetClientId(), secret.GetClientSecret(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Instance) CreateOIDCCredentialsClientInactive(ctx context.Context) (machine *management.AddMachineUserResponse, name, clientID, clientSecret string, err error) {
|
||||||
|
name = gofakeit.Username()
|
||||||
|
machine, err = i.Client.Mgmt.AddMachineUser(ctx, &management.AddMachineUserRequest{
|
||||||
|
Name: name,
|
||||||
|
UserName: name,
|
||||||
|
AccessTokenType: user.AccessTokenType_ACCESS_TOKEN_TYPE_JWT,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", "", "", err
|
||||||
|
}
|
||||||
|
secret, err := i.Client.Mgmt.GenerateMachineSecret(ctx, &management.GenerateMachineSecretRequest{
|
||||||
|
UserId: machine.GetUserId(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", "", "", err
|
||||||
|
}
|
||||||
|
_, err = i.Client.UserV2.DeactivateUser(ctx, &user_v2.DeactivateUserRequest{
|
||||||
|
UserId: machine.GetUserId(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", "", "", err
|
||||||
|
}
|
||||||
|
return machine, name, secret.GetClientId(), secret.GetClientSecret(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Instance) CreateOIDCJWTProfileClient(ctx context.Context) (machine *management.AddMachineUserResponse, name string, keyData []byte, err error) {
|
func (i *Instance) CreateOIDCJWTProfileClient(ctx context.Context) (machine *management.AddMachineUserResponse, name string, keyData []byte, err error) {
|
||||||
name = gofakeit.Username()
|
name = gofakeit.Username()
|
||||||
machine, err = i.Client.Mgmt.AddMachineUser(ctx, &management.AddMachineUserRequest{
|
machine, err = i.Client.Mgmt.AddMachineUser(ctx, &management.AddMachineUserRequest{
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
@ -141,7 +140,7 @@ func (q *Queries) accessTokenByOIDCSessionAndTokenID(ctx context.Context, oidcSe
|
|||||||
|
|
||||||
// checkSessionNotTerminatedAfter checks if a [session.TerminateType] event (or user events leading to a session termination)
|
// checkSessionNotTerminatedAfter checks if a [session.TerminateType] event (or user events leading to a session termination)
|
||||||
// occurred after a certain time and will return an error if so.
|
// occurred after a certain time and will return an error if so.
|
||||||
func (q *Queries) checkSessionNotTerminatedAfter(ctx context.Context, sessionID, userID string, position decimal.Decimal, fingerprintID string) (err error) {
|
func (q *Queries) checkSessionNotTerminatedAfter(ctx context.Context, sessionID, userID string, position float64, fingerprintID string) (err error) {
|
||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
@ -163,7 +162,7 @@ func (q *Queries) checkSessionNotTerminatedAfter(ctx context.Context, sessionID,
|
|||||||
}
|
}
|
||||||
|
|
||||||
type sessionTerminatedModel struct {
|
type sessionTerminatedModel struct {
|
||||||
position decimal.Decimal
|
position float64
|
||||||
sessionID string
|
sessionID string
|
||||||
userID string
|
userID string
|
||||||
fingerPrintID string
|
fingerPrintID string
|
||||||
@ -183,7 +182,7 @@ func (s *sessionTerminatedModel) AppendEvents(events ...eventstore.Event) {
|
|||||||
|
|
||||||
func (s *sessionTerminatedModel) Query() *eventstore.SearchQueryBuilder {
|
func (s *sessionTerminatedModel) Query() *eventstore.SearchQueryBuilder {
|
||||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||||
PositionGreaterEqual(s.position).
|
PositionAfter(s.position).
|
||||||
AddQuery().
|
AddQuery().
|
||||||
AggregateTypes(session.AggregateType).
|
AggregateTypes(session.AggregateType).
|
||||||
AggregateIDs(s.sessionID).
|
AggregateIDs(s.sessionID).
|
||||||
|
@ -256,7 +256,7 @@ func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bo
|
|||||||
traceSpan.EndWithError(err)
|
traceSpan.EndWithError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt, scan := prepareAppQuery(ctx, q.client)
|
stmt, scan := prepareAppQuery(ctx, q.client, false)
|
||||||
eq := sq.Eq{
|
eq := sq.Eq{
|
||||||
AppColumnID.identifier(): appID,
|
AppColumnID.identifier(): appID,
|
||||||
AppColumnProjectID.identifier(): projectID,
|
AppColumnProjectID.identifier(): projectID,
|
||||||
@ -274,15 +274,20 @@ func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bo
|
|||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) AppByID(ctx context.Context, appID string) (app *App, err error) {
|
func (q *Queries) AppByID(ctx context.Context, appID string, activeOnly bool) (app *App, err error) {
|
||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
stmt, scan := prepareAppQuery(ctx, q.client)
|
stmt, scan := prepareAppQuery(ctx, q.client, activeOnly)
|
||||||
eq := sq.Eq{
|
eq := sq.Eq{
|
||||||
AppColumnID.identifier(): appID,
|
AppColumnID.identifier(): appID,
|
||||||
AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||||
}
|
}
|
||||||
|
if activeOnly {
|
||||||
|
eq[AppColumnState.identifier()] = domain.AppStateActive
|
||||||
|
eq[ProjectColumnState.identifier()] = domain.ProjectStateActive
|
||||||
|
eq[OrgColumnState.identifier()] = domain.OrgStateActive
|
||||||
|
}
|
||||||
query, args, err := stmt.Where(eq).ToSql()
|
query, args, err := stmt.Where(eq).ToSql()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, zerrors.ThrowInternal(err, "QUERY-immt9", "Errors.Query.SQLStatement")
|
return nil, zerrors.ThrowInternal(err, "QUERY-immt9", "Errors.Query.SQLStatement")
|
||||||
@ -295,7 +300,7 @@ func (q *Queries) AppByID(ctx context.Context, appID string) (app *App, err erro
|
|||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) AppBySAMLEntityID(ctx context.Context, entityID string) (app *App, err error) {
|
func (q *Queries) ActiveAppBySAMLEntityID(ctx context.Context, entityID string) (app *App, err error) {
|
||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
@ -303,6 +308,9 @@ func (q *Queries) AppBySAMLEntityID(ctx context.Context, entityID string) (app *
|
|||||||
eq := sq.Eq{
|
eq := sq.Eq{
|
||||||
AppSAMLConfigColumnEntityID.identifier(): entityID,
|
AppSAMLConfigColumnEntityID.identifier(): entityID,
|
||||||
AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||||
|
AppColumnState.identifier(): domain.AppStateActive,
|
||||||
|
ProjectColumnState.identifier(): domain.ProjectStateActive,
|
||||||
|
OrgColumnState.identifier(): domain.OrgStateActive,
|
||||||
}
|
}
|
||||||
query, args, err := stmt.Where(eq).ToSql()
|
query, args, err := stmt.Where(eq).ToSql()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -413,8 +421,13 @@ func (q *Queries) AppByClientID(ctx context.Context, clientID string) (app *App,
|
|||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
stmt, scan := prepareAppQuery(ctx, q.client)
|
stmt, scan := prepareAppQuery(ctx, q.client, true)
|
||||||
eq := sq.Eq{AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()}
|
eq := sq.Eq{
|
||||||
|
AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||||
|
AppColumnState.identifier(): domain.AppStateActive,
|
||||||
|
ProjectColumnState.identifier(): domain.ProjectStateActive,
|
||||||
|
OrgColumnState.identifier(): domain.OrgStateActive,
|
||||||
|
}
|
||||||
query, args, err := stmt.Where(sq.And{
|
query, args, err := stmt.Where(sq.And{
|
||||||
eq,
|
eq,
|
||||||
sq.Or{
|
sq.Or{
|
||||||
@ -491,107 +504,121 @@ func NewAppProjectIDSearchQuery(id string) (SearchQuery, error) {
|
|||||||
return NewTextQuery(AppColumnProjectID, id, TextEquals)
|
return NewTextQuery(AppColumnProjectID, id, TextEquals)
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareAppQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
func prepareAppQuery(ctx context.Context, db prepareDatabase, activeOnly bool) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||||
return sq.Select(
|
query := sq.Select(
|
||||||
AppColumnID.identifier(),
|
AppColumnID.identifier(),
|
||||||
AppColumnName.identifier(),
|
AppColumnName.identifier(),
|
||||||
AppColumnProjectID.identifier(),
|
AppColumnProjectID.identifier(),
|
||||||
AppColumnCreationDate.identifier(),
|
AppColumnCreationDate.identifier(),
|
||||||
AppColumnChangeDate.identifier(),
|
AppColumnChangeDate.identifier(),
|
||||||
AppColumnResourceOwner.identifier(),
|
AppColumnResourceOwner.identifier(),
|
||||||
AppColumnState.identifier(),
|
AppColumnState.identifier(),
|
||||||
AppColumnSequence.identifier(),
|
AppColumnSequence.identifier(),
|
||||||
|
|
||||||
AppAPIConfigColumnAppID.identifier(),
|
AppAPIConfigColumnAppID.identifier(),
|
||||||
AppAPIConfigColumnClientID.identifier(),
|
AppAPIConfigColumnClientID.identifier(),
|
||||||
AppAPIConfigColumnAuthMethod.identifier(),
|
AppAPIConfigColumnAuthMethod.identifier(),
|
||||||
|
|
||||||
AppOIDCConfigColumnAppID.identifier(),
|
AppOIDCConfigColumnAppID.identifier(),
|
||||||
AppOIDCConfigColumnVersion.identifier(),
|
AppOIDCConfigColumnVersion.identifier(),
|
||||||
AppOIDCConfigColumnClientID.identifier(),
|
AppOIDCConfigColumnClientID.identifier(),
|
||||||
AppOIDCConfigColumnRedirectUris.identifier(),
|
AppOIDCConfigColumnRedirectUris.identifier(),
|
||||||
AppOIDCConfigColumnResponseTypes.identifier(),
|
AppOIDCConfigColumnResponseTypes.identifier(),
|
||||||
AppOIDCConfigColumnGrantTypes.identifier(),
|
AppOIDCConfigColumnGrantTypes.identifier(),
|
||||||
AppOIDCConfigColumnApplicationType.identifier(),
|
AppOIDCConfigColumnApplicationType.identifier(),
|
||||||
AppOIDCConfigColumnAuthMethodType.identifier(),
|
AppOIDCConfigColumnAuthMethodType.identifier(),
|
||||||
AppOIDCConfigColumnPostLogoutRedirectUris.identifier(),
|
AppOIDCConfigColumnPostLogoutRedirectUris.identifier(),
|
||||||
AppOIDCConfigColumnDevMode.identifier(),
|
AppOIDCConfigColumnDevMode.identifier(),
|
||||||
AppOIDCConfigColumnAccessTokenType.identifier(),
|
AppOIDCConfigColumnAccessTokenType.identifier(),
|
||||||
AppOIDCConfigColumnAccessTokenRoleAssertion.identifier(),
|
AppOIDCConfigColumnAccessTokenRoleAssertion.identifier(),
|
||||||
AppOIDCConfigColumnIDTokenRoleAssertion.identifier(),
|
AppOIDCConfigColumnIDTokenRoleAssertion.identifier(),
|
||||||
AppOIDCConfigColumnIDTokenUserinfoAssertion.identifier(),
|
AppOIDCConfigColumnIDTokenUserinfoAssertion.identifier(),
|
||||||
AppOIDCConfigColumnClockSkew.identifier(),
|
AppOIDCConfigColumnClockSkew.identifier(),
|
||||||
AppOIDCConfigColumnAdditionalOrigins.identifier(),
|
AppOIDCConfigColumnAdditionalOrigins.identifier(),
|
||||||
AppOIDCConfigColumnSkipNativeAppSuccessPage.identifier(),
|
AppOIDCConfigColumnSkipNativeAppSuccessPage.identifier(),
|
||||||
|
|
||||||
AppSAMLConfigColumnAppID.identifier(),
|
AppSAMLConfigColumnAppID.identifier(),
|
||||||
AppSAMLConfigColumnEntityID.identifier(),
|
AppSAMLConfigColumnEntityID.identifier(),
|
||||||
AppSAMLConfigColumnMetadata.identifier(),
|
AppSAMLConfigColumnMetadata.identifier(),
|
||||||
AppSAMLConfigColumnMetadataURL.identifier(),
|
AppSAMLConfigColumnMetadataURL.identifier(),
|
||||||
).From(appsTable.identifier()).
|
).From(appsTable.identifier()).
|
||||||
|
PlaceholderFormat(sq.Dollar)
|
||||||
|
|
||||||
|
if activeOnly {
|
||||||
|
return query.
|
||||||
|
LeftJoin(join(AppAPIConfigColumnAppID, AppColumnID)).
|
||||||
|
LeftJoin(join(AppOIDCConfigColumnAppID, AppColumnID)).
|
||||||
|
LeftJoin(join(AppSAMLConfigColumnAppID, AppColumnID)).
|
||||||
|
LeftJoin(join(ProjectColumnID, AppColumnProjectID)).
|
||||||
|
LeftJoin(join(OrgColumnID, AppColumnResourceOwner) + db.Timetravel(call.Took(ctx))),
|
||||||
|
scanApp
|
||||||
|
}
|
||||||
|
return query.
|
||||||
LeftJoin(join(AppAPIConfigColumnAppID, AppColumnID)).
|
LeftJoin(join(AppAPIConfigColumnAppID, AppColumnID)).
|
||||||
LeftJoin(join(AppOIDCConfigColumnAppID, AppColumnID)).
|
LeftJoin(join(AppOIDCConfigColumnAppID, AppColumnID)).
|
||||||
LeftJoin(join(AppSAMLConfigColumnAppID, AppColumnID) + db.Timetravel(call.Took(ctx))).
|
LeftJoin(join(AppSAMLConfigColumnAppID, AppColumnID) + db.Timetravel(call.Took(ctx))),
|
||||||
PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*App, error) {
|
scanApp
|
||||||
app := new(App)
|
}
|
||||||
|
|
||||||
var (
|
func scanApp(row *sql.Row) (*App, error) {
|
||||||
apiConfig = sqlAPIConfig{}
|
app := new(App)
|
||||||
oidcConfig = sqlOIDCConfig{}
|
|
||||||
samlConfig = sqlSAMLConfig{}
|
|
||||||
)
|
|
||||||
|
|
||||||
err := row.Scan(
|
var (
|
||||||
&app.ID,
|
apiConfig = sqlAPIConfig{}
|
||||||
&app.Name,
|
oidcConfig = sqlOIDCConfig{}
|
||||||
&app.ProjectID,
|
samlConfig = sqlSAMLConfig{}
|
||||||
&app.CreationDate,
|
)
|
||||||
&app.ChangeDate,
|
|
||||||
&app.ResourceOwner,
|
|
||||||
&app.State,
|
|
||||||
&app.Sequence,
|
|
||||||
|
|
||||||
&apiConfig.appID,
|
err := row.Scan(
|
||||||
&apiConfig.clientID,
|
&app.ID,
|
||||||
&apiConfig.authMethod,
|
&app.Name,
|
||||||
|
&app.ProjectID,
|
||||||
|
&app.CreationDate,
|
||||||
|
&app.ChangeDate,
|
||||||
|
&app.ResourceOwner,
|
||||||
|
&app.State,
|
||||||
|
&app.Sequence,
|
||||||
|
|
||||||
&oidcConfig.appID,
|
&apiConfig.appID,
|
||||||
&oidcConfig.version,
|
&apiConfig.clientID,
|
||||||
&oidcConfig.clientID,
|
&apiConfig.authMethod,
|
||||||
&oidcConfig.redirectUris,
|
|
||||||
&oidcConfig.responseTypes,
|
|
||||||
&oidcConfig.grantTypes,
|
|
||||||
&oidcConfig.applicationType,
|
|
||||||
&oidcConfig.authMethodType,
|
|
||||||
&oidcConfig.postLogoutRedirectUris,
|
|
||||||
&oidcConfig.devMode,
|
|
||||||
&oidcConfig.accessTokenType,
|
|
||||||
&oidcConfig.accessTokenRoleAssertion,
|
|
||||||
&oidcConfig.iDTokenRoleAssertion,
|
|
||||||
&oidcConfig.iDTokenUserinfoAssertion,
|
|
||||||
&oidcConfig.clockSkew,
|
|
||||||
&oidcConfig.additionalOrigins,
|
|
||||||
&oidcConfig.skipNativeAppSuccessPage,
|
|
||||||
|
|
||||||
&samlConfig.appID,
|
&oidcConfig.appID,
|
||||||
&samlConfig.entityID,
|
&oidcConfig.version,
|
||||||
&samlConfig.metadata,
|
&oidcConfig.clientID,
|
||||||
&samlConfig.metadataURL,
|
&oidcConfig.redirectUris,
|
||||||
)
|
&oidcConfig.responseTypes,
|
||||||
|
&oidcConfig.grantTypes,
|
||||||
|
&oidcConfig.applicationType,
|
||||||
|
&oidcConfig.authMethodType,
|
||||||
|
&oidcConfig.postLogoutRedirectUris,
|
||||||
|
&oidcConfig.devMode,
|
||||||
|
&oidcConfig.accessTokenType,
|
||||||
|
&oidcConfig.accessTokenRoleAssertion,
|
||||||
|
&oidcConfig.iDTokenRoleAssertion,
|
||||||
|
&oidcConfig.iDTokenUserinfoAssertion,
|
||||||
|
&oidcConfig.clockSkew,
|
||||||
|
&oidcConfig.additionalOrigins,
|
||||||
|
&oidcConfig.skipNativeAppSuccessPage,
|
||||||
|
|
||||||
if err != nil {
|
&samlConfig.appID,
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
&samlConfig.entityID,
|
||||||
return nil, zerrors.ThrowNotFound(err, "QUERY-pCP8P", "Errors.App.NotExisting")
|
&samlConfig.metadata,
|
||||||
}
|
&samlConfig.metadataURL,
|
||||||
return nil, zerrors.ThrowInternal(err, "QUERY-4SJlx", "Errors.Internal")
|
)
|
||||||
}
|
|
||||||
|
|
||||||
apiConfig.set(app)
|
if err != nil {
|
||||||
oidcConfig.set(app)
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
samlConfig.set(app)
|
return nil, zerrors.ThrowNotFound(err, "QUERY-pCP8P", "Errors.App.NotExisting")
|
||||||
|
|
||||||
return app, nil
|
|
||||||
}
|
}
|
||||||
|
return nil, zerrors.ThrowInternal(err, "QUERY-4SJlx", "Errors.Internal")
|
||||||
|
}
|
||||||
|
|
||||||
|
apiConfig.set(app)
|
||||||
|
oidcConfig.set(app)
|
||||||
|
samlConfig.set(app)
|
||||||
|
|
||||||
|
return app, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareOIDCAppQuery() (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
func prepareOIDCAppQuery() (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||||
@ -690,6 +717,8 @@ func prepareSAMLAppQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil
|
|||||||
AppSAMLConfigColumnMetadataURL.identifier(),
|
AppSAMLConfigColumnMetadataURL.identifier(),
|
||||||
).From(appsTable.identifier()).
|
).From(appsTable.identifier()).
|
||||||
Join(join(AppSAMLConfigColumnAppID, AppColumnID)).
|
Join(join(AppSAMLConfigColumnAppID, AppColumnID)).
|
||||||
|
Join(join(ProjectColumnID, AppColumnProjectID)).
|
||||||
|
Join(join(OrgColumnID, AppColumnResourceOwner)).
|
||||||
PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*App, error) {
|
PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*App, error) {
|
||||||
|
|
||||||
app := new(App)
|
app := new(App)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package query
|
package query
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"errors"
|
"errors"
|
||||||
@ -9,13 +10,15 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
sq "github.com/Masterminds/squirrel"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/database"
|
"github.com/zitadel/zitadel/internal/database"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
expectedAppQuery = regexp.QuoteMeta(`SELECT projections.apps7.id,` +
|
expectedAppQueryBase = `SELECT projections.apps7.id,` +
|
||||||
` projections.apps7.name,` +
|
` projections.apps7.name,` +
|
||||||
` projections.apps7.project_id,` +
|
` projections.apps7.project_id,` +
|
||||||
` projections.apps7.creation_date,` +
|
` projections.apps7.creation_date,` +
|
||||||
@ -53,8 +56,11 @@ var (
|
|||||||
` FROM projections.apps7` +
|
` FROM projections.apps7` +
|
||||||
` LEFT JOIN projections.apps7_api_configs ON projections.apps7.id = projections.apps7_api_configs.app_id AND projections.apps7.instance_id = projections.apps7_api_configs.instance_id` +
|
` LEFT JOIN projections.apps7_api_configs ON projections.apps7.id = projections.apps7_api_configs.app_id AND projections.apps7.instance_id = projections.apps7_api_configs.instance_id` +
|
||||||
` LEFT JOIN projections.apps7_oidc_configs ON projections.apps7.id = projections.apps7_oidc_configs.app_id AND projections.apps7.instance_id = projections.apps7_oidc_configs.instance_id` +
|
` LEFT JOIN projections.apps7_oidc_configs ON projections.apps7.id = projections.apps7_oidc_configs.app_id AND projections.apps7.instance_id = projections.apps7_oidc_configs.instance_id` +
|
||||||
` LEFT JOIN projections.apps7_saml_configs ON projections.apps7.id = projections.apps7_saml_configs.app_id AND projections.apps7.instance_id = projections.apps7_saml_configs.instance_id` +
|
` LEFT JOIN projections.apps7_saml_configs ON projections.apps7.id = projections.apps7_saml_configs.app_id AND projections.apps7.instance_id = projections.apps7_saml_configs.instance_id`
|
||||||
` AS OF SYSTEM TIME '-1 ms'`)
|
expectedAppQuery = regexp.QuoteMeta(expectedAppQueryBase)
|
||||||
|
expectedActiveAppQuery = regexp.QuoteMeta(expectedAppQueryBase +
|
||||||
|
` LEFT JOIN projections.projects4 ON projections.apps7.project_id = projections.projects4.id AND projections.apps7.instance_id = projections.projects4.instance_id` +
|
||||||
|
` LEFT JOIN projections.orgs1 ON projections.apps7.resource_owner = projections.orgs1.id AND projections.apps7.instance_id = projections.orgs1.instance_id`)
|
||||||
expectedAppsQuery = regexp.QuoteMeta(`SELECT projections.apps7.id,` +
|
expectedAppsQuery = regexp.QuoteMeta(`SELECT projections.apps7.id,` +
|
||||||
` projections.apps7.name,` +
|
` projections.apps7.name,` +
|
||||||
` projections.apps7.project_id,` +
|
` projections.apps7.project_id,` +
|
||||||
@ -1140,8 +1146,10 @@ func Test_AppPrepare(t *testing.T) {
|
|||||||
object interface{}
|
object interface{}
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "prepareAppQuery no result",
|
name: "prepareAppQuery no result",
|
||||||
prepare: prepareAppQuery,
|
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||||
|
return prepareAppQuery(ctx, db, false)
|
||||||
|
},
|
||||||
want: want{
|
want: want{
|
||||||
sqlExpectations: mockQueriesScanErr(
|
sqlExpectations: mockQueriesScanErr(
|
||||||
expectedAppQuery,
|
expectedAppQuery,
|
||||||
@ -1158,8 +1166,10 @@ func Test_AppPrepare(t *testing.T) {
|
|||||||
object: (*App)(nil),
|
object: (*App)(nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "prepareAppQuery found",
|
name: "prepareAppQuery found",
|
||||||
prepare: prepareAppQuery,
|
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||||
|
return prepareAppQuery(ctx, db, false)
|
||||||
|
},
|
||||||
want: want{
|
want: want{
|
||||||
sqlExpectations: mockQuery(
|
sqlExpectations: mockQuery(
|
||||||
expectedAppQuery,
|
expectedAppQuery,
|
||||||
@ -1215,8 +1225,10 @@ func Test_AppPrepare(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "prepareAppQuery api app",
|
name: "prepareAppQuery api app",
|
||||||
prepare: prepareAppQuery,
|
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||||
|
return prepareAppQuery(ctx, db, false)
|
||||||
|
},
|
||||||
want: want{
|
want: want{
|
||||||
sqlExpectations: mockQueries(
|
sqlExpectations: mockQueries(
|
||||||
expectedAppQuery,
|
expectedAppQuery,
|
||||||
@ -1278,8 +1290,10 @@ func Test_AppPrepare(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "prepareAppQuery oidc app",
|
name: "prepareAppQuery oidc app",
|
||||||
prepare: prepareAppQuery,
|
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||||
|
return prepareAppQuery(ctx, db, false)
|
||||||
|
},
|
||||||
want: want{
|
want: want{
|
||||||
sqlExpectations: mockQueries(
|
sqlExpectations: mockQueries(
|
||||||
expectedAppQuery,
|
expectedAppQuery,
|
||||||
@ -1355,9 +1369,93 @@ func Test_AppPrepare(t *testing.T) {
|
|||||||
SkipNativeAppSuccessPage: false,
|
SkipNativeAppSuccessPage: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
},
|
||||||
name: "prepareAppQuery saml app",
|
{
|
||||||
prepare: prepareAppQuery,
|
name: "prepareAppQuery oidc app active only",
|
||||||
|
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||||
|
return prepareAppQuery(ctx, db, true)
|
||||||
|
},
|
||||||
|
want: want{
|
||||||
|
sqlExpectations: mockQueries(
|
||||||
|
expectedActiveAppQuery,
|
||||||
|
appCols,
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"app-id",
|
||||||
|
"app-name",
|
||||||
|
"project-id",
|
||||||
|
testNow,
|
||||||
|
testNow,
|
||||||
|
"ro",
|
||||||
|
domain.AppStateActive,
|
||||||
|
uint64(20211109),
|
||||||
|
// api config
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
// oidc config
|
||||||
|
"app-id",
|
||||||
|
domain.OIDCVersionV1,
|
||||||
|
"oidc-client-id",
|
||||||
|
database.TextArray[string]{"https://redirect.to/me"},
|
||||||
|
database.NumberArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken},
|
||||||
|
database.NumberArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit},
|
||||||
|
domain.OIDCApplicationTypeUserAgent,
|
||||||
|
domain.OIDCAuthMethodTypeNone,
|
||||||
|
database.TextArray[string]{"post.logout.ch"},
|
||||||
|
true,
|
||||||
|
domain.OIDCTokenTypeJWT,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
1 * time.Second,
|
||||||
|
database.TextArray[string]{"additional.origin"},
|
||||||
|
false,
|
||||||
|
// saml config
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
object: &App{
|
||||||
|
ID: "app-id",
|
||||||
|
CreationDate: testNow,
|
||||||
|
ChangeDate: testNow,
|
||||||
|
ResourceOwner: "ro",
|
||||||
|
State: domain.AppStateActive,
|
||||||
|
Sequence: 20211109,
|
||||||
|
Name: "app-name",
|
||||||
|
ProjectID: "project-id",
|
||||||
|
OIDCConfig: &OIDCApp{
|
||||||
|
Version: domain.OIDCVersionV1,
|
||||||
|
ClientID: "oidc-client-id",
|
||||||
|
RedirectURIs: database.TextArray[string]{"https://redirect.to/me"},
|
||||||
|
ResponseTypes: database.NumberArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken},
|
||||||
|
GrantTypes: database.NumberArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit},
|
||||||
|
AppType: domain.OIDCApplicationTypeUserAgent,
|
||||||
|
AuthMethodType: domain.OIDCAuthMethodTypeNone,
|
||||||
|
PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"},
|
||||||
|
IsDevMode: true,
|
||||||
|
AccessTokenType: domain.OIDCTokenTypeJWT,
|
||||||
|
AssertAccessTokenRole: true,
|
||||||
|
AssertIDTokenRole: true,
|
||||||
|
AssertIDTokenUserinfo: true,
|
||||||
|
ClockSkew: 1 * time.Second,
|
||||||
|
AdditionalOrigins: database.TextArray[string]{"additional.origin"},
|
||||||
|
ComplianceProblems: nil,
|
||||||
|
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
|
||||||
|
SkipNativeAppSuccessPage: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prepareAppQuery saml app",
|
||||||
|
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||||
|
return prepareAppQuery(ctx, db, false)
|
||||||
|
},
|
||||||
want: want{
|
want: want{
|
||||||
sqlExpectations: mockQueries(
|
sqlExpectations: mockQueries(
|
||||||
expectedAppQuery,
|
expectedAppQuery,
|
||||||
@ -1420,8 +1518,10 @@ func Test_AppPrepare(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "prepareAppQuery oidc app IsDevMode inactive",
|
name: "prepareAppQuery oidc app IsDevMode inactive",
|
||||||
prepare: prepareAppQuery,
|
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||||
|
return prepareAppQuery(ctx, db, false)
|
||||||
|
},
|
||||||
want: want{
|
want: want{
|
||||||
sqlExpectations: mockQueries(
|
sqlExpectations: mockQueries(
|
||||||
expectedAppQuery,
|
expectedAppQuery,
|
||||||
@ -1499,8 +1599,10 @@ func Test_AppPrepare(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "prepareAppQuery oidc app AssertAccessTokenRole inactive",
|
name: "prepareAppQuery oidc app AssertAccessTokenRole inactive",
|
||||||
prepare: prepareAppQuery,
|
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||||
|
return prepareAppQuery(ctx, db, false)
|
||||||
|
},
|
||||||
want: want{
|
want: want{
|
||||||
sqlExpectations: mockQueries(
|
sqlExpectations: mockQueries(
|
||||||
expectedAppQuery,
|
expectedAppQuery,
|
||||||
@ -1578,8 +1680,10 @@ func Test_AppPrepare(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "prepareAppQuery oidc app AssertIDTokenRole inactive",
|
name: "prepareAppQuery oidc app AssertIDTokenRole inactive",
|
||||||
prepare: prepareAppQuery,
|
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||||
|
return prepareAppQuery(ctx, db, false)
|
||||||
|
},
|
||||||
want: want{
|
want: want{
|
||||||
sqlExpectations: mockQueries(
|
sqlExpectations: mockQueries(
|
||||||
expectedAppQuery,
|
expectedAppQuery,
|
||||||
@ -1657,8 +1761,10 @@ func Test_AppPrepare(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "prepareAppQuery oidc app AssertIDTokenUserinfo inactive",
|
name: "prepareAppQuery oidc app AssertIDTokenUserinfo inactive",
|
||||||
prepare: prepareAppQuery,
|
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||||
|
return prepareAppQuery(ctx, db, false)
|
||||||
|
},
|
||||||
want: want{
|
want: want{
|
||||||
sqlExpectations: mockQueries(
|
sqlExpectations: mockQueries(
|
||||||
expectedAppQuery,
|
expectedAppQuery,
|
||||||
@ -1736,8 +1842,10 @@ func Test_AppPrepare(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "prepareAppQuery sql err",
|
name: "prepareAppQuery sql err",
|
||||||
prepare: prepareAppQuery,
|
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
|
||||||
|
return prepareAppQuery(ctx, db, false)
|
||||||
|
},
|
||||||
want: want{
|
want: want{
|
||||||
sqlExpectations: mockQueryErr(
|
sqlExpectations: mockQueryErr(
|
||||||
expectedAppQuery,
|
expectedAppQuery,
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/call"
|
"github.com/zitadel/zitadel/internal/api/call"
|
||||||
@ -27,7 +26,7 @@ type Stateful interface {
|
|||||||
type State struct {
|
type State struct {
|
||||||
LastRun time.Time
|
LastRun time.Time
|
||||||
|
|
||||||
Position decimal.Decimal
|
Position float64
|
||||||
EventCreatedAt time.Time
|
EventCreatedAt time.Time
|
||||||
AggregateID string
|
AggregateID string
|
||||||
AggregateType eventstore.AggregateType
|
AggregateType eventstore.AggregateType
|
||||||
@ -222,7 +221,7 @@ func prepareLatestState(ctx context.Context, db prepareDatabase) (sq.SelectBuild
|
|||||||
var (
|
var (
|
||||||
creationDate sql.NullTime
|
creationDate sql.NullTime
|
||||||
lastUpdated sql.NullTime
|
lastUpdated sql.NullTime
|
||||||
position decimal.NullDecimal
|
position sql.NullFloat64
|
||||||
)
|
)
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&creationDate,
|
&creationDate,
|
||||||
@ -235,7 +234,7 @@ func prepareLatestState(ctx context.Context, db prepareDatabase) (sq.SelectBuild
|
|||||||
return &State{
|
return &State{
|
||||||
EventCreatedAt: creationDate.Time,
|
EventCreatedAt: creationDate.Time,
|
||||||
LastRun: lastUpdated.Time,
|
LastRun: lastUpdated.Time,
|
||||||
Position: position.Decimal,
|
Position: position.Float64,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,7 +259,7 @@ func prepareCurrentStateQuery(ctx context.Context, db prepareDatabase) (sq.Selec
|
|||||||
var (
|
var (
|
||||||
lastRun sql.NullTime
|
lastRun sql.NullTime
|
||||||
eventDate sql.NullTime
|
eventDate sql.NullTime
|
||||||
currentPosition decimal.NullDecimal
|
currentPosition sql.NullFloat64
|
||||||
aggregateType sql.NullString
|
aggregateType sql.NullString
|
||||||
aggregateID sql.NullString
|
aggregateID sql.NullString
|
||||||
sequence sql.NullInt64
|
sequence sql.NullInt64
|
||||||
@ -281,7 +280,7 @@ func prepareCurrentStateQuery(ctx context.Context, db prepareDatabase) (sq.Selec
|
|||||||
}
|
}
|
||||||
currentState.State.EventCreatedAt = eventDate.Time
|
currentState.State.EventCreatedAt = eventDate.Time
|
||||||
currentState.State.LastRun = lastRun.Time
|
currentState.State.LastRun = lastRun.Time
|
||||||
currentState.Position = currentPosition.Decimal
|
currentState.Position = currentPosition.Float64
|
||||||
currentState.AggregateType = eventstore.AggregateType(aggregateType.String)
|
currentState.AggregateType = eventstore.AggregateType(aggregateType.String)
|
||||||
currentState.AggregateID = aggregateID.String
|
currentState.AggregateID = aggregateID.String
|
||||||
currentState.Sequence = uint64(sequence.Int64)
|
currentState.Sequence = uint64(sequence.Int64)
|
||||||
|
@ -7,8 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -89,7 +87,7 @@ func Test_CurrentSequencesPrepares(t *testing.T) {
|
|||||||
State: State{
|
State: State{
|
||||||
EventCreatedAt: testNow,
|
EventCreatedAt: testNow,
|
||||||
LastRun: testNow,
|
LastRun: testNow,
|
||||||
Position: decimal.NewFromInt(20211108),
|
Position: 20211108,
|
||||||
AggregateID: "agg-id",
|
AggregateID: "agg-id",
|
||||||
AggregateType: "agg-type",
|
AggregateType: "agg-type",
|
||||||
Sequence: 20211108,
|
Sequence: 20211108,
|
||||||
@ -136,7 +134,7 @@ func Test_CurrentSequencesPrepares(t *testing.T) {
|
|||||||
ProjectionName: "projection-name",
|
ProjectionName: "projection-name",
|
||||||
State: State{
|
State: State{
|
||||||
EventCreatedAt: testNow,
|
EventCreatedAt: testNow,
|
||||||
Position: decimal.NewFromInt(20211108),
|
Position: 20211108,
|
||||||
LastRun: testNow,
|
LastRun: testNow,
|
||||||
AggregateID: "agg-id",
|
AggregateID: "agg-id",
|
||||||
AggregateType: "agg-type",
|
AggregateType: "agg-type",
|
||||||
@ -147,7 +145,7 @@ func Test_CurrentSequencesPrepares(t *testing.T) {
|
|||||||
ProjectionName: "projection-name2",
|
ProjectionName: "projection-name2",
|
||||||
State: State{
|
State: State{
|
||||||
EventCreatedAt: testNow,
|
EventCreatedAt: testNow,
|
||||||
Position: decimal.NewFromInt(20211108),
|
Position: 20211108,
|
||||||
LastRun: testNow,
|
LastRun: testNow,
|
||||||
AggregateID: "agg-id",
|
AggregateID: "agg-id",
|
||||||
AggregateType: "agg-type",
|
AggregateType: "agg-type",
|
||||||
|
@ -52,7 +52,7 @@ type IntrospectionClient struct {
|
|||||||
//go:embed introspection_client_by_id.sql
|
//go:embed introspection_client_by_id.sql
|
||||||
var introspectionClientByIDQuery string
|
var introspectionClientByIDQuery string
|
||||||
|
|
||||||
func (q *Queries) GetIntrospectionClientByID(ctx context.Context, clientID string, getKeys bool) (_ *IntrospectionClient, err error) {
|
func (q *Queries) ActiveIntrospectionClientByID(ctx context.Context, clientID string, getKeys bool) (_ *IntrospectionClient, err error) {
|
||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ keys as (
|
|||||||
)
|
)
|
||||||
select config.app_id, config.client_id, config.client_secret, config.app_type, apps.project_id, apps.resource_owner, p.project_role_assertion, keys.public_keys
|
select config.app_id, config.client_id, config.client_secret, config.app_type, apps.project_id, apps.resource_owner, p.project_role_assertion, keys.public_keys
|
||||||
from config
|
from config
|
||||||
join projections.apps7 apps on apps.id = config.app_id and apps.instance_id = config.instance_id
|
join projections.apps7 apps on apps.id = config.app_id and apps.instance_id = config.instance_id and apps.state = 1
|
||||||
join projections.projects4 p on p.id = apps.project_id and p.instance_id = $1
|
join projections.projects4 p on p.id = apps.project_id and p.instance_id = $1 and p.state = 1
|
||||||
|
join projections.orgs1 o on o.id = p.resource_owner and o.instance_id = config.instance_id and o.org_state = 1
|
||||||
left join keys on keys.client_id = config.client_id;
|
left join keys on keys.client_id = config.client_id;
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/database"
|
"github.com/zitadel/zitadel/internal/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestQueries_GetIntrospectionClientByID(t *testing.T) {
|
func TestQueries_ActiveIntrospectionClientByID(t *testing.T) {
|
||||||
pubkeys := database.Map[[]byte]{
|
pubkeys := database.Map[[]byte]{
|
||||||
"key1": {1, 2, 3},
|
"key1": {1, 2, 3},
|
||||||
"key2": {4, 5, 6},
|
"key2": {4, 5, 6},
|
||||||
@ -96,7 +96,7 @@ func TestQueries_GetIntrospectionClientByID(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
ctx := authz.NewMockContext("instanceID", "orgID", "userID")
|
ctx := authz.NewMockContext("instanceID", "orgID", "userID")
|
||||||
got, err := q.GetIntrospectionClientByID(ctx, tt.args.clientID, tt.args.getKeys)
|
got, err := q.ActiveIntrospectionClientByID(ctx, tt.args.clientID, tt.args.getKeys)
|
||||||
require.ErrorIs(t, err, tt.wantErr)
|
require.ErrorIs(t, err, tt.wantErr)
|
||||||
assert.Equal(t, tt.want, got)
|
assert.Equal(t, tt.want, got)
|
||||||
})
|
})
|
||||||
|
@ -45,7 +45,7 @@ type OIDCClient struct {
|
|||||||
//go:embed oidc_client_by_id.sql
|
//go:embed oidc_client_by_id.sql
|
||||||
var oidcClientQuery string
|
var oidcClientQuery string
|
||||||
|
|
||||||
func (q *Queries) GetOIDCClientByID(ctx context.Context, clientID string, getKeys bool) (client *OIDCClient, err error) {
|
func (q *Queries) ActiveOIDCClientByID(ctx context.Context, clientID string, getKeys bool) (client *OIDCClient, err error) {
|
||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
|
@ -5,8 +5,9 @@ with client as (
|
|||||||
c.access_token_type, c.access_token_role_assertion, c.id_token_role_assertion,
|
c.access_token_type, c.access_token_role_assertion, c.id_token_role_assertion,
|
||||||
c.id_token_userinfo_assertion, c.clock_skew, c.additional_origins, a.project_id, p.project_role_assertion
|
c.id_token_userinfo_assertion, c.clock_skew, c.additional_origins, a.project_id, p.project_role_assertion
|
||||||
from projections.apps7_oidc_configs c
|
from projections.apps7_oidc_configs c
|
||||||
join projections.apps7 a on a.id = c.app_id and a.instance_id = c.instance_id
|
join projections.apps7 a on a.id = c.app_id and a.instance_id = c.instance_id and a.state = 1
|
||||||
join projections.projects4 p on p.id = a.project_id and p.instance_id = a.instance_id
|
join projections.projects4 p on p.id = a.project_id and p.instance_id = a.instance_id and p.state = 1
|
||||||
|
join projections.orgs1 o on o.id = p.resource_owner and o.instance_id = c.instance_id and o.org_state = 1
|
||||||
where c.instance_id = $1
|
where c.instance_id = $1
|
||||||
and c.client_id = $2
|
and c.client_id = $2
|
||||||
),
|
),
|
||||||
|
@ -29,7 +29,7 @@ var (
|
|||||||
testdataOidcClientNoSettings string
|
testdataOidcClientNoSettings string
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestQueries_GetOIDCClientByID(t *testing.T) {
|
func TestQueries_ActiveOIDCClientByID(t *testing.T) {
|
||||||
expQuery := regexp.QuoteMeta(oidcClientQuery)
|
expQuery := regexp.QuoteMeta(oidcClientQuery)
|
||||||
cols := []string{"client"}
|
cols := []string{"client"}
|
||||||
pubkey := `-----BEGIN RSA PUBLIC KEY-----
|
pubkey := `-----BEGIN RSA PUBLIC KEY-----
|
||||||
@ -232,7 +232,7 @@ low2kyJov38V4Uk2I8kuXpLcnrpw5Tio2ooiUE27b0vHZqBKOei9Uo88qCrn3EKx
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
ctx := authz.NewMockContext("instanceID", "orgID", "loginClient")
|
ctx := authz.NewMockContext("instanceID", "orgID", "loginClient")
|
||||||
got, err := q.GetOIDCClientByID(ctx, "clientID", true)
|
got, err := q.ActiveOIDCClientByID(ctx, "clientID", true)
|
||||||
require.ErrorIs(t, err, tt.wantErr)
|
require.ErrorIs(t, err, tt.wantErr)
|
||||||
assert.Equal(t, tt.want, got)
|
assert.Equal(t, tt.want, got)
|
||||||
})
|
})
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SMTPConfigProjectionTable = "projections.smtp_configs3"
|
SMTPConfigProjectionTable = "projections.smtp_configs4"
|
||||||
SMTPConfigTable = SMTPConfigProjectionTable + "_" + smtpConfigSMTPTableSuffix
|
SMTPConfigTable = SMTPConfigProjectionTable + "_" + smtpConfigSMTPTableSuffix
|
||||||
SMTPConfigHTTPTable = SMTPConfigProjectionTable + "_" + smtpConfigHTTPTableSuffix
|
SMTPConfigHTTPTable = SMTPConfigProjectionTable + "_" + smtpConfigHTTPTableSuffix
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ func (p *smtpConfigProjection) reduceSMTPConfigAdded(event eventstore.Event) (*h
|
|||||||
handler.AddCreateStatement(
|
handler.AddCreateStatement(
|
||||||
[]handler.Column{
|
[]handler.Column{
|
||||||
handler.NewCol(SMTPConfigSMTPColumnInstanceID, e.Aggregate().InstanceID),
|
handler.NewCol(SMTPConfigSMTPColumnInstanceID, e.Aggregate().InstanceID),
|
||||||
handler.NewCol(SMTPConfigSMTPColumnID, e.ID),
|
handler.NewCol(SMTPConfigSMTPColumnID, id),
|
||||||
handler.NewCol(SMTPConfigSMTPColumnTLS, e.TLS),
|
handler.NewCol(SMTPConfigSMTPColumnTLS, e.TLS),
|
||||||
handler.NewCol(SMTPConfigSMTPColumnSenderAddress, e.SenderAddress),
|
handler.NewCol(SMTPConfigSMTPColumnSenderAddress, e.SenderAddress),
|
||||||
handler.NewCol(SMTPConfigSMTPColumnSenderName, e.SenderName),
|
handler.NewCol(SMTPConfigSMTPColumnSenderName, e.SenderName),
|
||||||
|
@ -50,7 +50,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
uint64(15),
|
uint64(15),
|
||||||
@ -60,7 +60,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3_smtp SET (tls, sender_address, sender_name, reply_to_address, host, username) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (instance_id = $8)",
|
expectedStmt: "UPDATE projections.smtp_configs4_smtp SET (tls, sender_address, sender_name, reply_to_address, host, username) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (instance_id = $8)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
true,
|
true,
|
||||||
"sender",
|
"sender",
|
||||||
@ -100,7 +100,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
uint64(15),
|
uint64(15),
|
||||||
@ -137,7 +137,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
uint64(15),
|
uint64(15),
|
||||||
@ -146,7 +146,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3_smtp SET sender_address = $1 WHERE (id = $2) AND (instance_id = $3)",
|
expectedStmt: "UPDATE projections.smtp_configs4_smtp SET sender_address = $1 WHERE (id = $2) AND (instance_id = $3)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"sender",
|
"sender",
|
||||||
"config-id",
|
"config-id",
|
||||||
@ -182,7 +182,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
uint64(15),
|
uint64(15),
|
||||||
@ -192,7 +192,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3_http SET endpoint = $1 WHERE (id = $2) AND (instance_id = $3)",
|
expectedStmt: "UPDATE projections.smtp_configs4_http SET endpoint = $1 WHERE (id = $2) AND (instance_id = $3)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"endpoint",
|
"endpoint",
|
||||||
"config-id",
|
"config-id",
|
||||||
@ -227,7 +227,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
uint64(15),
|
uint64(15),
|
||||||
@ -264,7 +264,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
uint64(15),
|
uint64(15),
|
||||||
@ -273,7 +273,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3_http SET endpoint = $1 WHERE (id = $2) AND (instance_id = $3)",
|
expectedStmt: "UPDATE projections.smtp_configs4_http SET endpoint = $1 WHERE (id = $2) AND (instance_id = $3)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"endpoint",
|
"endpoint",
|
||||||
"config-id",
|
"config-id",
|
||||||
@ -284,6 +284,69 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "reduceSMTPConfigAdded (no id)",
|
||||||
|
args: args{
|
||||||
|
event: getEvent(
|
||||||
|
testEvent(
|
||||||
|
instance.SMTPConfigAddedEventType,
|
||||||
|
instance.AggregateType,
|
||||||
|
[]byte(`{
|
||||||
|
"instance_id": "instance-id",
|
||||||
|
"resource_owner": "ro-id",
|
||||||
|
"aggregate_id": "agg-id",
|
||||||
|
"tls": true,
|
||||||
|
"senderAddress": "sender",
|
||||||
|
"senderName": "name",
|
||||||
|
"replyToAddress": "reply-to",
|
||||||
|
"host": "host",
|
||||||
|
"user": "user",
|
||||||
|
"password": {
|
||||||
|
"cryptoType": 0,
|
||||||
|
"algorithm": "RSA-265",
|
||||||
|
"keyId": "key-id"
|
||||||
|
}
|
||||||
|
}`),
|
||||||
|
), eventstore.GenericEventMapper[instance.SMTPConfigAddedEvent]),
|
||||||
|
},
|
||||||
|
reduce: (&smtpConfigProjection{}).reduceSMTPConfigAdded,
|
||||||
|
want: wantReduce{
|
||||||
|
aggregateType: eventstore.AggregateType("instance"),
|
||||||
|
sequence: 15,
|
||||||
|
executer: &testExecuter{
|
||||||
|
executions: []execution{
|
||||||
|
{
|
||||||
|
expectedStmt: "INSERT INTO projections.smtp_configs4 (creation_date, change_date, instance_id, resource_owner, aggregate_id, id, sequence, state, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||||
|
expectedArgs: []interface{}{
|
||||||
|
anyArg{},
|
||||||
|
anyArg{},
|
||||||
|
"instance-id",
|
||||||
|
"ro-id",
|
||||||
|
"agg-id",
|
||||||
|
"ro-id",
|
||||||
|
uint64(15),
|
||||||
|
domain.SMTPConfigStateActive,
|
||||||
|
"generic",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expectedStmt: "INSERT INTO projections.smtp_configs4_smtp (instance_id, id, tls, sender_address, sender_name, reply_to_address, host, username, password) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||||
|
expectedArgs: []interface{}{
|
||||||
|
"instance-id",
|
||||||
|
"ro-id",
|
||||||
|
true,
|
||||||
|
"sender",
|
||||||
|
"name",
|
||||||
|
"reply-to",
|
||||||
|
"host",
|
||||||
|
"user",
|
||||||
|
anyArg{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "reduceSMTPConfigAdded",
|
name: "reduceSMTPConfigAdded",
|
||||||
args: args{
|
args: args{
|
||||||
@ -318,7 +381,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "INSERT INTO projections.smtp_configs3 (creation_date, change_date, instance_id, resource_owner, aggregate_id, id, sequence, state, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
expectedStmt: "INSERT INTO projections.smtp_configs4 (creation_date, change_date, instance_id, resource_owner, aggregate_id, id, sequence, state, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
anyArg{},
|
anyArg{},
|
||||||
@ -332,7 +395,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expectedStmt: "INSERT INTO projections.smtp_configs3_smtp (instance_id, id, tls, sender_address, sender_name, reply_to_address, host, username, password) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
expectedStmt: "INSERT INTO projections.smtp_configs4_smtp (instance_id, id, tls, sender_address, sender_name, reply_to_address, host, username, password) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"instance-id",
|
"instance-id",
|
||||||
"config-id",
|
"config-id",
|
||||||
@ -374,7 +437,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "INSERT INTO projections.smtp_configs3 (creation_date, change_date, instance_id, resource_owner, aggregate_id, id, sequence, state, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
expectedStmt: "INSERT INTO projections.smtp_configs4 (creation_date, change_date, instance_id, resource_owner, aggregate_id, id, sequence, state, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
anyArg{},
|
anyArg{},
|
||||||
@ -388,7 +451,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expectedStmt: "INSERT INTO projections.smtp_configs3_http (instance_id, id, endpoint) VALUES ($1, $2, $3)",
|
expectedStmt: "INSERT INTO projections.smtp_configs4_http (instance_id, id, endpoint) VALUES ($1, $2, $3)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"instance-id",
|
"instance-id",
|
||||||
"config-id",
|
"config-id",
|
||||||
@ -420,7 +483,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (NOT (id = $4)) AND (state = $5) AND (instance_id = $6)",
|
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (NOT (id = $4)) AND (state = $5) AND (instance_id = $6)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
uint64(15),
|
uint64(15),
|
||||||
@ -431,7 +494,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
uint64(15),
|
uint64(15),
|
||||||
@ -465,7 +528,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
uint64(15),
|
uint64(15),
|
||||||
@ -505,7 +568,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3_smtp SET password = $1 WHERE (id = $2) AND (instance_id = $3)",
|
expectedStmt: "UPDATE projections.smtp_configs4_smtp SET password = $1 WHERE (id = $2) AND (instance_id = $3)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
"config-id",
|
"config-id",
|
||||||
@ -513,7 +576,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
uint64(15),
|
uint64(15),
|
||||||
@ -546,7 +609,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "DELETE FROM projections.smtp_configs3 WHERE (id = $1) AND (instance_id = $2)",
|
expectedStmt: "DELETE FROM projections.smtp_configs4 WHERE (id = $1) AND (instance_id = $2)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"config-id",
|
"config-id",
|
||||||
"instance-id",
|
"instance-id",
|
||||||
@ -573,7 +636,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "DELETE FROM projections.smtp_configs3 WHERE (instance_id = $1)",
|
expectedStmt: "DELETE FROM projections.smtp_configs4 WHERE (instance_id = $1)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"agg-id",
|
"agg-id",
|
||||||
},
|
},
|
||||||
|
@ -14,26 +14,26 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
prepareSMTPConfigStmt = `SELECT projections.smtp_configs3.creation_date,` +
|
prepareSMTPConfigStmt = `SELECT projections.smtp_configs4.creation_date,` +
|
||||||
` projections.smtp_configs3.change_date,` +
|
` projections.smtp_configs4.change_date,` +
|
||||||
` projections.smtp_configs3.resource_owner,` +
|
` projections.smtp_configs4.resource_owner,` +
|
||||||
` projections.smtp_configs3.sequence,` +
|
` projections.smtp_configs4.sequence,` +
|
||||||
` projections.smtp_configs3.id,` +
|
` projections.smtp_configs4.id,` +
|
||||||
` projections.smtp_configs3.state,` +
|
` projections.smtp_configs4.state,` +
|
||||||
` projections.smtp_configs3.description,` +
|
` projections.smtp_configs4.description,` +
|
||||||
` projections.smtp_configs3_smtp.id,` +
|
` projections.smtp_configs4_smtp.id,` +
|
||||||
` projections.smtp_configs3_smtp.tls,` +
|
` projections.smtp_configs4_smtp.tls,` +
|
||||||
` projections.smtp_configs3_smtp.sender_address,` +
|
` projections.smtp_configs4_smtp.sender_address,` +
|
||||||
` projections.smtp_configs3_smtp.sender_name,` +
|
` projections.smtp_configs4_smtp.sender_name,` +
|
||||||
` projections.smtp_configs3_smtp.reply_to_address,` +
|
` projections.smtp_configs4_smtp.reply_to_address,` +
|
||||||
` projections.smtp_configs3_smtp.host,` +
|
` projections.smtp_configs4_smtp.host,` +
|
||||||
` projections.smtp_configs3_smtp.username,` +
|
` projections.smtp_configs4_smtp.username,` +
|
||||||
` projections.smtp_configs3_smtp.password,` +
|
` projections.smtp_configs4_smtp.password,` +
|
||||||
` projections.smtp_configs3_http.id,` +
|
` projections.smtp_configs4_http.id,` +
|
||||||
` projections.smtp_configs3_http.endpoint` +
|
` projections.smtp_configs4_http.endpoint` +
|
||||||
` FROM projections.smtp_configs3` +
|
` FROM projections.smtp_configs4` +
|
||||||
` LEFT JOIN projections.smtp_configs3_smtp ON projections.smtp_configs3.id = projections.smtp_configs3_smtp.id AND projections.smtp_configs3.instance_id = projections.smtp_configs3_smtp.instance_id` +
|
` LEFT JOIN projections.smtp_configs4_smtp ON projections.smtp_configs4.id = projections.smtp_configs4_smtp.id AND projections.smtp_configs4.instance_id = projections.smtp_configs4_smtp.instance_id` +
|
||||||
` LEFT JOIN projections.smtp_configs3_http ON projections.smtp_configs3.id = projections.smtp_configs3_http.id AND projections.smtp_configs3.instance_id = projections.smtp_configs3_http.instance_id` +
|
` LEFT JOIN projections.smtp_configs4_http ON projections.smtp_configs4.id = projections.smtp_configs4_http.id AND projections.smtp_configs4.instance_id = projections.smtp_configs4_http.instance_id` +
|
||||||
` AS OF SYSTEM TIME '-1 ms'`
|
` AS OF SYSTEM TIME '-1 ms'`
|
||||||
prepareSMTPConfigCols = []string{
|
prepareSMTPConfigCols = []string{
|
||||||
"creation_date",
|
"creation_date",
|
||||||
|
@ -143,6 +143,10 @@ func NewUserGrantRoleQuery(value string) (SearchQuery, error) {
|
|||||||
return NewTextQuery(UserGrantRoles, value, TextListContains)
|
return NewTextQuery(UserGrantRoles, value, TextListContains)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewUserGrantStateQuery(value domain.UserGrantState) (SearchQuery, error) {
|
||||||
|
return NewNumberQuery(UserGrantState, value, NumberEquals)
|
||||||
|
}
|
||||||
|
|
||||||
func NewUserGrantWithGrantedQuery(owner string) (SearchQuery, error) {
|
func NewUserGrantWithGrantedQuery(owner string) (SearchQuery, error) {
|
||||||
orgQuery, err := NewUserGrantResourceOwnerSearchQuery(owner)
|
orgQuery, err := NewUserGrantResourceOwnerSearchQuery(owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -277,7 +281,7 @@ func (q *Queries) UserGrants(ctx context.Context, queries *UserGrantsQueries, sh
|
|||||||
return nil, zerrors.ThrowInternal(err, "QUERY-wXnQR", "Errors.Query.SQLStatement")
|
return nil, zerrors.ThrowInternal(err, "QUERY-wXnQR", "Errors.Query.SQLStatement")
|
||||||
}
|
}
|
||||||
|
|
||||||
latestState, err := q.latestState(ctx, userGrantTable)
|
latestSequence, err := q.latestState(ctx, userGrantTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -290,7 +294,7 @@ func (q *Queries) UserGrants(ctx context.Context, queries *UserGrantsQueries, sh
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
grants.State = latestState
|
grants.State = latestSequence
|
||||||
return grants, nil
|
return grants, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ func (q *Queries) Memberships(ctx context.Context, queries *MembershipSearchQuer
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, zerrors.ThrowInvalidArgument(err, "QUERY-T84X9", "Errors.Query.InvalidRequest")
|
return nil, zerrors.ThrowInvalidArgument(err, "QUERY-T84X9", "Errors.Query.InvalidRequest")
|
||||||
}
|
}
|
||||||
latestState, err := q.latestState(ctx, orgMemberTable, instanceMemberTable, projectMemberTable, projectGrantMemberTable)
|
latestSequence, err := q.latestState(ctx, orgMemberTable, instanceMemberTable, projectMemberTable, projectGrantMemberTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -157,7 +157,7 @@ func (q *Queries) Memberships(ctx context.Context, queries *MembershipSearchQuer
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
memberships.State = latestState
|
memberships.State = latestSequence
|
||||||
return memberships, nil
|
return memberships, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ with usr as (
|
|||||||
select u.id, u.creation_date, u.change_date, u.sequence, u.state, u.resource_owner, u.username, n.login_name as preferred_login_name
|
select u.id, u.creation_date, u.change_date, u.sequence, u.state, u.resource_owner, u.username, n.login_name as preferred_login_name
|
||||||
from projections.users13 u
|
from projections.users13 u
|
||||||
left join projections.login_names3 n on u.id = n.user_id and u.instance_id = n.instance_id
|
left join projections.login_names3 n on u.id = n.user_id and u.instance_id = n.instance_id
|
||||||
where u.id = $1
|
where u.id = $1 and u.state = 1 -- only allow active users
|
||||||
and u.instance_id = $2
|
and u.instance_id = $2
|
||||||
and n.is_primary = true
|
and n.is_primary = true
|
||||||
),
|
),
|
||||||
@ -38,6 +38,7 @@ user_grants as (
|
|||||||
where user_id = $1
|
where user_id = $1
|
||||||
and instance_id = $2
|
and instance_id = $2
|
||||||
and project_id = any($3)
|
and project_id = any($3)
|
||||||
|
and state = 1
|
||||||
{{ if . -}}
|
{{ if . -}}
|
||||||
and resource_owner = any($4)
|
and resource_owner = any($4)
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
@ -3,7 +3,6 @@ package database
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
@ -95,7 +94,7 @@ func (c numberCompare) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type number interface {
|
type number interface {
|
||||||
constraints.Integer | constraints.Float | time.Time | decimal.Decimal
|
constraints.Integer | constraints.Float | time.Time
|
||||||
// TODO: condition must know if it's args are named parameters or not
|
// TODO: condition must know if it's args are named parameters or not
|
||||||
// constraints.Integer | constraints.Float | time.Time | placeholder
|
// constraints.Integer | constraints.Float | time.Time | placeholder
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ package eventstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewEventstore(querier Querier, pusher Pusher) *EventStore {
|
func NewEventstore(querier Querier, pusher Pusher) *EventStore {
|
||||||
@ -32,12 +30,12 @@ type healthier interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GlobalPosition struct {
|
type GlobalPosition struct {
|
||||||
Position decimal.Decimal
|
Position float64
|
||||||
InPositionOrder uint32
|
InPositionOrder uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gp GlobalPosition) IsLess(other GlobalPosition) bool {
|
func (gp GlobalPosition) IsLess(other GlobalPosition) bool {
|
||||||
return gp.Position.LessThan(other.Position) || (gp.Position.Equal(other.Position) && gp.InPositionOrder < other.InPositionOrder)
|
return gp.Position < other.Position || (gp.Position == other.Position && gp.InPositionOrder < other.InPositionOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Reducer interface {
|
type Reducer interface {
|
||||||
|
@ -8,8 +8,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/v2/database/mock"
|
"github.com/zitadel/zitadel/internal/v2/database/mock"
|
||||||
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
@ -820,7 +818,7 @@ func Test_push(t *testing.T) {
|
|||||||
[][]driver.Value{
|
[][]driver.Value{
|
||||||
{
|
{
|
||||||
time.Now(),
|
time.Now(),
|
||||||
decimal.NewFromFloat(123).String(),
|
float64(123),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -901,11 +899,11 @@ func Test_push(t *testing.T) {
|
|||||||
[][]driver.Value{
|
[][]driver.Value{
|
||||||
{
|
{
|
||||||
time.Now(),
|
time.Now(),
|
||||||
decimal.NewFromFloat(123).String(),
|
float64(123),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
time.Now(),
|
time.Now(),
|
||||||
decimal.NewFromFloat(123.1).String(),
|
float64(123.1),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -986,11 +984,11 @@ func Test_push(t *testing.T) {
|
|||||||
[][]driver.Value{
|
[][]driver.Value{
|
||||||
{
|
{
|
||||||
time.Now(),
|
time.Now(),
|
||||||
decimal.NewFromFloat(123).String(),
|
float64(123),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
time.Now(),
|
time.Now(),
|
||||||
decimal.NewFromFloat(123.1).String(),
|
float64(123.1),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -1046,7 +1044,7 @@ func Test_push(t *testing.T) {
|
|||||||
[][]driver.Value{
|
[][]driver.Value{
|
||||||
{
|
{
|
||||||
time.Now(),
|
time.Now(),
|
||||||
decimal.NewFromFloat(123).String(),
|
float64(123),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -1101,7 +1099,7 @@ func Test_push(t *testing.T) {
|
|||||||
[][]driver.Value{
|
[][]driver.Value{
|
||||||
{
|
{
|
||||||
time.Now(),
|
time.Now(),
|
||||||
decimal.NewFromFloat(123).String(),
|
float64(123),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -1183,11 +1181,11 @@ func Test_push(t *testing.T) {
|
|||||||
[][]driver.Value{
|
[][]driver.Value{
|
||||||
{
|
{
|
||||||
time.Now(),
|
time.Now(),
|
||||||
decimal.NewFromFloat(123).String(),
|
float64(123),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
time.Now(),
|
time.Now(),
|
||||||
decimal.NewFromFloat(123.1).String(),
|
float64(123.1),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -1274,11 +1272,11 @@ func Test_push(t *testing.T) {
|
|||||||
[][]driver.Value{
|
[][]driver.Value{
|
||||||
{
|
{
|
||||||
time.Now(),
|
time.Now(),
|
||||||
decimal.NewFromFloat(123).String(),
|
float64(123),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
time.Now(),
|
time.Now(),
|
||||||
decimal.NewFromFloat(123.1).String(),
|
float64(123.1),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -8,8 +8,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/v2/database"
|
"github.com/zitadel/zitadel/internal/v2/database"
|
||||||
"github.com/zitadel/zitadel/internal/v2/database/mock"
|
"github.com/zitadel/zitadel/internal/v2/database/mock"
|
||||||
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
||||||
@ -543,13 +541,13 @@ func Test_writeFilter(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
filter: eventstore.NewFilter(
|
filter: eventstore.NewFilter(
|
||||||
eventstore.FilterPagination(
|
eventstore.FilterPagination(
|
||||||
eventstore.PositionGreater(decimal.NewFromFloat(123.4), 0),
|
eventstore.PositionGreater(123.4, 0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
want: wantQuery{
|
want: wantQuery{
|
||||||
query: " WHERE instance_id = $1 AND position > $2 ORDER BY position, in_tx_order",
|
query: " WHERE instance_id = $1 AND position > $2 ORDER BY position, in_tx_order",
|
||||||
args: []any{"i1", decimal.NewFromFloat(123.4)},
|
args: []any{"i1", 123.4},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -557,18 +555,18 @@ func Test_writeFilter(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
filter: eventstore.NewFilter(
|
filter: eventstore.NewFilter(
|
||||||
eventstore.FilterPagination(
|
eventstore.FilterPagination(
|
||||||
// eventstore.PositionGreater(decimal.NewFromFloat(123.4), 0),
|
// eventstore.PositionGreater(123.4, 0),
|
||||||
// eventstore.PositionLess(125.4, 10),
|
// eventstore.PositionLess(125.4, 10),
|
||||||
eventstore.PositionBetween(
|
eventstore.PositionBetween(
|
||||||
&eventstore.GlobalPosition{Position: decimal.NewFromFloat(123.4)},
|
&eventstore.GlobalPosition{Position: 123.4},
|
||||||
&eventstore.GlobalPosition{Position: decimal.NewFromFloat(125.4), InPositionOrder: 10},
|
&eventstore.GlobalPosition{Position: 125.4, InPositionOrder: 10},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
want: wantQuery{
|
want: wantQuery{
|
||||||
query: " WHERE instance_id = $1 AND ((position = $2 AND in_tx_order < $3) OR position < $4) AND position > $5 ORDER BY position, in_tx_order",
|
query: " WHERE instance_id = $1 AND ((position = $2 AND in_tx_order < $3) OR position < $4) AND position > $5 ORDER BY position, in_tx_order",
|
||||||
args: []any{"i1", decimal.NewFromFloat(125.4), uint32(10), decimal.NewFromFloat(125.4), decimal.NewFromFloat(123.4)},
|
args: []any{"i1", 125.4, uint32(10), 125.4, 123.4},
|
||||||
// TODO: (adlerhurst) would require some refactoring to reuse existing args
|
// TODO: (adlerhurst) would require some refactoring to reuse existing args
|
||||||
// query: " WHERE instance_id = $1 AND position > $2 AND ((position = $3 AND in_tx_order < $4) OR position < $3) ORDER BY position, in_tx_order",
|
// query: " WHERE instance_id = $1 AND position > $2 AND ((position = $3 AND in_tx_order < $4) OR position < $3) ORDER BY position, in_tx_order",
|
||||||
// args: []any{"i1", 123.4, 125.4, uint32(10)},
|
// args: []any{"i1", 123.4, 125.4, uint32(10)},
|
||||||
@ -579,13 +577,13 @@ func Test_writeFilter(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
filter: eventstore.NewFilter(
|
filter: eventstore.NewFilter(
|
||||||
eventstore.FilterPagination(
|
eventstore.FilterPagination(
|
||||||
eventstore.PositionGreater(decimal.NewFromFloat(123.4), 12),
|
eventstore.PositionGreater(123.4, 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
want: wantQuery{
|
want: wantQuery{
|
||||||
query: " WHERE instance_id = $1 AND ((position = $2 AND in_tx_order > $3) OR position > $4) ORDER BY position, in_tx_order",
|
query: " WHERE instance_id = $1 AND ((position = $2 AND in_tx_order > $3) OR position > $4) ORDER BY position, in_tx_order",
|
||||||
args: []any{"i1", decimal.NewFromFloat(123.4), uint32(12), decimal.NewFromFloat(123.4)},
|
args: []any{"i1", 123.4, uint32(12), 123.4},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -595,13 +593,13 @@ func Test_writeFilter(t *testing.T) {
|
|||||||
eventstore.FilterPagination(
|
eventstore.FilterPagination(
|
||||||
eventstore.Limit(10),
|
eventstore.Limit(10),
|
||||||
eventstore.Offset(3),
|
eventstore.Offset(3),
|
||||||
eventstore.PositionGreater(decimal.NewFromFloat(123.4), 12),
|
eventstore.PositionGreater(123.4, 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
want: wantQuery{
|
want: wantQuery{
|
||||||
query: " WHERE instance_id = $1 AND ((position = $2 AND in_tx_order > $3) OR position > $4) ORDER BY position, in_tx_order LIMIT $5 OFFSET $6",
|
query: " WHERE instance_id = $1 AND ((position = $2 AND in_tx_order > $3) OR position > $4) ORDER BY position, in_tx_order LIMIT $5 OFFSET $6",
|
||||||
args: []any{"i1", decimal.NewFromFloat(123.4), uint32(12), decimal.NewFromFloat(123.4), uint32(10), uint32(3)},
|
args: []any{"i1", 123.4, uint32(12), 123.4, uint32(10), uint32(3)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -611,14 +609,14 @@ func Test_writeFilter(t *testing.T) {
|
|||||||
eventstore.FilterPagination(
|
eventstore.FilterPagination(
|
||||||
eventstore.Limit(10),
|
eventstore.Limit(10),
|
||||||
eventstore.Offset(3),
|
eventstore.Offset(3),
|
||||||
eventstore.PositionGreater(decimal.NewFromFloat(123.4), 12),
|
eventstore.PositionGreater(123.4, 12),
|
||||||
),
|
),
|
||||||
eventstore.AppendAggregateFilter("user"),
|
eventstore.AppendAggregateFilter("user"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
want: wantQuery{
|
want: wantQuery{
|
||||||
query: " WHERE instance_id = $1 AND aggregate_type = $2 AND ((position = $3 AND in_tx_order > $4) OR position > $5) ORDER BY position, in_tx_order LIMIT $6 OFFSET $7",
|
query: " WHERE instance_id = $1 AND aggregate_type = $2 AND ((position = $3 AND in_tx_order > $4) OR position > $5) ORDER BY position, in_tx_order LIMIT $6 OFFSET $7",
|
||||||
args: []any{"i1", "user", decimal.NewFromFloat(123.4), uint32(12), decimal.NewFromFloat(123.4), uint32(10), uint32(3)},
|
args: []any{"i1", "user", 123.4, uint32(12), 123.4, uint32(10), uint32(3)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -628,7 +626,7 @@ func Test_writeFilter(t *testing.T) {
|
|||||||
eventstore.FilterPagination(
|
eventstore.FilterPagination(
|
||||||
eventstore.Limit(10),
|
eventstore.Limit(10),
|
||||||
eventstore.Offset(3),
|
eventstore.Offset(3),
|
||||||
eventstore.PositionGreater(decimal.NewFromFloat(123.4), 12),
|
eventstore.PositionGreater(123.4, 12),
|
||||||
),
|
),
|
||||||
eventstore.AppendAggregateFilter("user"),
|
eventstore.AppendAggregateFilter("user"),
|
||||||
eventstore.AppendAggregateFilter(
|
eventstore.AppendAggregateFilter(
|
||||||
@ -639,7 +637,7 @@ func Test_writeFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
want: wantQuery{
|
want: wantQuery{
|
||||||
query: " WHERE instance_id = $1 AND (aggregate_type = $2 OR (aggregate_type = $3 AND aggregate_id = $4)) AND ((position = $5 AND in_tx_order > $6) OR position > $7) ORDER BY position, in_tx_order LIMIT $8 OFFSET $9",
|
query: " WHERE instance_id = $1 AND (aggregate_type = $2 OR (aggregate_type = $3 AND aggregate_id = $4)) AND ((position = $5 AND in_tx_order > $6) OR position > $7) ORDER BY position, in_tx_order LIMIT $8 OFFSET $9",
|
||||||
args: []any{"i1", "user", "org", "o1", decimal.NewFromFloat(123.4), uint32(12), decimal.NewFromFloat(123.4), uint32(10), uint32(3)},
|
args: []any{"i1", "user", "org", "o1", 123.4, uint32(12), 123.4, uint32(10), uint32(3)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -958,7 +956,7 @@ func Test_writeQueryUse_examples(t *testing.T) {
|
|||||||
),
|
),
|
||||||
eventstore.FilterPagination(
|
eventstore.FilterPagination(
|
||||||
// used because we need to check for first login and an app which is not console
|
// used because we need to check for first login and an app which is not console
|
||||||
eventstore.PositionGreater(decimal.NewFromInt(12), 4),
|
eventstore.PositionGreater(12, 4),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
eventstore.NewFilter(
|
eventstore.NewFilter(
|
||||||
@ -1067,9 +1065,9 @@ func Test_writeQueryUse_examples(t *testing.T) {
|
|||||||
"instance",
|
"instance",
|
||||||
"user",
|
"user",
|
||||||
"user.token.added",
|
"user.token.added",
|
||||||
decimal.NewFromInt(12),
|
float64(12),
|
||||||
uint32(4),
|
uint32(4),
|
||||||
decimal.NewFromInt(12),
|
float64(12),
|
||||||
"instance",
|
"instance",
|
||||||
"instance",
|
"instance",
|
||||||
[]string{"instance.idp.config.added", "instance.idp.oauth.added", "instance.idp.oidc.added", "instance.idp.jwt.added", "instance.idp.azure.added", "instance.idp.github.added", "instance.idp.github.enterprise.added", "instance.idp.gitlab.added", "instance.idp.gitlab.selfhosted.added", "instance.idp.google.added", "instance.idp.ldap.added", "instance.idp.config.apple.added", "instance.idp.saml.added"},
|
[]string{"instance.idp.config.added", "instance.idp.oauth.added", "instance.idp.oidc.added", "instance.idp.jwt.added", "instance.idp.azure.added", "instance.idp.github.added", "instance.idp.github.enterprise.added", "instance.idp.gitlab.added", "instance.idp.gitlab.selfhosted.added", "instance.idp.google.added", "instance.idp.ldap.added", "instance.idp.config.apple.added", "instance.idp.saml.added"},
|
||||||
@ -1203,7 +1201,7 @@ func Test_executeQuery(t *testing.T) {
|
|||||||
time.Now(),
|
time.Now(),
|
||||||
"event.type",
|
"event.type",
|
||||||
uint32(23),
|
uint32(23),
|
||||||
decimal.NewFromInt(123).String(),
|
float64(123),
|
||||||
uint32(0),
|
uint32(0),
|
||||||
nil,
|
nil,
|
||||||
"gigi",
|
"gigi",
|
||||||
@ -1237,7 +1235,7 @@ func Test_executeQuery(t *testing.T) {
|
|||||||
time.Now(),
|
time.Now(),
|
||||||
"event.type",
|
"event.type",
|
||||||
uint32(23),
|
uint32(23),
|
||||||
decimal.NewFromInt(123).String(),
|
float64(123),
|
||||||
uint32(0),
|
uint32(0),
|
||||||
[]byte(`{"name": "gigi"}`),
|
[]byte(`{"name": "gigi"}`),
|
||||||
"gigi",
|
"gigi",
|
||||||
@ -1271,7 +1269,7 @@ func Test_executeQuery(t *testing.T) {
|
|||||||
time.Now(),
|
time.Now(),
|
||||||
"event.type",
|
"event.type",
|
||||||
uint32(23),
|
uint32(23),
|
||||||
decimal.NewFromInt(123).String(),
|
float64(123),
|
||||||
uint32(0),
|
uint32(0),
|
||||||
nil,
|
nil,
|
||||||
"gigi",
|
"gigi",
|
||||||
@ -1285,7 +1283,7 @@ func Test_executeQuery(t *testing.T) {
|
|||||||
time.Now(),
|
time.Now(),
|
||||||
"event.type",
|
"event.type",
|
||||||
uint32(24),
|
uint32(24),
|
||||||
decimal.NewFromInt(124).String(),
|
float64(124),
|
||||||
uint32(0),
|
uint32(0),
|
||||||
[]byte(`{"name": "gigi"}`),
|
[]byte(`{"name": "gigi"}`),
|
||||||
"gigi",
|
"gigi",
|
||||||
@ -1319,7 +1317,7 @@ func Test_executeQuery(t *testing.T) {
|
|||||||
time.Now(),
|
time.Now(),
|
||||||
"event.type",
|
"event.type",
|
||||||
uint32(23),
|
uint32(23),
|
||||||
decimal.NewFromInt(123).String(),
|
float64(123),
|
||||||
uint32(0),
|
uint32(0),
|
||||||
nil,
|
nil,
|
||||||
"gigi",
|
"gigi",
|
||||||
@ -1333,7 +1331,7 @@ func Test_executeQuery(t *testing.T) {
|
|||||||
time.Now(),
|
time.Now(),
|
||||||
"event.type",
|
"event.type",
|
||||||
uint32(24),
|
uint32(24),
|
||||||
decimal.NewFromInt(124).String(),
|
float64(124),
|
||||||
uint32(0),
|
uint32(0),
|
||||||
[]byte(`{"name": "gigi"}`),
|
[]byte(`{"name": "gigi"}`),
|
||||||
"gigi",
|
"gigi",
|
||||||
|
@ -7,8 +7,6 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/v2/database"
|
"github.com/zitadel/zitadel/internal/v2/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -725,7 +723,7 @@ func (pc *PositionCondition) Min() *GlobalPosition {
|
|||||||
// PositionGreater prepares the condition as follows
|
// PositionGreater prepares the condition as follows
|
||||||
// if inPositionOrder is set: position = AND in_tx_order > OR or position >
|
// if inPositionOrder is set: position = AND in_tx_order > OR or position >
|
||||||
// if inPositionOrder is NOT set: position >
|
// if inPositionOrder is NOT set: position >
|
||||||
func PositionGreater(position decimal.Decimal, inPositionOrder uint32) paginationOpt {
|
func PositionGreater(position float64, inPositionOrder uint32) paginationOpt {
|
||||||
return func(p *Pagination) {
|
return func(p *Pagination) {
|
||||||
p.ensurePosition()
|
p.ensurePosition()
|
||||||
p.position.min = &GlobalPosition{
|
p.position.min = &GlobalPosition{
|
||||||
@ -745,7 +743,7 @@ func GlobalPositionGreater(position *GlobalPosition) paginationOpt {
|
|||||||
// PositionLess prepares the condition as follows
|
// PositionLess prepares the condition as follows
|
||||||
// if inPositionOrder is set: position = AND in_tx_order > OR or position >
|
// if inPositionOrder is set: position = AND in_tx_order > OR or position >
|
||||||
// if inPositionOrder is NOT set: position >
|
// if inPositionOrder is NOT set: position >
|
||||||
func PositionLess(position decimal.Decimal, inPositionOrder uint32) paginationOpt {
|
func PositionLess(position float64, inPositionOrder uint32) paginationOpt {
|
||||||
return func(p *Pagination) {
|
return func(p *Pagination) {
|
||||||
p.ensurePosition()
|
p.ensurePosition()
|
||||||
p.position.max = &GlobalPosition{
|
p.position.max = &GlobalPosition{
|
||||||
|
@ -6,8 +6,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/v2/database"
|
"github.com/zitadel/zitadel/internal/v2/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -76,13 +74,13 @@ func TestPaginationOpt(t *testing.T) {
|
|||||||
name: "global position greater",
|
name: "global position greater",
|
||||||
args: args{
|
args: args{
|
||||||
opts: []paginationOpt{
|
opts: []paginationOpt{
|
||||||
GlobalPositionGreater(&GlobalPosition{Position: decimal.NewFromInt(10)}),
|
GlobalPositionGreater(&GlobalPosition{Position: 10}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: &Pagination{
|
want: &Pagination{
|
||||||
position: &PositionCondition{
|
position: &PositionCondition{
|
||||||
min: &GlobalPosition{
|
min: &GlobalPosition{
|
||||||
Position: decimal.NewFromInt(10),
|
Position: 10,
|
||||||
InPositionOrder: 0,
|
InPositionOrder: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -92,13 +90,13 @@ func TestPaginationOpt(t *testing.T) {
|
|||||||
name: "position greater",
|
name: "position greater",
|
||||||
args: args{
|
args: args{
|
||||||
opts: []paginationOpt{
|
opts: []paginationOpt{
|
||||||
PositionGreater(decimal.NewFromInt(10), 0),
|
PositionGreater(10, 0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: &Pagination{
|
want: &Pagination{
|
||||||
position: &PositionCondition{
|
position: &PositionCondition{
|
||||||
min: &GlobalPosition{
|
min: &GlobalPosition{
|
||||||
Position: decimal.NewFromInt(10),
|
Position: 10,
|
||||||
InPositionOrder: 0,
|
InPositionOrder: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -109,13 +107,13 @@ func TestPaginationOpt(t *testing.T) {
|
|||||||
name: "position less",
|
name: "position less",
|
||||||
args: args{
|
args: args{
|
||||||
opts: []paginationOpt{
|
opts: []paginationOpt{
|
||||||
PositionLess(decimal.NewFromInt(10), 12),
|
PositionLess(10, 12),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: &Pagination{
|
want: &Pagination{
|
||||||
position: &PositionCondition{
|
position: &PositionCondition{
|
||||||
max: &GlobalPosition{
|
max: &GlobalPosition{
|
||||||
Position: decimal.NewFromInt(10),
|
Position: 10,
|
||||||
InPositionOrder: 12,
|
InPositionOrder: 12,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -125,13 +123,13 @@ func TestPaginationOpt(t *testing.T) {
|
|||||||
name: "global position less",
|
name: "global position less",
|
||||||
args: args{
|
args: args{
|
||||||
opts: []paginationOpt{
|
opts: []paginationOpt{
|
||||||
GlobalPositionLess(&GlobalPosition{Position: decimal.NewFromInt(12), InPositionOrder: 24}),
|
GlobalPositionLess(&GlobalPosition{Position: 12, InPositionOrder: 24}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: &Pagination{
|
want: &Pagination{
|
||||||
position: &PositionCondition{
|
position: &PositionCondition{
|
||||||
max: &GlobalPosition{
|
max: &GlobalPosition{
|
||||||
Position: decimal.NewFromInt(12),
|
Position: 12,
|
||||||
InPositionOrder: 24,
|
InPositionOrder: 24,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -142,19 +140,19 @@ func TestPaginationOpt(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
opts: []paginationOpt{
|
opts: []paginationOpt{
|
||||||
PositionBetween(
|
PositionBetween(
|
||||||
&GlobalPosition{decimal.NewFromInt(10), 12},
|
&GlobalPosition{10, 12},
|
||||||
&GlobalPosition{decimal.NewFromInt(20), 0},
|
&GlobalPosition{20, 0},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: &Pagination{
|
want: &Pagination{
|
||||||
position: &PositionCondition{
|
position: &PositionCondition{
|
||||||
min: &GlobalPosition{
|
min: &GlobalPosition{
|
||||||
Position: decimal.NewFromInt(10),
|
Position: 10,
|
||||||
InPositionOrder: 12,
|
InPositionOrder: 12,
|
||||||
},
|
},
|
||||||
max: &GlobalPosition{
|
max: &GlobalPosition{
|
||||||
Position: decimal.NewFromInt(20),
|
Position: 20,
|
||||||
InPositionOrder: 0,
|
InPositionOrder: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package readmodel
|
package readmodel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/v2/system"
|
"github.com/zitadel/zitadel/internal/v2/system"
|
||||||
"github.com/zitadel/zitadel/internal/v2/system/mirror"
|
"github.com/zitadel/zitadel/internal/v2/system/mirror"
|
||||||
@ -10,7 +8,7 @@ import (
|
|||||||
|
|
||||||
type LastSuccessfulMirror struct {
|
type LastSuccessfulMirror struct {
|
||||||
ID string
|
ID string
|
||||||
Position decimal.Decimal
|
Position float64
|
||||||
source string
|
source string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +53,7 @@ func (h *LastSuccessfulMirror) Reduce(events ...*eventstore.StorageEvent) (err e
|
|||||||
|
|
||||||
func (h *LastSuccessfulMirror) reduceSucceeded(event *eventstore.StorageEvent) error {
|
func (h *LastSuccessfulMirror) reduceSucceeded(event *eventstore.StorageEvent) error {
|
||||||
// if position is set we skip all older events
|
// if position is set we skip all older events
|
||||||
if h.Position.GreaterThan(decimal.NewFromInt(0)) {
|
if h.Position > 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package mirror
|
package mirror
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
)
|
)
|
||||||
@ -11,7 +9,7 @@ type succeededPayload struct {
|
|||||||
// Source is the name of the database data are mirrored from
|
// Source is the name of the database data are mirrored from
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
// Position until data will be mirrored
|
// Position until data will be mirrored
|
||||||
Position decimal.Decimal `json:"position"`
|
Position float64 `json:"position"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const SucceededType = eventTypePrefix + "succeeded"
|
const SucceededType = eventTypePrefix + "succeeded"
|
||||||
@ -40,7 +38,7 @@ func SucceededEventFromStorage(event *eventstore.StorageEvent) (e *SucceededEven
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSucceededCommand(source string, position decimal.Decimal) *eventstore.Command {
|
func NewSucceededCommand(source string, position float64) *eventstore.Command {
|
||||||
return &eventstore.Command{
|
return &eventstore.Command{
|
||||||
Action: eventstore.Action[any]{
|
Action: eventstore.Action[any]{
|
||||||
Creator: Creator,
|
Creator: Creator,
|
||||||
|
@ -4,43 +4,42 @@ ZITADEL_HOST ?=
|
|||||||
ADMIN_LOGIN_NAME ?=
|
ADMIN_LOGIN_NAME ?=
|
||||||
ADMIN_PASSWORD ?=
|
ADMIN_PASSWORD ?=
|
||||||
|
|
||||||
K6 := ./../../xk6-modules/k6
|
|
||||||
|
|
||||||
.PHONY: human_password_login
|
.PHONY: human_password_login
|
||||||
human_password_login: bundle
|
human_password_login: bundle
|
||||||
${K6} run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/human_password_login.js --vus ${VUS} --duration ${DURATION}
|
k6 run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/human_password_login.js --vus ${VUS} --duration ${DURATION}
|
||||||
|
|
||||||
.PHONY: machine_pat_login
|
.PHONY: machine_pat_login
|
||||||
machine_pat_login: bundle
|
machine_pat_login: bundle
|
||||||
${K6} run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/machine_pat_login.js --vus ${VUS} --duration ${DURATION}
|
k6 run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/machine_pat_login.js --vus ${VUS} --duration ${DURATION}
|
||||||
|
|
||||||
.PHONY: machine_client_credentials_login
|
.PHONY: machine_client_credentials_login
|
||||||
machine_client_credentials_login: bundle
|
machine_client_credentials_login: bundle
|
||||||
${K6} run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/machine_client_credentials_login.js --vus ${VUS} --duration ${DURATION}
|
k6 run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/machine_client_credentials_login.js --vus ${VUS} --duration ${DURATION}
|
||||||
|
|
||||||
.PHONY: user_info
|
.PHONY: user_info
|
||||||
user_info: bundle
|
user_info: bundle
|
||||||
${K6} run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/user_info.js --vus ${VUS} --duration ${DURATION}
|
k6 run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/user_info.js --vus ${VUS} --duration ${DURATION}
|
||||||
|
|
||||||
.PHONY: manipulate_user
|
.PHONY: manipulate_user
|
||||||
manipulate_user: bundle
|
manipulate_user: bundle
|
||||||
${K6} run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/manipulate_user.js --vus ${VUS} --duration ${DURATION}
|
k6 run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/manipulate_user.js --vus ${VUS} --duration ${DURATION}
|
||||||
|
|
||||||
.PHONY: introspect
|
.PHONY: introspect
|
||||||
introspect: ensure_modules bundle
|
introspect: ensure_modules bundle
|
||||||
go install go.k6.io/xk6/cmd/xk6@latest
|
go install go.k6.io/xk6/cmd/xk6@latest
|
||||||
cd ../../xk6-modules && xk6 build --with xk6-zitadel=.
|
cd ../../xk6-modules && xk6 build --with xk6-zitadel=.
|
||||||
${K6} run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/introspection.js --vus ${VUS} --duration ${DURATION}
|
./../../xk6-modules/k6 run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/introspection.js --vus ${VUS} --duration ${DURATION}
|
||||||
|
|
||||||
.PHONY: add_session
|
.PHONY: add_session
|
||||||
add_session: bundle
|
add_session: bundle
|
||||||
${K6} run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/session.js --vus ${VUS} --duration ${DURATION}
|
k6 run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/session.js --vus ${VUS} --duration ${DURATION}
|
||||||
|
|
||||||
.PHONY: machine_jwt_profile_grant
|
.PHONY: machine_jwt_profile_grant
|
||||||
machine_jwt_profile_grant: ensure_modules ensure_key_pair bundle
|
machine_jwt_profile_grant: ensure_modules ensure_key_pair bundle
|
||||||
go install go.k6.io/xk6/cmd/xk6@latest
|
go install go.k6.io/xk6/cmd/xk6@latest
|
||||||
cd ../../xk6-modules && xk6 build --with xk6-zitadel=.
|
cd ../../xk6-modules && xk6 build --with xk6-zitadel=.
|
||||||
${K6} run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/machine_jwt_profile_grant.js --vus ${VUS} --duration ${DURATION}
|
./../../xk6-modules/k6 run --summary-trend-stats "min,avg,max,p(50),p(95),p(99)" dist/machine_jwt_profile_grant.js --iterations 1
|
||||||
|
# --vus ${VUS} --duration ${DURATION}
|
||||||
|
|
||||||
.PHONY: machine_jwt_profile_grant_single_user
|
.PHONY: machine_jwt_profile_grant_single_user
|
||||||
machine_jwt_profile_grant_single_user: ensure_modules ensure_key_pair bundle
|
machine_jwt_profile_grant_single_user: ensure_modules ensure_key_pair bundle
|
||||||
@ -65,8 +64,6 @@ endif
|
|||||||
bundle:
|
bundle:
|
||||||
npm i
|
npm i
|
||||||
npm run bundle
|
npm run bundle
|
||||||
go install go.k6.io/xk6/cmd/xk6@latest
|
|
||||||
cd ../../xk6-modules && xk6 build --with xk6-zitadel=.
|
|
||||||
|
|
||||||
.PHONY: ensure_key_pair
|
.PHONY: ensure_key_pair
|
||||||
ensure_key_pair:
|
ensure_key_pair:
|
||||||
|
@ -45,4 +45,3 @@ export function teardown(data: any) {
|
|||||||
removeOrg(data.org, data.tokens.accessToken);
|
removeOrg(data.org, data.tokens.accessToken);
|
||||||
console.info('teardown: org removed');
|
console.info('teardown: org removed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,7 +437,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "SMTP";
|
tags: "SMTP Configs";
|
||||||
deprecated: true;
|
deprecated: true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -457,7 +457,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "SMTP";
|
tags: "SMTP Configs";
|
||||||
deprecated: true;
|
deprecated: true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -478,7 +478,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "SMTP";
|
tags: "SMTP Configs";
|
||||||
deprecated: true;
|
deprecated: true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -499,7 +499,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "SMTP";
|
tags: "SMTP Configs";
|
||||||
deprecated: true;
|
deprecated: true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -520,7 +520,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "SMTP";
|
tags: "SMTP Configs";
|
||||||
deprecated: true;
|
deprecated: true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -541,7 +541,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "SMTP Provider";
|
tags: "SMTP Configs";
|
||||||
summary: "Activate SMTP Provider";
|
summary: "Activate SMTP Provider";
|
||||||
description: "Activate an SMTP provider."
|
description: "Activate an SMTP provider."
|
||||||
deprecated: true;
|
deprecated: true;
|
||||||
@ -564,7 +564,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "SMTP Provider";
|
tags: "SMTP Configs";
|
||||||
deprecated: true;
|
deprecated: true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -584,7 +584,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "SMTP";
|
tags: "SMTP Configs";
|
||||||
deprecated: true;
|
deprecated: true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -605,7 +605,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "SMTP Provider";
|
tags: "SMTP Configs";
|
||||||
deprecated: true;
|
deprecated: true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -626,7 +626,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "SMTP Provider";
|
tags: "SMTP Configs";
|
||||||
deprecated: true;
|
deprecated: true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -663,7 +663,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "Email providers";
|
tags: "Email Provider";
|
||||||
summary: "List Email providers";
|
summary: "List Email providers";
|
||||||
description: "Returns a list of Email providers."
|
description: "Returns a list of Email providers."
|
||||||
};
|
};
|
||||||
@ -679,7 +679,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "Email";
|
tags: "Email Provider";
|
||||||
summary: "Get active Email provider";
|
summary: "Get active Email provider";
|
||||||
description: "Returns the active Email provider from the system. This is used to send E-Mails to the users."
|
description: "Returns the active Email provider from the system. This is used to send E-Mails to the users."
|
||||||
};
|
};
|
||||||
@ -695,7 +695,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "Email";
|
tags: "Email Provider";
|
||||||
summary: "Get Email provider by its id";
|
summary: "Get Email provider by its id";
|
||||||
description: "Get a specific Email provider by its ID.";
|
description: "Get a specific Email provider by its ID.";
|
||||||
};
|
};
|
||||||
@ -712,7 +712,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "Email";
|
tags: "Email Provider";
|
||||||
summary: "Add SMTP Email provider";
|
summary: "Add SMTP Email provider";
|
||||||
description: "Add a new SMTP Email provider if nothing is set yet."
|
description: "Add a new SMTP Email provider if nothing is set yet."
|
||||||
};
|
};
|
||||||
@ -729,7 +729,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "Email";
|
tags: "Email Provider";
|
||||||
summary: "Update SMTP Email provider";
|
summary: "Update SMTP Email provider";
|
||||||
description: "Update the SMTP Email provider, be aware that this will be activated as soon as it is saved. So the users will get notifications from the newly configured SMTP."
|
description: "Update the SMTP Email provider, be aware that this will be activated as soon as it is saved. So the users will get notifications from the newly configured SMTP."
|
||||||
};
|
};
|
||||||
@ -746,7 +746,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "Email";
|
tags: "Email Provider";
|
||||||
summary: "Add HTTP Email provider";
|
summary: "Add HTTP Email provider";
|
||||||
description: "Add a new HTTP Email provider if nothing is set yet."
|
description: "Add a new HTTP Email provider if nothing is set yet."
|
||||||
};
|
};
|
||||||
@ -763,7 +763,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "Email";
|
tags: "Email Provider";
|
||||||
summary: "Update HTTP Email provider";
|
summary: "Update HTTP Email provider";
|
||||||
description: "Update the HTTP Email provider, be aware that this will be activated as soon as it is saved. So the users will get notifications from the newly configured HTTP."
|
description: "Update the HTTP Email provider, be aware that this will be activated as soon as it is saved. So the users will get notifications from the newly configured HTTP."
|
||||||
};
|
};
|
||||||
@ -780,7 +780,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "SMTP";
|
tags: "Email Provider";
|
||||||
summary: "Update SMTP Password";
|
summary: "Update SMTP Password";
|
||||||
description: "Update the SMTP password that is used for the host, be aware that this will be activated as soon as it is saved. So the users will get notifications from the newly configured SMTP."
|
description: "Update the SMTP password that is used for the host, be aware that this will be activated as soon as it is saved. So the users will get notifications from the newly configured SMTP."
|
||||||
};
|
};
|
||||||
@ -830,7 +830,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "Email";
|
tags: "Email Provider";
|
||||||
summary: "Remove Email provider";
|
summary: "Remove Email provider";
|
||||||
description: "Remove the Email provider, be aware that the users will not get an E-Mail if no provider is set."
|
description: "Remove the Email provider, be aware that the users will not get an E-Mail if no provider is set."
|
||||||
};
|
};
|
||||||
@ -847,7 +847,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "SMTP Email Provider";
|
tags: "Email Provider";
|
||||||
summary: "Test SMTP Email Provider";
|
summary: "Test SMTP Email Provider";
|
||||||
description: "Test an SMTP Email provider identified by its ID. After testing the provider, the users will receive information about the test results."
|
description: "Test an SMTP Email provider identified by its ID. After testing the provider, the users will receive information about the test results."
|
||||||
};
|
};
|
||||||
@ -864,7 +864,7 @@ service AdminService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
tags: "SMTP EMailProvider";
|
tags: "Email Provider";
|
||||||
summary: "Test SMTP Email Provider";
|
summary: "Test SMTP Email Provider";
|
||||||
description: "Test an SMTP Email provider. After testing the provider, the users will receive information about the test results."
|
description: "Test an SMTP Email provider. After testing the provider, the users will receive information about the test results."
|
||||||
};
|
};
|
||||||
|
@ -1565,6 +1565,11 @@ message UserGrant {
|
|||||||
description: "type of the user (human / machine)"
|
description: "type of the user (human / machine)"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
zitadel.user.v1.UserGrantState state = 13 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
description: "current state of the user grant";
|
||||||
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListMyProjectOrgsRequest {
|
message ListMyProjectOrgsRequest {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user