mirror of
https://github.com/zitadel/zitadel.git
synced 2025-03-01 18:27:24 +00:00
Merge branch 'main' into next
This commit is contained in:
commit
6e60335789
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -52,6 +52,8 @@ jobs:
|
|||||||
go_version: "1.22"
|
go_version: "1.22"
|
||||||
core_cache_key: ${{ needs.core.outputs.cache_key }}
|
core_cache_key: ${{ needs.core.outputs.cache_key }}
|
||||||
core_cache_path: ${{ needs.core.outputs.cache_path }}
|
core_cache_path: ${{ needs.core.outputs.cache_path }}
|
||||||
|
secrets:
|
||||||
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
needs: [core, console]
|
needs: [core, console]
|
||||||
|
3
.github/workflows/core-test.yml
vendored
3
.github/workflows/core-test.yml
vendored
@ -12,6 +12,9 @@ on:
|
|||||||
core_cache_path:
|
core_cache_path:
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
secrets:
|
||||||
|
CODECOV_TOKEN:
|
||||||
|
required: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
postgres:
|
postgres:
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -82,3 +82,7 @@ go.work
|
|||||||
go.work.sum
|
go.work.sum
|
||||||
# Local Netlify folder
|
# Local Netlify folder
|
||||||
.netlify
|
.netlify
|
||||||
|
|
||||||
|
load-test/node_modules
|
||||||
|
load-test/yarn-error.log
|
||||||
|
load-test/dist
|
15
README.md
15
README.md
@ -96,7 +96,7 @@ Yet it offers everything you need for a customer identity ([CIAM](https://zitade
|
|||||||
|
|
||||||
- [API-first approach](https://zitadel.com/docs/apis/introduction)
|
- [API-first approach](https://zitadel.com/docs/apis/introduction)
|
||||||
- [Multi-tenancy](https://zitadel.com/docs/guides/solution-scenarios/b2b) authentication and access management
|
- [Multi-tenancy](https://zitadel.com/docs/guides/solution-scenarios/b2b) authentication and access management
|
||||||
- Strong audit trail thanks to [event sourcing](https://zitadel.com/docs/concepts/eventstore/overview) as storage pattern
|
- [Strong audit trail](https://zitadel.com/docs/concepts/features/audit-trail) thanks to [event sourcing](https://zitadel.com/docs/concepts/eventstore/overview) as storage pattern
|
||||||
- [Actions](https://zitadel.com/docs/apis/actions/introduction) to react on events with custom code and extended ZITADEL for you needs
|
- [Actions](https://zitadel.com/docs/apis/actions/introduction) to react on events with custom code and extended ZITADEL for you needs
|
||||||
- [Branding](https://zitadel.com/docs/guides/manage/customize/branding) for a uniform user experience across multiple organizations
|
- [Branding](https://zitadel.com/docs/guides/manage/customize/branding) for a uniform user experience across multiple organizations
|
||||||
- [Self-service](https://zitadel.com/docs/concepts/features/selfservice) for end-users, business customers, and administrators
|
- [Self-service](https://zitadel.com/docs/concepts/features/selfservice) for end-users, business customers, and administrators
|
||||||
@ -107,16 +107,17 @@ Yet it offers everything you need for a customer identity ([CIAM](https://zitade
|
|||||||
Authentication
|
Authentication
|
||||||
|
|
||||||
- Single Sign On (SSO)
|
- Single Sign On (SSO)
|
||||||
- Passkeys support (FIDO2 / WebAuthN)
|
- [Passkeys support (FIDO2 / WebAuthN)](https://zitadel.com/docs/concepts/features/passkeys)
|
||||||
- Username / Password
|
- Username / Password
|
||||||
- Multifactor authentication with OTP, U2F, Email OTP, SMS OTP
|
- Multifactor authentication with OTP, U2F, Email OTP, SMS OTP
|
||||||
- LDAP
|
- [LDAP](https://zitadel.com/docs/guides/integrate/identity-providers/ldap)
|
||||||
- External enterprise identity providers and social logins
|
- [External enterprise identity providers and social logins](https://zitadel.com/docs/guides/integrate/identity-providers/introduction)
|
||||||
- [Device authorization](https://zitadel.com/docs/guides/solution-scenarios/device-authorization)
|
- [Device authorization](https://zitadel.com/docs/guides/solution-scenarios/device-authorization)
|
||||||
- [OpenID Connect certified](https://openid.net/certification/#OPs) => [OIDC Endpoints](https://zitadel.com/docs/apis/openidoauth/endpoints)
|
- [OpenID Connect certified](https://openid.net/certification/#OPs) => [OIDC Endpoints](https://zitadel.com/docs/apis/openidoauth/endpoints)
|
||||||
- [SAML 2.0](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html) => [SAML Endpoints](https://zitadel.com/docs/apis/saml/endpoints)
|
- [SAML 2.0](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html) => [SAML Endpoints](https://zitadel.com/docs/apis/saml/endpoints)
|
||||||
- [Custom sessions](https://zitadel.com/docs/guides/integrate/login-ui/username-password) if you need to go beyond OIDC or SAML
|
- [Custom sessions](https://zitadel.com/docs/guides/integrate/login-ui/username-password) if you need to go beyond OIDC or SAML
|
||||||
- [Machine-to-machine](https://zitadel.com/docs/guides/integrate/serviceusers) with JWT profile, Personal Access Tokens (PAT), and Client Credentials
|
- [Machine-to-machine](https://zitadel.com/docs/guides/integrate/service-users/authenticate-service-users) with JWT profile, Personal Access Tokens (PAT), and Client Credentials
|
||||||
|
- [Token exchange and impersonation](https://zitadel.com/docs/guides/integrate/token-exchange)
|
||||||
|
|
||||||
Multi-Tenancy
|
Multi-Tenancy
|
||||||
|
|
||||||
@ -130,6 +131,10 @@ Integration
|
|||||||
- [GRPC and REST APIs](https://zitadel.com/docs/apis/introduction) for every functionality and resource
|
- [GRPC and REST APIs](https://zitadel.com/docs/apis/introduction) for every functionality and resource
|
||||||
- [Actions](https://zitadel.com/docs/apis/actions/introduction) to call any API, send webhooks, adjust workflows, or customize tokens
|
- [Actions](https://zitadel.com/docs/apis/actions/introduction) to call any API, send webhooks, adjust workflows, or customize tokens
|
||||||
- [Role Based Access Control (RBAC)](https://zitadel.com/docs/guides/integrate/retrieve-user-roles)
|
- [Role Based Access Control (RBAC)](https://zitadel.com/docs/guides/integrate/retrieve-user-roles)
|
||||||
|
- [Examples and SDKs](https://zitadel.com/docs/sdk-examples/introduction)
|
||||||
|
- [Audit Log and SOC/SIEM](https://zitadel.com/docs/guides/integrate/external-audit-log)
|
||||||
|
- [User registration and onboarding](https://zitadel.com/docs/guides/integrate/onboarding)
|
||||||
|
- [Hosted and custom login user interface](https://zitadel.com/docs/guides/integrate/login-ui)
|
||||||
|
|
||||||
Self-Service
|
Self-Service
|
||||||
- [Self-registration](https://zitadel.com/docs/concepts/features/selfservice#registration) including verification
|
- [Self-registration](https://zitadel.com/docs/concepts/features/selfservice#registration) including verification
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
<ng-container matColumnDef="expirationDate">
|
<ng-container matColumnDef="expirationDate">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'USER.MACHINE.EXPIRATIONDATE' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'USER.MACHINE.EXPIRATIONDATE' | translate }}</th>
|
||||||
<td mat-cell *matCellDef="let key">
|
<td mat-cell *matCellDef="let key">
|
||||||
{{ key.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm' }}
|
{{ key.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM yyyy, HH:mm' }}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<span>{{ length }} </span>{{ 'PAGINATOR.COUNT' | translate }}
|
<span>{{ length }} </span>{{ 'PAGINATOR.COUNT' | translate }}
|
||||||
</p>
|
</p>
|
||||||
<p class="ts cnsl-secondary-text" *ngIf="timestamp" data-e2e="timestamp">
|
<p class="ts cnsl-secondary-text" *ngIf="timestamp" data-e2e="timestamp">
|
||||||
{{ timestamp | timestampToDate | localizedDate: 'EEEE dd. MMM YYYY, HH:mm' }}
|
{{ timestamp | timestampToDate | localizedDate: 'EEEE dd. MMM yyyy, HH:mm' }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
<ng-container matColumnDef="expirationDate">
|
<ng-container matColumnDef="expirationDate">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'USER.MACHINE.EXPIRATIONDATE' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'USER.MACHINE.EXPIRATIONDATE' | translate }}</th>
|
||||||
<td mat-cell *matCellDef="let key">
|
<td mat-cell *matCellDef="let key">
|
||||||
{{ key.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm' }}
|
{{ key.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM yyyy, HH:mm' }}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -13,14 +13,14 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<p class="left cnsl-secondary-text">{{ 'USER.MACHINE.CREATIONDATE' | translate }}</p>
|
<p class="left cnsl-secondary-text">{{ 'USER.MACHINE.CREATIONDATE' | translate }}</p>
|
||||||
<p *ngIf="keyResponse.details && keyResponse.details.creationDate" class="right">
|
<p *ngIf="keyResponse.details && keyResponse.details.creationDate" class="right">
|
||||||
{{ keyResponse.details.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm' }}
|
{{ keyResponse.details.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM yyyy, HH:mm' }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" *ngIf="expirationDate">
|
<div class="row" *ngIf="expirationDate">
|
||||||
<p class="left cnsl-secondary-text">{{ 'USER.MACHINE.EXPIRATIONDATE' | translate }}</p>
|
<p class="left cnsl-secondary-text">{{ 'USER.MACHINE.EXPIRATIONDATE' | translate }}</p>
|
||||||
<p class="right">
|
<p class="right">
|
||||||
{{ expirationDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm' }}
|
{{ expirationDate | localizedDate: 'EEE dd. MMM yyyy, HH:mm' }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
|||||||
import { Metadata } from 'src/app/proto/generated/zitadel/metadata_pb';
|
import { Metadata } from 'src/app/proto/generated/zitadel/metadata_pb';
|
||||||
import { Org, OrgState } from 'src/app/proto/generated/zitadel/org_pb';
|
import { Org, OrgState } from 'src/app/proto/generated/zitadel/org_pb';
|
||||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||||
|
import { AdminService } from 'src/app/services/admin.service';
|
||||||
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
|
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
|
||||||
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';
|
||||||
@ -48,6 +49,7 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
|||||||
private auth: GrpcAuthService,
|
private auth: GrpcAuthService,
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
public mgmtService: ManagementService,
|
public mgmtService: ManagementService,
|
||||||
|
private adminService: AdminService,
|
||||||
private toast: ToastService,
|
private toast: ToastService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
breadcrumbService: BreadcrumbService,
|
breadcrumbService: BreadcrumbService,
|
||||||
@ -146,15 +148,34 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
|||||||
width: '400px',
|
width: '400px',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Before we remove the org we get the current default org
|
||||||
|
// we have to query before the current org is removed
|
||||||
dialogRef.afterClosed().subscribe((resp) => {
|
dialogRef.afterClosed().subscribe((resp) => {
|
||||||
if (resp) {
|
if (resp) {
|
||||||
this.mgmtService
|
this.adminService
|
||||||
.removeOrg()
|
.getDefaultOrg()
|
||||||
.then(() => {
|
.then((response) => {
|
||||||
setTimeout(() => {
|
const org = response?.org;
|
||||||
this.router.navigate(['/orgs']);
|
if (org) {
|
||||||
}, 1000);
|
// We now remove the org
|
||||||
this.toast.showInfo('ORG.TOAST.DELETED', true);
|
this.mgmtService
|
||||||
|
.removeOrg()
|
||||||
|
.then(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
// We change active org to default org as
|
||||||
|
// current org was deleted to avoid Organization doesn't exist
|
||||||
|
this.auth.setActiveOrg(org);
|
||||||
|
// Now we visit orgs
|
||||||
|
this.router.navigate(['/orgs']);
|
||||||
|
}, 1000);
|
||||||
|
this.toast.showInfo('ORG.TOAST.DELETED', true);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.toast.showError('ORG.TOAST.DEFAULTORGNOTFOUND', false, true);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.toast.showError(error);
|
this.toast.showError(error);
|
||||||
|
@ -90,6 +90,8 @@ import {
|
|||||||
GetDefaultLanguageResponse,
|
GetDefaultLanguageResponse,
|
||||||
GetDefaultLoginTextsRequest,
|
GetDefaultLoginTextsRequest,
|
||||||
GetDefaultLoginTextsResponse,
|
GetDefaultLoginTextsResponse,
|
||||||
|
GetDefaultOrgRequest,
|
||||||
|
GetDefaultOrgResponse,
|
||||||
GetDefaultPasswordChangeMessageTextRequest,
|
GetDefaultPasswordChangeMessageTextRequest,
|
||||||
GetDefaultPasswordChangeMessageTextResponse,
|
GetDefaultPasswordChangeMessageTextResponse,
|
||||||
GetDefaultPasswordlessRegistrationMessageTextRequest,
|
GetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||||
@ -429,6 +431,11 @@ export class AdminService {
|
|||||||
this.storageService.getItem('onboarding-dismissed', StorageLocation.local) === 'true' ? true : false;
|
this.storageService.getItem('onboarding-dismissed', StorageLocation.local) === 'true' ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getDefaultOrg(): Promise<GetDefaultOrgResponse.AsObject> {
|
||||||
|
const req = new GetDefaultOrgRequest();
|
||||||
|
return this.grpcService.admin.getDefaultOrg(req, null).then((resp) => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
public setDefaultOrg(orgId: string): Promise<SetDefaultOrgResponse.AsObject> {
|
public setDefaultOrg(orgId: string): Promise<SetDefaultOrgResponse.AsObject> {
|
||||||
const req = new SetDefaultOrgRequest();
|
const req = new SetDefaultOrgRequest();
|
||||||
req.setOrgId(orgId);
|
req.setOrgId(orgId);
|
||||||
|
@ -353,6 +353,7 @@ import {
|
|||||||
RemoveOrgMetadataRequest,
|
RemoveOrgMetadataRequest,
|
||||||
RemoveOrgMetadataResponse,
|
RemoveOrgMetadataResponse,
|
||||||
RemoveOrgRequest,
|
RemoveOrgRequest,
|
||||||
|
RemoveOrgResponse,
|
||||||
RemovePersonalAccessTokenRequest,
|
RemovePersonalAccessTokenRequest,
|
||||||
RemovePersonalAccessTokenResponse,
|
RemovePersonalAccessTokenResponse,
|
||||||
RemoveProjectGrantMemberRequest,
|
RemoveProjectGrantMemberRequest,
|
||||||
@ -1749,7 +1750,7 @@ export class ManagementService {
|
|||||||
return this.grpcService.mgmt.removeUser(req, null).then((resp) => resp.toObject());
|
return this.grpcService.mgmt.removeUser(req, null).then((resp) => resp.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeOrg(): Promise<RemoveUserResponse.AsObject> {
|
public removeOrg(): Promise<RemoveOrgResponse.AsObject> {
|
||||||
const req = new RemoveOrgRequest();
|
const req = new RemoveOrgRequest();
|
||||||
return this.grpcService.mgmt.removeOrg(req, null).then((resp) => resp.toObject());
|
return this.grpcService.mgmt.removeOrg(req, null).then((resp) => resp.toObject());
|
||||||
}
|
}
|
||||||
|
@ -1285,6 +1285,7 @@
|
|||||||
"MEMBERCHANGED": "Сменен управител.",
|
"MEMBERCHANGED": "Сменен управител.",
|
||||||
"SETPRIMARY": "Основен набор от домейни.",
|
"SETPRIMARY": "Основен набор от домейни.",
|
||||||
"DELETED": "Организацията е изтрита успешно",
|
"DELETED": "Организацията е изтрита успешно",
|
||||||
|
"DEFAULTORGNOTFOUND": "Организацията по подразбиране не беше намерена",
|
||||||
"ORG_WAS_DELETED": "Организацията е изтрита."
|
"ORG_WAS_DELETED": "Организацията е изтрита."
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
|
@ -1292,6 +1292,7 @@
|
|||||||
"MEMBERCHANGED": "Manažer změněn.",
|
"MEMBERCHANGED": "Manažer změněn.",
|
||||||
"SETPRIMARY": "Nastavena primární doména.",
|
"SETPRIMARY": "Nastavena primární doména.",
|
||||||
"DELETED": "Organizace úspěšně smazána",
|
"DELETED": "Organizace úspěšně smazána",
|
||||||
|
"DEFAULTORGNOTFOUND": "Výchozí organizace nebyla nalezena",
|
||||||
"ORG_WAS_DELETED": "Organizace byla smazána."
|
"ORG_WAS_DELETED": "Organizace byla smazána."
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
|
@ -1291,6 +1291,7 @@
|
|||||||
"MEMBERCHANGED": "Manager geändert.",
|
"MEMBERCHANGED": "Manager geändert.",
|
||||||
"SETPRIMARY": "Primäre Domain gesetzt.",
|
"SETPRIMARY": "Primäre Domain gesetzt.",
|
||||||
"DELETED": "Organisation erfolgreich gelöscht",
|
"DELETED": "Organisation erfolgreich gelöscht",
|
||||||
|
"DEFAULTORGNOTFOUND": "Die Standardorganisation wurde nicht gefunden",
|
||||||
"ORG_WAS_DELETED": "Organisation wurde gelöscht."
|
"ORG_WAS_DELETED": "Organisation wurde gelöscht."
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
|
@ -1291,8 +1291,9 @@
|
|||||||
"MEMBERREMOVED": "Manager removed.",
|
"MEMBERREMOVED": "Manager removed.",
|
||||||
"MEMBERCHANGED": "Manager changed.",
|
"MEMBERCHANGED": "Manager changed.",
|
||||||
"SETPRIMARY": "Primary domain set.",
|
"SETPRIMARY": "Primary domain set.",
|
||||||
"DELETED": "Organisation deleted successfully",
|
"DELETED": "Organization deleted successfully",
|
||||||
"ORG_WAS_DELETED": "Organisation has been deleted."
|
"DEFAULTORGNOTFOUND": "The default organization was not found",
|
||||||
|
"ORG_WAS_DELETED": "Organization has been deleted."
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
"DEACTIVATE": {
|
"DEACTIVATE": {
|
||||||
|
@ -1293,6 +1293,7 @@
|
|||||||
"MEMBERCHANGED": "Mánager modificado.",
|
"MEMBERCHANGED": "Mánager modificado.",
|
||||||
"SETPRIMARY": "Dominio primario establecido.",
|
"SETPRIMARY": "Dominio primario establecido.",
|
||||||
"DELETED": "Organización borrada con éxito",
|
"DELETED": "Organización borrada con éxito",
|
||||||
|
"DEFAULTORGNOTFOUND": "No se encontró la organización por defecto",
|
||||||
"ORG_WAS_DELETED": "La organización ha sido borrada."
|
"ORG_WAS_DELETED": "La organización ha sido borrada."
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
|
@ -1291,6 +1291,7 @@
|
|||||||
"MEMBERCHANGED": "Gestionnaire modifié.",
|
"MEMBERCHANGED": "Gestionnaire modifié.",
|
||||||
"SETPRIMARY": "Domaine primaire défini.",
|
"SETPRIMARY": "Domaine primaire défini.",
|
||||||
"DELETED": "Organisation supprimée avec succès",
|
"DELETED": "Organisation supprimée avec succès",
|
||||||
|
"DEFAULTORGNOTFOUND": "L'organisation par défaut est introuvable",
|
||||||
"ORG_WAS_DELETED": "L'organisation a été supprimée"
|
"ORG_WAS_DELETED": "L'organisation a été supprimée"
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
|
@ -1291,6 +1291,7 @@
|
|||||||
"MEMBERCHANGED": "Manager cambiato con successo",
|
"MEMBERCHANGED": "Manager cambiato con successo",
|
||||||
"SETPRIMARY": "Dominio primario cambiato con successo",
|
"SETPRIMARY": "Dominio primario cambiato con successo",
|
||||||
"DELETED": "Organizzazione eliminata con successo",
|
"DELETED": "Organizzazione eliminata con successo",
|
||||||
|
"DEFAULTORGNOTFOUND": "Impossibile trovare l'organizzazione predefinita",
|
||||||
"ORG_WAS_DELETED": "Organizzazione è stata eliminata"
|
"ORG_WAS_DELETED": "Organizzazione è stata eliminata"
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
|
@ -1292,6 +1292,7 @@
|
|||||||
"MEMBERCHANGED": "マネージャーが変更されました。",
|
"MEMBERCHANGED": "マネージャーが変更されました。",
|
||||||
"SETPRIMARY": "プライマリドメインが設定されました。",
|
"SETPRIMARY": "プライマリドメインが設定されました。",
|
||||||
"DELETED": "組織が正常に削除されました。",
|
"DELETED": "組織が正常に削除されました。",
|
||||||
|
"DEFAULTORGNOTFOUND": "デフォルトの組織が見つかりませんでした",
|
||||||
"ORG_WAS_DELETED": "組織が削除されました。"
|
"ORG_WAS_DELETED": "組織が削除されました。"
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
|
@ -1293,6 +1293,7 @@
|
|||||||
"MEMBERCHANGED": "Променет менаџер.",
|
"MEMBERCHANGED": "Променет менаџер.",
|
||||||
"SETPRIMARY": "Поставен основен домен.",
|
"SETPRIMARY": "Поставен основен домен.",
|
||||||
"DELETED": "Организацијата успешно избришана",
|
"DELETED": "Организацијата успешно избришана",
|
||||||
|
"DEFAULTORGNOTFOUND": "Стандардната организација не беше пронајдена",
|
||||||
"ORG_WAS_DELETED": "Организацијата е избришана."
|
"ORG_WAS_DELETED": "Организацијата е избришана."
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
|
@ -1292,6 +1292,7 @@
|
|||||||
"MEMBERCHANGED": "Beheerder gewijzigd.",
|
"MEMBERCHANGED": "Beheerder gewijzigd.",
|
||||||
"SETPRIMARY": "Primaire domein ingesteld.",
|
"SETPRIMARY": "Primaire domein ingesteld.",
|
||||||
"DELETED": "Organisatie succesvol verwijderd",
|
"DELETED": "Organisatie succesvol verwijderd",
|
||||||
|
"DEFAULTORGNOTFOUND": "De standaardorganisatie is niet gevonden",
|
||||||
"ORG_WAS_DELETED": "Organisatie is verwijderd."
|
"ORG_WAS_DELETED": "Organisatie is verwijderd."
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
|
@ -1291,6 +1291,7 @@
|
|||||||
"MEMBERCHANGED": "Zmieniono managera.",
|
"MEMBERCHANGED": "Zmieniono managera.",
|
||||||
"SETPRIMARY": "Ustawiono domenę podstawową.",
|
"SETPRIMARY": "Ustawiono domenę podstawową.",
|
||||||
"DELETED": "Organizacja została usunięta pomyślnie",
|
"DELETED": "Organizacja została usunięta pomyślnie",
|
||||||
|
"DEFAULTORGNOTFOUND": "Nie znaleziono organizacji domyślnej",
|
||||||
"ORG_WAS_DELETED": "Organizacja została usunięta."
|
"ORG_WAS_DELETED": "Organizacja została usunięta."
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
|
@ -1293,6 +1293,7 @@
|
|||||||
"MEMBERCHANGED": "Gerente alterado.",
|
"MEMBERCHANGED": "Gerente alterado.",
|
||||||
"SETPRIMARY": "Domínio principal definido.",
|
"SETPRIMARY": "Domínio principal definido.",
|
||||||
"DELETED": "Organização excluída com sucesso",
|
"DELETED": "Organização excluída com sucesso",
|
||||||
|
"DEFAULTORGNOTFOUND": "A organização padrão não foi encontrada",
|
||||||
"ORG_WAS_DELETED": "Organização foi excluída."
|
"ORG_WAS_DELETED": "Organização foi excluída."
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
|
@ -1335,6 +1335,7 @@
|
|||||||
"MEMBERCHANGED": "Менеджер изменён.",
|
"MEMBERCHANGED": "Менеджер изменён.",
|
||||||
"SETPRIMARY": "Установлен основной домен.",
|
"SETPRIMARY": "Установлен основной домен.",
|
||||||
"DELETED": "Организация успешно удалена",
|
"DELETED": "Организация успешно удалена",
|
||||||
|
"DEFAULTORGNOTFOUND": "Организация по умолчанию не найдена",
|
||||||
"ORG_WAS_DELETED": "Организация удалена."
|
"ORG_WAS_DELETED": "Организация удалена."
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
|
@ -1291,6 +1291,7 @@
|
|||||||
"MEMBERCHANGED": "管理者以改变。",
|
"MEMBERCHANGED": "管理者以改变。",
|
||||||
"SETPRIMARY": "已设为主域名。",
|
"SETPRIMARY": "已设为主域名。",
|
||||||
"DELETED": "成功删除的组织",
|
"DELETED": "成功删除的组织",
|
||||||
|
"DEFAULTORGNOTFOUND": "未找到默认组织",
|
||||||
"ORG_WAS_DELETED": "组织被删除"
|
"ORG_WAS_DELETED": "组织被删除"
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
|
@ -57,18 +57,6 @@ spec:
|
|||||||
selector:
|
selector:
|
||||||
app: cockroachdb
|
app: cockroachdb
|
||||||
---
|
---
|
||||||
apiVersion: policy/v1beta1
|
|
||||||
kind: PodDisruptionBudget
|
|
||||||
metadata:
|
|
||||||
name: cockroachdb-budget
|
|
||||||
labels:
|
|
||||||
app: cockroachdb
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: cockroachdb
|
|
||||||
maxUnavailable: 1
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: StatefulSet
|
kind: StatefulSet
|
||||||
metadata:
|
metadata:
|
||||||
|
@ -7,7 +7,7 @@ spec:
|
|||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
client.knative.dev/user-image: ghcr.io/zitadel/zitadel:stable
|
client.knative.dev/user-image: ghcr.io/zitadel/zitadel:latest
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
spec:
|
spec:
|
||||||
containerConcurrency: 0
|
containerConcurrency: 0
|
||||||
@ -28,7 +28,7 @@ spec:
|
|||||||
value: "80"
|
value: "80"
|
||||||
- name: ZITADEL_EXTERNALDOMAIN
|
- name: ZITADEL_EXTERNALDOMAIN
|
||||||
value: zitadel.default.127.0.0.1.sslip.io
|
value: zitadel.default.127.0.0.1.sslip.io
|
||||||
image: ghcr.io/zitadel/zitadel:stable
|
image: ghcr.io/zitadel/zitadel:latest
|
||||||
name: user-container
|
name: user-container
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
|
@ -68,6 +68,19 @@ https://github.com/zitadel/actions/blob/main/examples/custom_roles.js
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### Custom role mapping including org metadata in claims
|
||||||
|
|
||||||
|
There's even a possibility to use the metadata of organizations the user is granted to
|
||||||
|
|
||||||
|
<details open="">
|
||||||
|
<summary>Code example</summary>
|
||||||
|
|
||||||
|
```js reference
|
||||||
|
https://github.com/zitadel/actions/blob/main/examples/custom_roles_org_metadata.js
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
## Customize SAML response
|
## Customize SAML response
|
||||||
|
|
||||||
Append attributes returned on SAML requests.
|
Append attributes returned on SAML requests.
|
||||||
|
@ -210,3 +210,5 @@ This object represents a list of user grant stored in ZITADEL.
|
|||||||
The name of the organization, where the user was granted
|
The name of the organization, where the user was granted
|
||||||
- `projectId` *string*
|
- `projectId` *string*
|
||||||
- `projectName` *string*
|
- `projectName` *string*
|
||||||
|
- `getOrgMetadata()` [*metadataResult*](#metadata-result)
|
||||||
|
Get the metadata of the organization where the user was granted
|
@ -1,23 +1,78 @@
|
|||||||
---
|
---
|
||||||
title: Identity Brokering in ZITADEL
|
title: Identity Brokering
|
||||||
sidebar_label: Identity Brokering
|
sidebar_label: Identity Brokering
|
||||||
---
|
---
|
||||||
|
|
||||||
## What are Identity Brokering and Federated Identities?
|
Link social logins and external identity providers with your identity management platform allowing users to login with their preferred identity provider.
|
||||||
|
|
||||||
|
Establish a trusted connection between your central identity provider (IdP) and third party identity providers.
|
||||||
|
|
||||||
|
By using a central identity brokering service you don't need to develop and establish a trust relationship between each application and each identity provider individually.
|
||||||
|
|
||||||
|
## What are federated identities?
|
||||||
|
|
||||||
Federated identity management is an arrangement built upon the trust between two or more domains. Users of these domains are allowed to access applications and services using the same identity.
|
Federated identity management is an arrangement built upon the trust between two or more domains. Users of these domains are allowed to access applications and services using the same identity.
|
||||||
This identity is known as federated identity and the pattern behind this is identity federation.
|
This identity is known as federated identity and the pattern behind this is identity federation.
|
||||||
|
|
||||||
|
Compatibility across various IdPs is ensured by using industry standard protocols, such as:
|
||||||
|
|
||||||
|
* OpenID Connect (OIDC): A modern and versatile protocol for secure authentication.
|
||||||
|
* SAML2: A widely adopted protocol for secure single sign-on (SSO) in enterprise environments.
|
||||||
|
* LDAP: A lightweight protocol for accessing user data directories commonly used in corporate networks.
|
||||||
|
|
||||||
|
## What is identity brokering?
|
||||||
|
|
||||||
A service provider that specializes in brokering access control between multiple service providers (also referred to as relying parties) is called an identity broker.
|
A service provider that specializes in brokering access control between multiple service providers (also referred to as relying parties) is called an identity broker.
|
||||||
Federated identity management is an arrangement that is made between two or more such identity brokers across organizations.
|
Federated identity management is an arrangement that is made between two or more such identity brokers across organizations.
|
||||||
|
|
||||||
For example, if Google is configured as an identity provider in your organization, the user will get the option to use his Google Account on the Login Screen of ZITADEL. Because Google is registered as a trusted identity provider, the user will be able to login in with the Google account after the user is linked with an existing ZITADEL account (if the user is already registered) or a new one with the claims provided by Google.
|
For example, if Google is configured as an identity provider in your organization, the user will get the option to use his Google Account on the Login Screen of ZITADEL.
|
||||||
|
Because Google is registered as a trusted identity provider, the user will be able to login in with the Google account after the user is linked with an existing ZITADEL account (if the user is already registered) or a new one with the claims provided by Google.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## How to use external identity providers in ZITADEL
|
The schema is a very simplified version, but shows the essential steps for identity brokering
|
||||||
|
|
||||||
Configure external identity providers on the instance level or just for one organization via the [Console](/guides/manage/console/default-settings#identity-providers) or ZITADEL APIs.
|
1. An unauthenticated user wants to use the alpha.com's application.
|
||||||
|
2. The application redirects the user to alpha.com's identity provider (IdP).
|
||||||
|
3. Based on the user's tenants configuration the IdP presents the configured identity providers, or redirects the user directly to the primary external IdP. The user authenticates with their external identity provider (eg, Entra ID).
|
||||||
|
4. After the authentication, the user is redirected back to alpha.com's identity provider. If the user doesn't exist in the IdP the user will be created just-in-time and linked to the external identity provider for future reference.
|
||||||
|
5. As with a local authentication, the IdP issues a token to the user that can be used to access the application. The IdP redirects the user, which is now authenticated, eventually to the application.
|
||||||
|
|
||||||
You will find [detailed integration guides for many Identity Providers](/guides/integrate/identity-providers/introduction) in our docs.
|
## Is single-sign-on (SSO) the same as identity brokering?
|
||||||
ZITADEL also provides templates to configure generic identity providers, which don't have templates.
|
|
||||||
|
Sometimes single-sign-on (SSO) and login with third party identity providers is used interchangeably.
|
||||||
|
Typically SSO describes an authentication scheme that allows users to log in once at a central identity provider and access service providers (client applications) without to login again.
|
||||||
|
|
||||||
|
Identity brokering describes an authentication scheme where users can login with external identity providers that have a established trust with an identity provider which facilitates the authentication for the requested applications.
|
||||||
|
|
||||||
|
The connection between the two lies in how SSO can be implemented as part of an identity brokering solution.
|
||||||
|
In such cases, the identity broker uses SSO to enable seamless access across multiple systems, handling the complexities of different authentication protocols and standards behind the scenes.
|
||||||
|
This allows users to log in once and gain access to multiple systems that the broker facilitates.
|
||||||
|
|
||||||
|
## Multitenancy and identity brokering
|
||||||
|
|
||||||
|
In a multi-tenancy application, you want to be able to configure an external identity provider per tenant.
|
||||||
|
For example some organizations might use their EntraID, some other want to login with their OKTA, or Google Workspace.
|
||||||
|
|
||||||
|
Using an identity provider with strong multitenancy capabilities such as ZITADEL, you can configure a different set of external identity providers per organization.
|
||||||
|
|
||||||
|
[Domain discovery](/docs/guides/solution-scenarios/domain-discovery) ensures that users are redirected to their external identity provider based on their email-address or username.
|
||||||
|
[Managers](../structure/managers) can configure organization domains that are used for domain-based redirection to an external IdP.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Simplify identity brokering with ZITADEL templates
|
||||||
|
|
||||||
|
ZITADEL works with SAML, OpenID Connect, and LDAP external identity providers.
|
||||||
|
|
||||||
|
For popular IdPs such as EntraID, Okta, Google, Facebook, and GitHub, ZITADEL [offers pre-configured templates](/docs/guides/integrate/identity-providers/introduction).
|
||||||
|
These templates expedite the configuration process, allowing organizations to quickly integrate these providers with minimal effort.
|
||||||
|
|
||||||
|
ZITADEL recognizes that specific needs may extend beyond pre-built templates.
|
||||||
|
To address this, ZITADEL provides generic templates that enable connection to virtually any IdP. This ensures maximum flexibility and future-proofs login infrastructure, accommodating future integrations with ease.
|
||||||
|
|
||||||
|
### References
|
||||||
|
|
||||||
|
* [Detailed integration guide for many identity providers](/guides/integrate/identity-providers/introduction)
|
||||||
|
* [Setup identity providers with Console](/guides/manage/console/default-settings#identity-providers)
|
||||||
|
* [Configure identity providers with the ZITADEL API](/docs/category/apis/resources/mgmt/identity-providers)
|
||||||
|
@ -9,9 +9,13 @@ import CustomLoginPolicy from './_custom_login_policy.mdx';
|
|||||||
import IDPsOverview from './_idps_overview.mdx';
|
import IDPsOverview from './_idps_overview.mdx';
|
||||||
import Activate from './_activate.mdx';
|
import Activate from './_activate.mdx';
|
||||||
import TestSetup from './_test_setup.mdx';
|
import TestSetup from './_test_setup.mdx';
|
||||||
|
import { ResponsivePlayer } from "../../../../src/components/player";
|
||||||
|
|
||||||
<Intro provider="Google"/>
|
<Intro provider="Google"/>
|
||||||
|
|
||||||
|
<ResponsivePlayer playing controls url='https://www.youtube.com/watch?v=wg-ee-EnHdE' />
|
||||||
|
|
||||||
|
|
||||||
## Open the Google Identity Provider Template
|
## Open the Google Identity Provider Template
|
||||||
|
|
||||||
<IDPsOverview templates="Google"/>
|
<IDPsOverview templates="Google"/>
|
||||||
|
BIN
docs/static/img/concepts/features/domain-discovery.png
vendored
Normal file
BIN
docs/static/img/concepts/features/domain-discovery.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
BIN
docs/static/img/concepts/features/identity-brokering.png
vendored
Normal file
BIN
docs/static/img/concepts/features/identity-brokering.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
BIN
docs/static/img/guides/identity_brokering.png
vendored
BIN
docs/static/img/guides/identity_brokering.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 272 KiB |
@ -1,6 +1,7 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -77,6 +78,21 @@ func UserMetadataListFromSlice(c *actions.FieldConfig, metadata []query.UserMeta
|
|||||||
return c.Runtime.ToValue(result)
|
return c.Runtime.ToValue(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetOrganizationMetadata(ctx context.Context, queries *query.Queries, c *actions.FieldConfig, organizationID string) goja.Value {
|
||||||
|
metadata, err := queries.SearchOrgMetadata(
|
||||||
|
ctx,
|
||||||
|
true,
|
||||||
|
organizationID,
|
||||||
|
&query.OrgMetadataSearchQueries{},
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logging.WithError(err).Info("unable to get org metadata in action")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return OrgMetadataListFromQuery(c, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
func metadataByteArrayToValue(val []byte, runtime *goja.Runtime) goja.Value {
|
func metadataByteArrayToValue(val []byte, runtime *goja.Runtime) goja.Value {
|
||||||
var value interface{}
|
var value interface{}
|
||||||
if !json.Valid(val) {
|
if !json.Valid(val) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/dop251/goja"
|
"github.com/dop251/goja"
|
||||||
@ -44,6 +45,8 @@ type userGrant struct {
|
|||||||
|
|
||||||
ProjectId string
|
ProjectId string
|
||||||
ProjectName string
|
ProjectName string
|
||||||
|
|
||||||
|
GetOrgMetadata func(goja.FunctionCall) goja.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func AppendGrantFunc(userGrants *UserGrants) func(c *actions.FieldConfig) func(call goja.FunctionCall) goja.Value {
|
func AppendGrantFunc(userGrants *UserGrants) func(c *actions.FieldConfig) func(call goja.FunctionCall) goja.Value {
|
||||||
@ -58,10 +61,11 @@ func AppendGrantFunc(userGrants *UserGrants) func(c *actions.FieldConfig) func(c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserGrantsFromQuery(c *actions.FieldConfig, userGrants *query.UserGrants) goja.Value {
|
func UserGrantsFromQuery(ctx context.Context, queries *query.Queries, c *actions.FieldConfig, userGrants *query.UserGrants) goja.Value {
|
||||||
if userGrants == nil {
|
if userGrants == nil {
|
||||||
return c.Runtime.ToValue(nil)
|
return c.Runtime.ToValue(nil)
|
||||||
}
|
}
|
||||||
|
orgMetadata := make(map[string]goja.Value)
|
||||||
grantList := &userGrantList{
|
grantList := &userGrantList{
|
||||||
Count: userGrants.Count,
|
Count: userGrants.Count,
|
||||||
Sequence: userGrants.Sequence,
|
Sequence: userGrants.Sequence,
|
||||||
@ -84,16 +88,24 @@ func UserGrantsFromQuery(c *actions.FieldConfig, userGrants *query.UserGrants) g
|
|||||||
UserGrantResourceOwnerName: grant.OrgName,
|
UserGrantResourceOwnerName: grant.OrgName,
|
||||||
ProjectId: grant.ProjectID,
|
ProjectId: grant.ProjectID,
|
||||||
ProjectName: grant.ProjectName,
|
ProjectName: grant.ProjectName,
|
||||||
|
GetOrgMetadata: func(call goja.FunctionCall) goja.Value {
|
||||||
|
if md, ok := orgMetadata[grant.ResourceOwner]; ok {
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
orgMetadata[grant.ResourceOwner] = GetOrganizationMetadata(ctx, queries, c, grant.ResourceOwner)
|
||||||
|
return orgMetadata[grant.ResourceOwner]
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Runtime.ToValue(grantList)
|
return c.Runtime.ToValue(grantList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserGrantsFromSlice(c *actions.FieldConfig, userGrants []query.UserGrant) goja.Value {
|
func UserGrantsFromSlice(ctx context.Context, queries *query.Queries, c *actions.FieldConfig, userGrants []query.UserGrant) goja.Value {
|
||||||
if userGrants == nil {
|
if userGrants == nil {
|
||||||
return c.Runtime.ToValue(nil)
|
return c.Runtime.ToValue(nil)
|
||||||
}
|
}
|
||||||
|
orgMetadata := make(map[string]goja.Value)
|
||||||
grantList := &userGrantList{
|
grantList := &userGrantList{
|
||||||
Count: uint64(len(userGrants)),
|
Count: uint64(len(userGrants)),
|
||||||
Grants: make([]*userGrant, len(userGrants)),
|
Grants: make([]*userGrant, len(userGrants)),
|
||||||
@ -114,6 +126,13 @@ func UserGrantsFromSlice(c *actions.FieldConfig, userGrants []query.UserGrant) g
|
|||||||
UserGrantResourceOwnerName: grant.OrgName,
|
UserGrantResourceOwnerName: grant.OrgName,
|
||||||
ProjectId: grant.ProjectID,
|
ProjectId: grant.ProjectID,
|
||||||
ProjectName: grant.ProjectName,
|
ProjectName: grant.ProjectName,
|
||||||
|
GetOrgMetadata: func(goja.FunctionCall) goja.Value {
|
||||||
|
if md, ok := orgMetadata[grant.ResourceOwner]; ok {
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
orgMetadata[grant.ResourceOwner] = GetOrganizationMetadata(ctx, queries, c, grant.ResourceOwner)
|
||||||
|
return orgMetadata[grant.ResourceOwner]
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ func (s *Server) ResendMyEmailVerification(ctx context.Context, _ *auth_pb.Resen
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
objectDetails, err := s.command.CreateHumanEmailVerificationCode(ctx, ctxData.UserID, ctxData.ResourceOwner, emailCodeGenerator)
|
objectDetails, err := s.command.CreateHumanEmailVerificationCode(ctx, ctxData.UserID, ctxData.ResourceOwner, emailCodeGenerator, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -473,7 +473,7 @@ func (s *Server) ResendHumanInitialization(ctx context.Context, req *mgmt_pb.Res
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
details, err := s.command.ResendInitialMail(ctx, req.UserId, domain.EmailAddress(req.Email), authz.GetCtxData(ctx).OrgID, initCodeGenerator)
|
details, err := s.command.ResendInitialMail(ctx, req.UserId, domain.EmailAddress(req.Email), authz.GetCtxData(ctx).OrgID, initCodeGenerator, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -487,7 +487,7 @@ func (s *Server) ResendHumanEmailVerification(ctx context.Context, req *mgmt_pb.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
objectDetails, err := s.command.CreateHumanEmailVerificationCode(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, emailCodeGenerator)
|
objectDetails, err := s.command.CreateHumanEmailVerificationCode(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, emailCodeGenerator, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -590,7 +590,7 @@ func (s *Server) SendHumanResetPasswordNotification(ctx context.Context, req *mg
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
objectDetails, err := s.command.RequestSetPassword(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, notifyTypeToDomain(req.Type), passwordCodeGenerator)
|
objectDetails, err := s.command.RequestSetPassword(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, notifyTypeToDomain(req.Type), passwordCodeGenerator, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -774,13 +774,22 @@ func (s *Server) ListMachineKeys(ctx context.Context, req *mgmt_pb.ListMachineKe
|
|||||||
|
|
||||||
func (s *Server) AddMachineKey(ctx context.Context, req *mgmt_pb.AddMachineKeyRequest) (*mgmt_pb.AddMachineKeyResponse, error) {
|
func (s *Server) AddMachineKey(ctx context.Context, req *mgmt_pb.AddMachineKeyRequest) (*mgmt_pb.AddMachineKeyResponse, error) {
|
||||||
machineKey := AddMachineKeyRequestToCommand(req, authz.GetCtxData(ctx).OrgID)
|
machineKey := AddMachineKeyRequestToCommand(req, authz.GetCtxData(ctx).OrgID)
|
||||||
|
// If there is no pubkey supplied, then AddUserMachineKey will generate a new one
|
||||||
|
pubkeySupplied := len(machineKey.PublicKey) > 0
|
||||||
details, err := s.command.AddUserMachineKey(ctx, machineKey)
|
details, err := s.command.AddUserMachineKey(ctx, machineKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
keyDetails, err := machineKey.Detail()
|
|
||||||
if err != nil {
|
// Return key details only if the pubkey wasn't supplied, otherwise the user already has
|
||||||
return nil, err
|
// private key locally
|
||||||
|
var keyDetails []byte
|
||||||
|
if !pubkeySupplied {
|
||||||
|
var err error
|
||||||
|
keyDetails, err = machineKey.Detail()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return &mgmt_pb.AddMachineKeyResponse{
|
return &mgmt_pb.AddMachineKeyResponse{
|
||||||
KeyId: machineKey.KeyID,
|
KeyId: machineKey.KeyID,
|
||||||
|
@ -237,6 +237,7 @@ func AddMachineKeyRequestToCommand(req *mgmt_pb.AddMachineKeyRequest, resourceOw
|
|||||||
},
|
},
|
||||||
ExpirationDate: expDate,
|
ExpirationDate: expDate,
|
||||||
Type: authn.KeyTypeToDomain(req.Type),
|
Type: authn.KeyTypeToDomain(req.Type),
|
||||||
|
PublicKey: req.PublicKey,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,25 +490,16 @@ func (o *OPStorage) userinfoFlows(ctx context.Context, user *query.User, userGra
|
|||||||
return object.UserMetadataListFromQuery(c, metadata)
|
return object.UserMetadataListFromQuery(c, metadata)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
actions.SetFields("grants", func(c *actions.FieldConfig) interface{} {
|
actions.SetFields("grants",
|
||||||
return object.UserGrantsFromQuery(c, userGrants)
|
func(c *actions.FieldConfig) interface{} {
|
||||||
}),
|
return object.UserGrantsFromQuery(ctx, o.query, c, userGrants)
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
actions.SetFields("org",
|
actions.SetFields("org",
|
||||||
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
|
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
|
||||||
return func(goja.FunctionCall) goja.Value {
|
return func(goja.FunctionCall) goja.Value {
|
||||||
metadata, err := o.query.SearchOrgMetadata(
|
return object.GetOrganizationMetadata(ctx, o.query, c, user.ResourceOwner)
|
||||||
ctx,
|
|
||||||
true,
|
|
||||||
user.ResourceOwner,
|
|
||||||
&query.OrgMetadataSearchQueries{},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
logging.WithError(err).Info("unable to get org metadata in action")
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return object.OrgMetadataListFromQuery(c, metadata)
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@ -714,24 +705,13 @@ func (o *OPStorage) privateClaimsFlows(ctx context.Context, userID string, userG
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
actions.SetFields("grants", func(c *actions.FieldConfig) interface{} {
|
actions.SetFields("grants", func(c *actions.FieldConfig) interface{} {
|
||||||
return object.UserGrantsFromQuery(c, userGrants)
|
return object.UserGrantsFromQuery(ctx, o.query, c, userGrants)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
actions.SetFields("org",
|
actions.SetFields("org",
|
||||||
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
|
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
|
||||||
return func(goja.FunctionCall) goja.Value {
|
return func(goja.FunctionCall) goja.Value {
|
||||||
metadata, err := o.query.SearchOrgMetadata(
|
return object.GetOrganizationMetadata(ctx, o.query, c, user.ResourceOwner)
|
||||||
ctx,
|
|
||||||
true,
|
|
||||||
user.ResourceOwner,
|
|
||||||
&query.OrgMetadataSearchQueries{},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
logging.WithError(err).Info("unable to get org metadata in action")
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return object.OrgMetadataListFromQuery(c, metadata)
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
76
internal/api/oidc/server_integration_test.go
Normal file
76
internal/api/oidc/server_integration_test.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
//go:build integration
|
||||||
|
|
||||||
|
package oidc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/zitadel/oidc/v3/pkg/client"
|
||||||
|
"github.com/zitadel/oidc/v3/pkg/client/rp"
|
||||||
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||||
|
"github.com/zitadel/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServer_RefreshToken_Status(t *testing.T) {
|
||||||
|
clientID, _ := createClient(t)
|
||||||
|
provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
refreshToken string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "invalid base64",
|
||||||
|
refreshToken: "~!~@#$%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid after decrypt",
|
||||||
|
refreshToken: "DEADBEEFDEADBEEF",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "short input",
|
||||||
|
refreshToken: "DEAD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty input",
|
||||||
|
refreshToken: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
request := rp.RefreshTokenRequest{
|
||||||
|
RefreshToken: tt.refreshToken,
|
||||||
|
ClientID: clientID,
|
||||||
|
GrantType: oidc.GrantTypeRefreshToken,
|
||||||
|
}
|
||||||
|
client.CallTokenEndpoint(CTX, request, tokenEndpointCaller{RelyingParty: provider})
|
||||||
|
|
||||||
|
values := make(url.Values)
|
||||||
|
err := schema.NewEncoder().Encode(request, values)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
resp, err := http.Post(provider.OAuthConfig().Endpoint.TokenURL, "application/x-www-form-urlencoded", strings.NewReader(values.Encode()))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
assert.Less(t, resp.StatusCode, 500)
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Log(string(body))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenEndpointCaller struct {
|
||||||
|
rp.RelyingParty
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t tokenEndpointCaller) TokenEndpoint() string {
|
||||||
|
return t.OAuthConfig().Endpoint.TokenURL
|
||||||
|
}
|
@ -252,24 +252,13 @@ func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, user
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
actions.SetFields("grants", func(c *actions.FieldConfig) interface{} {
|
actions.SetFields("grants", func(c *actions.FieldConfig) interface{} {
|
||||||
return object.UserGrantsFromSlice(c, qu.UserGrants)
|
return object.UserGrantsFromSlice(ctx, s.query, c, qu.UserGrants)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
actions.SetFields("org",
|
actions.SetFields("org",
|
||||||
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
|
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
|
||||||
return func(goja.FunctionCall) goja.Value {
|
return func(goja.FunctionCall) goja.Value {
|
||||||
metadata, err := s.query.SearchOrgMetadata(
|
return object.GetOrganizationMetadata(ctx, s.query, c, qu.User.ResourceOwner)
|
||||||
ctx,
|
|
||||||
true,
|
|
||||||
qu.User.ResourceOwner,
|
|
||||||
&query.OrgMetadataSearchQueries{},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
logging.WithError(err).Info("unable to get org metadata in action")
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return object.OrgMetadataListFromQuery(c, metadata)
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
@ -246,24 +246,13 @@ func (p *Storage) getCustomAttributes(ctx context.Context, user *query.User, use
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
actions.SetFields("grants", func(c *actions.FieldConfig) interface{} {
|
actions.SetFields("grants", func(c *actions.FieldConfig) interface{} {
|
||||||
return object.UserGrantsFromQuery(c, userGrants)
|
return object.UserGrantsFromQuery(ctx, p.query, c, userGrants)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
actions.SetFields("org",
|
actions.SetFields("org",
|
||||||
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
|
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
|
||||||
return func(goja.FunctionCall) goja.Value {
|
return func(goja.FunctionCall) goja.Value {
|
||||||
metadata, err := p.query.SearchOrgMetadata(
|
return object.GetOrganizationMetadata(ctx, p.query, c, user.ResourceOwner)
|
||||||
ctx,
|
|
||||||
true,
|
|
||||||
user.ResourceOwner,
|
|
||||||
&query.OrgMetadataSearchQueries{},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
logging.WithError(err).Info("unable to get org metadata in action")
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return object.OrgMetadataListFromQuery(c, metadata)
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
@ -3,6 +3,8 @@ package login
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
)
|
)
|
||||||
@ -33,3 +35,23 @@ func (l *Login) getAuthRequestAndParseData(r *http.Request, data interface{}) (*
|
|||||||
func (l *Login) getParseData(r *http.Request, data interface{}) error {
|
func (l *Login) getParseData(r *http.Request, data interface{}) error {
|
||||||
return l.parser.Parse(r, data)
|
return l.parser.Parse(r, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkOptionalAuthRequestOfEmailLinks tries to get the [domain.AuthRequest] from the request.
|
||||||
|
// In case any error occurs, e.g. if the user agent does not correspond, the `authRequestID` query parameter will be
|
||||||
|
// removed from the request URL and form to ensure subsequent functions and pages do not use it.
|
||||||
|
// This function is used for handling links in emails, which could possibly be opened on another device than the
|
||||||
|
// auth request was initiated.
|
||||||
|
func (l *Login) checkOptionalAuthRequestOfEmailLinks(r *http.Request) *domain.AuthRequest {
|
||||||
|
authReq, err := l.getAuthRequest(r)
|
||||||
|
if err == nil {
|
||||||
|
return authReq
|
||||||
|
}
|
||||||
|
logging.WithError(err).Infof("authrequest could not be found for email link on path %s", r.URL.RequestURI())
|
||||||
|
queries := r.URL.Query()
|
||||||
|
queries.Del(QueryAuthRequestID)
|
||||||
|
r.URL.RawQuery = queries.Encode()
|
||||||
|
r.RequestURI = r.URL.RequestURI()
|
||||||
|
r.Form.Del(QueryAuthRequestID)
|
||||||
|
r.PostForm.Del(QueryAuthRequestID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package login
|
package login
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
@ -38,14 +38,20 @@ type initPasswordData struct {
|
|||||||
HasSymbol string
|
HasSymbol string
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitPasswordLink(origin, userID, code, orgID string) string {
|
func InitPasswordLink(origin, userID, code, orgID, authRequestID string) string {
|
||||||
return fmt.Sprintf("%s%s?userID=%s&code=%s&orgID=%s", externalLink(origin), EndpointInitPassword, userID, code, orgID)
|
v := url.Values{}
|
||||||
|
v.Set(queryInitPWUserID, userID)
|
||||||
|
v.Set(queryInitPWCode, code)
|
||||||
|
v.Set(queryOrgID, orgID)
|
||||||
|
v.Set(QueryAuthRequestID, authRequestID)
|
||||||
|
return externalLink(origin) + EndpointInitPassword + "?" + v.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) handleInitPassword(w http.ResponseWriter, r *http.Request) {
|
func (l *Login) handleInitPassword(w http.ResponseWriter, r *http.Request) {
|
||||||
|
authReq := l.checkOptionalAuthRequestOfEmailLinks(r)
|
||||||
userID := r.FormValue(queryInitPWUserID)
|
userID := r.FormValue(queryInitPWUserID)
|
||||||
code := r.FormValue(queryInitPWCode)
|
code := r.FormValue(queryInitPWCode)
|
||||||
l.renderInitPassword(w, r, nil, userID, code, nil)
|
l.renderInitPassword(w, r, authReq, userID, code, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) handleInitPasswordCheck(w http.ResponseWriter, r *http.Request) {
|
func (l *Login) handleInitPasswordCheck(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -94,7 +100,7 @@ func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authRe
|
|||||||
l.renderInitPassword(w, r, authReq, userID, "", err)
|
l.renderInitPassword(w, r, authReq, userID, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err = l.command.RequestSetPassword(setContext(r.Context(), userOrg), userID, userOrg, domain.NotificationTypeEmail, passwordCodeGenerator)
|
_, err = l.command.RequestSetPassword(setContext(r.Context(), userOrg), userID, userOrg, domain.NotificationTypeEmail, passwordCodeGenerator, authReq.ID)
|
||||||
l.renderInitPassword(w, r, authReq, userID, "", err)
|
l.renderInitPassword(w, r, authReq, userID, "", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package login
|
package login
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
||||||
@ -44,16 +44,24 @@ type initUserData struct {
|
|||||||
HasSymbol string
|
HasSymbol string
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitUserLink(origin, userID, loginName, code, orgID string, passwordSet bool) string {
|
func InitUserLink(origin, userID, loginName, code, orgID string, passwordSet bool, authRequestID string) string {
|
||||||
return fmt.Sprintf("%s%s?userID=%s&loginname=%s&code=%s&orgID=%s&passwordset=%t", externalLink(origin), EndpointInitUser, userID, loginName, code, orgID, passwordSet)
|
v := url.Values{}
|
||||||
|
v.Set(queryInitUserUserID, userID)
|
||||||
|
v.Set(queryInitUserLoginName, loginName)
|
||||||
|
v.Set(queryInitUserCode, code)
|
||||||
|
v.Set(queryOrgID, orgID)
|
||||||
|
v.Set(queryInitUserPassword, strconv.FormatBool(passwordSet))
|
||||||
|
v.Set(QueryAuthRequestID, authRequestID)
|
||||||
|
return externalLink(origin) + EndpointInitUser + "?" + v.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) handleInitUser(w http.ResponseWriter, r *http.Request) {
|
func (l *Login) handleInitUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
authReq := l.checkOptionalAuthRequestOfEmailLinks(r)
|
||||||
userID := r.FormValue(queryInitUserUserID)
|
userID := r.FormValue(queryInitUserUserID)
|
||||||
code := r.FormValue(queryInitUserCode)
|
code := r.FormValue(queryInitUserCode)
|
||||||
loginName := r.FormValue(queryInitUserLoginName)
|
loginName := r.FormValue(queryInitUserLoginName)
|
||||||
passwordSet, _ := strconv.ParseBool(r.FormValue(queryInitUserPassword))
|
passwordSet, _ := strconv.ParseBool(r.FormValue(queryInitUserPassword))
|
||||||
l.renderInitUser(w, r, nil, userID, loginName, code, passwordSet, nil)
|
l.renderInitUser(w, r, authReq, userID, loginName, code, passwordSet, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) handleInitUserCheck(w http.ResponseWriter, r *http.Request) {
|
func (l *Login) handleInitUserCheck(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -105,7 +113,7 @@ func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq *
|
|||||||
l.renderInitUser(w, r, authReq, userID, loginName, "", showPassword, err)
|
l.renderInitUser(w, r, authReq, userID, loginName, "", showPassword, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err = l.command.ResendInitialMail(setContext(r.Context(), userOrgID), userID, "", userOrgID, initCodeGenerator)
|
_, err = l.command.ResendInitialMail(setContext(r.Context(), userOrgID), userID, "", userOrgID, initCodeGenerator, authReq.ID)
|
||||||
l.renderInitUser(w, r, authReq, userID, loginName, "", showPassword, err)
|
l.renderInitUser(w, r, authReq, userID, loginName, "", showPassword, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package login
|
package login
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
)
|
)
|
||||||
@ -27,18 +27,24 @@ type mailVerificationData struct {
|
|||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func MailVerificationLink(origin, userID, code, orgID string) string {
|
func MailVerificationLink(origin, userID, code, orgID, authRequestID string) string {
|
||||||
return fmt.Sprintf("%s%s?userID=%s&code=%s&orgID=%s", externalLink(origin), EndpointMailVerification, userID, code, orgID)
|
v := url.Values{}
|
||||||
|
v.Set(queryUserID, userID)
|
||||||
|
v.Set(queryCode, code)
|
||||||
|
v.Set(queryOrgID, orgID)
|
||||||
|
v.Set(QueryAuthRequestID, authRequestID)
|
||||||
|
return externalLink(origin) + EndpointMailVerification + "?" + v.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) handleMailVerification(w http.ResponseWriter, r *http.Request) {
|
func (l *Login) handleMailVerification(w http.ResponseWriter, r *http.Request) {
|
||||||
|
authReq := l.checkOptionalAuthRequestOfEmailLinks(r)
|
||||||
userID := r.FormValue(queryUserID)
|
userID := r.FormValue(queryUserID)
|
||||||
code := r.FormValue(queryCode)
|
code := r.FormValue(queryCode)
|
||||||
if code != "" {
|
if code != "" {
|
||||||
l.checkMailCode(w, r, nil, userID, code)
|
l.checkMailCode(w, r, authReq, userID, code)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l.renderMailVerification(w, r, nil, userID, nil)
|
l.renderMailVerification(w, r, authReq, userID, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) handleMailVerificationCheck(w http.ResponseWriter, r *http.Request) {
|
func (l *Login) handleMailVerificationCheck(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -61,7 +67,7 @@ func (l *Login) handleMailVerificationCheck(w http.ResponseWriter, r *http.Reque
|
|||||||
l.checkMailCode(w, r, authReq, data.UserID, data.Code)
|
l.checkMailCode(w, r, authReq, data.UserID, data.Code)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err = l.command.CreateHumanEmailVerificationCode(setContext(r.Context(), userOrg), data.UserID, userOrg, emailCodeGenerator)
|
_, err = l.command.CreateHumanEmailVerificationCode(setContext(r.Context(), userOrg), data.UserID, userOrg, emailCodeGenerator, authReq.ID)
|
||||||
l.renderMailVerification(w, r, authReq, data.UserID, err)
|
l.renderMailVerification(w, r, authReq, data.UserID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ func (l *Login) handlePasswordReset(w http.ResponseWriter, r *http.Request) {
|
|||||||
l.renderPasswordResetDone(w, r, authReq, err)
|
l.renderPasswordResetDone(w, r, authReq, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err = l.command.RequestSetPassword(setContext(r.Context(), authReq.UserOrgID), user.ID, authReq.UserOrgID, domain.NotificationTypeEmail, passwordCodeGenerator)
|
_, err = l.command.RequestSetPassword(setContext(r.Context(), authReq.UserOrgID), user.ID, authReq.UserOrgID, domain.NotificationTypeEmail, passwordCodeGenerator, authReq.ID)
|
||||||
l.renderPasswordResetDone(w, r, authReq, err)
|
l.renderPasswordResetDone(w, r, authReq, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
||||||
|
"github.com/zitadel/zitadel/internal/command"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
)
|
)
|
||||||
@ -67,22 +68,6 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
|
|||||||
if authRequest != nil && authRequest.RequestedOrgID != "" && authRequest.RequestedOrgID != resourceOwner {
|
if authRequest != nil && authRequest.RequestedOrgID != "" && authRequest.RequestedOrgID != resourceOwner {
|
||||||
resourceOwner = authRequest.RequestedOrgID
|
resourceOwner = authRequest.RequestedOrgID
|
||||||
}
|
}
|
||||||
initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.userCodeAlg)
|
|
||||||
if err != nil {
|
|
||||||
l.renderRegister(w, r, authRequest, data, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
emailCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyEmailCode, l.userCodeAlg)
|
|
||||||
if err != nil {
|
|
||||||
l.renderRegister(w, r, authRequest, data, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.userCodeAlg)
|
|
||||||
if err != nil {
|
|
||||||
l.renderRegister(w, r, authRequest, data, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// For consistency with the external authentication flow,
|
// For consistency with the external authentication flow,
|
||||||
// the setMetadata() function is provided on the pre creation hook, for now,
|
// the setMetadata() function is provided on the pre creation hook, for now,
|
||||||
// like for the ExternalAuthentication flow.
|
// like for the ExternalAuthentication flow.
|
||||||
@ -96,22 +81,14 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
|
|||||||
l.renderRegister(w, r, authRequest, data, err)
|
l.renderRegister(w, r, authRequest, data, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user, err = l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, user, nil, nil, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator)
|
|
||||||
|
human := command.AddHumanFromDomain(user, metadatas, authRequest, nil)
|
||||||
|
err = l.command.AddUserHuman(setContext(r.Context(), resourceOwner), resourceOwner, human, true, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderRegister(w, r, authRequest, data, err)
|
l.renderRegister(w, r, authRequest, data, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
userGrants, err := l.runPostCreationActions(human.ID, authRequest, r, resourceOwner, domain.FlowTypeInternalAuthentication)
|
||||||
if len(metadatas) > 0 {
|
|
||||||
_, err = l.command.BulkSetUserMetadata(r.Context(), user.AggregateID, resourceOwner, metadatas...)
|
|
||||||
if err != nil {
|
|
||||||
// TODO: What if action is configured to be allowed to fail? Same question for external registration.
|
|
||||||
l.renderRegister(w, r, authRequest, data, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
userGrants, err := l.runPostCreationActions(user.AggregateID, authRequest, r, resourceOwner, domain.FlowTypeInternalAuthentication)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderError(w, r, authRequest, err)
|
l.renderError(w, r, authRequest, err)
|
||||||
return
|
return
|
||||||
@ -128,7 +105,7 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||||
err = l.authRepo.SelectUser(r.Context(), authRequest.ID, user.AggregateID, userAgentID)
|
err = l.authRepo.SelectUser(r.Context(), authRequest.ID, human.ID, userAgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderRegister(w, r, authRequest, data, err)
|
l.renderRegister(w, r, authRequest, data, err)
|
||||||
return
|
return
|
||||||
|
@ -543,23 +543,19 @@ func (repo *AuthRequestRepo) AutoRegisterExternalUser(ctx context.Context, regis
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
initCodeGenerator, err := repo.Query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, repo.UserCodeAlg)
|
addMetadata := make([]*command.AddMetadataEntry, len(metadatas))
|
||||||
|
for i, metadata := range metadatas {
|
||||||
|
addMetadata[i] = &command.AddMetadataEntry{
|
||||||
|
Key: metadata.Key,
|
||||||
|
Value: metadata.Value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
human := command.AddHumanFromDomain(registerUser, metadatas, request, externalIDP)
|
||||||
|
err = repo.Command.AddUserHuman(ctx, resourceOwner, human, true, repo.UserCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
emailCodeGenerator, err := repo.Query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, repo.UserCodeAlg)
|
request.SetUserInfo(human.ID, human.Username, human.Username, human.DisplayName, "", resourceOwner)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
phoneCodeGenerator, err := repo.Query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, repo.UserCodeAlg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
human, err := repo.Command.RegisterHuman(ctx, resourceOwner, registerUser, externalIDP, orgMemberRoles, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
request.SetUserInfo(human.AggregateID, human.Username, human.PreferredLoginName, human.DisplayName, "", human.ResourceOwner)
|
|
||||||
request.SelectedIDPConfigID = externalIDP.IDPConfigID
|
request.SelectedIDPConfigID = externalIDP.IDPConfigID
|
||||||
request.LinkingUsers = nil
|
request.LinkingUsers = nil
|
||||||
err = repo.Command.UserIDPLoginChecked(ctx, request.UserOrgID, request.UserID, request.WithCurrentInfo(info))
|
err = repo.Command.UserIDPLoginChecked(ctx, request.UserOrgID, request.UserID, request.WithCurrentInfo(info))
|
||||||
|
@ -160,6 +160,10 @@ func (s *UserSession) Reducers() []handler.AggregateReducer {
|
|||||||
Event: user.UserRemovedType,
|
Event: user.UserRemovedType,
|
||||||
Reduce: s.Reduce,
|
Reduce: s.Reduce,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Event: user.HumanRegisteredType,
|
||||||
|
Reduce: s.Reduce,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -234,6 +238,8 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
|||||||
handler.NewCol("multi_factor_verification_type", domain.MFALevelNotSetUp),
|
handler.NewCol("multi_factor_verification_type", domain.MFALevelNotSetUp),
|
||||||
handler.NewCol("external_login_verification", time.Time{}),
|
handler.NewCol("external_login_verification", time.Time{}),
|
||||||
handler.NewCol("state", domain.UserSessionStateTerminated),
|
handler.NewCol("state", domain.UserSessionStateTerminated),
|
||||||
|
handler.NewCol("change_date", event.CreatedAt()),
|
||||||
|
handler.NewCol("sequence", event.Sequence()),
|
||||||
},
|
},
|
||||||
[]handler.Condition{
|
[]handler.Condition{
|
||||||
handler.NewCond("instance_id", event.Aggregate().InstanceID),
|
handler.NewCond("instance_id", event.Aggregate().InstanceID),
|
||||||
@ -247,16 +253,30 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return handler.NewUpdateStatement(event,
|
return handler.NewMultiStatement(event,
|
||||||
[]handler.Column{
|
handler.AddUpdateStatement(
|
||||||
handler.NewCol("password_verification", time.Time{}),
|
[]handler.Column{
|
||||||
},
|
handler.NewCol("password_verification", event.CreatedAt()),
|
||||||
[]handler.Condition{
|
handler.NewCol("change_date", event.CreatedAt()),
|
||||||
handler.NewCond("instance_id", event.Aggregate().InstanceID),
|
handler.NewCol("sequence", event.Sequence()),
|
||||||
handler.NewCond("user_id", event.Aggregate().ID),
|
},
|
||||||
handler.Not(handler.NewCond("user_agent_id", userAgent)),
|
[]handler.Condition{
|
||||||
handler.Not(handler.NewCond("state", domain.UserSessionStateTerminated)),
|
handler.NewCond("instance_id", event.Aggregate().InstanceID),
|
||||||
},
|
handler.NewCond("user_id", event.Aggregate().ID),
|
||||||
|
handler.NewCond("user_agent_id", userAgent),
|
||||||
|
}),
|
||||||
|
handler.AddUpdateStatement(
|
||||||
|
[]handler.Column{
|
||||||
|
handler.NewCol("password_verification", time.Time{}),
|
||||||
|
handler.NewCol("change_date", event.CreatedAt()),
|
||||||
|
handler.NewCol("sequence", event.Sequence()),
|
||||||
|
},
|
||||||
|
[]handler.Condition{
|
||||||
|
handler.NewCond("instance_id", event.Aggregate().InstanceID),
|
||||||
|
handler.NewCond("user_id", event.Aggregate().ID),
|
||||||
|
handler.Not(handler.NewCond("user_agent_id", userAgent)),
|
||||||
|
handler.Not(handler.NewCond("state", domain.UserSessionStateTerminated)),
|
||||||
|
}),
|
||||||
), nil
|
), nil
|
||||||
case user.UserV1MFAOTPRemovedType,
|
case user.UserV1MFAOTPRemovedType,
|
||||||
user.HumanMFAOTPRemovedType,
|
user.HumanMFAOTPRemovedType,
|
||||||
@ -264,6 +284,8 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
|||||||
return handler.NewUpdateStatement(event,
|
return handler.NewUpdateStatement(event,
|
||||||
[]handler.Column{
|
[]handler.Column{
|
||||||
handler.NewCol("second_factor_verification", time.Time{}),
|
handler.NewCol("second_factor_verification", time.Time{}),
|
||||||
|
handler.NewCol("change_date", event.CreatedAt()),
|
||||||
|
handler.NewCol("sequence", event.Sequence()),
|
||||||
},
|
},
|
||||||
[]handler.Condition{
|
[]handler.Condition{
|
||||||
handler.NewCond("instance_id", event.Aggregate().InstanceID),
|
handler.NewCond("instance_id", event.Aggregate().InstanceID),
|
||||||
@ -277,6 +299,8 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
|||||||
[]handler.Column{
|
[]handler.Column{
|
||||||
handler.NewCol("external_login_verification", time.Time{}),
|
handler.NewCol("external_login_verification", time.Time{}),
|
||||||
handler.NewCol("selected_idp_config_id", ""),
|
handler.NewCol("selected_idp_config_id", ""),
|
||||||
|
handler.NewCol("change_date", event.CreatedAt()),
|
||||||
|
handler.NewCol("sequence", event.Sequence()),
|
||||||
},
|
},
|
||||||
[]handler.Condition{
|
[]handler.Condition{
|
||||||
handler.NewCond("instance_id", event.Aggregate().InstanceID),
|
handler.NewCond("instance_id", event.Aggregate().InstanceID),
|
||||||
@ -289,6 +313,8 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
|||||||
[]handler.Column{
|
[]handler.Column{
|
||||||
handler.NewCol("passwordless_verification", time.Time{}),
|
handler.NewCol("passwordless_verification", time.Time{}),
|
||||||
handler.NewCol("multi_factor_verification", time.Time{}),
|
handler.NewCol("multi_factor_verification", time.Time{}),
|
||||||
|
handler.NewCol("change_date", event.CreatedAt()),
|
||||||
|
handler.NewCol("sequence", event.Sequence()),
|
||||||
},
|
},
|
||||||
[]handler.Condition{
|
[]handler.Condition{
|
||||||
handler.NewCond("instance_id", event.Aggregate().InstanceID),
|
handler.NewCond("instance_id", event.Aggregate().InstanceID),
|
||||||
@ -300,6 +326,23 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
|||||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||||
return u.view.DeleteUserSessions(event.Aggregate().ID, event.Aggregate().InstanceID)
|
return u.view.DeleteUserSessions(event.Aggregate().ID, event.Aggregate().InstanceID)
|
||||||
}), nil
|
}), nil
|
||||||
|
case user.HumanRegisteredType:
|
||||||
|
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||||
|
eventData, err := view_model.UserSessionFromEvent(event)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
session := &view_model.UserSessionView{
|
||||||
|
CreationDate: event.CreatedAt(),
|
||||||
|
ResourceOwner: event.Aggregate().ResourceOwner,
|
||||||
|
UserAgentID: eventData.UserAgentID,
|
||||||
|
UserID: event.Aggregate().ID,
|
||||||
|
State: int32(domain.UserSessionStateActive),
|
||||||
|
InstanceID: event.Aggregate().InstanceID,
|
||||||
|
PasswordVerification: event.CreatedAt(),
|
||||||
|
}
|
||||||
|
return u.updateSession(session, event)
|
||||||
|
}), nil
|
||||||
case instance.InstanceRemovedEventType:
|
case instance.InstanceRemovedEventType:
|
||||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||||
return u.view.DeleteInstanceUserSessions(event.Aggregate().InstanceID)
|
return u.view.DeleteInstanceUserSessions(event.Aggregate().InstanceID)
|
||||||
|
@ -1427,6 +1427,7 @@ func TestCommandSide_SetUpOrg(t *testing.T) {
|
|||||||
Crypted: []byte("userinit"),
|
Crypted: []byte("userinit"),
|
||||||
},
|
},
|
||||||
1*time.Hour,
|
1*time.Hour,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
eventFromEventPusher(org.NewMemberAddedEvent(context.Background(),
|
eventFromEventPusher(org.NewMemberAddedEvent(context.Background(),
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
|
||||||
"github.com/zitadel/zitadel/internal/repository/user"
|
"github.com/zitadel/zitadel/internal/repository/user"
|
||||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
@ -57,7 +56,13 @@ type AddHuman struct {
|
|||||||
Passwordless bool
|
Passwordless bool
|
||||||
ExternalIDP bool
|
ExternalIDP bool
|
||||||
Register bool
|
Register bool
|
||||||
Metadata []*AddMetadataEntry
|
// UserAgentID is optional and can be passed in case the user registered themselves.
|
||||||
|
// This will be used in the login UI to handle authentication automatically.
|
||||||
|
UserAgentID string
|
||||||
|
// AuthRequestID is optional and can be passed in case the user registered themselves.
|
||||||
|
// This will be used to pass the information in notifications for links to the login UI.
|
||||||
|
AuthRequestID string
|
||||||
|
Metadata []*AddMetadataEntry
|
||||||
|
|
||||||
// Links are optional
|
// Links are optional
|
||||||
Links []*AddLink
|
Links []*AddLink
|
||||||
@ -200,6 +205,7 @@ func (c *Commands) AddHumanCommand(human *AddHuman, orgID string, hasher *crypto
|
|||||||
human.Gender,
|
human.Gender,
|
||||||
human.Email.Address,
|
human.Email.Address,
|
||||||
domainPolicy.UserLoginMustBeDomain,
|
domainPolicy.UserLoginMustBeDomain,
|
||||||
|
"", // no user agent id available
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
createCmd = user.NewHumanAddedEvent(
|
createCmd = user.NewHumanAddedEvent(
|
||||||
@ -272,7 +278,7 @@ func (c *Commands) addHumanCommandEmail(ctx context.Context, filter preparation.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return append(cmds, user.NewHumanInitialCodeAddedEvent(ctx, &a.Aggregate, initCode.Crypted, initCode.Expiry)), nil
|
return append(cmds, user.NewHumanInitialCodeAddedEvent(ctx, &a.Aggregate, initCode.Crypted, initCode.Expiry, human.AuthRequestID)), nil
|
||||||
}
|
}
|
||||||
if !human.Email.Verified {
|
if !human.Email.Verified {
|
||||||
emailCode, err := c.newEmailCode(ctx, filter, codeAlg)
|
emailCode, err := c.newEmailCode(ctx, filter, codeAlg)
|
||||||
@ -460,61 +466,6 @@ func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain.
|
|||||||
return writeModelToHuman(addedHuman), passwordlessCode, nil
|
return writeModelToHuman(addedHuman), passwordlessCode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: use commands.AddUserHuman
|
|
||||||
func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, orgMemberRoles []string, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator crypto.Generator) (*domain.Human, error) {
|
|
||||||
if orgID == "" {
|
|
||||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-GEdf2", "Errors.ResourceOwnerMissing")
|
|
||||||
}
|
|
||||||
domainPolicy, err := c.getOrgDomainPolicy(ctx, orgID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-33M9f", "Errors.Org.DomainPolicy.NotFound")
|
|
||||||
}
|
|
||||||
pwPolicy, err := c.getOrgPasswordComplexityPolicy(ctx, orgID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexityPolicy.NotFound")
|
|
||||||
}
|
|
||||||
loginPolicy, err := c.getOrgLoginPolicy(ctx, orgID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-Dfg3g", "Errors.Org.LoginPolicy.NotFound")
|
|
||||||
}
|
|
||||||
// check only if local registration is allowed, the idp will be checked separately
|
|
||||||
if !loginPolicy.AllowRegister && link == nil {
|
|
||||||
return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-SAbr3", "Errors.Org.LoginPolicy.RegistrationNotAllowed")
|
|
||||||
}
|
|
||||||
userEvents, registeredHuman, err := c.registerHuman(ctx, orgID, human, link, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
orgMemberWriteModel := NewOrgMemberWriteModel(orgID, registeredHuman.AggregateID)
|
|
||||||
orgAgg := OrgAggregateFromWriteModel(&orgMemberWriteModel.WriteModel)
|
|
||||||
if len(orgMemberRoles) > 0 {
|
|
||||||
orgMember := &domain.Member{
|
|
||||||
ObjectRoot: models.ObjectRoot{
|
|
||||||
AggregateID: orgID,
|
|
||||||
},
|
|
||||||
UserID: human.AggregateID,
|
|
||||||
Roles: orgMemberRoles,
|
|
||||||
}
|
|
||||||
memberEvent, err := c.addOrgMember(ctx, orgAgg, orgMemberWriteModel, orgMember)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
userEvents = append(userEvents, memberEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
pushedEvents, err := c.eventstore.Push(ctx, userEvents...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = AppendAndReduce(registeredHuman, pushedEvents...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return writeModelToHuman(registeredHuman), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.Human, passwordless bool, links []*domain.UserIDPLink, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessCodeGenerator crypto.Generator) (events []eventstore.Command, humanWriteModel *HumanWriteModel, passwordlessCodeWriteModel *HumanPasswordlessInitCodeWriteModel, code string, err error) {
|
func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.Human, passwordless bool, links []*domain.UserIDPLink, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessCodeGenerator crypto.Generator) (events []eventstore.Command, humanWriteModel *HumanWriteModel, passwordlessCodeWriteModel *HumanPasswordlessInitCodeWriteModel, code string, err error) {
|
||||||
if orgID == "" {
|
if orgID == "" {
|
||||||
return nil, nil, nil, "", zerrors.ThrowInvalidArgument(nil, "COMMAND-00p2b", "Errors.Org.Empty")
|
return nil, nil, nil, "", zerrors.ThrowInvalidArgument(nil, "COMMAND-00p2b", "Errors.Org.Empty")
|
||||||
@ -522,7 +473,7 @@ func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.
|
|||||||
if err := human.Normalize(); err != nil {
|
if err := human.Normalize(); err != nil {
|
||||||
return nil, nil, nil, "", err
|
return nil, nil, nil, "", err
|
||||||
}
|
}
|
||||||
events, humanWriteModel, err = c.createHuman(ctx, orgID, human, links, false, passwordless, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator)
|
events, humanWriteModel, err = c.createHuman(ctx, orgID, human, links, passwordless, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, "", err
|
return nil, nil, nil, "", err
|
||||||
}
|
}
|
||||||
@ -537,33 +488,8 @@ func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.
|
|||||||
return events, humanWriteModel, passwordlessCodeWriteModel, code, nil
|
return events, humanWriteModel, passwordlessCodeWriteModel, code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator crypto.Generator) ([]eventstore.Command, *HumanWriteModel, error) {
|
//nolint:gocognit
|
||||||
if human == nil {
|
func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.Human, links []*domain.UserIDPLink, passwordless bool, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator crypto.Generator) (events []eventstore.Command, addedHuman *HumanWriteModel, err error) {
|
||||||
return nil, nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-JKefw", "Errors.User.Invalid")
|
|
||||||
}
|
|
||||||
if human.Username = strings.TrimSpace(human.Username); human.Username == "" {
|
|
||||||
human.Username = string(human.EmailAddress)
|
|
||||||
}
|
|
||||||
if orgID == "" {
|
|
||||||
return nil, nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-hYsVH", "Errors.Org.Empty")
|
|
||||||
}
|
|
||||||
if err := human.Normalize(); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if link == nil && (human.Password == nil || human.Password.SecretString == "") {
|
|
||||||
return nil, nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-X23na", "Errors.User.Password.Empty")
|
|
||||||
}
|
|
||||||
if human.Password != nil && human.Password.SecretString != "" {
|
|
||||||
human.Password.ChangeRequired = false
|
|
||||||
}
|
|
||||||
var links []*domain.UserIDPLink
|
|
||||||
if link != nil {
|
|
||||||
links = append(links, link)
|
|
||||||
}
|
|
||||||
return c.createHuman(ctx, orgID, human, links, true, false, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.Human, links []*domain.UserIDPLink, selfregister, passwordless bool, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator crypto.Generator) (events []eventstore.Command, addedHuman *HumanWriteModel, err error) {
|
|
||||||
if err := human.CheckDomainPolicy(domainPolicy); err != nil {
|
if err := human.CheckDomainPolicy(domainPolicy); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -601,11 +527,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
|
|||||||
//TODO: adlerhurst maybe we could simplify the code below
|
//TODO: adlerhurst maybe we could simplify the code below
|
||||||
userAgg := UserAggregateFromWriteModel(&addedHuman.WriteModel)
|
userAgg := UserAggregateFromWriteModel(&addedHuman.WriteModel)
|
||||||
|
|
||||||
if selfregister {
|
events = append(events, createAddHumanEvent(ctx, userAgg, human, domainPolicy.UserLoginMustBeDomain))
|
||||||
events = append(events, createRegisterHumanEvent(ctx, userAgg, human, domainPolicy.UserLoginMustBeDomain))
|
|
||||||
} else {
|
|
||||||
events = append(events, createAddHumanEvent(ctx, userAgg, human, domainPolicy.UserLoginMustBeDomain))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, link := range links {
|
for _, link := range links {
|
||||||
event, err := c.addUserIDPLink(ctx, userAgg, link, false)
|
event, err := c.addUserIDPLink(ctx, userAgg, link, false)
|
||||||
@ -620,7 +542,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
events = append(events, user.NewHumanInitialCodeAddedEvent(ctx, userAgg, initCode.Code, initCode.Expiry))
|
events = append(events, user.NewHumanInitialCodeAddedEvent(ctx, userAgg, initCode.Code, initCode.Expiry, ""))
|
||||||
} else {
|
} else {
|
||||||
if human.Email != nil && human.EmailAddress != "" && human.IsEmailVerified {
|
if human.Email != nil && human.EmailAddress != "" && human.IsEmailVerified {
|
||||||
events = append(events, user.NewHumanEmailVerifiedEvent(ctx, userAgg))
|
events = append(events, user.NewHumanEmailVerifiedEvent(ctx, userAgg))
|
||||||
@ -629,7 +551,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
events = append(events, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry))
|
events = append(events, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry, ""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -699,40 +621,6 @@ func createAddHumanEvent(ctx context.Context, aggregate *eventstore.Aggregate, h
|
|||||||
return addEvent
|
return addEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
func createRegisterHumanEvent(ctx context.Context, aggregate *eventstore.Aggregate, human *domain.Human, userLoginMustBeDomain bool) *user.HumanRegisteredEvent {
|
|
||||||
addEvent := user.NewHumanRegisteredEvent(
|
|
||||||
ctx,
|
|
||||||
aggregate,
|
|
||||||
human.Username,
|
|
||||||
human.FirstName,
|
|
||||||
human.LastName,
|
|
||||||
human.NickName,
|
|
||||||
human.DisplayName,
|
|
||||||
human.PreferredLanguage,
|
|
||||||
human.Gender,
|
|
||||||
human.EmailAddress,
|
|
||||||
userLoginMustBeDomain,
|
|
||||||
)
|
|
||||||
if human.Phone != nil {
|
|
||||||
addEvent.AddPhoneData(human.PhoneNumber)
|
|
||||||
}
|
|
||||||
if human.Address != nil {
|
|
||||||
addEvent.AddAddressData(
|
|
||||||
human.Country,
|
|
||||||
human.Locality,
|
|
||||||
human.PostalCode,
|
|
||||||
human.Region,
|
|
||||||
human.StreetAddress)
|
|
||||||
}
|
|
||||||
if human.Password != nil {
|
|
||||||
addEvent.AddPasswordData(human.Password.EncodedSecret, human.Password.ChangeRequired)
|
|
||||||
}
|
|
||||||
if human.HashedPassword != "" {
|
|
||||||
addEvent.AddPasswordData(human.HashedPassword, false)
|
|
||||||
}
|
|
||||||
return addEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Commands) HumansSignOut(ctx context.Context, agentID string, userIDs []string) error {
|
func (c *Commands) HumansSignOut(ctx context.Context, agentID string, userIDs []string) error {
|
||||||
if agentID == "" {
|
if agentID == "" {
|
||||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-2M0ds", "Errors.User.UserIDMissing")
|
return zerrors.ThrowInvalidArgument(nil, "COMMAND-2M0ds", "Errors.User.UserIDMissing")
|
||||||
@ -783,3 +671,53 @@ func humanWriteModelByID(ctx context.Context, filter preparation.FilterToQueryRe
|
|||||||
err = humanWriteModel.Reduce()
|
err = humanWriteModel.Reduce()
|
||||||
return humanWriteModel, err
|
return humanWriteModel, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AddHumanFromDomain(user *domain.Human, metadataList []*domain.Metadata, authRequest *domain.AuthRequest, idp *domain.UserIDPLink) *AddHuman {
|
||||||
|
addMetadata := make([]*AddMetadataEntry, len(metadataList))
|
||||||
|
for i, metadata := range metadataList {
|
||||||
|
addMetadata[i] = &AddMetadataEntry{
|
||||||
|
Key: metadata.Key,
|
||||||
|
Value: metadata.Value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
human := new(AddHuman)
|
||||||
|
if user.Profile != nil {
|
||||||
|
human.Username = user.Username
|
||||||
|
human.FirstName = user.FirstName
|
||||||
|
human.LastName = user.LastName
|
||||||
|
human.NickName = user.NickName
|
||||||
|
human.DisplayName = user.DisplayName
|
||||||
|
human.PreferredLanguage = user.PreferredLanguage
|
||||||
|
human.Gender = user.Gender
|
||||||
|
human.Password = user.Password.SecretString
|
||||||
|
human.Register = true
|
||||||
|
human.Metadata = addMetadata
|
||||||
|
human.UserAgentID = authRequest.AgentID
|
||||||
|
human.AuthRequestID = authRequest.ID
|
||||||
|
}
|
||||||
|
if user.Email != nil {
|
||||||
|
human.Email = Email{
|
||||||
|
Address: user.EmailAddress,
|
||||||
|
Verified: user.IsEmailVerified,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if user.Phone != nil {
|
||||||
|
human.Phone = Phone{
|
||||||
|
Number: user.Phone.PhoneNumber,
|
||||||
|
Verified: user.Phone.IsPhoneVerified,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if idp != nil {
|
||||||
|
human.Links = []*AddLink{
|
||||||
|
{
|
||||||
|
IDPID: idp.IDPConfigID,
|
||||||
|
DisplayName: idp.DisplayName,
|
||||||
|
IDPExternalID: idp.ExternalUserID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if human.Username = strings.TrimSpace(human.Username); human.Username == "" {
|
||||||
|
human.Username = string(human.Email.Address)
|
||||||
|
}
|
||||||
|
return human
|
||||||
|
}
|
||||||
|
@ -50,7 +50,7 @@ func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email, em
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
events = append(events, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry))
|
events = append(events, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry, ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
||||||
@ -99,7 +99,7 @@ func (c *Commands) VerifyHumanEmail(ctx context.Context, userID, code, resourceo
|
|||||||
return nil, zerrors.ThrowInvalidArgument(err, "COMMAND-Gdsgs", "Errors.User.Code.Invalid")
|
return nil, zerrors.ThrowInvalidArgument(err, "COMMAND-Gdsgs", "Errors.User.Code.Invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) CreateHumanEmailVerificationCode(ctx context.Context, userID, resourceOwner string, emailCodeGenerator crypto.Generator) (*domain.ObjectDetails, error) {
|
func (c *Commands) CreateHumanEmailVerificationCode(ctx context.Context, userID, resourceOwner string, emailCodeGenerator crypto.Generator, authRequestID string) (*domain.ObjectDetails, error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
|
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
@ -122,7 +122,10 @@ func (c *Commands) CreateHumanEmailVerificationCode(ctx context.Context, userID,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry))
|
if authRequestID == "" {
|
||||||
|
authRequestID = existingEmail.AuthRequestID
|
||||||
|
}
|
||||||
|
pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry, authRequestID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ type HumanEmailWriteModel struct {
|
|||||||
Code *crypto.CryptoValue
|
Code *crypto.CryptoValue
|
||||||
CodeCreationDate time.Time
|
CodeCreationDate time.Time
|
||||||
CodeExpiry time.Duration
|
CodeExpiry time.Duration
|
||||||
|
AuthRequestID string
|
||||||
|
|
||||||
UserState domain.UserState
|
UserState domain.UserState
|
||||||
}
|
}
|
||||||
@ -53,6 +54,7 @@ func (wm *HumanEmailWriteModel) Reduce() error {
|
|||||||
wm.Code = e.Code
|
wm.Code = e.Code
|
||||||
wm.CodeCreationDate = e.CreationDate()
|
wm.CodeCreationDate = e.CreationDate()
|
||||||
wm.CodeExpiry = e.Expiry
|
wm.CodeExpiry = e.Expiry
|
||||||
|
wm.AuthRequestID = e.AuthRequestID
|
||||||
case *user.HumanEmailVerifiedEvent:
|
case *user.HumanEmailVerifiedEvent:
|
||||||
wm.IsEmailVerified = true
|
wm.IsEmailVerified = true
|
||||||
wm.Code = nil
|
wm.Code = nil
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
|
|
||||||
func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
||||||
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
|
||||||
@ -39,9 +39,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "invalid email, invalid argument error",
|
name: "invalid email, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -59,8 +57,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, precondition error",
|
name: "user not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -81,8 +78,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not initialized, precondition error",
|
name: "user not initialized, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -102,6 +98,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
|||||||
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
&user.NewAggregate("user1", "org1").Aggregate,
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
nil, time.Hour*1,
|
nil, time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -124,8 +121,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "email not changed, precondition error",
|
name: "email not changed, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -161,8 +157,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "verified email changed, ok",
|
name: "verified email changed, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -215,8 +210,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "email verified, ok",
|
name: "email verified, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -265,8 +259,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "email verified, ok",
|
name: "email verified, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -315,8 +308,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "email changed with code, ok",
|
name: "email changed with code, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -347,6 +339,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -376,7 +369,7 @@ func TestCommandSide_ChangeHumanEmail(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
}
|
}
|
||||||
got, err := r.ChangeHumanEmail(tt.args.ctx, tt.args.email, tt.args.secretGenerator)
|
got, err := r.ChangeHumanEmail(tt.args.ctx, tt.args.email, tt.args.secretGenerator)
|
||||||
if tt.res.err == nil {
|
if tt.res.err == nil {
|
||||||
@ -394,7 +387,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||||
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
|
||||||
@ -416,9 +409,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -432,9 +423,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "code missing, invalid argument error",
|
name: "code missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -448,8 +437,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, precondition error",
|
name: "user not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -466,8 +454,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "code not existing, precondition error",
|
name: "code not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -499,8 +486,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "invalid code, invalid argument error",
|
name: "invalid code, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -526,6 +512,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -550,8 +537,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "valid code, ok",
|
name: "valid code, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -577,6 +563,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -604,7 +591,7 @@ func TestCommandSide_VerifyHumanEmail(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
}
|
}
|
||||||
got, err := r.VerifyHumanEmail(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.resourceOwner, tt.args.secretGenerator)
|
got, err := r.VerifyHumanEmail(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.resourceOwner, tt.args.secretGenerator)
|
||||||
if tt.res.err == nil {
|
if tt.res.err == nil {
|
||||||
@ -622,13 +609,14 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
||||||
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
|
||||||
userID string
|
userID string
|
||||||
resourceOwner string
|
resourceOwner string
|
||||||
secretGenerator crypto.Generator
|
secretGenerator crypto.Generator
|
||||||
|
authRequestID string
|
||||||
}
|
}
|
||||||
type res struct {
|
type res struct {
|
||||||
want *domain.ObjectDetails
|
want *domain.ObjectDetails
|
||||||
@ -643,9 +631,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -658,8 +644,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, precondition error",
|
name: "user not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -675,8 +660,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not initialized, precondition error",
|
name: "user not initialized, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -696,6 +680,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
|||||||
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
&user.NewAggregate("user1", "org1").Aggregate,
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
nil, time.Hour*1,
|
nil, time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -713,8 +698,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "email already verified, precondition error",
|
name: "email already verified, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -750,8 +734,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "new code, ok",
|
name: "new code, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -789,6 +772,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -805,13 +789,72 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "new code with authRequestID, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailChangedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"email2@test.ch",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
"authRequestID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
authRequestID: "authRequestID",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
}
|
}
|
||||||
got, err := r.CreateHumanEmailVerificationCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.secretGenerator)
|
got, err := r.CreateHumanEmailVerificationCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.secretGenerator, tt.args.authRequestID)
|
||||||
if tt.res.err == nil {
|
if tt.res.err == nil {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
@ -827,7 +870,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandSide_EmailVerificationCodeSent(t *testing.T) {
|
func TestCommandSide_EmailVerificationCodeSent(t *testing.T) {
|
||||||
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
|
||||||
@ -846,9 +889,7 @@ func TestCommandSide_EmailVerificationCodeSent(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -861,8 +902,7 @@ func TestCommandSide_EmailVerificationCodeSent(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, precondition error",
|
name: "user not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -878,8 +918,7 @@ func TestCommandSide_EmailVerificationCodeSent(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "code sent, ok",
|
name: "code sent, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -925,7 +964,7 @@ func TestCommandSide_EmailVerificationCodeSent(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
}
|
}
|
||||||
err := r.HumanEmailVerificationCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID)
|
err := r.HumanEmailVerificationCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID)
|
||||||
if tt.res.err == nil {
|
if tt.res.err == nil {
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ResendInitialMail resend initial mail and changes email if provided
|
// ResendInitialMail resend initial mail and changes email if provided
|
||||||
func (c *Commands) ResendInitialMail(ctx context.Context, userID string, email domain.EmailAddress, resourceOwner string, initCodeGenerator crypto.Generator) (objectDetails *domain.ObjectDetails, err error) {
|
func (c *Commands) ResendInitialMail(ctx context.Context, userID string, email domain.EmailAddress, resourceOwner string, initCodeGenerator crypto.Generator, authRequestID string) (objectDetails *domain.ObjectDetails, err error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-2n8vs", "Errors.User.UserIDMissing")
|
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-2n8vs", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
@ -38,7 +38,10 @@ func (c *Commands) ResendInitialMail(ctx context.Context, userID string, email d
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
events = append(events, user.NewHumanInitialCodeAddedEvent(ctx, userAgg, initCode.Code, initCode.Expiry))
|
if authRequestID == "" {
|
||||||
|
authRequestID = existingCode.AuthRequestID
|
||||||
|
}
|
||||||
|
events = append(events, user.NewHumanInitialCodeAddedEvent(ctx, userAgg, initCode.Code, initCode.Expiry, authRequestID))
|
||||||
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -19,6 +19,7 @@ type HumanInitCodeWriteModel struct {
|
|||||||
Code *crypto.CryptoValue
|
Code *crypto.CryptoValue
|
||||||
CodeCreationDate time.Time
|
CodeCreationDate time.Time
|
||||||
CodeExpiry time.Duration
|
CodeExpiry time.Duration
|
||||||
|
AuthRequestID string
|
||||||
|
|
||||||
UserState domain.UserState
|
UserState domain.UserState
|
||||||
}
|
}
|
||||||
@ -50,6 +51,7 @@ func (wm *HumanInitCodeWriteModel) Reduce() error {
|
|||||||
wm.Code = e.Code
|
wm.Code = e.Code
|
||||||
wm.CodeCreationDate = e.CreationDate()
|
wm.CodeCreationDate = e.CreationDate()
|
||||||
wm.CodeExpiry = e.Expiry
|
wm.CodeExpiry = e.Expiry
|
||||||
|
wm.AuthRequestID = e.AuthRequestID
|
||||||
wm.UserState = domain.UserStateInitial
|
wm.UserState = domain.UserStateInitial
|
||||||
case *user.HumanInitializedCheckSucceededEvent:
|
case *user.HumanInitializedCheckSucceededEvent:
|
||||||
wm.Code = nil
|
wm.Code = nil
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
|
|
||||||
func TestCommandSide_ResendInitialMail(t *testing.T) {
|
func TestCommandSide_ResendInitialMail(t *testing.T) {
|
||||||
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
|
||||||
@ -26,6 +26,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
email string
|
email string
|
||||||
resourceOwner string
|
resourceOwner string
|
||||||
secretGenerator crypto.Generator
|
secretGenerator crypto.Generator
|
||||||
|
authRequestID string
|
||||||
}
|
}
|
||||||
type res struct {
|
type res struct {
|
||||||
want *domain.ObjectDetails
|
want *domain.ObjectDetails
|
||||||
@ -40,9 +41,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -55,8 +54,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, precondition error",
|
name: "user not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -72,8 +70,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not initialized, precondition error",
|
name: "user not initialized, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -107,8 +104,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "new code email not changed, ok",
|
name: "new code email not changed, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -128,6 +124,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
&user.NewAggregate("user1", "org1").Aggregate,
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
nil, time.Hour*1,
|
nil, time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -141,6 +138,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -159,10 +157,9 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "new code, ok",
|
name: "new code email not changed with authRequestID, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -182,6 +179,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
&user.NewAggregate("user1", "org1").Aggregate,
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
nil, time.Hour*1,
|
nil, time.Hour*1,
|
||||||
|
"authRequestID",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -195,6 +193,63 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"authRequestID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
email: "email@test.ch",
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
authRequestID: "authRequestID",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "new code, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
nil, time.Hour*1,
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -212,10 +267,9 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "new code with change email, ok",
|
name: "new code with authRequestID, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -235,6 +289,62 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
&user.NewAggregate("user1", "org1").Aggregate,
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
nil, time.Hour*1,
|
nil, time.Hour*1,
|
||||||
|
"authRequestID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
"authRequestID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
authRequestID: "authRequestID",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "new code with change email, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
nil, time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -252,6 +362,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -273,9 +384,9 @@ func TestCommandSide_ResendInitialMail(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
}
|
}
|
||||||
got, err := r.ResendInitialMail(tt.args.ctx, tt.args.userID, domain.EmailAddress(tt.args.email), tt.args.resourceOwner, tt.args.secretGenerator)
|
got, err := r.ResendInitialMail(tt.args.ctx, tt.args.userID, domain.EmailAddress(tt.args.email), tt.args.resourceOwner, tt.args.secretGenerator, tt.args.authRequestID)
|
||||||
if tt.res.err == nil {
|
if tt.res.err == nil {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
@ -291,7 +402,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandSide_VerifyInitCode(t *testing.T) {
|
func TestCommandSide_VerifyInitCode(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
userPasswordHasher *crypto.Hasher
|
userPasswordHasher *crypto.Hasher
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
@ -316,9 +427,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -332,9 +441,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "code missing, invalid argument error",
|
name: "code missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -348,8 +455,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, precondition error",
|
name: "user not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -366,8 +472,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "code not existing, precondition error",
|
name: "code not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -399,8 +504,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "invalid code, invalid argument error",
|
name: "invalid code, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -426,6 +530,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -450,8 +555,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "valid code, ok",
|
name: "valid code, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -477,6 +581,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -506,8 +611,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "valid code with password, ok",
|
name: "valid code with password, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -536,6 +640,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -582,8 +687,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "valid code with password and userAgentID, ok",
|
name: "valid code with password and userAgentID, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -612,6 +716,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -660,7 +765,7 @@ func TestCommandSide_VerifyInitCode(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
userPasswordHasher: tt.fields.userPasswordHasher,
|
userPasswordHasher: tt.fields.userPasswordHasher,
|
||||||
}
|
}
|
||||||
err := r.HumanVerifyInitCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.code, tt.args.password, tt.args.userAgentID, tt.args.secretGenerator)
|
err := r.HumanVerifyInitCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.code, tt.args.password, tt.args.userAgentID, tt.args.secretGenerator)
|
||||||
@ -676,7 +781,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandSide_InitCodeSent(t *testing.T) {
|
func TestCommandSide_InitCodeSent(t *testing.T) {
|
||||||
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
|
||||||
@ -695,9 +800,7 @@ func TestCommandSide_InitCodeSent(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -710,8 +813,7 @@ func TestCommandSide_InitCodeSent(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, precondition error",
|
name: "user not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -727,8 +829,7 @@ func TestCommandSide_InitCodeSent(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "code sent, ok",
|
name: "code sent, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -763,7 +864,7 @@ func TestCommandSide_InitCodeSent(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
}
|
}
|
||||||
err := r.HumanInitCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID)
|
err := r.HumanInitCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID)
|
||||||
if tt.res.err == nil {
|
if tt.res.err == nil {
|
||||||
|
@ -165,7 +165,7 @@ func (c *Commands) canUpdatePassword(ctx context.Context, newPassword string, re
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RequestSetPassword generate and send out new code to change password for a specific user
|
// RequestSetPassword generate and send out new code to change password for a specific user
|
||||||
func (c *Commands) RequestSetPassword(ctx context.Context, userID, resourceOwner string, notifyType domain.NotificationType, passwordVerificationCode crypto.Generator) (objectDetails *domain.ObjectDetails, err error) {
|
func (c *Commands) RequestSetPassword(ctx context.Context, userID, resourceOwner string, notifyType domain.NotificationType, passwordVerificationCode crypto.Generator, authRequestID string) (objectDetails *domain.ObjectDetails, err error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-M00oL", "Errors.User.UserIDMissing")
|
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-M00oL", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
@ -185,7 +185,7 @@ func (c *Commands) RequestSetPassword(ctx context.Context, userID, resourceOwner
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanPasswordCodeAddedEvent(ctx, userAgg, passwordCode.Code, passwordCode.Expiry, notifyType))
|
pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanPasswordCodeAddedEvent(ctx, userAgg, passwordCode.Code, passwordCode.Expiry, notifyType, authRequestID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
|
|
||||||
func TestCommandSide_SetOneTimePassword(t *testing.T) {
|
func TestCommandSide_SetOneTimePassword(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
userPasswordHasher *crypto.Hasher
|
userPasswordHasher *crypto.Hasher
|
||||||
checkPermission domain.PermissionCheck
|
checkPermission domain.PermissionCheck
|
||||||
}
|
}
|
||||||
@ -46,9 +46,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -61,8 +59,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, precondition error",
|
name: "user not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -78,8 +75,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "missing permission, error",
|
name: "missing permission, error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -121,8 +117,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "change password onetime, ok",
|
name: "change password onetime, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -184,8 +179,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "change password no one time, ok",
|
name: "change password no one time, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -248,7 +242,7 @@ func TestCommandSide_SetOneTimePassword(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
userPasswordHasher: tt.fields.userPasswordHasher,
|
userPasswordHasher: tt.fields.userPasswordHasher,
|
||||||
checkPermission: tt.fields.checkPermission,
|
checkPermission: tt.fields.checkPermission,
|
||||||
}
|
}
|
||||||
@ -268,7 +262,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
userEncryption crypto.EncryptionAlgorithm
|
userEncryption crypto.EncryptionAlgorithm
|
||||||
userPasswordHasher *crypto.Hasher
|
userPasswordHasher *crypto.Hasher
|
||||||
}
|
}
|
||||||
@ -293,9 +287,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -308,9 +300,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "password missing, invalid argument error",
|
name: "password missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -324,8 +314,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, precondition error",
|
name: "user not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -342,8 +331,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "code not existing, precondition error",
|
name: "code not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -376,8 +364,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "invalid code, invalid argument error",
|
name: "invalid code, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -404,6 +391,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
|||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
domain.NotificationTypeEmail,
|
domain.NotificationTypeEmail,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -424,8 +412,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "set password, ok",
|
name: "set password, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -457,6 +444,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
|||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
domain.NotificationTypeEmail,
|
domain.NotificationTypeEmail,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -500,8 +488,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "set password with userAgentID, ok",
|
name: "set password with userAgentID, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -533,6 +520,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
|||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
domain.NotificationTypeEmail,
|
domain.NotificationTypeEmail,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -578,7 +566,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
userPasswordHasher: tt.fields.userPasswordHasher,
|
userPasswordHasher: tt.fields.userPasswordHasher,
|
||||||
userEncryption: tt.fields.userEncryption,
|
userEncryption: tt.fields.userEncryption,
|
||||||
}
|
}
|
||||||
@ -915,7 +903,7 @@ func TestCommandSide_ChangePassword(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandSide_RequestSetPassword(t *testing.T) {
|
func TestCommandSide_RequestSetPassword(t *testing.T) {
|
||||||
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
|
||||||
@ -923,6 +911,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
|
|||||||
resourceOwner string
|
resourceOwner string
|
||||||
notifyType domain.NotificationType
|
notifyType domain.NotificationType
|
||||||
secretGenerator crypto.Generator
|
secretGenerator crypto.Generator
|
||||||
|
authRequestID string
|
||||||
}
|
}
|
||||||
type res struct {
|
type res struct {
|
||||||
want *domain.ObjectDetails
|
want *domain.ObjectDetails
|
||||||
@ -937,9 +926,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -952,8 +939,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, precondition error",
|
name: "user not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -969,8 +955,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user initial, precondition error",
|
name: "user initial, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -990,6 +975,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
|
|||||||
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
&user.NewAggregate("user1", "org1").Aggregate,
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
nil, time.Hour*1,
|
nil, time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
@ -1018,8 +1004,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "new code, ok",
|
name: "new code, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -1055,6 +1040,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
|
|||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
domain.NotificationTypeEmail,
|
domain.NotificationTypeEmail,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -1071,13 +1057,70 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "new code with authRequestID, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
"username",
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"nickname",
|
||||||
|
"displayname",
|
||||||
|
language.German,
|
||||||
|
domain.GenderUnspecified,
|
||||||
|
"email@test.ch",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanEmailVerifiedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
user.NewHumanInitializedCheckSucceededEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate)),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
user.NewHumanPasswordCodeAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
&crypto.CryptoValue{
|
||||||
|
CryptoType: crypto.TypeEncryption,
|
||||||
|
Algorithm: "enc",
|
||||||
|
KeyID: "id",
|
||||||
|
Crypted: []byte("a"),
|
||||||
|
},
|
||||||
|
time.Hour*1,
|
||||||
|
domain.NotificationTypeEmail,
|
||||||
|
"authRequestID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
userID: "user1",
|
||||||
|
resourceOwner: "org1",
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
authRequestID: "authRequestID",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
}
|
}
|
||||||
got, err := r.RequestSetPassword(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.notifyType, tt.args.secretGenerator)
|
got, err := r.RequestSetPassword(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.notifyType, tt.args.secretGenerator, tt.args.authRequestID)
|
||||||
if tt.res.err == nil {
|
if tt.res.err == nil {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
@ -1093,7 +1136,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandSide_PasswordCodeSent(t *testing.T) {
|
func TestCommandSide_PasswordCodeSent(t *testing.T) {
|
||||||
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
|
||||||
@ -1112,9 +1155,7 @@ func TestCommandSide_PasswordCodeSent(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -1127,8 +1168,7 @@ func TestCommandSide_PasswordCodeSent(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, precondition error",
|
name: "user not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -1144,8 +1184,7 @@ func TestCommandSide_PasswordCodeSent(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "code sent, ok",
|
name: "code sent, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -1186,7 +1225,7 @@ func TestCommandSide_PasswordCodeSent(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
}
|
}
|
||||||
err := r.PasswordCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID)
|
err := r.PasswordCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID)
|
||||||
if tt.res.err == nil {
|
if tt.res.err == nil {
|
||||||
@ -1201,7 +1240,7 @@ func TestCommandSide_PasswordCodeSent(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandSide_CheckPassword(t *testing.T) {
|
func TestCommandSide_CheckPassword(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
userPasswordHasher *crypto.Hasher
|
userPasswordHasher *crypto.Hasher
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
@ -1224,9 +1263,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -1240,9 +1277,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "password missing, invalid argument error",
|
name: "password missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@ -1256,8 +1291,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "login policy not found, precondition error",
|
name: "login policy not found, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
@ -1275,8 +1309,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "login policy login password not allowed, precondition error",
|
name: "login policy login password not allowed, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
org.NewLoginPolicyAddedEvent(context.Background(),
|
org.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
@ -1316,8 +1349,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, precondition error",
|
name: "user not existing, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
org.NewLoginPolicyAddedEvent(context.Background(),
|
org.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
@ -1358,8 +1390,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user locked, precondition error",
|
name: "user locked, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
org.NewLoginPolicyAddedEvent(context.Background(),
|
org.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
@ -1420,8 +1451,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "existing password empty, precondition error",
|
name: "existing password empty, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
org.NewLoginPolicyAddedEvent(context.Background(),
|
org.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
@ -1478,8 +1508,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "password not matching lockout policy not relevant, precondition error",
|
name: "password not matching lockout policy not relevant, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
org.NewLoginPolicyAddedEvent(context.Background(),
|
org.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
@ -1562,8 +1591,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "password not matching, max password attempts reached - user locked, precondition error",
|
name: "password not matching, max password attempts reached - user locked, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
org.NewLoginPolicyAddedEvent(context.Background(),
|
org.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
@ -1653,8 +1681,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "check password, ok",
|
name: "check password, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
org.NewLoginPolicyAddedEvent(context.Background(),
|
org.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
@ -1734,8 +1761,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "check password, ok, updated hash",
|
name: "check password, ok, updated hash",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
org.NewLoginPolicyAddedEvent(context.Background(),
|
org.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
@ -1820,8 +1846,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "check password ok, locked in the mean time",
|
name: "check password ok, locked in the mean time",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
org.NewLoginPolicyAddedEvent(context.Background(),
|
org.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
@ -1900,8 +1925,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "regression test old version event",
|
name: "regression test old version event",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
org.NewLoginPolicyAddedEvent(context.Background(),
|
org.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
@ -1996,7 +2020,7 @@ func TestCommandSide_CheckPassword(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
userPasswordHasher: tt.fields.userPasswordHasher,
|
userPasswordHasher: tt.fields.userPasswordHasher,
|
||||||
}
|
}
|
||||||
err := r.HumanCheckPassword(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.password, tt.args.authReq, tt.args.lockoutPolicy)
|
err := r.HumanCheckPassword(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.password, tt.args.authReq, tt.args.lockoutPolicy)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||||
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||||
@ -78,6 +79,12 @@ func (key *MachineKey) valid() (err error) {
|
|||||||
if err := key.content(); err != nil {
|
if err := key.content(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// If a key is supplied, it should be a valid public key
|
||||||
|
if len(key.PublicKey) > 0 {
|
||||||
|
if _, err := crypto.BytesToPublicKey(key.PublicKey); err != nil {
|
||||||
|
return zerrors.ThrowInvalidArgument(nil, "COMMAND-5F3h1", "Errors.User.Machine.Key.Invalid")
|
||||||
|
}
|
||||||
|
}
|
||||||
key.ExpirationDate, err = domain.ValidateExpirationDate(key.ExpirationDate)
|
key.ExpirationDate, err = domain.ValidateExpirationDate(key.ExpirationDate)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,16 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const fakePubkey = `-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp4qNBuUu/HekF2E5bOtA
|
||||||
|
oEL76zS0NQdZL3ByEJ3hZplJhE30ITPIOLW3+uaMMM+obl/LLapwG2vdhvutQtx/
|
||||||
|
FOLJmXysbG3RL9zjXDBT5IE+nGFC7ctsi5FGbHQbAm45E3HHCSk7gfmTy9hxyk1K
|
||||||
|
GsyU8BDeOWasJO6aeXqpOnRM8vw/fY+6mHVC9CxcIroSfrIabFGe/mP6qpBGeFSn
|
||||||
|
APymBc/8lca4JaPv2/u/rBhnaAHZiUuCS1+MonWelOb+MSfq48VgtpiaYIVY9szI
|
||||||
|
esorA6EJ9pO17ROEUpX5wP5Oir+yGJU27jSvLCjvK6fOFX+OwUM9L8047JKoo+Nf
|
||||||
|
PwIDAQAB
|
||||||
|
-----END PUBLIC KEY-----`
|
||||||
|
|
||||||
func TestCommands_AddMachineKey(t *testing.T) {
|
func TestCommands_AddMachineKey(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore *eventstore.Eventstore
|
||||||
@ -145,7 +155,7 @@ func TestCommands_AddMachineKey(t *testing.T) {
|
|||||||
"key1",
|
"key1",
|
||||||
domain.AuthNKeyTypeJSON,
|
domain.AuthNKeyTypeJSON,
|
||||||
time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC),
|
time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||||
[]byte("public"),
|
[]byte(fakePubkey),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -161,14 +171,14 @@ func TestCommands_AddMachineKey(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Type: domain.AuthNKeyTypeJSON,
|
Type: domain.AuthNKeyTypeJSON,
|
||||||
ExpirationDate: time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC),
|
ExpirationDate: time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||||
PublicKey: []byte("public"),
|
PublicKey: []byte(fakePubkey),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
res{
|
res{
|
||||||
want: &domain.ObjectDetails{
|
want: &domain.ObjectDetails{
|
||||||
ResourceOwner: "org1",
|
ResourceOwner: "org1",
|
||||||
},
|
},
|
||||||
key: true,
|
key: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -194,7 +204,7 @@ func TestCommands_AddMachineKey(t *testing.T) {
|
|||||||
"key1",
|
"key1",
|
||||||
domain.AuthNKeyTypeJSON,
|
domain.AuthNKeyTypeJSON,
|
||||||
time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC),
|
time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||||
[]byte("public"),
|
[]byte(fakePubkey),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -210,14 +220,35 @@ func TestCommands_AddMachineKey(t *testing.T) {
|
|||||||
KeyID: "key1",
|
KeyID: "key1",
|
||||||
Type: domain.AuthNKeyTypeJSON,
|
Type: domain.AuthNKeyTypeJSON,
|
||||||
ExpirationDate: time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC),
|
ExpirationDate: time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||||
PublicKey: []byte("public"),
|
PublicKey: []byte(fakePubkey),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
res{
|
res{
|
||||||
want: &domain.ObjectDetails{
|
want: &domain.ObjectDetails{
|
||||||
ResourceOwner: "org1",
|
ResourceOwner: "org1",
|
||||||
},
|
},
|
||||||
key: true,
|
key: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key added with invalid public key",
|
||||||
|
fields{
|
||||||
|
eventstore: eventstoreExpect(t),
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
key: &MachineKey{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "user1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
KeyID: "key1",
|
||||||
|
Type: domain.AuthNKeyTypeJSON,
|
||||||
|
PublicKey: []byte("incorrect"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: zerrors.IsErrorInvalidArgument,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -237,9 +268,8 @@ func TestCommands_AddMachineKey(t *testing.T) {
|
|||||||
}
|
}
|
||||||
if tt.res.err == nil {
|
if tt.res.err == nil {
|
||||||
assert.Equal(t, tt.res.want, got)
|
assert.Equal(t, tt.res.want, got)
|
||||||
if tt.res.key {
|
receivedKey := len(tt.args.key.PrivateKey) > 0
|
||||||
assert.NotEqual(t, "", tt.args.key.PrivateKey)
|
assert.Equal(t, tt.res.key, receivedKey)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1797,6 +1797,7 @@ func TestExistsUser(t *testing.T) {
|
|||||||
domain.GenderFemale,
|
domain.GenderFemale,
|
||||||
"support@zitadel.com",
|
"support@zitadel.com",
|
||||||
true,
|
true,
|
||||||
|
"userAgentID",
|
||||||
),
|
),
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
|
@ -1838,7 +1838,7 @@ func TestCommands_verifyUserEmailWithGenerator(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommands_NewUserEmailEvents(t *testing.T) {
|
func TestCommands_NewUserEmailEvents(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
userID string
|
userID string
|
||||||
@ -1852,7 +1852,7 @@ func TestCommands_NewUserEmailEvents(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "missing userID",
|
name: "missing userID",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t),
|
eventstore: expectEventstore(),
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
userID: "",
|
userID: "",
|
||||||
@ -1862,7 +1862,7 @@ func TestCommands_NewUserEmailEvents(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "not found",
|
name: "not found",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t, expectFilter()),
|
eventstore: expectEventstore(expectFilter()),
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
userID: "user1",
|
userID: "user1",
|
||||||
@ -1872,8 +1872,7 @@ func TestCommands_NewUserEmailEvents(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not initialized",
|
name: "user not initialized",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -1893,6 +1892,7 @@ func TestCommands_NewUserEmailEvents(t *testing.T) {
|
|||||||
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
&user.NewAggregate("user1", "org1").Aggregate,
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
nil, time.Hour*1,
|
nil, time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -1907,7 +1907,7 @@ func TestCommands_NewUserEmailEvents(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),
|
||||||
}
|
}
|
||||||
_, err := c.NewUserEmailEvents(context.Background(), tt.args.userID)
|
_, err := c.NewUserEmailEvents(context.Background(), tt.args.userID)
|
||||||
require.ErrorIs(t, err, tt.wantErr)
|
require.ErrorIs(t, err, tt.wantErr)
|
||||||
|
@ -131,8 +131,10 @@ func (c *Commands) AddUserHuman(ctx context.Context, resourceOwner string, human
|
|||||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-7yiox1isql", "Errors.User.AlreadyExisting")
|
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-7yiox1isql", "Errors.User.AlreadyExisting")
|
||||||
}
|
}
|
||||||
// check for permission to create user on resourceOwner
|
// check for permission to create user on resourceOwner
|
||||||
if err := c.checkPermission(ctx, domain.PermissionUserWrite, resourceOwner, human.ID); err != nil {
|
if !human.Register {
|
||||||
return err
|
if err := c.checkPermission(ctx, domain.PermissionUserWrite, resourceOwner, human.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// add resourceowner for the events with the aggregate
|
// add resourceowner for the events with the aggregate
|
||||||
existingHuman.ResourceOwner = resourceOwner
|
existingHuman.ResourceOwner = resourceOwner
|
||||||
@ -159,6 +161,7 @@ func (c *Commands) AddUserHuman(ctx context.Context, resourceOwner string, human
|
|||||||
human.Gender,
|
human.Gender,
|
||||||
human.Email.Address,
|
human.Email.Address,
|
||||||
domainPolicy.UserLoginMustBeDomain,
|
domainPolicy.UserLoginMustBeDomain,
|
||||||
|
human.UserAgentID,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
createCmd = user.NewHumanAddedEvent(
|
createCmd = user.NewHumanAddedEvent(
|
||||||
|
@ -232,6 +232,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
|||||||
domain.GenderUnspecified,
|
domain.GenderUnspecified,
|
||||||
"email@test.ch",
|
"email@test.ch",
|
||||||
true,
|
true,
|
||||||
|
"userAgentID",
|
||||||
),
|
),
|
||||||
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
&userAgg.Aggregate,
|
&userAgg.Aggregate,
|
||||||
@ -242,6 +243,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
|||||||
Crypted: []byte("userinit"),
|
Crypted: []byte("userinit"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"authRequestID",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -261,6 +263,8 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
|||||||
},
|
},
|
||||||
PreferredLanguage: language.English,
|
PreferredLanguage: language.English,
|
||||||
Register: true,
|
Register: true,
|
||||||
|
UserAgentID: "userAgentID",
|
||||||
|
AuthRequestID: "authRequestID",
|
||||||
},
|
},
|
||||||
secretGenerator: GetMockSecretGenerator(t),
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
allowInitMail: true,
|
allowInitMail: true,
|
||||||
@ -344,6 +348,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
|||||||
Crypted: []byte("userinit"),
|
Crypted: []byte("userinit"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -414,6 +419,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
|||||||
Crypted: []byte("userinit"),
|
Crypted: []byte("userinit"),
|
||||||
},
|
},
|
||||||
1*time.Hour,
|
1*time.Hour,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -1031,6 +1037,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
|||||||
Crypted: []byte("userinit"),
|
Crypted: []byte("userinit"),
|
||||||
},
|
},
|
||||||
1*time.Hour,
|
1*time.Hour,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
user.NewHumanPhoneVerifiedEvent(
|
user.NewHumanPhoneVerifiedEvent(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
@ -1174,6 +1181,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
|||||||
Crypted: []byte("userinit"),
|
Crypted: []byte("userinit"),
|
||||||
},
|
},
|
||||||
1*time.Hour,
|
1*time.Hour,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
user.NewMetadataSetEvent(
|
user.NewMetadataSetEvent(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
@ -1993,6 +2001,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
|||||||
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
&userAgg.Aggregate,
|
&userAgg.Aggregate,
|
||||||
nil, time.Hour*1,
|
nil, time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -167,6 +167,7 @@ func TestCommandSide_userExistsWriteModel(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"authRequestID",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -225,6 +226,7 @@ func TestCommandSide_userExistsWriteModel(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"authRequestID",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
@ -280,6 +282,7 @@ func TestCommandSide_userExistsWriteModel(t *testing.T) {
|
|||||||
Crypted: []byte("a"),
|
Crypted: []byte("a"),
|
||||||
},
|
},
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
|
"authRequestID",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/muhlemmer/gu"
|
"github.com/muhlemmer/gu"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
@ -69,7 +70,7 @@ func TestCommands_RequestPasswordReset(t *testing.T) {
|
|||||||
),
|
),
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate,
|
user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate,
|
||||||
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second),
|
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second, ""),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -167,7 +168,7 @@ func TestCommands_RequestPasswordResetReturnCode(t *testing.T) {
|
|||||||
),
|
),
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate,
|
user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate,
|
||||||
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second),
|
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second, ""),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -279,7 +280,7 @@ func TestCommands_RequestPasswordResetURLTemplate(t *testing.T) {
|
|||||||
),
|
),
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate,
|
user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate,
|
||||||
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second),
|
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second, ""),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -390,7 +391,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
|||||||
),
|
),
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate,
|
user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate,
|
||||||
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second),
|
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second, ""),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
|
|
||||||
func TestCommandSide_LockUserV2(t *testing.T) {
|
func TestCommandSide_LockUserV2(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
checkPermission domain.PermissionCheck
|
checkPermission domain.PermissionCheck
|
||||||
}
|
}
|
||||||
type (
|
type (
|
||||||
@ -40,9 +40,7 @@ func TestCommandSide_LockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
checkPermission: newMockPermissionCheckAllowed(),
|
checkPermission: newMockPermissionCheckAllowed(),
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
@ -58,8 +56,7 @@ func TestCommandSide_LockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, not found error",
|
name: "user not existing, not found error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
checkPermission: newMockPermissionCheckAllowed(),
|
checkPermission: newMockPermissionCheckAllowed(),
|
||||||
@ -77,8 +74,7 @@ func TestCommandSide_LockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user already locked, precondition error",
|
name: "user already locked, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -116,8 +112,7 @@ func TestCommandSide_LockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user already locked, precondition error",
|
name: "user already locked, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewMachineAddedEvent(context.Background(),
|
user.NewMachineAddedEvent(context.Background(),
|
||||||
@ -151,8 +146,7 @@ func TestCommandSide_LockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "lock user, ok",
|
name: "lock user, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -190,8 +184,7 @@ func TestCommandSide_LockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "lock user, no permission",
|
name: "lock user, no permission",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -224,8 +217,7 @@ func TestCommandSide_LockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "lock user machine, ok",
|
name: "lock user machine, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewMachineAddedEvent(context.Background(),
|
user.NewMachineAddedEvent(context.Background(),
|
||||||
@ -260,7 +252,7 @@ func TestCommandSide_LockUserV2(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
checkPermission: tt.fields.checkPermission,
|
checkPermission: tt.fields.checkPermission,
|
||||||
}
|
}
|
||||||
got, err := r.LockUserV2(tt.args.ctx, tt.args.userID)
|
got, err := r.LockUserV2(tt.args.ctx, tt.args.userID)
|
||||||
@ -279,7 +271,7 @@ func TestCommandSide_LockUserV2(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandSide_UnlockUserV2(t *testing.T) {
|
func TestCommandSide_UnlockUserV2(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
checkPermission domain.PermissionCheck
|
checkPermission domain.PermissionCheck
|
||||||
}
|
}
|
||||||
type (
|
type (
|
||||||
@ -301,9 +293,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
checkPermission: newMockPermissionCheckAllowed(),
|
checkPermission: newMockPermissionCheckAllowed(),
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
@ -319,8 +309,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, not found error",
|
name: "user not existing, not found error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
checkPermission: newMockPermissionCheckAllowed(),
|
checkPermission: newMockPermissionCheckAllowed(),
|
||||||
@ -338,8 +327,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user already active, precondition error",
|
name: "user already active, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -372,8 +360,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user already active, precondition error",
|
name: "user already active, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
user.NewMachineAddedEvent(context.Background(),
|
user.NewMachineAddedEvent(context.Background(),
|
||||||
&user.NewAggregate("user1", "org1").Aggregate,
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
@ -400,8 +387,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "unlock user, ok",
|
name: "unlock user, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -443,8 +429,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "unlock user, no permission",
|
name: "unlock user, no permission",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -481,8 +466,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "unlock user machine, ok",
|
name: "unlock user machine, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewMachineAddedEvent(context.Background(),
|
user.NewMachineAddedEvent(context.Background(),
|
||||||
@ -521,7 +505,7 @@ func TestCommandSide_UnlockUserV2(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
checkPermission: tt.fields.checkPermission,
|
checkPermission: tt.fields.checkPermission,
|
||||||
}
|
}
|
||||||
got, err := r.UnlockUserV2(tt.args.ctx, tt.args.userID)
|
got, err := r.UnlockUserV2(tt.args.ctx, tt.args.userID)
|
||||||
@ -540,7 +524,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandSide_DeactivateUserV2(t *testing.T) {
|
func TestCommandSide_DeactivateUserV2(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
checkPermission domain.PermissionCheck
|
checkPermission domain.PermissionCheck
|
||||||
}
|
}
|
||||||
type (
|
type (
|
||||||
@ -562,9 +546,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
checkPermission: newMockPermissionCheckAllowed(),
|
checkPermission: newMockPermissionCheckAllowed(),
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
@ -580,8 +562,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, not found error",
|
name: "user not existing, not found error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
checkPermission: newMockPermissionCheckAllowed(),
|
checkPermission: newMockPermissionCheckAllowed(),
|
||||||
@ -599,8 +580,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user initial, precondition error",
|
name: "user initial, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -620,6 +600,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) {
|
|||||||
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||||
&user.NewAggregate("user1", "org1").Aggregate,
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
nil, time.Hour*1,
|
nil, time.Hour*1,
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -639,8 +620,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user already inactive, precondition error",
|
name: "user already inactive, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -678,8 +658,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "deactivate user, ok",
|
name: "deactivate user, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -722,8 +701,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "deactivate user, no permission",
|
name: "deactivate user, no permission",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -761,8 +739,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user machine already inactive, precondition error",
|
name: "user machine already inactive, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewMachineAddedEvent(context.Background(),
|
user.NewMachineAddedEvent(context.Background(),
|
||||||
@ -796,8 +773,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "deactivate user machine, ok",
|
name: "deactivate user machine, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewMachineAddedEvent(context.Background(),
|
user.NewMachineAddedEvent(context.Background(),
|
||||||
@ -832,7 +808,7 @@ func TestCommandSide_DeactivateUserV2(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
checkPermission: tt.fields.checkPermission,
|
checkPermission: tt.fields.checkPermission,
|
||||||
}
|
}
|
||||||
got, err := r.DeactivateUserV2(tt.args.ctx, tt.args.userID)
|
got, err := r.DeactivateUserV2(tt.args.ctx, tt.args.userID)
|
||||||
@ -851,7 +827,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandSide_ReactivateUserV2(t *testing.T) {
|
func TestCommandSide_ReactivateUserV2(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
checkPermission domain.PermissionCheck
|
checkPermission domain.PermissionCheck
|
||||||
}
|
}
|
||||||
type (
|
type (
|
||||||
@ -873,9 +849,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
checkPermission: newMockPermissionCheckAllowed(),
|
checkPermission: newMockPermissionCheckAllowed(),
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
@ -891,8 +865,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, not found error",
|
name: "user not existing, not found error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
checkPermission: newMockPermissionCheckAllowed(),
|
checkPermission: newMockPermissionCheckAllowed(),
|
||||||
@ -910,8 +883,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user already active, precondition error",
|
name: "user already active, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -944,8 +916,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user machine already active, precondition error",
|
name: "user machine already active, precondition error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewMachineAddedEvent(context.Background(),
|
user.NewMachineAddedEvent(context.Background(),
|
||||||
@ -974,8 +945,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "reactivate user, ok",
|
name: "reactivate user, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -1017,8 +987,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "reactivate user, no permission",
|
name: "reactivate user, no permission",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -1055,8 +1024,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "reactivate user machine, ok",
|
name: "reactivate user machine, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewMachineAddedEvent(context.Background(),
|
user.NewMachineAddedEvent(context.Background(),
|
||||||
@ -1095,7 +1063,7 @@ func TestCommandSide_ReactivateUserV2(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
checkPermission: tt.fields.checkPermission,
|
checkPermission: tt.fields.checkPermission,
|
||||||
}
|
}
|
||||||
got, err := r.ReactivateUserV2(tt.args.ctx, tt.args.userID)
|
got, err := r.ReactivateUserV2(tt.args.ctx, tt.args.userID)
|
||||||
@ -1114,7 +1082,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandSide_RemoveUserV2(t *testing.T) {
|
func TestCommandSide_RemoveUserV2(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
checkPermission domain.PermissionCheck
|
checkPermission domain.PermissionCheck
|
||||||
}
|
}
|
||||||
type (
|
type (
|
||||||
@ -1138,9 +1106,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "userid missing, invalid argument error",
|
name: "userid missing, invalid argument error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(),
|
||||||
t,
|
|
||||||
),
|
|
||||||
checkPermission: newMockPermissionCheckAllowed(),
|
checkPermission: newMockPermissionCheckAllowed(),
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
@ -1156,8 +1122,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user not existing, not found error",
|
name: "user not existing, not found error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(),
|
expectFilter(),
|
||||||
),
|
),
|
||||||
checkPermission: newMockPermissionCheckAllowed(),
|
checkPermission: newMockPermissionCheckAllowed(),
|
||||||
@ -1175,8 +1140,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user removed, notfound error",
|
name: "user removed, notfound error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -1217,8 +1181,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "remove user, ok",
|
name: "remove user, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -1269,8 +1232,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "remove user, no permission",
|
name: "remove user, no permission",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewHumanAddedEvent(context.Background(),
|
user.NewHumanAddedEvent(context.Background(),
|
||||||
@ -1308,8 +1270,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "user machine already removed, notfound error",
|
name: "user machine already removed, notfound error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewMachineAddedEvent(context.Background(),
|
user.NewMachineAddedEvent(context.Background(),
|
||||||
@ -1346,8 +1307,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "remove user machine, ok",
|
name: "remove user machine, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(
|
eventstore: expectEventstore(
|
||||||
t,
|
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusher(
|
eventFromEventPusher(
|
||||||
user.NewMachineAddedEvent(context.Background(),
|
user.NewMachineAddedEvent(context.Background(),
|
||||||
@ -1395,7 +1355,7 @@ func TestCommandSide_RemoveUserV2(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) {
|
||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
checkPermission: tt.fields.checkPermission,
|
checkPermission: tt.fields.checkPermission,
|
||||||
}
|
}
|
||||||
got, err := r.RemoveUserV2(tt.args.ctx, tt.args.userID, tt.args.cascadingMemberships, tt.args.grantIDs...)
|
got, err := r.RemoveUserV2(tt.args.ctx, tt.args.userID, tt.args.cascadingMemberships, tt.args.grantIDs...)
|
||||||
|
@ -169,7 +169,7 @@ func (u *userNotifier) reduceInitCodeAdded(event eventstore.Event) (*handler.Sta
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e).
|
err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e).
|
||||||
SendUserInitCode(ctx, notifyUser, code)
|
SendUserInitCode(ctx, notifyUser, code, e.AuthRequestID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -226,7 +226,7 @@ func (u *userNotifier) reduceEmailCodeAdded(event eventstore.Event) (*handler.St
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e).
|
err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e).
|
||||||
SendEmailVerificationCode(ctx, notifyUser, code, e.URLTemplate)
|
SendEmailVerificationCode(ctx, notifyUser, code, e.URLTemplate, e.AuthRequestID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -285,7 +285,7 @@ func (u *userNotifier) reducePasswordCodeAdded(event eventstore.Event) (*handler
|
|||||||
if e.NotificationType == domain.NotificationTypeSms {
|
if e.NotificationType == domain.NotificationTypeSms {
|
||||||
notify = types.SendSMSTwilio(ctx, u.channels, translator, notifyUser, colors, e)
|
notify = types.SendSMSTwilio(ctx, u.channels, translator, notifyUser, colors, e)
|
||||||
}
|
}
|
||||||
err = notify.SendPasswordCode(ctx, notifyUser, code, e.URLTemplate)
|
err = notify.SendPasswordCode(ctx, notifyUser, code, e.URLTemplate, e.AuthRequestID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ const (
|
|||||||
externalSecure = false
|
externalSecure = false
|
||||||
externalProtocol = "http"
|
externalProtocol = "http"
|
||||||
defaultOTPEmailTemplate = "/otp/verify?loginName={{.LoginName}}&code={{.Code}}"
|
defaultOTPEmailTemplate = "/otp/verify?loginName={{.LoginName}}&code={{.Code}}"
|
||||||
|
authRequestID = "authRequestID"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_userNotifier_reduceInitCodeAdded(t *testing.T) {
|
func Test_userNotifier_reduceInitCodeAdded(t *testing.T) {
|
||||||
@ -128,7 +129,7 @@ func Test_userNotifier_reduceInitCodeAdded(t *testing.T) {
|
|||||||
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
||||||
givenTemplate := "{{.URL}}"
|
givenTemplate := "{{.URL}}"
|
||||||
testCode := "testcode"
|
testCode := "testcode"
|
||||||
expectContent := fmt.Sprintf("%s/ui/login/user/init?userID=%s&loginname=%s&code=%s&orgID=%s&passwordset=%t", eventOrigin, userID, preferredLoginName, testCode, orgID, false)
|
expectContent := fmt.Sprintf("%s/ui/login/user/init?authRequestID=%s&code=%s&loginname=%s&orgID=%s&passwordset=%t&userID=%s", eventOrigin, "", testCode, preferredLoginName, orgID, false, userID)
|
||||||
w.message = messages.Email{
|
w.message = messages.Email{
|
||||||
Recipients: []string{lastEmail},
|
Recipients: []string{lastEmail},
|
||||||
Subject: expectMailSubject,
|
Subject: expectMailSubject,
|
||||||
@ -162,7 +163,7 @@ func Test_userNotifier_reduceInitCodeAdded(t *testing.T) {
|
|||||||
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
||||||
givenTemplate := "{{.URL}}"
|
givenTemplate := "{{.URL}}"
|
||||||
testCode := "testcode"
|
testCode := "testcode"
|
||||||
expectContent := fmt.Sprintf("%s://%s:%d/ui/login/user/init?userID=%s&loginname=%s&code=%s&orgID=%s&passwordset=%t", externalProtocol, instancePrimaryDomain, externalPort, userID, preferredLoginName, testCode, orgID, false)
|
expectContent := fmt.Sprintf("%s://%s:%d/ui/login/user/init?authRequestID=%s&code=%s&loginname=%s&orgID=%s&passwordset=%t&userID=%s", externalProtocol, instancePrimaryDomain, externalPort, "", testCode, preferredLoginName, orgID, false, userID)
|
||||||
w.message = messages.Email{
|
w.message = messages.Email{
|
||||||
Recipients: []string{lastEmail},
|
Recipients: []string{lastEmail},
|
||||||
Subject: expectMailSubject,
|
Subject: expectMailSubject,
|
||||||
@ -196,6 +197,46 @@ func Test_userNotifier_reduceInitCodeAdded(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, w
|
}, w
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
name: "button url without event trigger url with authRequestID",
|
||||||
|
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
||||||
|
givenTemplate := "{{.URL}}"
|
||||||
|
testCode := "testcode"
|
||||||
|
expectContent := fmt.Sprintf("%s://%s:%d/ui/login/user/init?authRequestID=%s&code=%s&loginname=%s&orgID=%s&passwordset=%t&userID=%s", externalProtocol, instancePrimaryDomain, externalPort, authRequestID, testCode, preferredLoginName, orgID, false, userID)
|
||||||
|
w.message = messages.Email{
|
||||||
|
Recipients: []string{lastEmail},
|
||||||
|
Subject: expectMailSubject,
|
||||||
|
Content: expectContent,
|
||||||
|
}
|
||||||
|
codeAlg, code := cryptoValue(t, ctrl, testCode)
|
||||||
|
queries.EXPECT().SearchInstanceDomains(gomock.Any(), gomock.Any()).Return(&query.InstanceDomains{
|
||||||
|
Domains: []*query.InstanceDomain{{
|
||||||
|
Domain: instancePrimaryDomain,
|
||||||
|
IsPrimary: true,
|
||||||
|
}},
|
||||||
|
}, nil)
|
||||||
|
expectTemplateQueries(queries, givenTemplate)
|
||||||
|
commands.EXPECT().HumanInitCodeSent(gomock.Any(), orgID, userID).Return(nil)
|
||||||
|
return fields{
|
||||||
|
queries: queries,
|
||||||
|
commands: commands,
|
||||||
|
es: eventstore.NewEventstore(&eventstore.Config{
|
||||||
|
Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier,
|
||||||
|
}),
|
||||||
|
userDataCrypto: codeAlg,
|
||||||
|
}, args{
|
||||||
|
event: &user.HumanInitialCodeAddedEvent{
|
||||||
|
BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{
|
||||||
|
AggregateID: userID,
|
||||||
|
ResourceOwner: sql.NullString{String: orgID},
|
||||||
|
CreationDate: time.Now().UTC(),
|
||||||
|
}),
|
||||||
|
Code: code,
|
||||||
|
Expiry: time.Hour,
|
||||||
|
AuthRequestID: authRequestID,
|
||||||
|
},
|
||||||
|
}, w
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
// TODO: Why don't we have an url template on user.HumanInitialCodeAddedEvent?
|
// TODO: Why don't we have an url template on user.HumanInitialCodeAddedEvent?
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -305,7 +346,7 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
|
|||||||
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
||||||
givenTemplate := "{{.URL}}"
|
givenTemplate := "{{.URL}}"
|
||||||
testCode := "testcode"
|
testCode := "testcode"
|
||||||
expectContent := fmt.Sprintf("%s/ui/login/mail/verification?userID=%s&code=%s&orgID=%s", eventOrigin, userID, testCode, orgID)
|
expectContent := fmt.Sprintf("%s/ui/login/mail/verification?authRequestID=%s&code=%s&orgID=%s&userID=%s", eventOrigin, "", testCode, orgID, userID)
|
||||||
w.message = messages.Email{
|
w.message = messages.Email{
|
||||||
Recipients: []string{lastEmail},
|
Recipients: []string{lastEmail},
|
||||||
Subject: expectMailSubject,
|
Subject: expectMailSubject,
|
||||||
@ -342,7 +383,7 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
|
|||||||
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
||||||
givenTemplate := "{{.URL}}"
|
givenTemplate := "{{.URL}}"
|
||||||
testCode := "testcode"
|
testCode := "testcode"
|
||||||
expectContent := fmt.Sprintf("%s://%s:%d/ui/login/mail/verification?userID=%s&code=%s&orgID=%s", externalProtocol, instancePrimaryDomain, externalPort, userID, testCode, orgID)
|
expectContent := fmt.Sprintf("%s://%s:%d/ui/login/mail/verification?authRequestID=%s&code=%s&orgID=%s&userID=%s", externalProtocol, instancePrimaryDomain, externalPort, "", testCode, orgID, userID)
|
||||||
w.message = messages.Email{
|
w.message = messages.Email{
|
||||||
Recipients: []string{lastEmail},
|
Recipients: []string{lastEmail},
|
||||||
Subject: expectMailSubject,
|
Subject: expectMailSubject,
|
||||||
@ -378,6 +419,48 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, w
|
}, w
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
name: "button url without event trigger url with authRequestID",
|
||||||
|
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
||||||
|
givenTemplate := "{{.URL}}"
|
||||||
|
testCode := "testcode"
|
||||||
|
expectContent := fmt.Sprintf("%s://%s:%d/ui/login/mail/verification?authRequestID=%s&code=%s&orgID=%s&userID=%s", externalProtocol, instancePrimaryDomain, externalPort, authRequestID, testCode, orgID, userID)
|
||||||
|
w.message = messages.Email{
|
||||||
|
Recipients: []string{lastEmail},
|
||||||
|
Subject: expectMailSubject,
|
||||||
|
Content: expectContent,
|
||||||
|
}
|
||||||
|
codeAlg, code := cryptoValue(t, ctrl, testCode)
|
||||||
|
queries.EXPECT().SearchInstanceDomains(gomock.Any(), gomock.Any()).Return(&query.InstanceDomains{
|
||||||
|
Domains: []*query.InstanceDomain{{
|
||||||
|
Domain: instancePrimaryDomain,
|
||||||
|
IsPrimary: true,
|
||||||
|
}},
|
||||||
|
}, nil)
|
||||||
|
expectTemplateQueries(queries, givenTemplate)
|
||||||
|
commands.EXPECT().HumanEmailVerificationCodeSent(gomock.Any(), orgID, userID).Return(nil)
|
||||||
|
return fields{
|
||||||
|
queries: queries,
|
||||||
|
commands: commands,
|
||||||
|
es: eventstore.NewEventstore(&eventstore.Config{
|
||||||
|
Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier,
|
||||||
|
}),
|
||||||
|
userDataCrypto: codeAlg,
|
||||||
|
}, args{
|
||||||
|
event: &user.HumanEmailCodeAddedEvent{
|
||||||
|
BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{
|
||||||
|
AggregateID: userID,
|
||||||
|
ResourceOwner: sql.NullString{String: orgID},
|
||||||
|
CreationDate: time.Now().UTC(),
|
||||||
|
}),
|
||||||
|
Code: code,
|
||||||
|
Expiry: time.Hour,
|
||||||
|
URLTemplate: "",
|
||||||
|
CodeReturned: false,
|
||||||
|
AuthRequestID: authRequestID,
|
||||||
|
},
|
||||||
|
}, w
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
name: "button url with url template and event trigger url",
|
name: "button url with url template and event trigger url",
|
||||||
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
||||||
@ -524,7 +607,7 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) {
|
|||||||
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
||||||
givenTemplate := "{{.URL}}"
|
givenTemplate := "{{.URL}}"
|
||||||
testCode := "testcode"
|
testCode := "testcode"
|
||||||
expectContent := fmt.Sprintf("%s/ui/login/password/init?userID=%s&code=%s&orgID=%s", eventOrigin, userID, testCode, orgID)
|
expectContent := fmt.Sprintf("%s/ui/login/password/init?authRequestID=%s&code=%s&orgID=%s&userID=%s", eventOrigin, "", testCode, orgID, userID)
|
||||||
w.message = messages.Email{
|
w.message = messages.Email{
|
||||||
Recipients: []string{lastEmail},
|
Recipients: []string{lastEmail},
|
||||||
Subject: expectMailSubject,
|
Subject: expectMailSubject,
|
||||||
@ -561,7 +644,7 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) {
|
|||||||
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
||||||
givenTemplate := "{{.URL}}"
|
givenTemplate := "{{.URL}}"
|
||||||
testCode := "testcode"
|
testCode := "testcode"
|
||||||
expectContent := fmt.Sprintf("%s://%s:%d/ui/login/password/init?userID=%s&code=%s&orgID=%s", externalProtocol, instancePrimaryDomain, externalPort, userID, testCode, orgID)
|
expectContent := fmt.Sprintf("%s://%s:%d/ui/login/password/init?authRequestID=%s&code=%s&orgID=%s&userID=%s", externalProtocol, instancePrimaryDomain, externalPort, "", testCode, orgID, userID)
|
||||||
w.message = messages.Email{
|
w.message = messages.Email{
|
||||||
Recipients: []string{lastEmail},
|
Recipients: []string{lastEmail},
|
||||||
Subject: expectMailSubject,
|
Subject: expectMailSubject,
|
||||||
@ -597,6 +680,48 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, w
|
}, w
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
name: "button url without event trigger url with authRequestID",
|
||||||
|
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
||||||
|
givenTemplate := "{{.URL}}"
|
||||||
|
testCode := "testcode"
|
||||||
|
expectContent := fmt.Sprintf("%s://%s:%d/ui/login/password/init?authRequestID=%s&code=%s&orgID=%s&userID=%s", externalProtocol, instancePrimaryDomain, externalPort, authRequestID, testCode, orgID, userID)
|
||||||
|
w.message = messages.Email{
|
||||||
|
Recipients: []string{lastEmail},
|
||||||
|
Subject: expectMailSubject,
|
||||||
|
Content: expectContent,
|
||||||
|
}
|
||||||
|
codeAlg, code := cryptoValue(t, ctrl, testCode)
|
||||||
|
queries.EXPECT().SearchInstanceDomains(gomock.Any(), gomock.Any()).Return(&query.InstanceDomains{
|
||||||
|
Domains: []*query.InstanceDomain{{
|
||||||
|
Domain: instancePrimaryDomain,
|
||||||
|
IsPrimary: true,
|
||||||
|
}},
|
||||||
|
}, nil)
|
||||||
|
expectTemplateQueries(queries, givenTemplate)
|
||||||
|
commands.EXPECT().PasswordCodeSent(gomock.Any(), orgID, userID).Return(nil)
|
||||||
|
return fields{
|
||||||
|
queries: queries,
|
||||||
|
commands: commands,
|
||||||
|
es: eventstore.NewEventstore(&eventstore.Config{
|
||||||
|
Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier,
|
||||||
|
}),
|
||||||
|
userDataCrypto: codeAlg,
|
||||||
|
}, args{
|
||||||
|
event: &user.HumanPasswordCodeAddedEvent{
|
||||||
|
BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{
|
||||||
|
AggregateID: userID,
|
||||||
|
ResourceOwner: sql.NullString{String: orgID},
|
||||||
|
CreationDate: time.Now().UTC(),
|
||||||
|
}),
|
||||||
|
Code: code,
|
||||||
|
Expiry: time.Hour,
|
||||||
|
URLTemplate: "",
|
||||||
|
CodeReturned: false,
|
||||||
|
AuthRequestID: authRequestID,
|
||||||
|
},
|
||||||
|
}, w
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
name: "button url with url template and event trigger url",
|
name: "button url with url template and event trigger url",
|
||||||
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
|
||||||
|
@ -10,10 +10,10 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/query"
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (notify Notify) SendEmailVerificationCode(ctx context.Context, user *query.NotifyUser, code string, urlTmpl string) error {
|
func (notify Notify) SendEmailVerificationCode(ctx context.Context, user *query.NotifyUser, code string, urlTmpl, authRequestID string) error {
|
||||||
var url string
|
var url string
|
||||||
if urlTmpl == "" {
|
if urlTmpl == "" {
|
||||||
url = login.MailVerificationLink(http_utils.ComposedOrigin(ctx), user.ID, code, user.ResourceOwner)
|
url = login.MailVerificationLink(http_utils.ComposedOrigin(ctx), user.ID, code, user.ResourceOwner, authRequestID)
|
||||||
} else {
|
} else {
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
if err := domain.RenderConfirmURLTemplate(&buf, urlTmpl, user.ID, code, user.ResourceOwner); err != nil {
|
if err := domain.RenderConfirmURLTemplate(&buf, urlTmpl, user.ID, code, user.ResourceOwner); err != nil {
|
||||||
|
@ -15,10 +15,11 @@ import (
|
|||||||
|
|
||||||
func TestNotify_SendEmailVerificationCode(t *testing.T) {
|
func TestNotify_SendEmailVerificationCode(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
user *query.NotifyUser
|
user *query.NotifyUser
|
||||||
origin string
|
origin string
|
||||||
code string
|
code string
|
||||||
urlTmpl string
|
urlTmpl string
|
||||||
|
authRequestID string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -33,12 +34,13 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) {
|
|||||||
ID: "user1",
|
ID: "user1",
|
||||||
ResourceOwner: "org1",
|
ResourceOwner: "org1",
|
||||||
},
|
},
|
||||||
origin: "https://example.com",
|
origin: "https://example.com",
|
||||||
code: "123",
|
code: "123",
|
||||||
urlTmpl: "",
|
urlTmpl: "",
|
||||||
|
authRequestID: "authRequestID",
|
||||||
},
|
},
|
||||||
want: ¬ifyResult{
|
want: ¬ifyResult{
|
||||||
url: "https://example.com/ui/login/mail/verification?userID=user1&code=123&orgID=org1",
|
url: "https://example.com/ui/login/mail/verification?authRequestID=authRequestID&code=123&orgID=org1&userID=user1",
|
||||||
args: map[string]interface{}{"Code": "123"},
|
args: map[string]interface{}{"Code": "123"},
|
||||||
messageType: domain.VerifyEmailMessageType,
|
messageType: domain.VerifyEmailMessageType,
|
||||||
allowUnverifiedNotificationChannel: true,
|
allowUnverifiedNotificationChannel: true,
|
||||||
@ -51,9 +53,10 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) {
|
|||||||
ID: "user1",
|
ID: "user1",
|
||||||
ResourceOwner: "org1",
|
ResourceOwner: "org1",
|
||||||
},
|
},
|
||||||
origin: "https://example.com",
|
origin: "https://example.com",
|
||||||
code: "123",
|
code: "123",
|
||||||
urlTmpl: "{{",
|
urlTmpl: "{{",
|
||||||
|
authRequestID: "authRequestID",
|
||||||
},
|
},
|
||||||
want: ¬ifyResult{},
|
want: ¬ifyResult{},
|
||||||
wantErr: zerrors.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"),
|
wantErr: zerrors.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"),
|
||||||
@ -65,9 +68,10 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) {
|
|||||||
ID: "user1",
|
ID: "user1",
|
||||||
ResourceOwner: "org1",
|
ResourceOwner: "org1",
|
||||||
},
|
},
|
||||||
origin: "https://example.com",
|
origin: "https://example.com",
|
||||||
code: "123",
|
code: "123",
|
||||||
urlTmpl: "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}",
|
urlTmpl: "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}",
|
||||||
|
authRequestID: "authRequestID",
|
||||||
},
|
},
|
||||||
want: ¬ifyResult{
|
want: ¬ifyResult{
|
||||||
url: "https://example.com/email/verify?userID=user1&code=123&orgID=org1",
|
url: "https://example.com/email/verify?userID=user1&code=123&orgID=org1",
|
||||||
@ -80,7 +84,7 @@ func TestNotify_SendEmailVerificationCode(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) {
|
||||||
got, notify := mockNotify()
|
got, notify := mockNotify()
|
||||||
err := notify.SendEmailVerificationCode(http_utils.WithComposedOrigin(context.Background(), tt.args.origin), tt.args.user, tt.args.code, tt.args.urlTmpl)
|
err := notify.SendEmailVerificationCode(http_utils.WithComposedOrigin(context.Background(), tt.args.origin), tt.args.user, tt.args.code, tt.args.urlTmpl, tt.args.authRequestID)
|
||||||
require.ErrorIs(t, err, tt.wantErr)
|
require.ErrorIs(t, err, tt.wantErr)
|
||||||
assert.Equal(t, tt.want, got)
|
assert.Equal(t, tt.want, got)
|
||||||
})
|
})
|
||||||
|
@ -9,8 +9,8 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/query"
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (notify Notify) SendUserInitCode(ctx context.Context, user *query.NotifyUser, code string) error {
|
func (notify Notify) SendUserInitCode(ctx context.Context, user *query.NotifyUser, code, authRequestID string) error {
|
||||||
url := login.InitUserLink(http_utils.ComposedOrigin(ctx), user.ID, user.PreferredLoginName, code, user.ResourceOwner, user.PasswordSet)
|
url := login.InitUserLink(http_utils.ComposedOrigin(ctx), user.ID, user.PreferredLoginName, code, user.ResourceOwner, user.PasswordSet, authRequestID)
|
||||||
args := make(map[string]interface{})
|
args := make(map[string]interface{})
|
||||||
args["Code"] = code
|
args["Code"] = code
|
||||||
return notify(url, args, domain.InitCodeMessageType, true)
|
return notify(url, args, domain.InitCodeMessageType, true)
|
||||||
|
@ -10,10 +10,10 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/query"
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (notify Notify) SendPasswordCode(ctx context.Context, user *query.NotifyUser, code, urlTmpl string) error {
|
func (notify Notify) SendPasswordCode(ctx context.Context, user *query.NotifyUser, code, urlTmpl, authRequestID string) error {
|
||||||
var url string
|
var url string
|
||||||
if urlTmpl == "" {
|
if urlTmpl == "" {
|
||||||
url = login.InitPasswordLink(http_utils.ComposedOrigin(ctx), user.ID, code, user.ResourceOwner)
|
url = login.InitPasswordLink(http_utils.ComposedOrigin(ctx), user.ID, code, user.ResourceOwner, authRequestID)
|
||||||
} else {
|
} else {
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
if err := domain.RenderConfirmURLTemplate(&buf, urlTmpl, user.ID, code, user.ResourceOwner); err != nil {
|
if err := domain.RenderConfirmURLTemplate(&buf, urlTmpl, user.ID, code, user.ResourceOwner); err != nil {
|
||||||
|
@ -157,6 +157,8 @@ type HumanRegisteredEvent struct {
|
|||||||
Secret *crypto.CryptoValue `json:"secret,omitempty"` // legacy
|
Secret *crypto.CryptoValue `json:"secret,omitempty"` // legacy
|
||||||
EncodedHash string `json:"encodedHash,omitempty"`
|
EncodedHash string `json:"encodedHash,omitempty"`
|
||||||
ChangeRequired bool `json:"changeRequired,omitempty"`
|
ChangeRequired bool `json:"changeRequired,omitempty"`
|
||||||
|
|
||||||
|
UserAgentID string `json:"userAgentID,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *HumanRegisteredEvent) Payload() interface{} {
|
func (e *HumanRegisteredEvent) Payload() interface{} {
|
||||||
@ -208,6 +210,7 @@ func NewHumanRegisteredEvent(
|
|||||||
gender domain.Gender,
|
gender domain.Gender,
|
||||||
emailAddress domain.EmailAddress,
|
emailAddress domain.EmailAddress,
|
||||||
userLoginMustBeDomain bool,
|
userLoginMustBeDomain bool,
|
||||||
|
userAgentID string,
|
||||||
) *HumanRegisteredEvent {
|
) *HumanRegisteredEvent {
|
||||||
return &HumanRegisteredEvent{
|
return &HumanRegisteredEvent{
|
||||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
@ -224,6 +227,7 @@ func NewHumanRegisteredEvent(
|
|||||||
Gender: gender,
|
Gender: gender,
|
||||||
EmailAddress: emailAddress,
|
EmailAddress: emailAddress,
|
||||||
userLoginMustBeDomain: userLoginMustBeDomain,
|
userLoginMustBeDomain: userLoginMustBeDomain,
|
||||||
|
UserAgentID: userAgentID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,6 +248,7 @@ type HumanInitialCodeAddedEvent struct {
|
|||||||
Code *crypto.CryptoValue `json:"code,omitempty"`
|
Code *crypto.CryptoValue `json:"code,omitempty"`
|
||||||
Expiry time.Duration `json:"expiry,omitempty"`
|
Expiry time.Duration `json:"expiry,omitempty"`
|
||||||
TriggeredAtOrigin string `json:"triggerOrigin,omitempty"`
|
TriggeredAtOrigin string `json:"triggerOrigin,omitempty"`
|
||||||
|
AuthRequestID string `json:"authRequestID,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *HumanInitialCodeAddedEvent) Payload() interface{} {
|
func (e *HumanInitialCodeAddedEvent) Payload() interface{} {
|
||||||
@ -263,6 +268,7 @@ func NewHumanInitialCodeAddedEvent(
|
|||||||
aggregate *eventstore.Aggregate,
|
aggregate *eventstore.Aggregate,
|
||||||
code *crypto.CryptoValue,
|
code *crypto.CryptoValue,
|
||||||
expiry time.Duration,
|
expiry time.Duration,
|
||||||
|
authRequestID string,
|
||||||
) *HumanInitialCodeAddedEvent {
|
) *HumanInitialCodeAddedEvent {
|
||||||
return &HumanInitialCodeAddedEvent{
|
return &HumanInitialCodeAddedEvent{
|
||||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
@ -273,6 +279,7 @@ func NewHumanInitialCodeAddedEvent(
|
|||||||
Code: code,
|
Code: code,
|
||||||
Expiry: expiry,
|
Expiry: expiry,
|
||||||
TriggeredAtOrigin: http.ComposedOrigin(ctx),
|
TriggeredAtOrigin: http.ComposedOrigin(ctx),
|
||||||
|
AuthRequestID: authRequestID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +126,8 @@ type HumanEmailCodeAddedEvent struct {
|
|||||||
URLTemplate string `json:"url_template,omitempty"`
|
URLTemplate string `json:"url_template,omitempty"`
|
||||||
CodeReturned bool `json:"code_returned,omitempty"`
|
CodeReturned bool `json:"code_returned,omitempty"`
|
||||||
TriggeredAtOrigin string `json:"triggerOrigin,omitempty"`
|
TriggeredAtOrigin string `json:"triggerOrigin,omitempty"`
|
||||||
|
// AuthRequest is only used in V1 Login UI
|
||||||
|
AuthRequestID string `json:"authRequestID,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *HumanEmailCodeAddedEvent) Payload() interface{} {
|
func (e *HumanEmailCodeAddedEvent) Payload() interface{} {
|
||||||
@ -145,8 +147,19 @@ func NewHumanEmailCodeAddedEvent(
|
|||||||
aggregate *eventstore.Aggregate,
|
aggregate *eventstore.Aggregate,
|
||||||
code *crypto.CryptoValue,
|
code *crypto.CryptoValue,
|
||||||
expiry time.Duration,
|
expiry time.Duration,
|
||||||
|
authRequestID string,
|
||||||
) *HumanEmailCodeAddedEvent {
|
) *HumanEmailCodeAddedEvent {
|
||||||
return NewHumanEmailCodeAddedEventV2(ctx, aggregate, code, expiry, "", false)
|
return &HumanEmailCodeAddedEvent{
|
||||||
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
|
ctx,
|
||||||
|
aggregate,
|
||||||
|
HumanEmailCodeAddedType,
|
||||||
|
),
|
||||||
|
Code: code,
|
||||||
|
Expiry: expiry,
|
||||||
|
TriggeredAtOrigin: http.ComposedOrigin(ctx),
|
||||||
|
AuthRequestID: authRequestID,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHumanEmailCodeAddedEventV2(
|
func NewHumanEmailCodeAddedEventV2(
|
||||||
|
@ -87,6 +87,8 @@ type HumanPasswordCodeAddedEvent struct {
|
|||||||
URLTemplate string `json:"url_template,omitempty"`
|
URLTemplate string `json:"url_template,omitempty"`
|
||||||
CodeReturned bool `json:"code_returned,omitempty"`
|
CodeReturned bool `json:"code_returned,omitempty"`
|
||||||
TriggeredAtOrigin string `json:"triggerOrigin,omitempty"`
|
TriggeredAtOrigin string `json:"triggerOrigin,omitempty"`
|
||||||
|
// AuthRequest is only used in V1 Login UI
|
||||||
|
AuthRequestID string `json:"authRequestID,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *HumanPasswordCodeAddedEvent) Payload() interface{} {
|
func (e *HumanPasswordCodeAddedEvent) Payload() interface{} {
|
||||||
@ -107,8 +109,20 @@ func NewHumanPasswordCodeAddedEvent(
|
|||||||
code *crypto.CryptoValue,
|
code *crypto.CryptoValue,
|
||||||
expiry time.Duration,
|
expiry time.Duration,
|
||||||
notificationType domain.NotificationType,
|
notificationType domain.NotificationType,
|
||||||
|
authRequestID string,
|
||||||
) *HumanPasswordCodeAddedEvent {
|
) *HumanPasswordCodeAddedEvent {
|
||||||
return NewHumanPasswordCodeAddedEventV2(ctx, aggregate, code, expiry, notificationType, "", false)
|
return &HumanPasswordCodeAddedEvent{
|
||||||
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
|
ctx,
|
||||||
|
aggregate,
|
||||||
|
HumanPasswordCodeAddedType,
|
||||||
|
),
|
||||||
|
Code: code,
|
||||||
|
Expiry: expiry,
|
||||||
|
NotificationType: notificationType,
|
||||||
|
TriggeredAtOrigin: http.ComposedOrigin(ctx),
|
||||||
|
AuthRequestID: authRequestID,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHumanPasswordCodeAddedEventV2(
|
func NewHumanPasswordCodeAddedEventV2(
|
||||||
|
@ -111,6 +111,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: Машинният ключ не е намерен
|
NotFound: Машинният ключ не е намерен
|
||||||
AlreadyExisting: Машинният ключ вече съществува
|
AlreadyExisting: Машинният ключ вече съществува
|
||||||
|
Invalid: Публичният ключ не е валиден RSA публичен ключ във формат PKIX с PEM кодиране
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: Тайната не съществува
|
NotExisting: Тайната не съществува
|
||||||
Invalid: Тайната е невалидна
|
Invalid: Тайната е невалидна
|
||||||
|
@ -109,6 +109,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: Klíč stroje nenalezen
|
NotFound: Klíč stroje nenalezen
|
||||||
AlreadyExisting: Klíč stroje již existuje
|
AlreadyExisting: Klíč stroje již existuje
|
||||||
|
Invalid: Veřejný klíč není platný veřejný klíč RSA ve formátu PKIX s kódováním PEM
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: Tajemství neexistuje
|
NotExisting: Tajemství neexistuje
|
||||||
Invalid: Tajemství je neplatné
|
Invalid: Tajemství je neplatné
|
||||||
|
@ -109,6 +109,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: Maschinen Schlüssel nicht gefunden
|
NotFound: Maschinen Schlüssel nicht gefunden
|
||||||
AlreadyExisting: Machine Schlüssel exisiert bereits
|
AlreadyExisting: Machine Schlüssel exisiert bereits
|
||||||
|
Invalid: Der öffentliche Schlüssel ist kein gültiger öffentlicher RSA-Schlüssel im PKIX-Format mit PEM-Kodierung
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: Secret existiert nicht
|
NotExisting: Secret existiert nicht
|
||||||
Invalid: Secret ist ungültig
|
Invalid: Secret ist ungültig
|
||||||
|
@ -109,6 +109,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: Machine key not found
|
NotFound: Machine key not found
|
||||||
AlreadyExisting: Machine key already existing
|
AlreadyExisting: Machine key already existing
|
||||||
|
Invalid: Public key is not a valid RSA public key in PKIX format with PEM encoding
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: Secret doesn't exist
|
NotExisting: Secret doesn't exist
|
||||||
Invalid: Secret is invalid
|
Invalid: Secret is invalid
|
||||||
|
@ -109,6 +109,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: Clave de máquina no encontrada
|
NotFound: Clave de máquina no encontrada
|
||||||
AlreadyExisting: La clave de máquina ya existe
|
AlreadyExisting: La clave de máquina ya existe
|
||||||
|
Invalid: La clave pública no es una clave pública RSA válida en formato PKIX con codificación PEM
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: El secreto no existe
|
NotExisting: El secreto no existe
|
||||||
Invalid: El secret no es válido
|
Invalid: El secret no es válido
|
||||||
|
@ -109,6 +109,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: Clé de la machine non trouvée
|
NotFound: Clé de la machine non trouvée
|
||||||
AlreadyExisting: Clé de la machine déjà existante
|
AlreadyExisting: Clé de la machine déjà existante
|
||||||
|
Invalid: La clé publique n'est pas une clé publique RSA valide au format PKIX avec encodage PEM
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: Secret n'existe pas
|
NotExisting: Secret n'existe pas
|
||||||
Invalid: Secret n'est pas valide
|
Invalid: Secret n'est pas valide
|
||||||
|
@ -109,6 +109,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: Chiave macchina non trovato
|
NotFound: Chiave macchina non trovato
|
||||||
AlreadyExisting: Chiave macchina già esistente
|
AlreadyExisting: Chiave macchina già esistente
|
||||||
|
Invalid: La chiave pubblica non è una chiave pubblica RSA valida in formato PKIX con codifica PEM
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: Secret non esiste
|
NotExisting: Secret non esiste
|
||||||
Invalid: Secret non è valido
|
Invalid: Secret non è valido
|
||||||
|
@ -102,6 +102,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: マシーンキーが見つかりません
|
NotFound: マシーンキーが見つかりません
|
||||||
AlreadyExisting: すでに存在しているマシーンキーです
|
AlreadyExisting: すでに存在しているマシーンキーです
|
||||||
|
Invalid: 公開キーは、PEM エンコードを使用した PKIX 形式の有効な RSA 公開キーではありません
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: シークレットは存在しません
|
NotExisting: シークレットは存在しません
|
||||||
Invalid: 無効なシークレットです
|
Invalid: 無効なシークレットです
|
||||||
|
@ -109,6 +109,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: Machine key не е пронајден
|
NotFound: Machine key не е пронајден
|
||||||
AlreadyExisting: Machine key веќе постои
|
AlreadyExisting: Machine key веќе постои
|
||||||
|
Invalid: Јавниот клуч не е валиден јавен клуч RSA во формат PKIX со PEM кодирање
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: Тајната не постои
|
NotExisting: Тајната не постои
|
||||||
Invalid: Тајната е невалидна
|
Invalid: Тајната е невалидна
|
||||||
|
@ -108,6 +108,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: Machine sleutel niet gevonden
|
NotFound: Machine sleutel niet gevonden
|
||||||
AlreadyExisting: Machine sleutel al bestaand
|
AlreadyExisting: Machine sleutel al bestaand
|
||||||
|
Invalid: De openbare sleutel is geen geldige openbare RSA-sleutel in PKIX-indeling met PEM-codering
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: Geheim bestaat niet
|
NotExisting: Geheim bestaat niet
|
||||||
Invalid: Geheim is ongeldig
|
Invalid: Geheim is ongeldig
|
||||||
|
@ -109,6 +109,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: Klucz maszyny nie znaleziony
|
NotFound: Klucz maszyny nie znaleziony
|
||||||
AlreadyExisting: Klucz maszyny już istnieje
|
AlreadyExisting: Klucz maszyny już istnieje
|
||||||
|
Invalid: Klucz publiczny nie jest prawidłowym kluczem publicznym RSA w formacie PKIX z kodowaniem PEM
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: Sekret nie istnieje
|
NotExisting: Sekret nie istnieje
|
||||||
Invalid: Sekret jest nieprawidłowy
|
Invalid: Sekret jest nieprawidłowy
|
||||||
|
@ -109,6 +109,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: Chave de máquina não encontrada
|
NotFound: Chave de máquina não encontrada
|
||||||
AlreadyExisting: Chave de máquina já existe
|
AlreadyExisting: Chave de máquina já existe
|
||||||
|
Invalid: A chave pública não é uma chave pública RSA válida no formato PKIX com codificação PEM
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: Segredo não existe
|
NotExisting: Segredo não existe
|
||||||
Invalid: Segredo é inválido
|
Invalid: Segredo é inválido
|
||||||
|
@ -110,6 +110,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: Машинный ключ не найден
|
NotFound: Машинный ключ не найден
|
||||||
AlreadyExisting: Машинный ключ уже существует
|
AlreadyExisting: Машинный ключ уже существует
|
||||||
|
Invalid: Открытый ключ не является допустимым открытым ключом RSA в формате PKIX с кодировкой PEM
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: Ключ не существует
|
NotExisting: Ключ не существует
|
||||||
Invalid: Ключ недействителен
|
Invalid: Ключ недействителен
|
||||||
|
@ -109,6 +109,7 @@ Errors:
|
|||||||
Key:
|
Key:
|
||||||
NotFound: 未找到机器密钥
|
NotFound: 未找到机器密钥
|
||||||
AlreadyExisting: 已有的机器钥匙
|
AlreadyExisting: 已有的机器钥匙
|
||||||
|
Invalid: 公钥不是采用 PEM 编码的 PKIX 格式的有效 RSA 公钥
|
||||||
Secret:
|
Secret:
|
||||||
NotExisting: 秘密并不存在
|
NotExisting: 秘密并不存在
|
||||||
Invalid: 秘密是无效的
|
Invalid: 秘密是无效的
|
||||||
|
33
internal/v2/database/filter.go
Normal file
33
internal/v2/database/filter.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
type Condition interface {
|
||||||
|
Write(stmt *Statement, columnName string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Filter[C compare, V value] struct {
|
||||||
|
comp C
|
||||||
|
value V
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Filter[C, V]) Write(stmt *Statement, columnName string) {
|
||||||
|
prepareWrite(stmt, columnName, f.comp)
|
||||||
|
stmt.WriteArg(f.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareWrite[C compare](stmt *Statement, columnName string, comp C) {
|
||||||
|
stmt.WriteString(columnName)
|
||||||
|
stmt.WriteRune(' ')
|
||||||
|
stmt.WriteString(comp.String())
|
||||||
|
stmt.WriteRune(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
type compare interface {
|
||||||
|
numberCompare | textCompare | listCompare
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type value interface {
|
||||||
|
number | text
|
||||||
|
// TODO: condition must know if it's args are named parameters or not
|
||||||
|
// number | text | placeholder
|
||||||
|
}
|
57
internal/v2/database/list_filter.go
Normal file
57
internal/v2/database/list_filter.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import "github.com/zitadel/logging"
|
||||||
|
|
||||||
|
type ListFilter[V value] struct {
|
||||||
|
comp listCompare
|
||||||
|
list []V
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListEquals[V value](list ...V) *ListFilter[V] {
|
||||||
|
return newListFilter[V](listEqual, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListContains[V value](list ...V) *ListFilter[V] {
|
||||||
|
return newListFilter[V](listContain, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListNotContains[V value](list ...V) *ListFilter[V] {
|
||||||
|
return newListFilter[V](listNotContain, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newListFilter[V value](comp listCompare, list []V) *ListFilter[V] {
|
||||||
|
return &ListFilter[V]{
|
||||||
|
comp: comp,
|
||||||
|
list: list,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f ListFilter[V]) Write(stmt *Statement, columnName string) {
|
||||||
|
if len(f.list) == 0 {
|
||||||
|
logging.WithFields("column", columnName).Debug("skip list filter because no entries defined")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f.comp == listNotContain {
|
||||||
|
stmt.WriteString("NOT(")
|
||||||
|
}
|
||||||
|
stmt.WriteString(columnName)
|
||||||
|
stmt.WriteString(" = ")
|
||||||
|
if f.comp != listEqual {
|
||||||
|
stmt.WriteString("ANY(")
|
||||||
|
}
|
||||||
|
stmt.WriteArg(f.list)
|
||||||
|
if f.comp != listEqual {
|
||||||
|
stmt.WriteString(")")
|
||||||
|
}
|
||||||
|
if f.comp == listNotContain {
|
||||||
|
stmt.WriteRune(')')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type listCompare uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
listEqual listCompare = iota
|
||||||
|
listContain
|
||||||
|
listNotContain
|
||||||
|
)
|
122
internal/v2/database/list_filter_test.go
Normal file
122
internal/v2/database/list_filter_test.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewListConstructors(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
constructor func(t ...string) *ListFilter[string]
|
||||||
|
t []string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *ListFilter[string]
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "NewListEquals",
|
||||||
|
args: args{
|
||||||
|
constructor: NewListEquals[string],
|
||||||
|
t: []string{"as", "df"},
|
||||||
|
},
|
||||||
|
want: &ListFilter[string]{
|
||||||
|
comp: listEqual,
|
||||||
|
list: []string{"as", "df"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NewListContains",
|
||||||
|
args: args{
|
||||||
|
constructor: NewListContains[string],
|
||||||
|
t: []string{"as", "df"},
|
||||||
|
},
|
||||||
|
want: &ListFilter[string]{
|
||||||
|
comp: listContain,
|
||||||
|
list: []string{"as", "df"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NewListNotContains",
|
||||||
|
args: args{
|
||||||
|
constructor: NewListNotContains[string],
|
||||||
|
t: []string{"as", "df"},
|
||||||
|
},
|
||||||
|
want: &ListFilter[string]{
|
||||||
|
comp: listNotContain,
|
||||||
|
list: []string{"as", "df"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := tt.args.constructor(tt.args.t...); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("number constructor = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewListConditionWrite(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
constructor func(t ...string) *ListFilter[string]
|
||||||
|
t []string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want wantQuery
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ListEquals",
|
||||||
|
args: args{
|
||||||
|
constructor: NewListEquals[string],
|
||||||
|
t: []string{"as", "df"},
|
||||||
|
},
|
||||||
|
want: wantQuery{
|
||||||
|
query: "test = $1",
|
||||||
|
args: []any{[]string{"as", "df"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ListContains",
|
||||||
|
args: args{
|
||||||
|
constructor: NewListContains[string],
|
||||||
|
t: []string{"as", "df"},
|
||||||
|
},
|
||||||
|
want: wantQuery{
|
||||||
|
query: "test = ANY($1)",
|
||||||
|
args: []any{[]string{"as", "df"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ListNotContains",
|
||||||
|
args: args{
|
||||||
|
constructor: NewListNotContains[string],
|
||||||
|
t: []string{"as", "df"},
|
||||||
|
},
|
||||||
|
want: wantQuery{
|
||||||
|
query: "NOT(test = ANY($1))",
|
||||||
|
args: []any{[]string{"as", "df"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty list",
|
||||||
|
args: args{
|
||||||
|
constructor: NewListNotContains[string],
|
||||||
|
},
|
||||||
|
want: wantQuery{
|
||||||
|
query: "",
|
||||||
|
args: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
var stmt Statement
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tt.args.constructor(tt.args.t...).Write(&stmt, "test")
|
||||||
|
assertQuery(t, &stmt, tt.want)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
139
internal/v2/database/mock/sql_mock.go
Normal file
139
internal/v2/database/mock/sql_mock.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SQLMock struct {
|
||||||
|
DB *sql.DB
|
||||||
|
mock sqlmock.Sqlmock
|
||||||
|
}
|
||||||
|
|
||||||
|
type Expectation func(m sqlmock.Sqlmock)
|
||||||
|
|
||||||
|
func NewSQLMock(t *testing.T, expectations ...Expectation) *SQLMock {
|
||||||
|
db, mock, err := sqlmock.New(
|
||||||
|
sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual),
|
||||||
|
sqlmock.ValueConverterOption(new(TypeConverter)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("create mock failed", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, expectation := range expectations {
|
||||||
|
expectation(mock)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &SQLMock{
|
||||||
|
DB: db,
|
||||||
|
mock: mock,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SQLMock) Assert(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
if err := m.mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("expectations not met: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.DB.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExpectBegin(err error) Expectation {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
e := m.ExpectBegin()
|
||||||
|
if err != nil {
|
||||||
|
e.WillReturnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExpectCommit(err error) Expectation {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
e := m.ExpectCommit()
|
||||||
|
if err != nil {
|
||||||
|
e.WillReturnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecOpt func(e *sqlmock.ExpectedExec) *sqlmock.ExpectedExec
|
||||||
|
|
||||||
|
func WithExecArgs(args ...driver.Value) ExecOpt {
|
||||||
|
return func(e *sqlmock.ExpectedExec) *sqlmock.ExpectedExec {
|
||||||
|
return e.WithArgs(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithExecErr(err error) ExecOpt {
|
||||||
|
return func(e *sqlmock.ExpectedExec) *sqlmock.ExpectedExec {
|
||||||
|
return e.WillReturnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithExecNoRowsAffected() ExecOpt {
|
||||||
|
return func(e *sqlmock.ExpectedExec) *sqlmock.ExpectedExec {
|
||||||
|
return e.WillReturnResult(driver.ResultNoRows)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithExecRowsAffected(affected driver.RowsAffected) ExecOpt {
|
||||||
|
return func(e *sqlmock.ExpectedExec) *sqlmock.ExpectedExec {
|
||||||
|
return e.WillReturnResult(affected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExpectExec(stmt string, opts ...ExecOpt) Expectation {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
e := m.ExpectExec(stmt)
|
||||||
|
for _, opt := range opts {
|
||||||
|
e = opt(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryOpt func(m sqlmock.Sqlmock, e *sqlmock.ExpectedQuery) *sqlmock.ExpectedQuery
|
||||||
|
|
||||||
|
func WithQueryArgs(args ...driver.Value) QueryOpt {
|
||||||
|
return func(_ sqlmock.Sqlmock, e *sqlmock.ExpectedQuery) *sqlmock.ExpectedQuery {
|
||||||
|
return e.WithArgs(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithQueryErr(err error) QueryOpt {
|
||||||
|
return func(_ sqlmock.Sqlmock, e *sqlmock.ExpectedQuery) *sqlmock.ExpectedQuery {
|
||||||
|
return e.WillReturnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithQueryResult(columns []string, rows [][]driver.Value) QueryOpt {
|
||||||
|
return func(m sqlmock.Sqlmock, e *sqlmock.ExpectedQuery) *sqlmock.ExpectedQuery {
|
||||||
|
mockedRows := m.NewRows(columns)
|
||||||
|
for _, row := range rows {
|
||||||
|
mockedRows = mockedRows.AddRow(row...)
|
||||||
|
}
|
||||||
|
return e.WillReturnRows(mockedRows)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExpectQuery(stmt string, opts ...QueryOpt) Expectation {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
e := m.ExpectQuery(stmt)
|
||||||
|
for _, opt := range opts {
|
||||||
|
e = opt(m, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnyType[T interface{}] struct{}
|
||||||
|
|
||||||
|
// Match satisfies sqlmock.Argument interface
|
||||||
|
func (a AnyType[T]) Match(v driver.Value) bool {
|
||||||
|
return reflect.TypeOf(new(T)).Elem().Kind().String() == reflect.TypeOf(v).Kind().String()
|
||||||
|
}
|
78
internal/v2/database/mock/type_converter.go
Normal file
78
internal/v2/database/mock/type_converter.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ driver.ValueConverter = (*TypeConverter)(nil)
|
||||||
|
|
||||||
|
type TypeConverter struct{}
|
||||||
|
|
||||||
|
// ConvertValue converts a value to a driver Value.
|
||||||
|
func (s TypeConverter) ConvertValue(v any) (driver.Value, error) {
|
||||||
|
if driver.IsValue(v) {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
value := reflect.ValueOf(v)
|
||||||
|
|
||||||
|
if rawMessage, ok := v.(json.RawMessage); ok {
|
||||||
|
return convertBytes(rawMessage), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.Kind() == reflect.Slice {
|
||||||
|
//nolint: exhaustive
|
||||||
|
// only defined types
|
||||||
|
switch value.Type().Elem().Kind() {
|
||||||
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
|
return convertSigned(value), nil
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||||
|
return convertUnsigned(value), nil
|
||||||
|
case reflect.String:
|
||||||
|
return convertText(value), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// converts a text array to valid pgx v5 representation
|
||||||
|
func convertSigned(array reflect.Value) string {
|
||||||
|
slice := make([]string, array.Len())
|
||||||
|
for i := 0; i < array.Len(); i++ {
|
||||||
|
slice[i] = strconv.FormatInt(array.Index(i).Int(), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "{" + strings.Join(slice, ",") + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// converts a text array to valid pgx v5 representation
|
||||||
|
func convertUnsigned(array reflect.Value) string {
|
||||||
|
slice := make([]string, array.Len())
|
||||||
|
for i := 0; i < array.Len(); i++ {
|
||||||
|
slice[i] = strconv.FormatUint(array.Index(i).Uint(), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "{" + strings.Join(slice, ",") + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// converts a text array to valid pgx v5 representation
|
||||||
|
func convertText(array reflect.Value) string {
|
||||||
|
slice := make([]string, array.Len())
|
||||||
|
for i := 0; i < array.Len(); i++ {
|
||||||
|
slice[i] = array.Index(i).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return "{" + strings.Join(slice, ",") + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertBytes(array []byte) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
builder.Grow(hex.EncodedLen(len(array)) + 4)
|
||||||
|
builder.WriteString(`\x`)
|
||||||
|
builder.Write(hex.AppendEncode(nil, array))
|
||||||
|
return builder.String()
|
||||||
|
}
|
100
internal/v2/database/number_filter.go
Normal file
100
internal/v2/database/number_filter.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/zitadel/logging"
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NumberFilter[N number] struct {
|
||||||
|
Filter[numberCompare, N]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNumberEquals[N number](n N) *NumberFilter[N] {
|
||||||
|
return newNumberFilter(numberEqual, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNumberAtLeast[N number](n N) *NumberFilter[N] {
|
||||||
|
return newNumberFilter(numberAtLeast, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNumberAtMost[N number](n N) *NumberFilter[N] {
|
||||||
|
return newNumberFilter(numberAtMost, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNumberGreater[N number](n N) *NumberFilter[N] {
|
||||||
|
return newNumberFilter(numberGreater, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNumberLess[N number](n N) *NumberFilter[N] {
|
||||||
|
return newNumberFilter(numberLess, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNumberUnequal[N number](n N) *NumberFilter[N] {
|
||||||
|
return newNumberFilter(numberUnequal, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNumberFilter[N number](comp numberCompare, n N) *NumberFilter[N] {
|
||||||
|
return &NumberFilter[N]{
|
||||||
|
Filter: Filter[numberCompare, N]{
|
||||||
|
comp: comp,
|
||||||
|
value: n,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumberBetweenFilter combines [AtLeast] and [AtMost] comparisons
|
||||||
|
type NumberBetweenFilter[N number] struct {
|
||||||
|
min, max N
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNumberBetween[N number](min, max N) *NumberBetweenFilter[N] {
|
||||||
|
return &NumberBetweenFilter[N]{
|
||||||
|
min: min,
|
||||||
|
max: max,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f NumberBetweenFilter[N]) Write(stmt *Statement, columnName string) {
|
||||||
|
NewNumberAtLeast[N](f.min).Write(stmt, columnName)
|
||||||
|
stmt.WriteString(" AND ")
|
||||||
|
NewNumberAtMost[N](f.max).Write(stmt, columnName)
|
||||||
|
}
|
||||||
|
|
||||||
|
type numberCompare uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
numberEqual numberCompare = iota
|
||||||
|
numberAtLeast
|
||||||
|
numberAtMost
|
||||||
|
numberGreater
|
||||||
|
numberLess
|
||||||
|
numberUnequal
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c numberCompare) String() string {
|
||||||
|
switch c {
|
||||||
|
case numberEqual:
|
||||||
|
return "="
|
||||||
|
case numberAtLeast:
|
||||||
|
return ">="
|
||||||
|
case numberAtMost:
|
||||||
|
return "<="
|
||||||
|
case numberGreater:
|
||||||
|
return ">"
|
||||||
|
case numberLess:
|
||||||
|
return "<"
|
||||||
|
case numberUnequal:
|
||||||
|
return "<>"
|
||||||
|
default:
|
||||||
|
logging.WithFields("compare", c).Panic("comparison type not implemented")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type number interface {
|
||||||
|
constraints.Integer | constraints.Float | time.Time
|
||||||
|
// TODO: condition must know if it's args are named parameters or not
|
||||||
|
// constraints.Integer | constraints.Float | time.Time | placeholder
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user