mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 16:27:23 +00:00
feat: V2 alpha import and export of organizations (#3798)
* feat(import): add functionality to import data into an instance * feat(import): move import to admin api and additional checks for nil pointer * fix(export): export implementation with filtered members and grants * fix: export and import implementation * fix: add possibility to export hashed passwords with the user * fix(import): import with structure of v1 and v2 * docs: add v1 proto * fix(import): check im imported user is already existing * fix(import): add otp import function * fix(import): add external idps, domains, custom text and messages * fix(import): correct usage of default values from login policy * fix(export): fix renaming of add project function * fix(import): move checks for unit tests * expect filter * fix(import): move checks for unit tests * fix(import): move checks for unit tests * fix(import): produce prerelease from branch * fix(import): correctly use provided user id for machine user imports * fix(import): corrected otp import and added guide for export and import * fix: import verified and primary domains * fix(import): add reading from gcs, s3 and localfile with tracing * fix(import): gcs and s3, file size correction and error logging * Delete docker-compose.yml * fix(import): progress logging and count of resources * fix(import): progress logging and count of resources * log subscription * fix(import): incorporate review * fix(import): incorporate review * docs: add suggestion for import Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com> * fix(import): add verification otp event and handling of deleted but existing users Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: Fabienne <fabienne.gerschwiler@gmail.com> Co-authored-by: Silvan <silvan.reusser@gmail.com> Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com>
This commit is contained in:
parent
d620126aab
commit
bc9a85daf3
@ -3,7 +3,7 @@ module.exports = {
|
||||
{name: 'main'},
|
||||
{name: '1.x.x', range: '1.x.x', channel: '1.x.x'},
|
||||
{name: 'v2-alpha', prerelease: true},
|
||||
{name: 'scheduler', prerelease: true},
|
||||
{name: 'v2-alpha-import', prerelease: true},
|
||||
],
|
||||
plugins: [
|
||||
"@semantic-release/commit-analyzer"
|
||||
|
@ -182,6 +182,9 @@ protoc \
|
||||
-I=/proto/include \
|
||||
--doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,settings.md \
|
||||
${PROTO_PATH}/settings.proto
|
||||
|
||||
protoc \
|
||||
-I=/proto/include \
|
||||
--doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,v1.md \
|
||||
${PROTO_PATH}/v1.proto
|
||||
|
||||
echo "done generating grpc"
|
||||
|
@ -100,7 +100,7 @@ func startZitadel(config *Config, masterKey string) error {
|
||||
return fmt.Errorf("cannot start eventstore for queries: %w", err)
|
||||
}
|
||||
|
||||
queries, err := query.StartQueries(ctx, eventstoreClient, dbClient, config.Projections, keys.OIDC, config.InternalAuthZ.RolePermissionMappings)
|
||||
queries, err := query.StartQueries(ctx, eventstoreClient, dbClient, config.Projections, config.SystemDefaults, keys.IDPConfig, keys.OTP, keys.OIDC, config.InternalAuthZ.RolePermissionMappings)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot start queries: %w", err)
|
||||
}
|
||||
@ -178,7 +178,7 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
|
||||
if err := apis.RegisterServer(ctx, system.CreateServer(commands, queries, adminRepo, config.Database.Database, config.DefaultInstance)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := apis.RegisterServer(ctx, admin.CreateServer(config.Database.Database, commands, queries, adminRepo, config.ExternalSecure, keys.User)); err != nil {
|
||||
if err := apis.RegisterServer(ctx, admin.CreateServer(config.Database.Database, commands, queries, config.SystemDefaults, adminRepo, config.ExternalSecure, keys.User)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := apis.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, keys.User, config.ExternalSecure, config.AuditLogRetention)); err != nil {
|
||||
|
@ -1493,6 +1493,30 @@ failed event. You can find out if it worked on the `failure_count`
|
||||
DELETE: /failedevents/{database}/{view_name}/{failed_sequence}
|
||||
|
||||
|
||||
### ImportData
|
||||
|
||||
> **rpc** ImportData([ImportDataRequest](#importdatarequest))
|
||||
[ImportDataResponse](#importdataresponse)
|
||||
|
||||
Imports data into instance and creates different objects
|
||||
|
||||
|
||||
|
||||
POST: /import
|
||||
|
||||
|
||||
### ExportData
|
||||
|
||||
> **rpc** ExportData([ExportDataRequest](#exportdatarequest))
|
||||
[ExportDataResponse](#exportdataresponse)
|
||||
|
||||
Exports data from instance
|
||||
|
||||
|
||||
|
||||
POST: /export
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1789,6 +1813,49 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### DataOrg
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| org_id | string | - | |
|
||||
| org | zitadel.management.v1.AddOrgRequest | - | |
|
||||
| domain_policy | AddCustomDomainPolicyRequest | - | |
|
||||
| label_policy | zitadel.management.v1.AddCustomLabelPolicyRequest | - | |
|
||||
| lockout_policy | zitadel.management.v1.AddCustomLockoutPolicyRequest | - | |
|
||||
| login_policy | zitadel.management.v1.AddCustomLoginPolicyRequest | - | |
|
||||
| password_complexity_policy | zitadel.management.v1.AddCustomPasswordComplexityPolicyRequest | - | |
|
||||
| privacy_policy | zitadel.management.v1.AddCustomPrivacyPolicyRequest | - | |
|
||||
| projects | repeated zitadel.v1.v1.DataProject | - | |
|
||||
| project_roles | repeated zitadel.management.v1.AddProjectRoleRequest | - | |
|
||||
| api_apps | repeated zitadel.v1.v1.DataAPIApplication | - | |
|
||||
| oidc_apps | repeated zitadel.v1.v1.DataOIDCApplication | - | |
|
||||
| human_users | repeated zitadel.v1.v1.DataHumanUser | - | |
|
||||
| machine_users | repeated zitadel.v1.v1.DataMachineUser | - | |
|
||||
| trigger_actions | repeated zitadel.management.v1.SetTriggerActionsRequest | - | |
|
||||
| actions | repeated zitadel.v1.v1.DataAction | - | |
|
||||
| project_grants | repeated zitadel.v1.v1.DataProjectGrant | - | |
|
||||
| user_grants | repeated zitadel.management.v1.AddUserGrantRequest | - | |
|
||||
| org_members | repeated zitadel.management.v1.AddOrgMemberRequest | - | |
|
||||
| project_members | repeated zitadel.management.v1.AddProjectMemberRequest | - | |
|
||||
| project_grant_members | repeated zitadel.management.v1.AddProjectGrantMemberRequest | - | |
|
||||
| user_metadata | repeated zitadel.management.v1.SetUserMetadataRequest | - | |
|
||||
| login_texts | repeated zitadel.management.v1.SetCustomLoginTextsRequest | - | |
|
||||
| init_messages | repeated zitadel.management.v1.SetCustomInitMessageTextRequest | - | |
|
||||
| password_reset_messages | repeated zitadel.management.v1.SetCustomPasswordResetMessageTextRequest | - | |
|
||||
| verify_email_messages | repeated zitadel.management.v1.SetCustomVerifyEmailMessageTextRequest | - | |
|
||||
| verify_phone_messages | repeated zitadel.management.v1.SetCustomVerifyPhoneMessageTextRequest | - | |
|
||||
| domain_claimed_messages | repeated zitadel.management.v1.SetCustomDomainClaimedMessageTextRequest | - | |
|
||||
| passwordless_registration_messages | repeated zitadel.management.v1.SetCustomPasswordlessRegistrationMessageTextRequest | - | |
|
||||
| oidc_idps | repeated zitadel.v1.v1.DataOIDCIDP | - | |
|
||||
| jwt_idps | repeated zitadel.v1.v1.DataJWTIDP | - | |
|
||||
| user_links | repeated zitadel.idp.v1.IDPUserLink | - | |
|
||||
| domains | repeated zitadel.org.v1.Domain | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DeactivateIDPRequest
|
||||
|
||||
|
||||
@ -1833,6 +1900,76 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### ExportDataRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| org_ids | repeated string | - | |
|
||||
| excluded_org_ids | repeated string | - | |
|
||||
| with_passwords | bool | - | |
|
||||
| with_otp | bool | - | |
|
||||
| response_output | bool | - | |
|
||||
| local_output | ExportDataRequest.LocalOutput | - | |
|
||||
| s3_output | ExportDataRequest.S3Output | - | |
|
||||
| gcs_output | ExportDataRequest.GCSOutput | - | |
|
||||
| timeout | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ExportDataRequest.GCSOutput
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| bucket | string | - | |
|
||||
| serviceaccount_json | string | - | |
|
||||
| path | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ExportDataRequest.LocalOutput
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| path | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ExportDataRequest.S3Output
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| path | string | - | |
|
||||
| endpoint | string | - | |
|
||||
| access_key_id | string | - | |
|
||||
| secret_access_key | string | - | |
|
||||
| ssl | bool | - | |
|
||||
| bucket | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ExportDataResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| orgs | repeated DataOrg | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### FailedEvent
|
||||
|
||||
|
||||
@ -2603,6 +2740,218 @@ This is an empty response
|
||||
|
||||
|
||||
|
||||
### ImportDataError
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| type | string | - | |
|
||||
| id | string | - | |
|
||||
| message | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataOrg
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| orgs | repeated DataOrg | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.data_orgs | ImportDataOrg | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.data_orgsv1 | zitadel.v1.v1.ImportDataOrg | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.data_orgs_local | ImportDataRequest.LocalInput | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.data_orgsv1_local | ImportDataRequest.LocalInput | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.data_orgs_s3 | ImportDataRequest.S3Input | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.data_orgsv1_s3 | ImportDataRequest.S3Input | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.data_orgs_gcs | ImportDataRequest.GCSInput | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) data.data_orgsv1_gcs | ImportDataRequest.GCSInput | - | |
|
||||
| timeout | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataRequest.GCSInput
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| bucket | string | - | |
|
||||
| serviceaccount_json | string | - | |
|
||||
| path | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataRequest.LocalInput
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| path | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataRequest.S3Input
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| path | string | - | |
|
||||
| endpoint | string | - | |
|
||||
| access_key_id | string | - | |
|
||||
| secret_access_key | string | - | |
|
||||
| ssl | bool | - | |
|
||||
| bucket | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| errors | repeated ImportDataError | - | |
|
||||
| success | ImportDataSuccess | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataSuccess
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| orgs | repeated ImportDataSuccessOrg | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataSuccessOrg
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| org_id | string | - | |
|
||||
| project_ids | repeated string | - | |
|
||||
| project_roles | repeated string | - | |
|
||||
| oidc_app_ids | repeated string | - | |
|
||||
| api_app_ids | repeated string | - | |
|
||||
| human_user_ids | repeated string | - | |
|
||||
| machine_user_ids | repeated string | - | |
|
||||
| action_ids | repeated string | - | |
|
||||
| trigger_actions | repeated zitadel.management.v1.SetTriggerActionsRequest | - | |
|
||||
| project_grants | repeated ImportDataSuccessProjectGrant | - | |
|
||||
| user_grants | repeated ImportDataSuccessUserGrant | - | |
|
||||
| org_members | repeated string | - | |
|
||||
| project_members | repeated ImportDataSuccessProjectMember | - | |
|
||||
| project_grant_members | repeated ImportDataSuccessProjectGrantMember | - | |
|
||||
| oidc_ipds | repeated string | - | |
|
||||
| jwt_idps | repeated string | - | |
|
||||
| idp_links | repeated string | - | |
|
||||
| user_links | repeated ImportDataSuccessUserLinks | - | |
|
||||
| user_metadata | repeated ImportDataSuccessUserMetadata | - | |
|
||||
| domains | repeated string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataSuccessProjectGrant
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| grant_id | string | - | |
|
||||
| project_id | string | - | |
|
||||
| org_id | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataSuccessProjectGrantMember
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| project_id | string | - | |
|
||||
| grant_id | string | - | |
|
||||
| user_id | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataSuccessProjectMember
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| project_id | string | - | |
|
||||
| user_id | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataSuccessUserGrant
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| project_id | string | - | |
|
||||
| user_id | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataSuccessUserLinks
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| user_id | string | - | |
|
||||
| external_user_id | string | - | |
|
||||
| display_name | string | - | |
|
||||
| idp_id | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataSuccessUserMetadata
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| user_id | string | - | |
|
||||
| key | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### IsOrgUniqueRequest
|
||||
if name or domain is already in use, org is not unique
|
||||
at least one argument has to be provided
|
||||
|
@ -5176,8 +5176,10 @@ This is an empty response
|
||||
| email | ImportHumanUserRequest.Email | - | message.required: true<br /> |
|
||||
| phone | ImportHumanUserRequest.Phone | - | |
|
||||
| password | string | - | |
|
||||
| hashed_password | ImportHumanUserRequest.HashedPassword | - | |
|
||||
| password_change_required | bool | - | |
|
||||
| request_passwordless_registration | bool | - | |
|
||||
| otp_code | string | - | |
|
||||
|
||||
|
||||
|
||||
@ -5194,6 +5196,18 @@ This is an empty response
|
||||
|
||||
|
||||
|
||||
### ImportHumanUserRequest.HashedPassword
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| value | string | - | |
|
||||
| algorithm | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportHumanUserRequest.Phone
|
||||
|
||||
|
||||
|
261
docs/docs/apis/proto/v1.md
Normal file
261
docs/docs/apis/proto/v1.md
Normal file
@ -0,0 +1,261 @@
|
||||
---
|
||||
title: zitadel/v1.proto
|
||||
---
|
||||
> This document reflects the state from API 1.0 (available from 20.04.2021)
|
||||
|
||||
|
||||
|
||||
|
||||
## Messages
|
||||
|
||||
|
||||
### AddCustomOrgIAMPolicyRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| org_id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| user_login_must_be_domain | bool | the username has to end with the domain of it's organisation (uniqueness is organisation based) | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DataAPIApplication
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| app_id | string | - | |
|
||||
| app | zitadel.management.v1.AddAPIAppRequest | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DataAction
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| action_id | string | - | |
|
||||
| action | zitadel.management.v1.CreateActionRequest | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DataHumanUser
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| user_id | string | - | |
|
||||
| user | zitadel.management.v1.ImportHumanUserRequest | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DataJWTIDP
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| idp_id | string | - | |
|
||||
| idp | zitadel.management.v1.AddOrgJWTIDPRequest | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DataMachineUser
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| user_id | string | - | |
|
||||
| user | zitadel.management.v1.AddMachineUserRequest | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DataOIDCApplication
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| app_id | string | - | |
|
||||
| app | zitadel.management.v1.AddOIDCAppRequest | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DataOIDCIDP
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| idp_id | string | - | |
|
||||
| idp | zitadel.management.v1.AddOrgOIDCIDPRequest | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DataOrg
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| org_id | string | - | |
|
||||
| org | zitadel.management.v1.AddOrgRequest | - | |
|
||||
| iam_policy | AddCustomOrgIAMPolicyRequest | - | |
|
||||
| label_policy | zitadel.management.v1.AddCustomLabelPolicyRequest | - | |
|
||||
| lockout_policy | zitadel.management.v1.AddCustomLockoutPolicyRequest | - | |
|
||||
| login_policy | zitadel.management.v1.AddCustomLoginPolicyRequest | - | |
|
||||
| password_complexity_policy | zitadel.management.v1.AddCustomPasswordComplexityPolicyRequest | - | |
|
||||
| privacy_policy | zitadel.management.v1.AddCustomPrivacyPolicyRequest | - | |
|
||||
| projects | repeated DataProject | - | |
|
||||
| project_roles | repeated zitadel.management.v1.AddProjectRoleRequest | - | |
|
||||
| api_apps | repeated DataAPIApplication | - | |
|
||||
| oidc_apps | repeated DataOIDCApplication | - | |
|
||||
| human_users | repeated DataHumanUser | - | |
|
||||
| machine_users | repeated DataMachineUser | - | |
|
||||
| trigger_actions | repeated zitadel.management.v1.SetTriggerActionsRequest | - | |
|
||||
| actions | repeated DataAction | - | |
|
||||
| project_grants | repeated DataProjectGrant | - | |
|
||||
| user_grants | repeated zitadel.management.v1.AddUserGrantRequest | - | |
|
||||
| org_members | repeated zitadel.management.v1.AddOrgMemberRequest | - | |
|
||||
| project_members | repeated zitadel.management.v1.AddProjectMemberRequest | - | |
|
||||
| project_grant_members | repeated zitadel.management.v1.AddProjectGrantMemberRequest | - | |
|
||||
| user_metadata | repeated zitadel.management.v1.SetUserMetadataRequest | - | |
|
||||
| login_texts | repeated zitadel.management.v1.SetCustomLoginTextsRequest | - | |
|
||||
| init_messages | repeated zitadel.management.v1.SetCustomInitMessageTextRequest | - | |
|
||||
| password_reset_messages | repeated zitadel.management.v1.SetCustomPasswordResetMessageTextRequest | - | |
|
||||
| verify_email_messages | repeated zitadel.management.v1.SetCustomVerifyEmailMessageTextRequest | - | |
|
||||
| verify_phone_messages | repeated zitadel.management.v1.SetCustomVerifyPhoneMessageTextRequest | - | |
|
||||
| domain_claimed_messages | repeated zitadel.management.v1.SetCustomDomainClaimedMessageTextRequest | - | |
|
||||
| passwordless_registration_messages | repeated zitadel.management.v1.SetCustomPasswordlessRegistrationMessageTextRequest | - | |
|
||||
| oidc_idps | repeated DataOIDCIDP | - | |
|
||||
| jwt_idps | repeated DataJWTIDP | - | |
|
||||
| second_factors | repeated zitadel.management.v1.AddSecondFactorToLoginPolicyRequest | - | |
|
||||
| multi_factors | repeated zitadel.management.v1.AddMultiFactorToLoginPolicyRequest | - | |
|
||||
| idps | repeated zitadel.management.v1.AddIDPToLoginPolicyRequest | - | |
|
||||
| user_links | repeated zitadel.idp.v1.IDPUserLink | - | |
|
||||
| domains | repeated zitadel.org.v1.Domain | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DataProject
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| project_id | string | - | |
|
||||
| project | zitadel.management.v1.AddProjectRequest | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DataProjectGrant
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| grant_id | string | - | |
|
||||
| project_grant | zitadel.management.v1.AddProjectGrantRequest | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ExportHumanUser
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| user_name | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| profile | ExportHumanUser.Profile | - | message.required: true<br /> |
|
||||
| email | ExportHumanUser.Email | - | message.required: true<br /> |
|
||||
| phone | ExportHumanUser.Phone | - | |
|
||||
| password | string | - | |
|
||||
| hashed_password | ExportHumanUser.HashedPassword | - | |
|
||||
| password_change_required | bool | - | |
|
||||
| request_passwordless_registration | bool | - | |
|
||||
| otp_code | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ExportHumanUser.Email
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| email | string | TODO: check if no value is allowed | string.email: true<br /> |
|
||||
| is_email_verified | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ExportHumanUser.HashedPassword
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| value | string | - | |
|
||||
| algorithm | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ExportHumanUser.Phone
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| phone | string | has to be a global number | string.min_len: 1<br /> string.max_len: 50<br /> string.prefix: +<br /> |
|
||||
| is_phone_verified | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ExportHumanUser.Profile
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| first_name | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| last_name | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| nick_name | string | - | string.max_len: 200<br /> |
|
||||
| display_name | string | - | string.max_len: 200<br /> |
|
||||
| preferred_language | string | - | string.max_len: 10<br /> |
|
||||
| gender | zitadel.user.v1.Gender | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ImportDataOrg
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| orgs | repeated DataOrg | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
129
docs/docs/guides/api/export-and-import.md
Normal file
129
docs/docs/guides/api/export-and-import.md
Normal file
@ -0,0 +1,129 @@
|
||||
---
|
||||
title: Export and import with ZITADEL
|
||||
---
|
||||
|
||||
## Export from V1 to Import into V2
|
||||
|
||||
To migrate from ZITADEL V1 to V2 the API provides you with a possibility to export all resources which are under your organizations.
|
||||
Currently, this doesn't include the following points:
|
||||
|
||||
* Global policies
|
||||
* IAM members
|
||||
* Global IDPs
|
||||
* Global second/multi factors
|
||||
* Machine keys
|
||||
* PAT's
|
||||
* Application keys
|
||||
|
||||
Which results in that if you want to import, and you have no defined organization-specific custom policies, the experience for your users will not be exactly like in your old instance.
|
||||
```suggestion
|
||||
|
||||
::note Note that the ressources will be migrated without the event stream. This means that you will not have the audit trail for the imported objects.
|
||||
|
||||
*** With this export and import the current audit trail is not included, the resources will be newly created ***
|
||||
|
||||
### Export from V1 to import into V2 directly
|
||||
|
||||
***To use this requests you have to have an access token with enough permissions to export and import.***
|
||||
|
||||
To export all necessary data you only have to use one request, as an example:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url {your_domain}/admin/v1/export \
|
||||
--header 'Authorization: Bearer XXXX' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"org_ids": [ "70669144072186707", "70671105999825752" ],
|
||||
"excluded_org_ids": [ ],
|
||||
"with_passwords": true,
|
||||
"with_otp": true
|
||||
"timeout": "30s",
|
||||
"response_output": true,
|
||||
}' -o export.json
|
||||
```
|
||||
|
||||
* "org_ids": to select which organizations should be exported
|
||||
* "excluded_org_ids": to exclude several organization, if for example no organizations are selected
|
||||
* "with_passwords": to include the hashed_passwords of the users in the export
|
||||
* "with_otp": to include the OTP-code of the users in the export
|
||||
* "timeout": timeout of the call to export the data
|
||||
* "response_output": to output the export as response to the call
|
||||
|
||||
***To import the exported data into you new instance, you have to have an already existing instance on a ZITADEL V2, with all desired configuration and global resources.***
|
||||
|
||||
Then as an example you can use one request for the import:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url {your_domain}/admin/v1/import \
|
||||
--header 'Authorization: Bearer XXXX' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"data_orgsv1": '$(cat export.json)'
|
||||
}'
|
||||
```
|
||||
|
||||
## Export from V1 to Import into V2 thorugh GCS
|
||||
|
||||
***To use this requests you have to have an access token with enough permissions to export and import.***
|
||||
|
||||
***The used serviceaccount has to have at least the role "Storage Object Creator" to create objects on GCS***
|
||||
|
||||
To export all necessary data you only have to use one request which results in a file in your GCS, as an example:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url {your_domain}/admin/v1/export \
|
||||
--header 'Authorization: Bearer XXXX' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data ' "{
|
||||
"org_ids": [ "70669144072186707", "70671105999825752" ],
|
||||
"excluded_org_ids": [ ],
|
||||
"with_passwords": true,
|
||||
"with_otp": true,
|
||||
"timeout": "10m",
|
||||
"gcs_output": {
|
||||
"path": "export.json",
|
||||
"bucket": "caos-zitadel-exports",
|
||||
"serviceaccount_json": "XXXX"
|
||||
}' -o export.json
|
||||
```
|
||||
|
||||
* "org_ids": to select which organizations should be exported
|
||||
* "excluded_org_ids": to exclude several organization, if for example no organizations are selected
|
||||
* "with_passwords": to include the hashed_passwords of the users in the export
|
||||
* "with_otp": to include the OTP-code of the users in the export
|
||||
* "timeout": timeout for the call to export the data
|
||||
* "gcs_output": to write a file into GCS as output to the call
|
||||
* "path": path to the output file on GCS
|
||||
* "bucket": used bucket for output on GCS
|
||||
* "serviceaccount_json": base64-encoded serviceaccount.json used to output the file on GCS
|
||||
|
||||
***To import the exported data into you new instance, you have to have an already existing instance on a ZITADEL V2, with all desired configuration and global resources.***
|
||||
|
||||
***The used serviceaccount has to have at least the role "Storage Object Viewer" to create objects on GCS***
|
||||
|
||||
Then as an example you can use one request for the import:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url {your_domain}/admin/v1/import \
|
||||
--header 'Authorization: Bearer XXXX' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"timeout": "10m",
|
||||
"data_orgsv1_gcs": {
|
||||
"path": "export.json",
|
||||
"bucket": "caos-zitadel-exports",
|
||||
"serviceaccount_json": "XXXX"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
* "timeout": timeout for the import task
|
||||
* "data_orgsv1_gcs": to read the export from GCS directly
|
||||
* "path": path to the exported file on GCS
|
||||
* "bucket": used bucket to read from GCS
|
||||
* "serviceaccount_json": base64-encoded serviceaccount.json used to read the file from GCS
|
||||
|
@ -100,7 +100,10 @@ module.exports = {
|
||||
type: "category",
|
||||
label: "API",
|
||||
collapsed: false,
|
||||
items: ["guides/api/access-zitadel-apis"],
|
||||
items: [
|
||||
"guides/api/access-zitadel-apis",
|
||||
"guides/api/export-and-import"
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
|
3
go.mod
3
go.mod
@ -3,6 +3,7 @@ module github.com/zitadel/zitadel
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.14.0
|
||||
github.com/BurntSushi/toml v0.4.1
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.0.0
|
||||
@ -171,7 +172,7 @@ require (
|
||||
golang.org/x/mod v0.5.1 // indirect
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/api v0.63.0 // indirect
|
||||
google.golang.org/api v0.63.0
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
3
go.sum
3
go.sum
@ -49,6 +49,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0 h1:6RRlFMv1omScs6iq2hfE3IvgE+l6RfJPampq8UZc5TU=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
cloud.google.com/go/trace v1.0.0 h1:laKx2y7IWMjguCe5zZx6n7qLtREk4kyE69SXVC0VSN8=
|
||||
cloud.google.com/go/trace v1.0.0/go.mod h1:4iErSByzxkyHWzzlAj63/Gmjz0NH1ASqhJguHpGcr6A=
|
||||
@ -366,9 +367,11 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-github/v31 v31.0.0/go.mod h1:NQPZol8/1sMoWYGN2yaALIBytu17gAWfhbweiEed3pM=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
|
||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
|
@ -27,7 +27,7 @@ func (s *Server) GetCustomDomainPolicy(ctx context.Context, req *admin_pb.GetCus
|
||||
}
|
||||
|
||||
func (s *Server) AddCustomDomainPolicy(ctx context.Context, req *admin_pb.AddCustomDomainPolicyRequest) (*admin_pb.AddCustomDomainPolicyResponse, error) {
|
||||
policy, err := s.command.AddOrgDomainPolicy(ctx, req.OrgId, domainPolicyToDomain(req.UserLoginMustBeDomain, req.ValidateOrgDomains, req.SmtpSenderAddressMatchesInstanceDomain))
|
||||
policy, err := s.command.AddOrgDomainPolicy(ctx, req.OrgId, DomainPolicyToDomain(req.UserLoginMustBeDomain, req.ValidateOrgDomains, req.SmtpSenderAddressMatchesInstanceDomain))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -76,7 +76,7 @@ func (s *Server) ResetCustomDomainPolicyToDefault(ctx context.Context, req *admi
|
||||
return &admin_pb.ResetCustomDomainPolicyToDefaultResponse{Details: object.DomainToChangeDetailsPb(details)}, nil
|
||||
}
|
||||
|
||||
func domainPolicyToDomain(userLoginMustBeDomain, validateOrgDomains, smtpSenderAddressMatchesInstanceDomain bool) *domain.DomainPolicy {
|
||||
func DomainPolicyToDomain(userLoginMustBeDomain, validateOrgDomains, smtpSenderAddressMatchesInstanceDomain bool) *domain.DomainPolicy {
|
||||
return &domain.DomainPolicy{
|
||||
UserLoginMustBeDomain: userLoginMustBeDomain,
|
||||
ValidateOrgDomains: validateOrgDomains,
|
||||
@ -104,7 +104,7 @@ func updateCustomDomainPolicyToDomain(req *admin_pb.UpdateCustomDomainPolicyRequ
|
||||
}
|
||||
|
||||
func (s *Server) AddCustomOrgIAMPolicy(ctx context.Context, req *admin_pb.AddCustomOrgIAMPolicyRequest) (*admin_pb.AddCustomOrgIAMPolicyResponse, error) {
|
||||
policy, err := s.command.AddOrgDomainPolicy(ctx, req.OrgId, domainPolicyToDomain(req.UserLoginMustBeDomain, true, true))
|
||||
policy, err := s.command.AddOrgDomainPolicy(ctx, req.OrgId, DomainPolicyToDomain(req.UserLoginMustBeDomain, true, true))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
1151
internal/api/grpc/admin/export.go
Normal file
1151
internal/api/grpc/admin/export.go
Normal file
File diff suppressed because it is too large
Load Diff
898
internal/api/grpc/admin/import.go
Normal file
898
internal/api/grpc/admin/import.go
Normal file
@ -0,0 +1,898 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"cloud.google.com/go/storage"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"github.com/zitadel/logging"
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/management"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||
management_pb "github.com/zitadel/zitadel/pkg/grpc/management"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/policy"
|
||||
v1_pb "github.com/zitadel/zitadel/pkg/grpc/v1"
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type importResponse struct {
|
||||
ret *admin_pb.ImportDataResponse
|
||||
count *count
|
||||
err error
|
||||
}
|
||||
type count struct {
|
||||
humanUserCount int
|
||||
humanUserLen int
|
||||
machineUserCount int
|
||||
machineUserLen int
|
||||
userMetadataCount int
|
||||
userMetadataLen int
|
||||
userLinksCount int
|
||||
userLinksLen int
|
||||
projectCount int
|
||||
projectLen int
|
||||
oidcAppCount int
|
||||
oidcAppLen int
|
||||
apiAppCount int
|
||||
apiAppLen int
|
||||
actionCount int
|
||||
actionLen int
|
||||
projectRolesCount int
|
||||
projectRolesLen int
|
||||
projectGrantCount int
|
||||
projectGrantLen int
|
||||
userGrantCount int
|
||||
userGrantLen int
|
||||
projectMembersCount int
|
||||
projectMembersLen int
|
||||
orgMemberCount int
|
||||
orgMemberLen int
|
||||
projectGrantMemberCount int
|
||||
projectGrantMemberLen int
|
||||
}
|
||||
|
||||
func (c *count) getProgress() string {
|
||||
return "progress:" +
|
||||
"human_users " + strconv.Itoa(c.humanUserCount) + "/" + strconv.Itoa(c.humanUserLen) + ", " +
|
||||
"machine_users " + strconv.Itoa(c.machineUserCount) + "/" + strconv.Itoa(c.machineUserLen) + ", " +
|
||||
"user_metadata " + strconv.Itoa(c.userMetadataCount) + "/" + strconv.Itoa(c.userMetadataLen) + ", " +
|
||||
"user_links " + strconv.Itoa(c.userLinksCount) + "/" + strconv.Itoa(c.userLinksLen) + ", " +
|
||||
"projects " + strconv.Itoa(c.projectCount) + "/" + strconv.Itoa(c.projectLen) + ", " +
|
||||
"oidc_apps " + strconv.Itoa(c.oidcAppCount) + "/" + strconv.Itoa(c.oidcAppLen) + ", " +
|
||||
"api_apps " + strconv.Itoa(c.apiAppCount) + "/" + strconv.Itoa(c.apiAppLen) + ", " +
|
||||
"actions " + strconv.Itoa(c.actionCount) + "/" + strconv.Itoa(c.actionLen) + ", " +
|
||||
"project_roles " + strconv.Itoa(c.projectRolesCount) + "/" + strconv.Itoa(c.projectRolesLen) + ", " +
|
||||
"project_grant " + strconv.Itoa(c.projectGrantCount) + "/" + strconv.Itoa(c.projectGrantLen) + ", " +
|
||||
"user_grants " + strconv.Itoa(c.userGrantCount) + "/" + strconv.Itoa(c.userGrantLen) + ", " +
|
||||
"project_members " + strconv.Itoa(c.projectMembersCount) + "/" + strconv.Itoa(c.projectMembersLen) + ", " +
|
||||
"org_members " + strconv.Itoa(c.orgMemberCount) + "/" + strconv.Itoa(c.orgMemberLen) + ", " +
|
||||
"project_grant_members " + strconv.Itoa(c.projectGrantMemberCount) + "/" + strconv.Itoa(c.projectGrantMemberLen)
|
||||
}
|
||||
|
||||
func Detach(ctx context.Context) context.Context { return detachedContext{ctx} }
|
||||
|
||||
type detachedContext struct {
|
||||
parent context.Context
|
||||
}
|
||||
|
||||
func (v detachedContext) Deadline() (time.Time, bool) { return time.Time{}, false }
|
||||
func (v detachedContext) Done() <-chan struct{} { return nil }
|
||||
func (v detachedContext) Err() error { return nil }
|
||||
func (v detachedContext) Value(key interface{}) interface{} { return v.parent.Value(key) }
|
||||
|
||||
func (s *Server) ImportData(ctx context.Context, req *admin_pb.ImportDataRequest) (_ *admin_pb.ImportDataResponse, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
if req.GetDataOrgs() != nil || req.GetDataOrgsv1() != nil {
|
||||
timeoutDuration, err := time.ParseDuration(req.Timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ch := make(chan importResponse, 1)
|
||||
ctxTimeout, cancel := context.WithTimeout(ctx, timeoutDuration)
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
orgs := make([]*admin_pb.DataOrg, 0)
|
||||
if req.GetDataOrgsv1() != nil {
|
||||
dataOrgs, err := s.dataOrgsV1ToDataOrgs(ctx, req.GetDataOrgsv1())
|
||||
if err != nil {
|
||||
ch <- importResponse{ret: nil, err: err}
|
||||
return
|
||||
}
|
||||
orgs = dataOrgs.GetOrgs()
|
||||
} else {
|
||||
orgs = req.GetDataOrgs().GetOrgs()
|
||||
}
|
||||
|
||||
ret, count, err := s.importData(ctx, orgs)
|
||||
ch <- importResponse{ret: ret, count: count, err: err}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctxTimeout.Done():
|
||||
logging.Errorf("Import to response timeout: %v", ctxTimeout.Err())
|
||||
return nil, ctxTimeout.Err()
|
||||
case result := <-ch:
|
||||
logging.OnError(result.err).Errorf("error while importing: %v", result.err)
|
||||
logging.Infof("Import done: %s", result.count.getProgress())
|
||||
return result.ret, result.err
|
||||
}
|
||||
} else {
|
||||
v1Transformation := false
|
||||
var gcsInput *admin_pb.ImportDataRequest_GCSInput
|
||||
var s3Input *admin_pb.ImportDataRequest_S3Input
|
||||
var localInput *admin_pb.ImportDataRequest_LocalInput
|
||||
if req.GetDataOrgsGcs() != nil {
|
||||
gcsInput = req.GetDataOrgsGcs()
|
||||
}
|
||||
if req.GetDataOrgsv1Gcs() != nil {
|
||||
gcsInput = req.GetDataOrgsv1Gcs()
|
||||
v1Transformation = true
|
||||
}
|
||||
if req.GetDataOrgsS3() != nil {
|
||||
s3Input = req.GetDataOrgsS3()
|
||||
}
|
||||
if req.GetDataOrgsv1S3() != nil {
|
||||
s3Input = req.GetDataOrgsv1S3()
|
||||
v1Transformation = true
|
||||
}
|
||||
if req.GetDataOrgsLocal() != nil {
|
||||
localInput = req.GetDataOrgsLocal()
|
||||
}
|
||||
if req.GetDataOrgsv1Local() != nil {
|
||||
localInput = req.GetDataOrgsv1Local()
|
||||
v1Transformation = true
|
||||
}
|
||||
|
||||
timeoutDuration, err := time.ParseDuration(req.Timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dctx := Detach(ctx)
|
||||
go func() {
|
||||
ch := make(chan importResponse, 1)
|
||||
ctxTimeout, cancel := context.WithTimeout(dctx, timeoutDuration)
|
||||
defer cancel()
|
||||
go func() {
|
||||
dataOrgs, err := s.transportDataFromFile(ctxTimeout, v1Transformation, gcsInput, s3Input, localInput)
|
||||
if err != nil {
|
||||
ch <- importResponse{nil, nil, err}
|
||||
return
|
||||
}
|
||||
resp, count, err := s.importData(ctxTimeout, dataOrgs)
|
||||
if err != nil {
|
||||
ch <- importResponse{nil, count, err}
|
||||
return
|
||||
}
|
||||
ch <- importResponse{resp, count, nil}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctxTimeout.Done():
|
||||
logging.Errorf("Export to response timeout: %v", ctxTimeout.Err())
|
||||
return
|
||||
case result := <-ch:
|
||||
logging.OnError(result.err).Errorf("error while importing: %v", err)
|
||||
if result.count != nil {
|
||||
logging.Infof("Import done: %s", result.count.getProgress())
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
return &admin_pb.ImportDataResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *Server) transportDataFromFile(ctx context.Context, v1Transformation bool, gcsInput *admin_pb.ImportDataRequest_GCSInput, s3Input *admin_pb.ImportDataRequest_S3Input, localInput *admin_pb.ImportDataRequest_LocalInput) (_ []*admin_pb.DataOrg, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
dataOrgs := make([]*admin_pb.DataOrg, 0)
|
||||
data := make([]byte, 0)
|
||||
if gcsInput != nil {
|
||||
gcsData, err := getFileFromGCS(ctx, gcsInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = gcsData
|
||||
}
|
||||
if s3Input != nil {
|
||||
s3Data, err := getFileFromS3(ctx, s3Input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = s3Data
|
||||
}
|
||||
if localInput != nil {
|
||||
localData, err := ioutil.ReadFile(localInput.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = localData
|
||||
}
|
||||
|
||||
if v1Transformation {
|
||||
dataImportV1 := new(v1_pb.ImportDataOrg)
|
||||
if err := json.Unmarshal(data, dataImportV1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dataImport, err := s.dataOrgsV1ToDataOrgs(ctx, dataImportV1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dataOrgs = dataImport.Orgs
|
||||
} else {
|
||||
dataImport := new(admin_pb.ImportDataOrg)
|
||||
if err := json.Unmarshal(data, dataImport); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dataOrgs = dataImport.Orgs
|
||||
}
|
||||
|
||||
return dataOrgs, nil
|
||||
}
|
||||
|
||||
func getFileFromS3(ctx context.Context, input *admin_pb.ImportDataRequest_S3Input) ([]byte, error) {
|
||||
minioClient, err := minio.New(input.Endpoint, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(input.AccessKeyId, input.SecretAccessKey, ""),
|
||||
Secure: input.Ssl,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exists, err := minioClient.BucketExists(ctx, input.Bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("bucket not existing: %v", err)
|
||||
}
|
||||
|
||||
object, err := minioClient.GetObject(ctx, input.Bucket, input.Path, minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer object.Close()
|
||||
return ioutil.ReadAll(object)
|
||||
}
|
||||
|
||||
func getFileFromGCS(ctx context.Context, input *admin_pb.ImportDataRequest_GCSInput) ([]byte, error) {
|
||||
saJson, err := base64.StdEncoding.DecodeString(input.ServiceaccountJson)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := storage.NewClient(ctx, option.WithCredentialsJSON(saJson))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bucket := client.Bucket(input.Bucket)
|
||||
reader, err := bucket.Object(input.Path).NewReader(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer reader.Close()
|
||||
return ioutil.ReadAll(reader)
|
||||
}
|
||||
|
||||
func (s *Server) importData(ctx context.Context, orgs []*admin_pb.DataOrg) (*admin_pb.ImportDataResponse, *count, error) {
|
||||
errors := make([]*admin_pb.ImportDataError, 0)
|
||||
success := &admin_pb.ImportDataSuccess{}
|
||||
count := &count{}
|
||||
|
||||
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.passwordHashAlg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.userCodeAlg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.userCodeAlg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.userCodeAlg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
for _, org := range orgs {
|
||||
count.humanUserLen += len(org.GetHumanUsers())
|
||||
count.machineUserLen += len(org.GetMachineUsers())
|
||||
count.userMetadataLen += len(org.GetUserMetadata())
|
||||
count.userLinksLen += len(org.GetUserLinks())
|
||||
count.projectLen += len(org.GetProjects())
|
||||
count.oidcAppLen += len(org.GetOidcApps())
|
||||
count.apiAppLen += len(org.GetApiApps())
|
||||
count.actionLen += len(org.GetActions())
|
||||
count.projectRolesLen += len(org.GetProjectRoles())
|
||||
count.projectGrantLen += len(org.GetProjectGrants())
|
||||
count.userGrantLen += len(org.GetUserGrants())
|
||||
count.projectMembersLen += len(org.GetProjectMembers())
|
||||
count.orgMemberLen += len(org.GetOrgMembers())
|
||||
count.projectGrantMemberLen += len(org.GetProjectGrantMembers())
|
||||
}
|
||||
|
||||
for _, org := range orgs {
|
||||
_, err := s.command.AddOrgWithID(ctx, org.GetOrg().GetName(), ctxData.UserID, ctxData.ResourceOwner, org.GetOrgId(), []string{})
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "org", Id: org.GetOrgId(), Message: err.Error()})
|
||||
}
|
||||
successOrg := &admin_pb.ImportDataSuccessOrg{
|
||||
OrgId: org.GetOrgId(),
|
||||
ProjectIds: []string{},
|
||||
OidcAppIds: []string{},
|
||||
ApiAppIds: []string{},
|
||||
HumanUserIds: []string{},
|
||||
MachineUserIds: []string{},
|
||||
ActionIds: []string{},
|
||||
ProjectGrants: []*admin_pb.ImportDataSuccessProjectGrant{},
|
||||
UserGrants: []*admin_pb.ImportDataSuccessUserGrant{},
|
||||
OrgMembers: []string{},
|
||||
ProjectMembers: []*admin_pb.ImportDataSuccessProjectMember{},
|
||||
ProjectGrantMembers: []*admin_pb.ImportDataSuccessProjectGrantMember{},
|
||||
}
|
||||
logging.Debugf("successful org: %s", successOrg.OrgId)
|
||||
success.Orgs = append(success.Orgs, successOrg)
|
||||
|
||||
domainPolicy := org.GetDomainPolicy()
|
||||
if org.DomainPolicy != nil {
|
||||
_, err := s.command.AddOrgDomainPolicy(ctx, org.GetOrgId(), DomainPolicyToDomain(domainPolicy.UserLoginMustBeDomain, domainPolicy.ValidateOrgDomains, domainPolicy.SmtpSenderAddressMatchesInstanceDomain))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "domain_policy", Id: org.GetOrgId(), Message: err.Error()})
|
||||
}
|
||||
}
|
||||
if org.Domains != nil {
|
||||
for _, domainR := range org.Domains {
|
||||
orgDomain := &domain.OrgDomain{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: org.GetOrgId(),
|
||||
},
|
||||
Domain: domainR.DomainName,
|
||||
Verified: domainR.IsVerified,
|
||||
Primary: domainR.IsPrimary,
|
||||
}
|
||||
_, err := s.command.AddOrgDomain(ctx, org.GetOrgId(), domainR.DomainName, []string{})
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "domain", Id: org.GetOrgId() + "_" + domainR.DomainName, Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
logging.Debugf("successful domain: %s", domainR.DomainName)
|
||||
successOrg.Domains = append(successOrg.Domains, domainR.DomainName)
|
||||
|
||||
if domainR.IsVerified {
|
||||
if _, err := s.command.VerifyOrgDomain(ctx, org.GetOrgId(), domainR.DomainName); err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "domain_isverified", Id: org.GetOrgId() + "_" + domainR.DomainName, Message: err.Error()})
|
||||
}
|
||||
}
|
||||
if domainR.IsPrimary {
|
||||
if _, err := s.command.SetPrimaryOrgDomain(ctx, orgDomain); err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "domain_isprimary", Id: org.GetOrgId() + "_" + domainR.DomainName, Message: err.Error()})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if org.LabelPolicy != nil {
|
||||
_, err = s.command.AddLabelPolicy(ctx, org.GetOrgId(), management.AddLabelPolicyToDomain(org.GetLabelPolicy()))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "label_policy", Id: org.GetOrgId(), Message: err.Error()})
|
||||
}
|
||||
}
|
||||
if org.LockoutPolicy != nil {
|
||||
_, err = s.command.AddLockoutPolicy(ctx, org.GetOrgId(), management.AddLockoutPolicyToDomain(org.GetLockoutPolicy()))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "lockout_policy", Id: org.GetOrgId(), Message: err.Error()})
|
||||
}
|
||||
}
|
||||
if org.OidcIdps != nil {
|
||||
for _, idp := range org.OidcIdps {
|
||||
logging.Debugf("import oidcidp: %s", idp.IdpId)
|
||||
_, err := s.command.ImportIDPConfig(ctx, management.AddOIDCIDPRequestToDomain(idp.Idp), idp.IdpId, org.GetOrgId())
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "oidc_idp", Id: idp.IdpId, Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
logging.Debugf("successful oidcidp: %s", idp.GetIdpId())
|
||||
successOrg.OidcIpds = append(successOrg.OidcIpds, idp.GetIdpId())
|
||||
}
|
||||
}
|
||||
if org.JwtIdps != nil {
|
||||
for _, idp := range org.JwtIdps {
|
||||
logging.Debugf("import jwtidp: %s", idp.IdpId)
|
||||
_, err := s.command.ImportIDPConfig(ctx, management.AddJWTIDPRequestToDomain(idp.Idp), idp.IdpId, org.GetOrgId())
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "jwt_idp", Id: idp.IdpId, Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
logging.Debugf("successful jwtidp: %s", idp.GetIdpId())
|
||||
successOrg.JwtIdps = append(successOrg.JwtIdps, idp.GetIdpId())
|
||||
}
|
||||
}
|
||||
if org.LoginPolicy != nil {
|
||||
_, err = s.command.AddLoginPolicy(ctx, org.GetOrgId(), management.AddLoginPolicyToDomain(org.GetLoginPolicy()))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "login_policy", Id: org.GetOrgId(), Message: err.Error()})
|
||||
}
|
||||
}
|
||||
if org.PasswordComplexityPolicy != nil {
|
||||
_, err = s.command.AddPasswordComplexityPolicy(ctx, org.GetOrgId(), management.AddPasswordComplexityPolicyToDomain(org.GetPasswordComplexityPolicy()))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "password_complexity_policy", Id: org.GetOrgId(), Message: err.Error()})
|
||||
}
|
||||
}
|
||||
if org.PrivacyPolicy != nil {
|
||||
_, err = s.command.AddPrivacyPolicy(ctx, org.GetOrgId(), management.AddPrivacyPolicyToDomain(org.GetPrivacyPolicy()))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "privacy_policy", Id: org.GetOrgId(), Message: err.Error()})
|
||||
}
|
||||
}
|
||||
if org.LoginTexts != nil {
|
||||
for _, text := range org.GetLoginTexts() {
|
||||
_, err := s.command.SetOrgLoginText(ctx, org.GetOrgId(), management.SetLoginCustomTextToDomain(text))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "login_texts", Id: org.GetOrgId() + "_" + text.Language, Message: err.Error()})
|
||||
}
|
||||
}
|
||||
}
|
||||
if org.InitMessages != nil {
|
||||
for _, message := range org.GetInitMessages() {
|
||||
_, err := s.command.SetOrgMessageText(ctx, authz.GetCtxData(ctx).OrgID, management.SetInitCustomTextToDomain(message))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "init_message", Id: org.GetOrgId() + "_" + message.Language, Message: err.Error()})
|
||||
}
|
||||
}
|
||||
}
|
||||
if org.PasswordResetMessages != nil {
|
||||
for _, message := range org.GetPasswordResetMessages() {
|
||||
_, err := s.command.SetOrgMessageText(ctx, authz.GetCtxData(ctx).OrgID, management.SetPasswordResetCustomTextToDomain(message))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "password_reset_message", Id: org.GetOrgId() + "_" + message.Language, Message: err.Error()})
|
||||
}
|
||||
}
|
||||
}
|
||||
if org.VerifyEmailMessages != nil {
|
||||
for _, message := range org.GetVerifyEmailMessages() {
|
||||
_, err := s.command.SetOrgMessageText(ctx, authz.GetCtxData(ctx).OrgID, management.SetVerifyEmailCustomTextToDomain(message))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "verify_email_message", Id: org.GetOrgId() + "_" + message.Language, Message: err.Error()})
|
||||
}
|
||||
}
|
||||
}
|
||||
if org.VerifyPhoneMessages != nil {
|
||||
for _, message := range org.GetVerifyPhoneMessages() {
|
||||
_, err := s.command.SetOrgMessageText(ctx, authz.GetCtxData(ctx).OrgID, management.SetVerifyPhoneCustomTextToDomain(message))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "verify_phone_message", Id: org.GetOrgId() + "_" + message.Language, Message: err.Error()})
|
||||
}
|
||||
}
|
||||
}
|
||||
if org.DomainClaimedMessages != nil {
|
||||
for _, message := range org.GetDomainClaimedMessages() {
|
||||
_, err := s.command.SetOrgMessageText(ctx, authz.GetCtxData(ctx).OrgID, management.SetDomainClaimedCustomTextToDomain(message))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "domain_claimed_message", Id: org.GetOrgId() + "_" + message.Language, Message: err.Error()})
|
||||
}
|
||||
}
|
||||
}
|
||||
if org.PasswordlessRegistrationMessages != nil {
|
||||
for _, message := range org.GetPasswordlessRegistrationMessages() {
|
||||
_, err := s.command.SetOrgMessageText(ctx, authz.GetCtxData(ctx).OrgID, management.SetPasswordlessRegistrationCustomTextToDomain(message))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "passwordless_registration_message", Id: org.GetOrgId() + "_" + message.Language, Message: err.Error()})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if org.HumanUsers != nil {
|
||||
for _, user := range org.GetHumanUsers() {
|
||||
logging.Debugf("import user: %s", user.GetUserId())
|
||||
human, passwordless := management.ImportHumanUserRequestToDomain(user.User)
|
||||
human.AggregateID = user.UserId
|
||||
_, _, err := s.command.ImportHuman(ctx, org.GetOrgId(), human, passwordless, initCodeGenerator, phoneCodeGenerator, passwordlessInitCode)
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "human_user", Id: user.GetUserId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
} else {
|
||||
count.humanUserCount += 1
|
||||
logging.Debugf("successful user %d: %s", count.humanUserCount, user.GetUserId())
|
||||
successOrg.HumanUserIds = append(successOrg.HumanUserIds, user.GetUserId())
|
||||
}
|
||||
|
||||
if user.User.OtpCode != "" {
|
||||
logging.Debugf("import user otp: %s", user.GetUserId())
|
||||
if err := s.command.ImportHumanOTP(ctx, user.UserId, "", org.GetOrgId(), user.User.OtpCode); err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "human_user_otp", Id: user.GetUserId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
} else {
|
||||
logging.Debugf("successful user otp: %s", user.GetUserId())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if org.MachineUsers != nil {
|
||||
for _, user := range org.GetMachineUsers() {
|
||||
logging.Debugf("import user: %s", user.GetUserId())
|
||||
_, err := s.command.AddMachineWithID(ctx, org.GetOrgId(), user.GetUserId(), management.AddMachineUserRequestToDomain(user.GetUser()))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "machine_user", Id: user.GetUserId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
count.machineUserCount += 1
|
||||
logging.Debugf("successful user %d: %s", count.machineUserCount, user.GetUserId())
|
||||
successOrg.MachineUserIds = append(successOrg.MachineUserIds, user.GetUserId())
|
||||
}
|
||||
}
|
||||
if org.UserMetadata != nil {
|
||||
for _, userMetadata := range org.GetUserMetadata() {
|
||||
logging.Debugf("import usermetadata: %s", userMetadata.GetId()+"_"+userMetadata.GetKey())
|
||||
_, err := s.command.SetUserMetadata(ctx, &domain.Metadata{Key: userMetadata.GetKey(), Value: userMetadata.GetValue()}, userMetadata.GetId(), org.GetOrgId())
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "user_metadata", Id: userMetadata.GetId() + "_" + userMetadata.GetKey(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
count.userMetadataCount += 1
|
||||
logging.Debugf("successful usermetadata %d: %s", count.userMetadataCount, userMetadata.GetId()+"_"+userMetadata.GetKey())
|
||||
successOrg.UserMetadata = append(successOrg.UserMetadata, &admin_pb.ImportDataSuccessUserMetadata{UserId: userMetadata.GetId(), Key: userMetadata.GetKey()})
|
||||
}
|
||||
}
|
||||
if org.UserLinks != nil {
|
||||
for _, userLinks := range org.GetUserLinks() {
|
||||
logging.Debugf("import userlink: %s", userLinks.GetUserId()+"_"+userLinks.GetIdpId()+"_"+userLinks.GetProvidedUserId()+"_"+userLinks.GetProvidedUserName())
|
||||
externalIDP := &domain.UserIDPLink{
|
||||
ObjectRoot: es_models.ObjectRoot{AggregateID: userLinks.UserId},
|
||||
IDPConfigID: userLinks.IdpId,
|
||||
ExternalUserID: userLinks.ProvidedUserId,
|
||||
DisplayName: userLinks.ProvidedUserName,
|
||||
}
|
||||
if err := s.command.AddUserIDPLink(ctx, userLinks.UserId, org.GetOrgId(), externalIDP); err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "user_link", Id: userLinks.UserId + "_" + userLinks.IdpId, Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
count.userLinksCount += 1
|
||||
logging.Debugf("successful userlink %d: %s", count.userLinksCount, userLinks.GetUserId()+"_"+userLinks.GetIdpId()+"_"+userLinks.GetProvidedUserId()+"_"+userLinks.GetProvidedUserName())
|
||||
successOrg.UserLinks = append(successOrg.UserLinks, &admin_pb.ImportDataSuccessUserLinks{UserId: userLinks.GetUserId(), IdpId: userLinks.GetIdpId(), ExternalUserId: userLinks.GetProvidedUserId(), DisplayName: userLinks.GetProvidedUserName()})
|
||||
}
|
||||
}
|
||||
if org.Projects != nil {
|
||||
for _, project := range org.GetProjects() {
|
||||
logging.Debugf("import project: %s", project.GetProjectId())
|
||||
_, err := s.command.AddProjectWithID(ctx, management.ProjectCreateToDomain(project.GetProject()), org.GetOrgId(), project.GetProjectId())
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "project", Id: project.GetProjectId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
count.projectCount += 1
|
||||
logging.Debugf("successful project %d: %s", count.projectCount, project.GetProjectId())
|
||||
successOrg.ProjectIds = append(successOrg.ProjectIds, project.GetProjectId())
|
||||
}
|
||||
}
|
||||
if org.OidcApps != nil {
|
||||
for _, app := range org.GetOidcApps() {
|
||||
logging.Debugf("import oidcapplication: %s", app.GetAppId())
|
||||
_, err := s.command.AddOIDCApplicationWithID(ctx, management.AddOIDCAppRequestToDomain(app.App), org.GetOrgId(), app.GetAppId(), appSecretGenerator)
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "oidc_app", Id: app.GetAppId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
count.oidcAppCount += 1
|
||||
logging.Debugf("successful oidcapplication %d: %s", count.oidcAppCount, app.GetAppId())
|
||||
successOrg.OidcAppIds = append(successOrg.OidcAppIds, app.GetAppId())
|
||||
}
|
||||
}
|
||||
if org.ApiApps != nil {
|
||||
for _, app := range org.GetApiApps() {
|
||||
logging.Debugf("import apiapplication: %s", app.GetAppId())
|
||||
_, err := s.command.AddAPIApplicationWithID(ctx, management.AddAPIAppRequestToDomain(app.GetApp()), org.GetOrgId(), app.GetAppId(), appSecretGenerator)
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "api_app", Id: app.GetAppId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
count.apiAppCount += 1
|
||||
logging.Debugf("successful apiapplication %d: %s", count.apiAppCount, app.GetAppId())
|
||||
successOrg.ApiAppIds = append(successOrg.ApiAppIds, app.GetAppId())
|
||||
}
|
||||
}
|
||||
if org.Actions != nil {
|
||||
for _, action := range org.GetActions() {
|
||||
logging.Debugf("import action: %s", action.GetActionId())
|
||||
_, _, err := s.command.AddActionWithID(ctx, management.CreateActionRequestToDomain(action.GetAction()), org.GetOrgId(), action.GetActionId())
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "action", Id: action.GetActionId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
count.actionCount += 1
|
||||
logging.Debugf("successful action %d: %s", count.actionCount, action.GetActionId())
|
||||
successOrg.ActionIds = append(successOrg.ActionIds, action.ActionId)
|
||||
}
|
||||
}
|
||||
if org.ProjectRoles != nil {
|
||||
for _, role := range org.GetProjectRoles() {
|
||||
logging.Debugf("import projectroles: %s", role.ProjectId+"_"+role.RoleKey)
|
||||
_, err := s.command.AddProjectRole(ctx, management.AddProjectRoleRequestToDomain(role), org.GetOrgId())
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "project_role", Id: role.ProjectId + "_" + role.RoleKey, Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
count.projectRolesCount += 1
|
||||
logging.Debugf("successful projectroles %d: %s", count.projectRolesCount, role.ProjectId+"_"+role.RoleKey)
|
||||
successOrg.ProjectRoles = append(successOrg.ActionIds, role.ProjectId+"_"+role.RoleKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, org := range orgs {
|
||||
var successOrg *admin_pb.ImportDataSuccessOrg
|
||||
for _, oldOrd := range success.Orgs {
|
||||
if org.OrgId == oldOrd.OrgId {
|
||||
successOrg = oldOrd
|
||||
}
|
||||
}
|
||||
if org.TriggerActions != nil {
|
||||
for _, triggerAction := range org.GetTriggerActions() {
|
||||
_, err := s.command.SetTriggerActions(ctx, domain.FlowType(triggerAction.FlowType), domain.TriggerType(triggerAction.TriggerType), triggerAction.ActionIds, org.GetOrgId())
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "trigger_action", Id: triggerAction.FlowType.String() + "_" + triggerAction.TriggerType.String(), Message: err.Error()})
|
||||
continue
|
||||
}
|
||||
successOrg.TriggerActions = append(successOrg.TriggerActions, &management_pb.SetTriggerActionsRequest{FlowType: triggerAction.FlowType, TriggerType: triggerAction.TriggerType, ActionIds: triggerAction.GetActionIds()})
|
||||
}
|
||||
}
|
||||
if org.ProjectGrants != nil {
|
||||
for _, grant := range org.GetProjectGrants() {
|
||||
logging.Debugf("import projectgrant: %s", grant.GetGrantId()+"_"+grant.GetProjectGrant().GetProjectId()+"_"+grant.GetProjectGrant().GetGrantedOrgId())
|
||||
_, err := s.command.AddProjectGrantWithID(ctx, management.AddProjectGrantRequestToDomain(grant.GetProjectGrant()), grant.GetGrantId(), org.GetOrgId())
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "project_grant", Id: org.GetOrgId() + "_" + grant.GetProjectGrant().GetProjectId() + "_" + grant.GetProjectGrant().GetGrantedOrgId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
count.projectGrantCount += 1
|
||||
logging.Debugf("successful projectgrant %d: %s", count.projectGrantCount, grant.GetGrantId()+"_"+grant.GetProjectGrant().GetProjectId()+"_"+grant.GetProjectGrant().GetGrantedOrgId())
|
||||
successOrg.ProjectGrants = append(successOrg.ProjectGrants, &admin_pb.ImportDataSuccessProjectGrant{GrantId: grant.GetGrantId(), ProjectId: grant.GetProjectGrant().GetProjectId(), OrgId: grant.GetProjectGrant().GetGrantedOrgId()})
|
||||
}
|
||||
}
|
||||
if org.UserGrants != nil {
|
||||
for _, grant := range org.GetUserGrants() {
|
||||
logging.Debugf("import usergrant: %s", grant.GetProjectId()+"_"+grant.GetUserId())
|
||||
_, err := s.command.AddUserGrant(ctx, management.AddUserGrantRequestToDomain(grant), org.GetOrgId())
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "user_grant", Id: org.GetOrgId() + "_" + grant.GetProjectId() + "_" + grant.GetUserId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
count.userGrantCount += 1
|
||||
logging.Debugf("successful usergrant %d: %s", count.userGrantCount, grant.GetProjectId()+"_"+grant.GetUserId())
|
||||
successOrg.UserGrants = append(successOrg.UserGrants, &admin_pb.ImportDataSuccessUserGrant{ProjectId: grant.GetProjectId(), UserId: grant.GetUserId()})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if success != nil && success.Orgs != nil {
|
||||
for _, org := range orgs {
|
||||
var successOrg *admin_pb.ImportDataSuccessOrg
|
||||
for _, oldOrd := range success.Orgs {
|
||||
if org.OrgId == oldOrd.OrgId {
|
||||
successOrg = oldOrd
|
||||
}
|
||||
}
|
||||
if successOrg == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if org.OrgMembers != nil {
|
||||
for _, member := range org.GetOrgMembers() {
|
||||
logging.Debugf("import orgmember: %s", member.GetUserId())
|
||||
_, err := s.command.AddOrgMember(ctx, org.GetOrgId(), member.GetUserId(), member.GetRoles()...)
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "org_member", Id: org.GetOrgId() + "_" + member.GetUserId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
count.orgMemberCount += 1
|
||||
logging.Debugf("successful orgmember %d: %s", count.orgMemberCount, member.GetUserId())
|
||||
successOrg.OrgMembers = append(successOrg.OrgMembers, member.GetUserId())
|
||||
}
|
||||
}
|
||||
if org.ProjectGrantMembers != nil {
|
||||
for _, member := range org.GetProjectGrantMembers() {
|
||||
logging.Debugf("import projectgrantmember: %s", member.GetProjectId()+"_"+member.GetGrantId()+"_"+member.GetUserId())
|
||||
_, err := s.command.AddProjectGrantMember(ctx, management.AddProjectGrantMemberRequestToDomain(member))
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "project_grant_member", Id: org.GetOrgId() + "_" + member.GetProjectId() + "_" + member.GetGrantId() + "_" + member.GetUserId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
count.projectGrantMemberCount += 1
|
||||
logging.Debugf("successful projectgrantmember %d: %s", count.projectGrantMemberCount, member.GetProjectId()+"_"+member.GetGrantId()+"_"+member.GetUserId())
|
||||
successOrg.ProjectGrantMembers = append(successOrg.ProjectGrantMembers, &admin_pb.ImportDataSuccessProjectGrantMember{ProjectId: member.GetProjectId(), GrantId: member.GetGrantId(), UserId: member.GetUserId()})
|
||||
}
|
||||
}
|
||||
if org.ProjectMembers != nil {
|
||||
for _, member := range org.GetProjectMembers() {
|
||||
logging.Debugf("import orgmember: %s", member.GetProjectId()+"_"+member.GetUserId())
|
||||
_, err := s.command.AddProjectMember(ctx, management.AddProjectMemberRequestToDomain(member), org.GetOrgId())
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "project_member", Id: org.GetOrgId() + "_" + member.GetProjectId() + "_" + member.GetUserId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
count.projectMembersCount += 1
|
||||
logging.Debugf("successful orgmember %d: %s", count.projectMembersCount, member.GetProjectId()+"_"+member.GetUserId())
|
||||
successOrg.ProjectMembers = append(successOrg.ProjectMembers, &admin_pb.ImportDataSuccessProjectMember{ProjectId: member.GetProjectId(), UserId: member.GetUserId()})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return &admin_pb.ImportDataResponse{
|
||||
Errors: errors,
|
||||
Success: success,
|
||||
}, count, nil
|
||||
}
|
||||
|
||||
func (s *Server) dataOrgsV1ToDataOrgs(ctx context.Context, dataOrgs *v1_pb.ImportDataOrg) (_ *admin_pb.ImportDataOrg, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
orgs := make([]*admin_pb.DataOrg, 0)
|
||||
for _, orgV1 := range dataOrgs.Orgs {
|
||||
org := &admin_pb.DataOrg{
|
||||
OrgId: orgV1.GetOrgId(),
|
||||
Org: orgV1.GetOrg(),
|
||||
DomainPolicy: nil,
|
||||
LabelPolicy: orgV1.GetLabelPolicy(),
|
||||
LockoutPolicy: orgV1.GetLockoutPolicy(),
|
||||
LoginPolicy: orgV1.GetLoginPolicy(),
|
||||
PasswordComplexityPolicy: orgV1.GetPasswordComplexityPolicy(),
|
||||
PrivacyPolicy: orgV1.GetPrivacyPolicy(),
|
||||
Projects: orgV1.GetProjects(),
|
||||
ProjectRoles: orgV1.GetProjectRoles(),
|
||||
ApiApps: orgV1.GetApiApps(),
|
||||
OidcApps: orgV1.GetOidcApps(),
|
||||
HumanUsers: orgV1.GetHumanUsers(),
|
||||
MachineUsers: orgV1.GetMachineUsers(),
|
||||
TriggerActions: orgV1.GetTriggerActions(),
|
||||
Actions: orgV1.GetActions(),
|
||||
ProjectGrants: orgV1.GetProjectGrants(),
|
||||
UserGrants: orgV1.GetUserGrants(),
|
||||
OrgMembers: orgV1.GetOrgMembers(),
|
||||
ProjectMembers: orgV1.GetProjectMembers(),
|
||||
ProjectGrantMembers: orgV1.GetProjectGrantMembers(),
|
||||
UserMetadata: orgV1.GetUserMetadata(),
|
||||
LoginTexts: orgV1.GetLoginTexts(),
|
||||
InitMessages: orgV1.GetInitMessages(),
|
||||
PasswordResetMessages: orgV1.GetPasswordResetMessages(),
|
||||
VerifyEmailMessages: orgV1.GetVerifyEmailMessages(),
|
||||
VerifyPhoneMessages: orgV1.GetVerifyPhoneMessages(),
|
||||
DomainClaimedMessages: orgV1.GetDomainClaimedMessages(),
|
||||
PasswordlessRegistrationMessages: orgV1.GetPasswordlessRegistrationMessages(),
|
||||
OidcIdps: orgV1.GetOidcIdps(),
|
||||
JwtIdps: orgV1.GetJwtIdps(),
|
||||
UserLinks: orgV1.GetUserLinks(),
|
||||
Domains: orgV1.GetDomains(),
|
||||
}
|
||||
if orgV1.IamPolicy != nil {
|
||||
defaultDomainPolicy, err := s.query.DefaultDomainPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
org.DomainPolicy = &admin_pb.AddCustomDomainPolicyRequest{
|
||||
UserLoginMustBeDomain: orgV1.IamPolicy.UserLoginMustBeDomain,
|
||||
ValidateOrgDomains: defaultDomainPolicy.ValidateOrgDomains,
|
||||
SmtpSenderAddressMatchesInstanceDomain: defaultDomainPolicy.SMTPSenderAddressMatchesInstanceDomain,
|
||||
}
|
||||
}
|
||||
if org.LoginPolicy != nil {
|
||||
defaultLoginPolicy, err := s.query.DefaultLoginPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
org.LoginPolicy.ExternalLoginCheckLifetime = durationpb.New(defaultLoginPolicy.ExternalLoginCheckLifetime)
|
||||
org.LoginPolicy.MultiFactorCheckLifetime = durationpb.New(defaultLoginPolicy.MultiFactorCheckLifetime)
|
||||
org.LoginPolicy.SecondFactorCheckLifetime = durationpb.New(defaultLoginPolicy.SecondFactorCheckLifetime)
|
||||
org.LoginPolicy.PasswordCheckLifetime = durationpb.New(defaultLoginPolicy.PasswordCheckLifetime)
|
||||
org.LoginPolicy.MfaInitSkipLifetime = durationpb.New(defaultLoginPolicy.MFAInitSkipLifetime)
|
||||
|
||||
if orgV1.SecondFactors != nil {
|
||||
org.LoginPolicy.SecondFactors = make([]policy.SecondFactorType, len(orgV1.SecondFactors))
|
||||
for i, factor := range orgV1.SecondFactors {
|
||||
org.LoginPolicy.SecondFactors[i] = factor.GetType()
|
||||
}
|
||||
}
|
||||
if orgV1.MultiFactors != nil {
|
||||
org.LoginPolicy.MultiFactors = make([]policy.MultiFactorType, len(orgV1.MultiFactors))
|
||||
for i, factor := range orgV1.MultiFactors {
|
||||
org.LoginPolicy.MultiFactors[i] = factor.GetType()
|
||||
}
|
||||
}
|
||||
if orgV1.Idps != nil {
|
||||
org.LoginPolicy.Idps = make([]*management_pb.AddCustomLoginPolicyRequest_IDP, len(orgV1.Idps))
|
||||
for i, idpR := range orgV1.Idps {
|
||||
org.LoginPolicy.Idps[i] = &management_pb.AddCustomLoginPolicyRequest_IDP{
|
||||
IdpId: idpR.GetIdpId(),
|
||||
OwnerType: idpR.GetOwnerType(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
orgs = append(orgs, org)
|
||||
}
|
||||
|
||||
return &admin_pb.ImportDataOrg{
|
||||
Orgs: orgs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func isCtxTimeout(ctx context.Context) bool {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/admin/repository"
|
||||
@ -11,6 +10,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/server"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||
@ -30,6 +30,7 @@ type Server struct {
|
||||
administrator repository.AdministratorRepository
|
||||
assetsAPIDomain func(context.Context) string
|
||||
userCodeAlg crypto.EncryptionAlgorithm
|
||||
passwordHashAlg crypto.HashAlgorithm
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@ -40,6 +41,7 @@ func CreateServer(
|
||||
database string,
|
||||
command *command.Commands,
|
||||
query *query.Queries,
|
||||
sd systemdefaults.SystemDefaults,
|
||||
repo repository.Repository,
|
||||
externalSecure bool,
|
||||
userCodeAlg crypto.EncryptionAlgorithm,
|
||||
@ -51,6 +53,7 @@ func CreateServer(
|
||||
administrator: repo,
|
||||
assetsAPIDomain: assets.AssetAPI(externalSecure),
|
||||
userCodeAlg: userCodeAlg,
|
||||
passwordHashAlg: crypto.NewBCrypt(sd.SecretGenerators.PasswordSaltCost),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ func (s *Server) GetAction(ctx context.Context, req *mgmt_pb.GetActionRequest) (
|
||||
}
|
||||
|
||||
func (s *Server) CreateAction(ctx context.Context, req *mgmt_pb.CreateActionRequest) (*mgmt_pb.CreateActionResponse, error) {
|
||||
id, details, err := s.command.AddAction(ctx, createActionRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
id, details, err := s.command.AddAction(ctx, CreateActionRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
mgmt_pb "github.com/zitadel/zitadel/pkg/grpc/management"
|
||||
)
|
||||
|
||||
func createActionRequestToDomain(req *mgmt_pb.CreateActionRequest) *domain.Action {
|
||||
func CreateActionRequestToDomain(req *mgmt_pb.CreateActionRequest) *domain.Action {
|
||||
return &domain.Action{
|
||||
Name: req.Name,
|
||||
Script: req.Script,
|
||||
|
@ -34,7 +34,7 @@ func (s *Server) ListOrgIDPs(ctx context.Context, req *mgmt_pb.ListOrgIDPsReques
|
||||
}
|
||||
|
||||
func (s *Server) AddOrgOIDCIDP(ctx context.Context, req *mgmt_pb.AddOrgOIDCIDPRequest) (*mgmt_pb.AddOrgOIDCIDPResponse, error) {
|
||||
config, err := s.command.AddIDPConfig(ctx, addOIDCIDPRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
config, err := s.command.AddIDPConfig(ctx, AddOIDCIDPRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -49,7 +49,7 @@ func (s *Server) AddOrgOIDCIDP(ctx context.Context, req *mgmt_pb.AddOrgOIDCIDPRe
|
||||
}
|
||||
|
||||
func (s *Server) AddOrgJWTIDP(ctx context.Context, req *mgmt_pb.AddOrgJWTIDPRequest) (*mgmt_pb.AddOrgJWTIDPResponse, error) {
|
||||
config, err := s.command.AddIDPConfig(ctx, addJWTIDPRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
config, err := s.command.AddIDPConfig(ctx, AddJWTIDPRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
mgmt_pb "github.com/zitadel/zitadel/pkg/grpc/management"
|
||||
)
|
||||
|
||||
func addOIDCIDPRequestToDomain(req *mgmt_pb.AddOrgOIDCIDPRequest) *domain.IDPConfig {
|
||||
func AddOIDCIDPRequestToDomain(req *mgmt_pb.AddOrgOIDCIDPRequest) *domain.IDPConfig {
|
||||
return &domain.IDPConfig{
|
||||
Name: req.Name,
|
||||
OIDCConfig: addOIDCIDPRequestToDomainOIDCIDPConfig(req),
|
||||
@ -35,7 +35,7 @@ func addOIDCIDPRequestToDomainOIDCIDPConfig(req *mgmt_pb.AddOrgOIDCIDPRequest) *
|
||||
}
|
||||
}
|
||||
|
||||
func addJWTIDPRequestToDomain(req *mgmt_pb.AddOrgJWTIDPRequest) *domain.IDPConfig {
|
||||
func AddJWTIDPRequestToDomain(req *mgmt_pb.AddOrgJWTIDPRequest) *domain.IDPConfig {
|
||||
return &domain.IDPConfig{
|
||||
Name: req.Name,
|
||||
JWTConfig: addJWTIDPRequestToDomainJWTIDPConfig(req),
|
||||
|
@ -35,7 +35,7 @@ func Test_addOIDCIDPRequestToDomain(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := addOIDCIDPRequestToDomain(tt.args.req)
|
||||
got := AddOIDCIDPRequestToDomain(tt.args.req)
|
||||
test.AssertFieldsMapped(t, got,
|
||||
"ObjectRoot",
|
||||
"OIDCConfig.ClientSecret",
|
||||
|
@ -34,7 +34,7 @@ func (s *Server) GetDefaultLabelPolicy(ctx context.Context, req *mgmt_pb.GetDefa
|
||||
}
|
||||
|
||||
func (s *Server) AddCustomLabelPolicy(ctx context.Context, req *mgmt_pb.AddCustomLabelPolicyRequest) (*mgmt_pb.AddCustomLabelPolicyResponse, error) {
|
||||
policy, err := s.command.AddLabelPolicy(ctx, authz.GetCtxData(ctx).OrgID, addLabelPolicyToDomain(req))
|
||||
policy, err := s.command.AddLabelPolicy(ctx, authz.GetCtxData(ctx).OrgID, AddLabelPolicyToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
mgmt_pb "github.com/zitadel/zitadel/pkg/grpc/management"
|
||||
)
|
||||
|
||||
func addLabelPolicyToDomain(p *mgmt_pb.AddCustomLabelPolicyRequest) *domain.LabelPolicy {
|
||||
func AddLabelPolicyToDomain(p *mgmt_pb.AddCustomLabelPolicyRequest) *domain.LabelPolicy {
|
||||
return &domain.LabelPolicy{
|
||||
PrimaryColor: p.PrimaryColor,
|
||||
BackgroundColor: p.BackgroundColor,
|
||||
|
@ -30,7 +30,7 @@ func (s *Server) GetDefaultLoginPolicy(ctx context.Context, req *mgmt_pb.GetDefa
|
||||
}
|
||||
|
||||
func (s *Server) AddCustomLoginPolicy(ctx context.Context, req *mgmt_pb.AddCustomLoginPolicyRequest) (*mgmt_pb.AddCustomLoginPolicyResponse, error) {
|
||||
policy, err := s.command.AddLoginPolicy(ctx, authz.GetCtxData(ctx).OrgID, addLoginPolicyToDomain(req))
|
||||
policy, err := s.command.AddLoginPolicy(ctx, authz.GetCtxData(ctx).OrgID, AddLoginPolicyToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
mgmt_pb "github.com/zitadel/zitadel/pkg/grpc/management"
|
||||
)
|
||||
|
||||
func addLoginPolicyToDomain(p *mgmt_pb.AddCustomLoginPolicyRequest) *domain.LoginPolicy {
|
||||
func AddLoginPolicyToDomain(p *mgmt_pb.AddCustomLoginPolicyRequest) *domain.LoginPolicy {
|
||||
return &domain.LoginPolicy{
|
||||
AllowUsernamePassword: p.AllowUsernamePassword,
|
||||
AllowRegister: p.AllowRegister,
|
||||
|
@ -184,6 +184,21 @@ func (s *Server) BulkRemoveUserMetadata(ctx context.Context, req *mgmt_pb.BulkRe
|
||||
}
|
||||
|
||||
func (s *Server) AddHumanUser(ctx context.Context, req *mgmt_pb.AddHumanUserRequest) (*mgmt_pb.AddHumanUserResponse, error) {
|
||||
details, err := s.command.AddHuman(ctx, authz.GetCtxData(ctx).OrgID, AddHumanUserRequestToAddHuman(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.AddHumanUserResponse{
|
||||
UserId: details.ID,
|
||||
Details: obj_grpc.AddToDetailsPb(
|
||||
details.Sequence,
|
||||
details.EventDate,
|
||||
details.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func AddHumanUserRequestToAddHuman(req *mgmt_pb.AddHumanUserRequest) *command.AddHuman {
|
||||
lang, err := language.Parse(req.Profile.PreferredLanguage)
|
||||
logging.OnError(err).Debug("unable to parse language")
|
||||
|
||||
@ -211,18 +226,7 @@ func (s *Server) AddHumanUser(ctx context.Context, req *mgmt_pb.AddHumanUserRequ
|
||||
Verified: req.Phone.IsPhoneVerified,
|
||||
}
|
||||
}
|
||||
details, err := s.command.AddHuman(ctx, authz.GetCtxData(ctx).OrgID, human)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.AddHumanUserResponse{
|
||||
UserId: details.ID,
|
||||
Details: obj_grpc.AddToDetailsPb(
|
||||
details.Sequence,
|
||||
details.EventDate,
|
||||
details.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
return human
|
||||
}
|
||||
|
||||
func (s *Server) ImportHumanUser(ctx context.Context, req *mgmt_pb.ImportHumanUserRequest) (*mgmt_pb.ImportHumanUserResponse, error) {
|
||||
|
@ -142,11 +142,16 @@ func ImportHumanUserRequestToDomain(req *mgmt_pb.ImportHumanUserRequest) (human
|
||||
IsPhoneVerified: req.Phone.IsPhoneVerified,
|
||||
}
|
||||
}
|
||||
|
||||
if req.Password != "" {
|
||||
human.Password = &domain.Password{SecretString: req.Password}
|
||||
human.Password = domain.NewPassword(req.Password)
|
||||
human.Password.ChangeRequired = req.PasswordChangeRequired
|
||||
}
|
||||
|
||||
if req.HashedPassword != nil && req.HashedPassword.Value != "" && req.HashedPassword.Algorithm != "" {
|
||||
human.HashedPassword = domain.NewHashedPassword(req.HashedPassword.Value, req.HashedPassword.Algorithm)
|
||||
}
|
||||
|
||||
return human, req.RequestPasswordlessRegistration
|
||||
}
|
||||
|
||||
|
@ -21,17 +21,19 @@ type OrgSetup struct {
|
||||
Roles []string
|
||||
}
|
||||
|
||||
func (c *Commands) SetUpOrg(ctx context.Context, o *OrgSetup, userIDs ...string) (string, *domain.ObjectDetails, error) {
|
||||
orgID, err := c.idGenerator.Next()
|
||||
func (c *Commands) SetUpOrgWithIDs(ctx context.Context, o *OrgSetup, orgID, userID string, userIDs ...string) (string, *domain.ObjectDetails, error) {
|
||||
existingOrg, err := c.getOrgWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
userID, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
if existingOrg != nil {
|
||||
return "", nil, errors.ThrowPreconditionFailed(nil, "COMMAND-poaj2", "Errors.Org.AlreadyExisting")
|
||||
}
|
||||
|
||||
return c.setUpOrgWithIDs(ctx, o, orgID, userID, userIDs...)
|
||||
}
|
||||
|
||||
func (c *Commands) setUpOrgWithIDs(ctx context.Context, o *OrgSetup, orgID, userID string, userIDs ...string) (string, *domain.ObjectDetails, error) {
|
||||
orgAgg := org.NewAggregate(orgID)
|
||||
userAgg := user_repo.NewAggregate(userID, orgID)
|
||||
|
||||
@ -65,6 +67,20 @@ func (c *Commands) SetUpOrg(ctx context.Context, o *OrgSetup, userIDs ...string)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Commands) SetUpOrg(ctx context.Context, o *OrgSetup, userIDs ...string) (string, *domain.ObjectDetails, error) {
|
||||
orgID, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
userID, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return c.setUpOrgWithIDs(ctx, o, orgID, userID, userIDs...)
|
||||
}
|
||||
|
||||
//AddOrgCommand defines the commands to create a new org,
|
||||
// this includes the verified default domain
|
||||
func AddOrgCommand(ctx context.Context, a *org.Aggregate, name string, userIDs ...string) preparation.Validation {
|
||||
@ -106,8 +122,33 @@ func (c *Commands) checkOrgExists(ctx context.Context, orgID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddOrgWithID(ctx context.Context, name, userID, resourceOwner, orgID string, claimedUserIDs []string) (*domain.Org, error) {
|
||||
existingOrg, err := c.getOrgWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingOrg.State != domain.OrgStateUnspecified {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-lapo2m", "Errors.Org.AlreadyExisting")
|
||||
}
|
||||
|
||||
return c.addOrgWithIDAndMember(ctx, name, userID, resourceOwner, orgID, claimedUserIDs)
|
||||
}
|
||||
|
||||
func (c *Commands) AddOrg(ctx context.Context, name, userID, resourceOwner string, claimedUserIDs []string) (*domain.Org, error) {
|
||||
orgAgg, addedOrg, events, err := c.addOrg(ctx, &domain.Org{Name: name}, claimedUserIDs)
|
||||
if name == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-Mf9sd", "Errors.Org.Invalid")
|
||||
}
|
||||
|
||||
orgID, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "COMMA-OwciI", "Errors.Internal")
|
||||
}
|
||||
|
||||
return c.addOrgWithIDAndMember(ctx, name, userID, resourceOwner, orgID, claimedUserIDs)
|
||||
}
|
||||
|
||||
func (c *Commands) addOrgWithIDAndMember(ctx context.Context, name, userID, resourceOwner, orgID string, claimedUserIDs []string) (*domain.Org, error) {
|
||||
orgAgg, addedOrg, events, err := c.addOrgWithID(ctx, &domain.Org{Name: name}, orgID, claimedUserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -136,6 +177,7 @@ func (c *Commands) ChangeOrg(ctx context.Context, orgID, name string) (*domain.O
|
||||
if orgID == "" || name == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-Mf9sd", "Errors.Org.Invalid")
|
||||
}
|
||||
|
||||
orgWriteModel, err := c.getOrgWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -242,15 +284,12 @@ func ExistsOrg(ctx context.Context, filter preparation.FilterToQueryReducer, id
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
func (c *Commands) addOrg(ctx context.Context, organisation *domain.Org, claimedUserIDs []string) (_ *eventstore.Aggregate, _ *OrgWriteModel, _ []eventstore.Command, err error) {
|
||||
func (c *Commands) addOrgWithID(ctx context.Context, organisation *domain.Org, orgID string, claimedUserIDs []string) (_ *eventstore.Aggregate, _ *OrgWriteModel, _ []eventstore.Command, err error) {
|
||||
if !organisation.IsValid() {
|
||||
return nil, nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMM-deLSk", "Errors.Org.Invalid")
|
||||
}
|
||||
|
||||
organisation.AggregateID, err = c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, nil, nil, caos_errs.ThrowInternal(err, "COMMA-OwciI", "Errors.Internal")
|
||||
}
|
||||
organisation.AggregateID = orgID
|
||||
organisation.AddIAMDomain(authz.GetInstance(ctx).RequestedDomain())
|
||||
addedOrg := NewOrgWriteModel(organisation.AggregateID)
|
||||
|
||||
|
@ -11,14 +11,33 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
func (c *Commands) AddActionWithID(ctx context.Context, addAction *domain.Action, resourceOwner, actionID string) (_ string, _ *domain.ObjectDetails, err error) {
|
||||
existingAction, err := c.getActionWriteModelByID(ctx, actionID, resourceOwner)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if existingAction.State != domain.ActionStateUnspecified {
|
||||
return "", nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-nau2k", "Errors.Action.AlreadyExisting")
|
||||
}
|
||||
|
||||
return c.addActionWithID(ctx, addAction, resourceOwner, actionID)
|
||||
}
|
||||
|
||||
func (c *Commands) AddAction(ctx context.Context, addAction *domain.Action, resourceOwner string) (_ string, _ *domain.ObjectDetails, err error) {
|
||||
if !addAction.IsValid() {
|
||||
return "", nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-eg2gf", "Errors.Action.Invalid")
|
||||
}
|
||||
addAction.AggregateID, err = c.idGenerator.Next()
|
||||
|
||||
actionID, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return c.addActionWithID(ctx, addAction, resourceOwner, actionID)
|
||||
}
|
||||
|
||||
func (c *Commands) addActionWithID(ctx context.Context, addAction *domain.Action, resourceOwner, actionID string) (_ string, _ *domain.ObjectDetails, err error) {
|
||||
addAction.AggregateID = actionID
|
||||
actionModel := NewActionWriteModel(addAction.AggregateID, resourceOwner)
|
||||
actionAgg := ActionAggregateFromWriteModel(&actionModel.WriteModel)
|
||||
|
||||
|
@ -51,7 +51,7 @@ func (c *Commands) prepareAddOrgDomain(a *org.Aggregate, addDomain string, userI
|
||||
}
|
||||
}
|
||||
|
||||
func VerifyOrgDomain(a *org.Aggregate, domain string) preparation.Validation {
|
||||
func verifyOrgDomain(a *org.Aggregate, domain string) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if domain = strings.TrimSpace(domain); domain == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "ORG-yqlVQ", "Errors.Invalid.Argument")
|
||||
@ -63,7 +63,7 @@ func VerifyOrgDomain(a *org.Aggregate, domain string) preparation.Validation {
|
||||
}
|
||||
}
|
||||
|
||||
func SetPrimaryOrgDomain(a *org.Aggregate, domain string) preparation.Validation {
|
||||
func setPrimaryOrgDomain(a *org.Aggregate, domain string) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if domain = strings.TrimSpace(domain); domain == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "ORG-gmNqY", "Errors.Invalid.Argument")
|
||||
@ -101,6 +101,19 @@ func orgDomain(ctx context.Context, filter preparation.FilterToQueryReducer, org
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
func (c *Commands) VerifyOrgDomain(ctx context.Context, orgID, domain string) (*domain.ObjectDetails, error) {
|
||||
orgAgg := org.NewAggregate(orgID)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, verifyOrgDomain(orgAgg, domain))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pushedEventsToObjectDetails(pushedEvents), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddOrgDomain(ctx context.Context, orgID, domain string, claimedUserIDs []string) (*domain.ObjectDetails, error) {
|
||||
orgAgg := org.NewAggregate(orgID)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddOrgDomain(orgAgg, domain, claimedUserIDs))
|
||||
|
@ -178,7 +178,7 @@ func TestVerifyDomain(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
AssertValidation(t, context.Background(), VerifyOrgDomain(tt.args.a, tt.args.domain), nil, tt.want)
|
||||
AssertValidation(t, context.Background(), verifyOrgDomain(tt.args.a, tt.args.domain), nil, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -273,7 +273,7 @@ func TestSetDomainPrimary(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
AssertValidation(t, context.Background(), SetPrimaryOrgDomain(tt.args.a, tt.args.domain), tt.args.filter, tt.want)
|
||||
AssertValidation(t, context.Background(), setPrimaryOrgDomain(tt.args.a, tt.args.domain), tt.args.filter, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,17 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
func (c *Commands) ImportIDPConfig(ctx context.Context, config *domain.IDPConfig, idpConfigID, resourceOwner string) (*domain.IDPConfig, error) {
|
||||
existingIDP, err := c.orgIDPConfigWriteModelByID(ctx, idpConfigID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingIDP.State != domain.IDPConfigStateRemoved && existingIDP.State != domain.IDPConfigStateUnspecified {
|
||||
return nil, errors.ThrowNotFound(nil, "Org-1J8fs", "Errors.Org.IDPConfig.AlreadyExisting")
|
||||
}
|
||||
return c.addIDPConfig(ctx, config, idpConfigID, resourceOwner)
|
||||
}
|
||||
|
||||
func (c *Commands) AddIDPConfig(ctx context.Context, config *domain.IDPConfig, resourceOwner string) (*domain.IDPConfig, error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "Org-0j8gs", "Errors.ResourceOwnerMissing")
|
||||
@ -18,11 +29,16 @@ func (c *Commands) AddIDPConfig(ctx context.Context, config *domain.IDPConfig, r
|
||||
if config.OIDCConfig == nil && config.JWTConfig == nil {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "Org-eUpQU", "Errors.idp.config.notset")
|
||||
}
|
||||
|
||||
idpConfigID, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.addIDPConfig(ctx, config, idpConfigID, resourceOwner)
|
||||
}
|
||||
|
||||
func (c *Commands) addIDPConfig(ctx context.Context, config *domain.IDPConfig, idpConfigID, resourceOwner string) (*domain.IDPConfig, error) {
|
||||
|
||||
addedConfig := NewOrgIDPConfigWriteModel(idpConfigID, resourceOwner)
|
||||
|
||||
orgAgg := OrgAggregateFromWriteModel(&addedConfig.WriteModel)
|
||||
|
@ -13,11 +13,46 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/repository/project"
|
||||
)
|
||||
|
||||
func (c *Commands) AddProject(ctx context.Context, project *domain.Project, resourceOwner, ownerUserID string) (_ *domain.Project, err error) {
|
||||
events, addedProject, err := c.addProject(ctx, project, resourceOwner, ownerUserID)
|
||||
func (c *Commands) AddProjectWithID(ctx context.Context, project *domain.Project, resourceOwner, projectID string) (_ *domain.Project, err error) {
|
||||
existingProject, err := c.getProjectWriteModelByID(ctx, projectID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingProject.State != domain.ProjectStateUnspecified {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-opamwu", "Errors.Project.AlreadyExisting")
|
||||
}
|
||||
return c.addProjectWithID(ctx, project, resourceOwner, projectID)
|
||||
}
|
||||
|
||||
func (c *Commands) AddProject(ctx context.Context, project *domain.Project, resourceOwner, ownerUserID string) (_ *domain.Project, err error) {
|
||||
if !project.IsValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-IOVCC", "Errors.Project.Invalid")
|
||||
}
|
||||
|
||||
projectID, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.addProjectWithIDWithOwner(ctx, project, resourceOwner, ownerUserID, projectID)
|
||||
}
|
||||
|
||||
func (c *Commands) addProjectWithID(ctx context.Context, projectAdd *domain.Project, resourceOwner, projectID string) (_ *domain.Project, err error) {
|
||||
projectAdd.AggregateID = projectID
|
||||
addedProject := NewProjectWriteModel(projectAdd.AggregateID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedProject.WriteModel)
|
||||
|
||||
events := []eventstore.Command{
|
||||
project.NewProjectAddedEvent(
|
||||
ctx,
|
||||
projectAgg,
|
||||
projectAdd.Name,
|
||||
projectAdd.ProjectRoleAssertion,
|
||||
projectAdd.ProjectRoleCheck,
|
||||
projectAdd.HasProjectCheck,
|
||||
projectAdd.PrivateLabelingSetting),
|
||||
}
|
||||
|
||||
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -29,14 +64,11 @@ func (c *Commands) AddProject(ctx context.Context, project *domain.Project, reso
|
||||
return projectWriteModelToProject(addedProject), nil
|
||||
}
|
||||
|
||||
func (c *Commands) addProject(ctx context.Context, projectAdd *domain.Project, resourceOwner, ownerUserID string) (_ []eventstore.Command, _ *ProjectWriteModel, err error) {
|
||||
func (c *Commands) addProjectWithIDWithOwner(ctx context.Context, projectAdd *domain.Project, resourceOwner, ownerUserID, projectID string) (_ *domain.Project, err error) {
|
||||
if !projectAdd.IsValid() {
|
||||
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-IOVCC", "Errors.Project.Invalid")
|
||||
}
|
||||
projectAdd.AggregateID, err = c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-IOVCC", "Errors.Project.Invalid")
|
||||
}
|
||||
projectAdd.AggregateID = projectID
|
||||
addedProject := NewProjectWriteModel(projectAdd.AggregateID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedProject.WriteModel)
|
||||
|
||||
@ -52,7 +84,16 @@ func (c *Commands) addProject(ctx context.Context, projectAdd *domain.Project, r
|
||||
projectAdd.PrivateLabelingSetting),
|
||||
project.NewProjectMemberAddedEvent(ctx, projectAgg, ownerUserID, projectRole),
|
||||
}
|
||||
return events, addedProject, nil
|
||||
|
||||
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(addedProject, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return projectWriteModelToProject(addedProject), nil
|
||||
}
|
||||
|
||||
func AddProjectCommand(
|
||||
|
@ -70,21 +70,70 @@ func (c *Commands) AddAPIAppCommand(app *addAPIApp, clientSecretAlg crypto.HashA
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) AddAPIApplication(ctx context.Context, application *domain.APIApp, resourceOwner string, appSecretGenerator crypto.Generator) (_ *domain.APIApp, err error) {
|
||||
if application == nil || application.AggregateID == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "PROJECT-5m9E", "Errors.Application.Invalid")
|
||||
}
|
||||
project, err := c.getProjectByID(ctx, application.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowPreconditionFailed(err, "PROJECT-9fnsf", "Errors.Project.NotFound")
|
||||
}
|
||||
addedApplication := NewAPIApplicationWriteModel(application.AggregateID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
|
||||
events, stringPw, err := c.addAPIApplication(ctx, projectAgg, project, application, resourceOwner, appSecretGenerator)
|
||||
func (c *Commands) AddAPIApplicationWithID(ctx context.Context, apiApp *domain.APIApp, resourceOwner, appID string, appSecretGenerator crypto.Generator) (_ *domain.APIApp, err error) {
|
||||
existingAPI, err := c.getAPIAppWriteModel(ctx, apiApp.AggregateID, appID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addedApplication.AppID = application.AppID
|
||||
if existingAPI.State != domain.AppStateUnspecified {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "PROJECT-mabu12", "Errors.Application.AlreadyExisting")
|
||||
}
|
||||
project, err := c.getProjectByID(ctx, apiApp.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowPreconditionFailed(err, "PROJECT-9fnsa", "Errors.Project.NotFound")
|
||||
}
|
||||
|
||||
return c.addAPIApplicationWithID(ctx, apiApp, resourceOwner, project, appID, appSecretGenerator)
|
||||
}
|
||||
|
||||
func (c *Commands) AddAPIApplication(ctx context.Context, apiApp *domain.APIApp, resourceOwner string, appSecretGenerator crypto.Generator) (_ *domain.APIApp, err error) {
|
||||
if apiApp == nil || apiApp.AggregateID == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "PROJECT-5m9E", "Errors.Application.Invalid")
|
||||
}
|
||||
project, err := c.getProjectByID(ctx, apiApp.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowPreconditionFailed(err, "PROJECT-9fnsf", "Errors.Project.NotFound")
|
||||
}
|
||||
|
||||
if !apiApp.IsValid() {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "PROJECT-Bff2g", "Errors.Application.Invalid")
|
||||
}
|
||||
|
||||
appID, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.addAPIApplicationWithID(ctx, apiApp, resourceOwner, project, appID, appSecretGenerator)
|
||||
}
|
||||
|
||||
func (c *Commands) addAPIApplicationWithID(ctx context.Context, apiApp *domain.APIApp, resourceOwner string, project *domain.Project, appID string, appSecretGenerator crypto.Generator) (_ *domain.APIApp, err error) {
|
||||
apiApp.AppID = appID
|
||||
|
||||
addedApplication := NewAPIApplicationWriteModel(apiApp.AggregateID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
|
||||
|
||||
events := []eventstore.Command{
|
||||
project_repo.NewApplicationAddedEvent(ctx, projectAgg, apiApp.AppID, apiApp.AppName),
|
||||
}
|
||||
|
||||
var stringPw string
|
||||
err = domain.SetNewClientID(apiApp, c.idGenerator, project)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stringPw, err = domain.SetNewClientSecretIfNeeded(apiApp, appSecretGenerator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events = append(events, project_repo.NewAPIConfigAddedEvent(ctx,
|
||||
projectAgg,
|
||||
apiApp.AppID,
|
||||
apiApp.ClientID,
|
||||
apiApp.ClientSecret,
|
||||
apiApp.AuthMethodType))
|
||||
|
||||
addedApplication.AppID = apiApp.AppID
|
||||
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -98,38 +147,6 @@ func (c *Commands) AddAPIApplication(ctx context.Context, application *domain.AP
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Commands) addAPIApplication(ctx context.Context, projectAgg *eventstore.Aggregate, proj *domain.Project, apiAppApp *domain.APIApp, resourceOwner string, appSecretGenerator crypto.Generator) (events []eventstore.Command, stringPW string, err error) {
|
||||
if !apiAppApp.IsValid() {
|
||||
return nil, "", errors.ThrowInvalidArgument(nil, "PROJECT-Bff2g", "Errors.Application.Invalid")
|
||||
}
|
||||
apiAppApp.AppID, err = c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
events = []eventstore.Command{
|
||||
project_repo.NewApplicationAddedEvent(ctx, projectAgg, apiAppApp.AppID, apiAppApp.AppName),
|
||||
}
|
||||
|
||||
var stringPw string
|
||||
err = domain.SetNewClientID(apiAppApp, c.idGenerator, proj)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
stringPw, err = domain.SetNewClientSecretIfNeeded(apiAppApp, appSecretGenerator)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
events = append(events, project_repo.NewAPIConfigAddedEvent(ctx,
|
||||
projectAgg,
|
||||
apiAppApp.AppID,
|
||||
apiAppApp.ClientID,
|
||||
apiAppApp.ClientSecret,
|
||||
apiAppApp.AuthMethodType))
|
||||
|
||||
return events, stringPw, nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeAPIApplication(ctx context.Context, apiApp *domain.APIApp, resourceOwner string) (*domain.APIApp, error) {
|
||||
if apiApp.AppID == "" || apiApp.AggregateID == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "COMMAND-1m900", "Errors.Project.App.APIConfigInvalid")
|
||||
|
@ -116,56 +116,63 @@ func (c *Commands) AddOIDCAppCommand(app *addOIDCApp, clientSecretAlg crypto.Has
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) AddOIDCApplication(ctx context.Context, application *domain.OIDCApp, resourceOwner string, appSecretGenerator crypto.Generator) (_ *domain.OIDCApp, err error) {
|
||||
if application == nil || application.AggregateID == "" {
|
||||
func (c *Commands) AddOIDCApplicationWithID(ctx context.Context, oidcApp *domain.OIDCApp, resourceOwner, appID string, appSecretGenerator crypto.Generator) (_ *domain.OIDCApp, err error) {
|
||||
existingApp, err := c.getOIDCAppWriteModel(ctx, oidcApp.AggregateID, appID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingApp.State != domain.AppStateUnspecified {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-lxowmp", "Errors.Application.AlreadyExisting")
|
||||
}
|
||||
|
||||
project, err := c.getProjectByID(ctx, oidcApp.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "PROJECT-3m9s2", "Errors.Project.NotFound")
|
||||
}
|
||||
|
||||
return c.addOIDCApplicationWithID(ctx, oidcApp, resourceOwner, project, appID, appSecretGenerator)
|
||||
}
|
||||
|
||||
func (c *Commands) AddOIDCApplication(ctx context.Context, oidcApp *domain.OIDCApp, resourceOwner string, appSecretGenerator crypto.Generator) (_ *domain.OIDCApp, err error) {
|
||||
if oidcApp == nil || oidcApp.AggregateID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-34Fm0", "Errors.Application.Invalid")
|
||||
}
|
||||
project, err := c.getProjectByID(ctx, application.AggregateID, resourceOwner)
|
||||
project, err := c.getProjectByID(ctx, oidcApp.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "PROJECT-3m9ss", "Errors.Project.NotFound")
|
||||
}
|
||||
addedApplication := NewOIDCApplicationWriteModel(application.AggregateID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
|
||||
events, stringPw, err := c.addOIDCApplication(ctx, projectAgg, project, application, resourceOwner, appSecretGenerator)
|
||||
|
||||
if oidcApp.AppName == "" || !oidcApp.IsValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-1n8df", "Errors.Application.Invalid")
|
||||
}
|
||||
|
||||
appID, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addedApplication.AppID = application.AppID
|
||||
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(addedApplication, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := oidcWriteModelToOIDCConfig(addedApplication)
|
||||
result.ClientSecretString = stringPw
|
||||
result.FillCompliance()
|
||||
return result, nil
|
||||
|
||||
return c.addOIDCApplicationWithID(ctx, oidcApp, resourceOwner, project, appID, appSecretGenerator)
|
||||
}
|
||||
|
||||
func (c *Commands) addOIDCApplication(ctx context.Context, projectAgg *eventstore.Aggregate, proj *domain.Project, oidcApp *domain.OIDCApp, resourceOwner string, appSecretGenerator crypto.Generator) (events []eventstore.Command, stringPW string, err error) {
|
||||
if oidcApp.AppName == "" || !oidcApp.IsValid() {
|
||||
return nil, "", caos_errs.ThrowInvalidArgument(nil, "PROJECT-1n8df", "Errors.Application.Invalid")
|
||||
}
|
||||
oidcApp.AppID, err = c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
func (c *Commands) addOIDCApplicationWithID(ctx context.Context, oidcApp *domain.OIDCApp, resourceOwner string, project *domain.Project, appID string, appSecretGenerator crypto.Generator) (_ *domain.OIDCApp, err error) {
|
||||
|
||||
events = []eventstore.Command{
|
||||
addedApplication := NewOIDCApplicationWriteModel(oidcApp.AggregateID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
|
||||
|
||||
oidcApp.AppID = appID
|
||||
|
||||
events := []eventstore.Command{
|
||||
project_repo.NewApplicationAddedEvent(ctx, projectAgg, oidcApp.AppID, oidcApp.AppName),
|
||||
}
|
||||
|
||||
var stringPw string
|
||||
err = domain.SetNewClientID(oidcApp, c.idGenerator, proj)
|
||||
err = domain.SetNewClientID(oidcApp, c.idGenerator, project)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
stringPw, err = domain.SetNewClientSecretIfNeeded(oidcApp, appSecretGenerator)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
events = append(events, project_repo.NewOIDCConfigAddedEvent(ctx,
|
||||
projectAgg,
|
||||
@ -187,7 +194,19 @@ func (c *Commands) addOIDCApplication(ctx context.Context, projectAgg *eventstor
|
||||
oidcApp.ClockSkew,
|
||||
oidcApp.AdditionalOrigins))
|
||||
|
||||
return events, stringPw, nil
|
||||
addedApplication.AppID = oidcApp.AppID
|
||||
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(addedApplication, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := oidcWriteModelToOIDCConfig(addedApplication)
|
||||
result.ClientSecretString = stringPw
|
||||
result.FillCompliance()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeOIDCApplication(ctx context.Context, oidc *domain.OIDCApp, resourceOwner string) (*domain.OIDCApp, error) {
|
||||
|
@ -12,6 +12,18 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
func (c *Commands) AddProjectGrantWithID(ctx context.Context, grant *domain.ProjectGrant, grantID string, resourceOwner string) (_ *domain.ProjectGrant, err error) {
|
||||
existingMember, err := c.projectGrantWriteModelByID(ctx, grantID, grant.AggregateID, resourceOwner)
|
||||
if err != nil && !caos_errs.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
if existingMember != nil && existingMember.State != domain.ProjectGrantStateUnspecified {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-2b8fs", "Errors.Project.Grant.AlreadyExisting")
|
||||
}
|
||||
|
||||
return c.addProjectGrantWithID(ctx, grant, grantID, resourceOwner)
|
||||
}
|
||||
|
||||
func (c *Commands) AddProjectGrant(ctx context.Context, grant *domain.ProjectGrant, resourceOwner string) (_ *domain.ProjectGrant, err error) {
|
||||
if !grant.IsValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-3b8fs", "Errors.Project.Grant.Invalid")
|
||||
@ -20,10 +32,18 @@ func (c *Commands) AddProjectGrant(ctx context.Context, grant *domain.ProjectGra
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grant.GrantID, err = c.idGenerator.Next()
|
||||
|
||||
grantID, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.addProjectGrantWithID(ctx, grant, grantID, resourceOwner)
|
||||
}
|
||||
|
||||
func (c *Commands) addProjectGrantWithID(ctx context.Context, grant *domain.ProjectGrant, grantID string, resourceOwner string) (_ *domain.ProjectGrant, err error) {
|
||||
grant.GrantID = grantID
|
||||
|
||||
addedGrant := NewProjectGrantWriteModel(grant.GrantID, grant.AggregateID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedGrant.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(
|
||||
|
@ -47,6 +47,8 @@ type AddHuman struct {
|
||||
Phone Phone
|
||||
//Password is optional
|
||||
Password string
|
||||
//BcryptedPassword is optional
|
||||
BcryptedPassword string
|
||||
//PasswordChangeRequired is used if the `Password`-field is set
|
||||
PasswordChangeRequired bool
|
||||
Passwordless bool
|
||||
@ -54,14 +56,19 @@ type AddHuman struct {
|
||||
Register bool
|
||||
}
|
||||
|
||||
func (c *Commands) AddHuman(ctx context.Context, resourceOwner string, human *AddHuman) (*domain.HumanDetails, error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "COMMA-5Ky74", "Errors.Internal")
|
||||
}
|
||||
userID, err := c.idGenerator.Next()
|
||||
func (c *Commands) AddHumanWithID(ctx context.Context, resourceOwner string, userID string, human *AddHuman) (*domain.HumanDetails, error) {
|
||||
existingHuman, err := c.getHumanWriteModelByID(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isUserStateExists(existingHuman.UserState) {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "COMMAND-k2unb", "Errors.User.AlreadyExisting")
|
||||
}
|
||||
|
||||
return c.addHumanWithID(ctx, resourceOwner, userID, human)
|
||||
}
|
||||
|
||||
func (c *Commands) addHumanWithID(ctx context.Context, resourceOwner string, userID string, human *AddHuman) (*domain.HumanDetails, error) {
|
||||
agg := user.NewAggregate(userID, resourceOwner)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, AddHumanCommand(agg, human, c.userPasswordAlg, c.userEncryption))
|
||||
if err != nil {
|
||||
@ -83,6 +90,18 @@ func (c *Commands) AddHuman(ctx context.Context, resourceOwner string, human *Ad
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddHuman(ctx context.Context, resourceOwner string, human *AddHuman) (*domain.HumanDetails, error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "COMMA-5Ky74", "Errors.Internal")
|
||||
}
|
||||
userID, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.addHumanWithID(ctx, resourceOwner, userID, human)
|
||||
}
|
||||
|
||||
type humanCreationCommand interface {
|
||||
eventstore.Command
|
||||
AddPhoneData(phoneNumber string)
|
||||
@ -167,6 +186,10 @@ func AddHumanCommand(a *user.Aggregate, human *AddHuman, passwordAlg crypto.Hash
|
||||
createCmd.AddPasswordData(secret, human.PasswordChangeRequired)
|
||||
}
|
||||
|
||||
if human.BcryptedPassword != "" {
|
||||
createCmd.AddPasswordData(crypto.FillHash([]byte(human.BcryptedPassword), passwordAlg), human.PasswordChangeRequired)
|
||||
}
|
||||
|
||||
cmds := make([]eventstore.Command, 0, 3)
|
||||
cmds = append(cmds, createCmd)
|
||||
|
||||
@ -274,6 +297,18 @@ func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain.
|
||||
if err != nil {
|
||||
return nil, nil, errors.ThrowPreconditionFailed(err, "COMMAND-4N8gs", "Errors.Org.PasswordComplexityPolicy.NotFound")
|
||||
}
|
||||
|
||||
if human.AggregateID != "" {
|
||||
existing, err := c.getHumanWriteModelByID(ctx, human.AggregateID, human.ResourceOwner)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if existing.UserState != domain.UserStateUnspecified {
|
||||
return nil, nil, errors.ThrowPreconditionFailed(nil, "COMMAND-ziuna", "Errors.User.AlreadyExisting")
|
||||
}
|
||||
}
|
||||
|
||||
events, addedHuman, addedCode, code, err := c.importHuman(ctx, orgID, human, passwordless, domainPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator, passwordlessCodeGenerator)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -355,8 +390,8 @@ func (c *Commands) addHuman(ctx context.Context, orgID string, human *domain.Hum
|
||||
if orgID == "" || !human.IsValid() {
|
||||
return nil, nil, errors.ThrowInvalidArgument(nil, "COMMAND-67Ms8", "Errors.User.Invalid")
|
||||
}
|
||||
if human.Password != nil && human.SecretString != "" {
|
||||
human.ChangeRequired = true
|
||||
if human.Password != nil && human.Password.SecretString != "" {
|
||||
human.Password.ChangeRequired = true
|
||||
}
|
||||
return c.createHuman(ctx, orgID, human, nil, false, false, domainPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
|
||||
}
|
||||
@ -384,11 +419,11 @@ func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domai
|
||||
if human != nil && human.Username == "" {
|
||||
human.Username = human.EmailAddress
|
||||
}
|
||||
if orgID == "" || !human.IsValid() || link == nil && (human.Password == nil || human.SecretString == "") {
|
||||
if orgID == "" || !human.IsValid() || link == nil && (human.Password == nil || human.Password.SecretString == "") {
|
||||
return nil, nil, errors.ThrowInvalidArgument(nil, "COMMAND-9dk45", "Errors.User.Invalid")
|
||||
}
|
||||
if human.Password != nil && human.SecretString != "" {
|
||||
human.ChangeRequired = false
|
||||
if human.Password != nil && human.Password.SecretString != "" {
|
||||
human.Password.ChangeRequired = false
|
||||
}
|
||||
return c.createHuman(ctx, orgID, human, link, true, false, domainPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
|
||||
}
|
||||
@ -410,14 +445,18 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
|
||||
return nil, nil, errors.ThrowInvalidArgument(nil, "COMMAND-SFd21", "Errors.User.DomainNotAllowedAsUsername")
|
||||
}
|
||||
}
|
||||
|
||||
if human.AggregateID == "" {
|
||||
userID, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
human.AggregateID = userID
|
||||
}
|
||||
|
||||
human.SetNamesAsDisplayname()
|
||||
if human.Password != nil {
|
||||
if err := human.HashPasswordIfExisting(pwPolicy, c.userPasswordAlg, human.ChangeRequired); err != nil {
|
||||
if err := human.HashPasswordIfExisting(pwPolicy, c.userPasswordAlg, human.Password.ChangeRequired); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
@ -510,7 +549,10 @@ func createAddHumanEvent(ctx context.Context, aggregate *eventstore.Aggregate, h
|
||||
human.StreetAddress)
|
||||
}
|
||||
if human.Password != nil {
|
||||
addEvent.AddPasswordData(human.SecretCrypto, human.ChangeRequired)
|
||||
addEvent.AddPasswordData(human.Password.SecretCrypto, human.Password.ChangeRequired)
|
||||
}
|
||||
if human.HashedPassword != nil {
|
||||
addEvent.AddPasswordData(human.HashedPassword.SecretCrypto, false)
|
||||
}
|
||||
return addEvent
|
||||
}
|
||||
@ -541,7 +583,10 @@ func createRegisterHumanEvent(ctx context.Context, aggregate *eventstore.Aggrega
|
||||
human.StreetAddress)
|
||||
}
|
||||
if human.Password != nil {
|
||||
addEvent.AddPasswordData(human.SecretCrypto, human.ChangeRequired)
|
||||
addEvent.AddPasswordData(human.Password.SecretCrypto, human.Password.ChangeRequired)
|
||||
}
|
||||
if human.HashedPassword != nil {
|
||||
addEvent.AddPasswordData(human.HashedPassword.SecretCrypto, false)
|
||||
}
|
||||
return addEvent
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
@ -11,6 +12,28 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
func (c *Commands) ImportHumanOTP(ctx context.Context, userID, userAgentID, resourceowner string, key string) error {
|
||||
encryptedSecret, err := crypto.Encrypt([]byte(key), c.multifactors.OTP.CryptoMFA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
otpWriteModel, err := c.otpWriteModelByID(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if otpWriteModel.State == domain.MFAStateReady {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "COMMAND-do9se", "Errors.User.MFA.OTP.AlreadyReady")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&otpWriteModel.WriteModel)
|
||||
|
||||
_, err = c.eventstore.Push(ctx,
|
||||
user.NewHumanOTPAddedEvent(ctx, userAgg, encryptedSecret),
|
||||
user.NewHumanOTPVerifiedEvent(ctx, userAgg, userAgentID),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string) (*domain.OTP, error) {
|
||||
if userID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-5M0sd", "Errors.User.UserIDMissing")
|
||||
@ -30,6 +53,7 @@ func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string
|
||||
logging.Log("COMMAND-y5zv9").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org policy for loginname")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-8ugTs", "Errors.Org.DomainPolicy.NotFound")
|
||||
}
|
||||
|
||||
otpWriteModel, err := c.otpWriteModelByID(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -38,6 +62,7 @@ func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-do9se", "Errors.User.MFA.OTP.AlreadyReady")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&otpWriteModel.WriteModel)
|
||||
|
||||
accountName := domain.GenerateLoginName(human.GetUsername(), org.PrimaryDomain, orgPolicy.UserLoginMustBeDomain)
|
||||
if accountName == "" {
|
||||
accountName = human.EmailAddress
|
||||
@ -46,8 +71,8 @@ func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = c.eventstore.Push(ctx, user.NewHumanOTPAddedEvent(ctx, userAgg, secret))
|
||||
|
||||
_, err = c.eventstore.Push(ctx, user.NewHumanOTPAddedEvent(ctx, userAgg, secret))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -11,6 +11,23 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
func (c *Commands) AddUserIDPLink(ctx context.Context, userID, resourceOwner string, link *domain.UserIDPLink) (err error) {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-03j8f", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
linkWriteModel := NewUserIDPLinkWriteModel(userID, link.IDPConfigID, link.ExternalUserID, resourceOwner)
|
||||
userAgg := UserAggregateFromWriteModel(&linkWriteModel.WriteModel)
|
||||
|
||||
event, err := c.addUserIDPLink(ctx, userAgg, link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.eventstore.Push(ctx, event)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Commands) BulkAddedUserIDPLinks(ctx context.Context, userID, resourceOwner string, links []*domain.UserIDPLink) (err error) {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-03j8f", "Errors.IDMissing")
|
||||
|
@ -2,7 +2,6 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
@ -24,6 +23,29 @@ func (c *Commands) AddMachine(ctx context.Context, orgID string, machine *domain
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.addMachineWithID(ctx, orgID, userID, machine, domainPolicy)
|
||||
}
|
||||
|
||||
func (c *Commands) AddMachineWithID(ctx context.Context, orgID string, userID string, machine *domain.Machine) (*domain.Machine, error) {
|
||||
existingMachine, err := c.machineWriteModelByID(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isUserStateExists(existingMachine.UserState) {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-k2una", "Errors.User.AlreadyExisting")
|
||||
}
|
||||
domainPolicy, err := c.getOrgDomainPolicy(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-3M9fs", "Errors.Org.DomainPolicy.NotFound")
|
||||
}
|
||||
if !domainPolicy.UserLoginMustBeDomain {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0dd", "Errors.User.Invalid")
|
||||
}
|
||||
return c.addMachineWithID(ctx, orgID, userID, machine, domainPolicy)
|
||||
}
|
||||
|
||||
func (c *Commands) addMachineWithID(ctx context.Context, orgID string, userID string, machine *domain.Machine, domainPolicy *domain.DomainPolicy) (*domain.Machine, error) {
|
||||
|
||||
machine.AggregateID = userID
|
||||
addedMachine := NewMachineWriteModel(machine.AggregateID, orgID)
|
||||
userAgg := UserAggregateFromWriteModel(&addedMachine.WriteModel)
|
||||
|
@ -124,3 +124,11 @@ func CompareHash(value *CryptoValue, comparer []byte, alg HashAlgorithm) error {
|
||||
}
|
||||
return alg.CompareHash(value.Crypted, comparer)
|
||||
}
|
||||
|
||||
func FillHash(value []byte, alg HashAlgorithm) *CryptoValue {
|
||||
return &CryptoValue{
|
||||
CryptoType: TypeHash,
|
||||
Algorithm: alg.Algorithm(),
|
||||
Crypted: value,
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ type Human struct {
|
||||
Username string
|
||||
State UserState
|
||||
*Password
|
||||
*HashedPassword
|
||||
*Profile
|
||||
*Email
|
||||
*Phone
|
||||
@ -88,7 +89,7 @@ func (u *Human) HashPasswordIfExisting(policy *PasswordComplexityPolicy, passwor
|
||||
}
|
||||
|
||||
func (u *Human) IsInitialState(passwordless, externalIDPs bool) bool {
|
||||
return u.Email == nil || !u.IsEmailVerified || !externalIDPs && !passwordless && (u.Password == nil || u.SecretString == "")
|
||||
return u.Email == nil || !u.IsEmailVerified || !externalIDPs && !passwordless && (u.Password == nil || u.Password.SecretString == "") && (u.HashedPassword == nil || u.HashedPassword.SecretString == "")
|
||||
}
|
||||
|
||||
func NewInitUserCode(generator crypto.Generator) (*InitUserCode, error) {
|
||||
|
24
internal/domain/human_hashed_password.go
Normal file
24
internal/domain/human_hashed_password.go
Normal file
@ -0,0 +1,24 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
type HashedPassword struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
SecretString string
|
||||
SecretCrypto *crypto.CryptoValue
|
||||
}
|
||||
|
||||
func NewHashedPassword(password, algorithm string) *HashedPassword {
|
||||
return &HashedPassword{
|
||||
SecretString: password,
|
||||
SecretCrypto: &crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeHash,
|
||||
Algorithm: algorithm,
|
||||
Crypted: []byte(password),
|
||||
},
|
||||
}
|
||||
}
|
@ -168,7 +168,7 @@ func (h *ProjectionHandler) subscribe(ctx context.Context) {
|
||||
|
||||
index, err := h.Process(ctx, events...)
|
||||
if err != nil || index < len(events)-1 {
|
||||
logging.WithFields("projection", h.ProjectionName).WithError(err).Error("unable to process all events from subscription")
|
||||
logging.WithFields("projection", h.ProjectionName).WithError(err).Warn("unable to process all events from subscription")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -502,3 +502,15 @@ func prepareIDPsQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPs, error)) {
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Queries) GetOIDCIDPClientSecret(ctx context.Context, shouldRealTime bool, resourceowner, idpID string) (string, error) {
|
||||
idp, err := q.IDPByIDAndResourceOwner(ctx, shouldRealTime, idpID, resourceowner)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if idp.ClientSecret != nil && idp.ClientSecret.Crypted != nil {
|
||||
return crypto.DecryptString(idp.ClientSecret, q.idpConfigEncryption)
|
||||
}
|
||||
return "", errors.ThrowNotFound(nil, "QUERY-bsm2o", "Errors.Query.NotFound")
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
sd "github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
@ -27,6 +29,8 @@ type Queries struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
client *sql.DB
|
||||
|
||||
idpConfigEncryption crypto.EncryptionAlgorithm
|
||||
|
||||
DefaultLanguage language.Tag
|
||||
LoginDir http.FileSystem
|
||||
NotificationDir http.FileSystem
|
||||
@ -35,9 +39,10 @@ type Queries struct {
|
||||
NotificationTranslationFileContents map[string][]byte
|
||||
supportedLangs []language.Tag
|
||||
zitadelRoles []authz.RoleMapping
|
||||
multifactors domain.MultifactorConfigs
|
||||
}
|
||||
|
||||
func StartQueries(ctx context.Context, es *eventstore.Eventstore, sqlClient *sql.DB, projections projection.Config, keyEncryptionAlgorithm crypto.EncryptionAlgorithm, zitadelRoles []authz.RoleMapping) (repo *Queries, err error) {
|
||||
func StartQueries(ctx context.Context, es *eventstore.Eventstore, sqlClient *sql.DB, projections projection.Config, defaults sd.SystemDefaults, idpConfigEncryption, otpEncryption, keyEncryptionAlgorithm crypto.EncryptionAlgorithm, zitadelRoles []authz.RoleMapping) (repo *Queries, err error) {
|
||||
statikLoginFS, err := fs.NewWithNamespace("login")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to start login statik dir")
|
||||
@ -66,6 +71,14 @@ func StartQueries(ctx context.Context, es *eventstore.Eventstore, sqlClient *sql
|
||||
keypair.RegisterEventMappers(repo.eventstore)
|
||||
usergrant.RegisterEventMappers(repo.eventstore)
|
||||
|
||||
repo.idpConfigEncryption = idpConfigEncryption
|
||||
repo.multifactors = domain.MultifactorConfigs{
|
||||
OTP: domain.OTPConfig{
|
||||
CryptoMFA: otpEncryption,
|
||||
Issuer: defaults.Multifactors.OTP.Issuer,
|
||||
},
|
||||
}
|
||||
|
||||
err = projection.Start(ctx, sqlClient, es, projections, keyEncryptionAlgorithm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
91
internal/query/user_otp.go
Normal file
91
internal/query/user_otp.go
Normal file
@ -0,0 +1,91 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
func (q *Queries) GetHumanOTPSecret(ctx context.Context, userID, resourceowner string) (string, error) {
|
||||
if userID == "" {
|
||||
return "", caos_errs.ThrowPreconditionFailed(nil, "QUERY-8N9ds", "Errors.User.UserIDMissing")
|
||||
}
|
||||
existingOTP, err := q.otpWriteModelByID(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if existingOTP.State != domain.MFAStateReady {
|
||||
return "", caos_errs.ThrowNotFound(nil, "QUERY-01982h", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
return crypto.DecryptString(existingOTP.Secret, q.multifactors.OTP.CryptoMFA)
|
||||
}
|
||||
|
||||
func (q *Queries) otpWriteModelByID(ctx context.Context, userID, resourceOwner string) (writeModel *HumanOTPWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
writeModel = NewHumanOTPWriteModel(userID, resourceOwner)
|
||||
err = q.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
||||
|
||||
type HumanOTPWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
State domain.MFAState
|
||||
Secret *crypto.CryptoValue
|
||||
}
|
||||
|
||||
func NewHumanOTPWriteModel(userID, resourceOwner string) *HumanOTPWriteModel {
|
||||
return &HumanOTPWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanOTPWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanOTPAddedEvent:
|
||||
wm.Secret = e.Secret
|
||||
wm.State = domain.MFAStateNotReady
|
||||
case *user.HumanOTPVerifiedEvent:
|
||||
wm.State = domain.MFAStateReady
|
||||
case *user.HumanOTPRemovedEvent:
|
||||
wm.State = domain.MFAStateRemoved
|
||||
case *user.UserRemovedEvent:
|
||||
wm.State = domain.MFAStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *HumanOTPWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
AddQuery().
|
||||
AggregateTypes(user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
EventTypes(user.HumanMFAOTPAddedType,
|
||||
user.HumanMFAOTPVerifiedType,
|
||||
user.HumanMFAOTPRemovedType,
|
||||
user.UserRemovedType,
|
||||
user.UserV1MFAOTPAddedType,
|
||||
user.UserV1MFAOTPVerifiedType,
|
||||
user.UserV1MFAOTPRemovedType).
|
||||
Builder()
|
||||
|
||||
if wm.ResourceOwner != "" {
|
||||
query.ResourceOwner(wm.ResourceOwner)
|
||||
}
|
||||
return query
|
||||
}
|
140
internal/query/user_password.go
Normal file
140
internal/query/user_password.go
Normal file
@ -0,0 +1,140 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HumanPasswordWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
Secret *crypto.CryptoValue
|
||||
SecretChangeRequired bool
|
||||
|
||||
Code *crypto.CryptoValue
|
||||
CodeCreationDate time.Time
|
||||
CodeExpiry time.Duration
|
||||
PasswordCheckFailedCount uint64
|
||||
|
||||
UserState domain.UserState
|
||||
}
|
||||
|
||||
func (q *Queries) GetHumanPassword(ctx context.Context, orgID, userID string) (passwordHash []byte, algorithm string, err error) {
|
||||
if userID == "" {
|
||||
return nil, "", caos_errs.ThrowInvalidArgument(nil, "QUERY-4Mfsf", "Errors.User.UserIDMissing")
|
||||
}
|
||||
existingPassword, err := q.passwordWriteModel(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return nil, "", caos_errs.ThrowInternal(nil, "QUERY-p1k1n2i", "Errors.User.NotFound")
|
||||
}
|
||||
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
|
||||
return nil, "", caos_errs.ThrowPreconditionFailed(nil, "QUERY-3n77z", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
if existingPassword.Secret != nil && existingPassword.Secret.Crypted != nil {
|
||||
return existingPassword.Secret.Crypted, existingPassword.Secret.Algorithm, nil
|
||||
}
|
||||
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
func (q *Queries) passwordWriteModel(ctx context.Context, userID, resourceOwner string) (writeModel *HumanPasswordWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
writeModel = NewHumanPasswordWriteModel(userID, resourceOwner)
|
||||
err = q.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
||||
|
||||
func NewHumanPasswordWriteModel(userID, resourceOwner string) *HumanPasswordWriteModel {
|
||||
return &HumanPasswordWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanPasswordWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanAddedEvent:
|
||||
wm.Secret = e.Secret
|
||||
wm.SecretChangeRequired = e.ChangeRequired
|
||||
wm.UserState = domain.UserStateActive
|
||||
case *user.HumanRegisteredEvent:
|
||||
wm.Secret = e.Secret
|
||||
wm.SecretChangeRequired = e.ChangeRequired
|
||||
wm.UserState = domain.UserStateActive
|
||||
case *user.HumanInitialCodeAddedEvent:
|
||||
wm.UserState = domain.UserStateInitial
|
||||
case *user.HumanInitializedCheckSucceededEvent:
|
||||
wm.UserState = domain.UserStateActive
|
||||
case *user.HumanPasswordChangedEvent:
|
||||
wm.Secret = e.Secret
|
||||
wm.SecretChangeRequired = e.ChangeRequired
|
||||
wm.Code = nil
|
||||
wm.PasswordCheckFailedCount = 0
|
||||
case *user.HumanPasswordCodeAddedEvent:
|
||||
wm.Code = e.Code
|
||||
wm.CodeCreationDate = e.CreationDate()
|
||||
wm.CodeExpiry = e.Expiry
|
||||
case *user.HumanEmailVerifiedEvent:
|
||||
if wm.UserState == domain.UserStateInitial {
|
||||
wm.UserState = domain.UserStateActive
|
||||
}
|
||||
case *user.HumanPasswordCheckFailedEvent:
|
||||
wm.PasswordCheckFailedCount += 1
|
||||
case *user.HumanPasswordCheckSucceededEvent:
|
||||
wm.PasswordCheckFailedCount = 0
|
||||
case *user.UserUnlockedEvent:
|
||||
wm.PasswordCheckFailedCount = 0
|
||||
case *user.UserRemovedEvent:
|
||||
wm.UserState = domain.UserStateDeleted
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *HumanPasswordWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
AddQuery().
|
||||
AggregateTypes(user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
EventTypes(user.HumanAddedType,
|
||||
user.HumanRegisteredType,
|
||||
user.HumanInitialCodeAddedType,
|
||||
user.HumanInitializedCheckSucceededType,
|
||||
user.HumanPasswordChangedType,
|
||||
user.HumanPasswordCodeAddedType,
|
||||
user.HumanEmailVerifiedType,
|
||||
user.HumanPasswordCheckFailedType,
|
||||
user.HumanPasswordCheckSucceededType,
|
||||
user.UserRemovedType,
|
||||
user.UserUnlockedType,
|
||||
user.UserV1AddedType,
|
||||
user.UserV1RegisteredType,
|
||||
user.UserV1InitialCodeAddedType,
|
||||
user.UserV1InitializedCheckSucceededType,
|
||||
user.UserV1PasswordChangedType,
|
||||
user.UserV1PasswordCodeAddedType,
|
||||
user.UserV1EmailVerifiedType,
|
||||
user.UserV1PasswordCheckFailedType,
|
||||
user.UserV1PasswordCheckSucceededType).
|
||||
Builder()
|
||||
|
||||
if wm.ResourceOwner != "" {
|
||||
query.ResourceOwner(wm.ResourceOwner)
|
||||
}
|
||||
return query
|
||||
}
|
@ -10,6 +10,8 @@ import "zitadel/policy.proto";
|
||||
import "zitadel/settings.proto";
|
||||
import "zitadel/text.proto";
|
||||
import "zitadel/member.proto";
|
||||
import "zitadel/management.proto";
|
||||
import "zitadel/v1.proto";
|
||||
|
||||
import "google/api/annotations.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
@ -2629,6 +2631,29 @@ service AdminService {
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Imports data into instance and creates different objects
|
||||
rpc ImportData(ImportDataRequest) returns (ImportDataResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/import";
|
||||
body: "*"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "iam.write";
|
||||
};
|
||||
}
|
||||
// Exports data from instance
|
||||
rpc ExportData(ExportDataRequest) returns (ExportDataResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/export";
|
||||
body: "*"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "iam.read";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4621,3 +4646,187 @@ message FailedEvent {
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message ImportDataRequest {
|
||||
message LocalInput{
|
||||
string path = 1;
|
||||
}
|
||||
message S3Input{
|
||||
string path = 1;
|
||||
string endpoint = 2;
|
||||
string access_key_id =3;
|
||||
string secret_access_key = 4;
|
||||
bool ssl = 5;
|
||||
string bucket = 6;
|
||||
}
|
||||
message GCSInput{
|
||||
string bucket = 1;
|
||||
string serviceaccount_json = 2;
|
||||
string path = 3;
|
||||
}
|
||||
|
||||
oneof data {
|
||||
ImportDataOrg data_orgs = 1;
|
||||
zitadel.v1.v1.ImportDataOrg data_orgsv1 = 2;
|
||||
LocalInput data_orgs_local = 3;
|
||||
LocalInput data_orgsv1_local = 4;
|
||||
S3Input data_orgs_s3 = 5;
|
||||
S3Input data_orgsv1_s3 = 6;
|
||||
GCSInput data_orgs_gcs = 7;
|
||||
GCSInput data_orgsv1_gcs = 8;
|
||||
}
|
||||
string timeout = 9;
|
||||
}
|
||||
|
||||
message ImportDataOrg {
|
||||
repeated DataOrg orgs = 1;
|
||||
}
|
||||
|
||||
message DataOrg {
|
||||
string org_id = 1;
|
||||
zitadel.management.v1.AddOrgRequest org = 3;
|
||||
AddCustomDomainPolicyRequest domain_policy = 4;
|
||||
zitadel.management.v1.AddCustomLabelPolicyRequest label_policy = 5;
|
||||
zitadel.management.v1.AddCustomLockoutPolicyRequest lockout_policy = 6;
|
||||
zitadel.management.v1.AddCustomLoginPolicyRequest login_policy = 7;
|
||||
zitadel.management.v1.AddCustomPasswordComplexityPolicyRequest password_complexity_policy = 8;
|
||||
zitadel.management.v1.AddCustomPrivacyPolicyRequest privacy_policy = 9;
|
||||
|
||||
repeated zitadel.v1.v1.DataProject projects = 10;
|
||||
repeated zitadel.management.v1.AddProjectRoleRequest project_roles = 11;
|
||||
repeated zitadel.v1.v1.DataAPIApplication api_apps = 12;
|
||||
repeated zitadel.v1.v1.DataOIDCApplication oidc_apps = 13;
|
||||
repeated zitadel.v1.v1.DataHumanUser human_users = 14;
|
||||
repeated zitadel.v1.v1.DataMachineUser machine_users = 15;
|
||||
repeated zitadel.management.v1.SetTriggerActionsRequest trigger_actions = 16;
|
||||
repeated zitadel.v1.v1.DataAction actions = 17;
|
||||
|
||||
repeated zitadel.v1.v1.DataProjectGrant project_grants = 18;
|
||||
repeated zitadel.management.v1.AddUserGrantRequest user_grants = 19;
|
||||
|
||||
repeated zitadel.management.v1.AddOrgMemberRequest org_members = 20;
|
||||
repeated zitadel.management.v1.AddProjectMemberRequest project_members = 21;
|
||||
repeated zitadel.management.v1.AddProjectGrantMemberRequest project_grant_members = 22;
|
||||
|
||||
repeated zitadel.management.v1.SetUserMetadataRequest user_metadata = 23;
|
||||
|
||||
repeated zitadel.management.v1.SetCustomLoginTextsRequest login_texts = 24;
|
||||
|
||||
repeated zitadel.management.v1.SetCustomInitMessageTextRequest init_messages = 25;
|
||||
repeated zitadel.management.v1.SetCustomPasswordResetMessageTextRequest password_reset_messages = 26;
|
||||
repeated zitadel.management.v1.SetCustomVerifyEmailMessageTextRequest verify_email_messages = 27;
|
||||
repeated zitadel.management.v1.SetCustomVerifyPhoneMessageTextRequest verify_phone_messages = 28;
|
||||
repeated zitadel.management.v1.SetCustomDomainClaimedMessageTextRequest domain_claimed_messages = 29;
|
||||
repeated zitadel.management.v1.SetCustomPasswordlessRegistrationMessageTextRequest passwordless_registration_messages = 30;
|
||||
|
||||
repeated zitadel.v1.v1.DataOIDCIDP oidc_idps = 31;
|
||||
repeated zitadel.v1.v1.DataJWTIDP jwt_idps = 32;
|
||||
|
||||
repeated zitadel.idp.v1.IDPUserLink user_links = 33;
|
||||
repeated zitadel.org.v1.Domain domains = 34;
|
||||
}
|
||||
|
||||
message ImportDataResponse{
|
||||
repeated ImportDataError errors = 1;
|
||||
ImportDataSuccess success = 2;
|
||||
}
|
||||
|
||||
message ImportDataError{
|
||||
string type = 1;
|
||||
string id = 2;
|
||||
string message = 3;
|
||||
}
|
||||
|
||||
message ImportDataSuccess {
|
||||
repeated ImportDataSuccessOrg orgs = 1;
|
||||
}
|
||||
|
||||
message ImportDataSuccessOrg{
|
||||
string org_id = 1;
|
||||
repeated string project_ids = 2;
|
||||
repeated string project_roles = 3;
|
||||
repeated string oidc_app_ids = 4;
|
||||
repeated string api_app_ids = 5;
|
||||
repeated string human_user_ids = 6;
|
||||
repeated string machine_user_ids = 7;
|
||||
repeated string action_ids = 8;
|
||||
repeated zitadel.management.v1.SetTriggerActionsRequest trigger_actions = 9;
|
||||
repeated ImportDataSuccessProjectGrant project_grants = 10;
|
||||
repeated ImportDataSuccessUserGrant user_grants = 11;
|
||||
repeated string org_members = 12;
|
||||
repeated ImportDataSuccessProjectMember project_members = 13;
|
||||
repeated ImportDataSuccessProjectGrantMember project_grant_members = 14;
|
||||
repeated string oidc_ipds = 15;
|
||||
repeated string jwt_idps = 16;
|
||||
repeated string idp_links = 17;
|
||||
repeated ImportDataSuccessUserLinks user_links = 18;
|
||||
repeated ImportDataSuccessUserMetadata user_metadata = 19;
|
||||
repeated string domains = 20;
|
||||
}
|
||||
|
||||
message ImportDataSuccessProjectGrant{
|
||||
string grant_id = 1;
|
||||
string project_id = 2;
|
||||
string org_id = 3;
|
||||
}
|
||||
|
||||
message ImportDataSuccessUserGrant{
|
||||
string project_id = 1;
|
||||
string user_id = 2;
|
||||
}
|
||||
|
||||
message ImportDataSuccessProjectMember{
|
||||
string project_id = 1;
|
||||
string user_id = 2;
|
||||
}
|
||||
|
||||
message ImportDataSuccessProjectGrantMember{
|
||||
string project_id = 1;
|
||||
string grant_id = 2;
|
||||
string user_id = 3;
|
||||
}
|
||||
|
||||
message ImportDataSuccessUserLinks {
|
||||
string user_id = 1;
|
||||
string external_user_id = 2;
|
||||
string display_name = 3;
|
||||
string idp_id = 4;
|
||||
}
|
||||
|
||||
message ImportDataSuccessUserMetadata {
|
||||
string user_id = 1;
|
||||
string key = 2;
|
||||
}
|
||||
|
||||
message ExportDataRequest {
|
||||
message LocalOutput{
|
||||
string path = 1;
|
||||
}
|
||||
message S3Output{
|
||||
string path = 1;
|
||||
string endpoint = 2;
|
||||
string access_key_id =3;
|
||||
string secret_access_key = 4;
|
||||
bool ssl = 5;
|
||||
string bucket = 6;
|
||||
}
|
||||
message GCSOutput{
|
||||
string bucket = 1;
|
||||
string serviceaccount_json = 2;
|
||||
string path = 3;
|
||||
}
|
||||
|
||||
repeated string org_ids = 1;
|
||||
repeated string excluded_org_ids = 2;
|
||||
bool with_passwords = 3;
|
||||
bool with_otp = 4;
|
||||
bool response_output = 5;
|
||||
LocalOutput local_output = 6;
|
||||
S3Output s3_output = 7;
|
||||
GCSOutput gcs_output = 8;
|
||||
string timeout = 9;
|
||||
}
|
||||
|
||||
message ExportDataResponse {
|
||||
repeated DataOrg orgs = 1;
|
||||
}
|
@ -3007,6 +3007,10 @@ message ImportHumanUserRequest {
|
||||
string phone = 1 [(validate.rules).string = {min_len: 1, max_len: 50, prefix: "+"}];
|
||||
bool is_phone_verified = 2;
|
||||
}
|
||||
message HashedPassword{
|
||||
string value = 1;
|
||||
string algorithm = 2;
|
||||
}
|
||||
|
||||
string user_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
|
||||
@ -3014,8 +3018,11 @@ message ImportHumanUserRequest {
|
||||
Email email = 3 [(validate.rules).message.required = true];
|
||||
Phone phone = 4;
|
||||
string password = 5;
|
||||
bool password_change_required = 6;
|
||||
bool request_passwordless_registration = 7;
|
||||
HashedPassword hashed_password = 6;
|
||||
bool password_change_required = 7;
|
||||
bool request_passwordless_registration = 8;
|
||||
|
||||
string otp_code = 9;
|
||||
}
|
||||
|
||||
message ImportHumanUserResponse {
|
||||
|
@ -11,7 +11,7 @@ import "validate/validate.proto";
|
||||
|
||||
package zitadel.system.v1;
|
||||
|
||||
option go_package ="github.com/zitadel/zitadel/pkg/grpc/system";
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/system";
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
@ -205,7 +205,6 @@ service SystemService {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//Returns all stored read models of ZITADEL
|
||||
// views are used for search optimisation and optimise request latencies
|
||||
// they represent the delta of the event happend on the objects
|
||||
|
161
proto/zitadel/v1.proto
Normal file
161
proto/zitadel/v1.proto
Normal file
@ -0,0 +1,161 @@
|
||||
syntax = "proto3";
|
||||
|
||||
import "zitadel/user.proto";
|
||||
import "zitadel/idp.proto";
|
||||
import "zitadel/org.proto";
|
||||
import "zitadel/management.proto";
|
||||
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
|
||||
import "validate/validate.proto";
|
||||
|
||||
package zitadel.v1.v1;
|
||||
|
||||
option go_package ="github.com/zitadel/zitadel/pkg/grpc/v1";
|
||||
|
||||
message AddCustomOrgIAMPolicyRequest {
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
|
||||
json_schema: {
|
||||
required: ["org_id"]
|
||||
};
|
||||
};
|
||||
|
||||
string org_id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"#69629023906488334\"";
|
||||
min_length: 1;
|
||||
max_length: 200;
|
||||
}
|
||||
];
|
||||
bool user_login_must_be_domain = 2 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "the username has to end with the domain of it's organisation"
|
||||
}
|
||||
]; // the username has to end with the domain of it's organisation (uniqueness is organisation based)
|
||||
}
|
||||
message ImportDataOrg {
|
||||
repeated DataOrg orgs = 1;
|
||||
}
|
||||
|
||||
message DataOrg {
|
||||
string org_id = 1;
|
||||
zitadel.management.v1.AddOrgRequest org = 3;
|
||||
AddCustomOrgIAMPolicyRequest iam_policy = 4;
|
||||
zitadel.management.v1.AddCustomLabelPolicyRequest label_policy = 5;
|
||||
zitadel.management.v1.AddCustomLockoutPolicyRequest lockout_policy = 6;
|
||||
zitadel.management.v1.AddCustomLoginPolicyRequest login_policy = 7;
|
||||
zitadel.management.v1.AddCustomPasswordComplexityPolicyRequest password_complexity_policy = 8;
|
||||
zitadel.management.v1.AddCustomPrivacyPolicyRequest privacy_policy = 9;
|
||||
|
||||
repeated DataProject projects = 10;
|
||||
repeated zitadel.management.v1.AddProjectRoleRequest project_roles = 11;
|
||||
repeated DataAPIApplication api_apps = 12;
|
||||
repeated DataOIDCApplication oidc_apps = 13;
|
||||
repeated DataHumanUser human_users = 14;
|
||||
repeated DataMachineUser machine_users = 15;
|
||||
repeated zitadel.management.v1.SetTriggerActionsRequest trigger_actions = 16;
|
||||
repeated DataAction actions = 17;
|
||||
|
||||
repeated DataProjectGrant project_grants = 18;
|
||||
repeated zitadel.management.v1.AddUserGrantRequest user_grants = 19;
|
||||
|
||||
repeated zitadel.management.v1.AddOrgMemberRequest org_members = 20;
|
||||
repeated zitadel.management.v1.AddProjectMemberRequest project_members = 21;
|
||||
repeated zitadel.management.v1.AddProjectGrantMemberRequest project_grant_members = 22;
|
||||
|
||||
repeated zitadel.management.v1.SetUserMetadataRequest user_metadata = 23;
|
||||
|
||||
repeated zitadel.management.v1.SetCustomLoginTextsRequest login_texts = 24;
|
||||
|
||||
repeated zitadel.management.v1.SetCustomInitMessageTextRequest init_messages = 25;
|
||||
repeated zitadel.management.v1.SetCustomPasswordResetMessageTextRequest password_reset_messages = 26;
|
||||
repeated zitadel.management.v1.SetCustomVerifyEmailMessageTextRequest verify_email_messages = 27;
|
||||
repeated zitadel.management.v1.SetCustomVerifyPhoneMessageTextRequest verify_phone_messages = 28;
|
||||
repeated zitadel.management.v1.SetCustomDomainClaimedMessageTextRequest domain_claimed_messages = 29;
|
||||
repeated zitadel.management.v1.SetCustomPasswordlessRegistrationMessageTextRequest passwordless_registration_messages = 30;
|
||||
|
||||
repeated DataOIDCIDP oidc_idps = 31;
|
||||
repeated DataJWTIDP jwt_idps = 32;
|
||||
|
||||
repeated zitadel.management.v1.AddSecondFactorToLoginPolicyRequest second_factors = 33;
|
||||
repeated zitadel.management.v1.AddMultiFactorToLoginPolicyRequest multi_factors = 34;
|
||||
repeated zitadel.management.v1.AddIDPToLoginPolicyRequest idps = 35;
|
||||
|
||||
repeated zitadel.idp.v1.IDPUserLink user_links = 36;
|
||||
repeated zitadel.org.v1.Domain domains = 37;
|
||||
}
|
||||
message DataOIDCIDP{
|
||||
string idp_id = 1;
|
||||
zitadel.management.v1.AddOrgOIDCIDPRequest idp = 2;
|
||||
}
|
||||
message DataJWTIDP{
|
||||
string idp_id = 1;
|
||||
zitadel.management.v1.AddOrgJWTIDPRequest idp = 32;
|
||||
}
|
||||
|
||||
message ExportHumanUser {
|
||||
message Profile {
|
||||
string first_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string last_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string nick_name = 3 [(validate.rules).string = {max_len: 200}];
|
||||
string display_name = 4 [(validate.rules).string = {max_len: 200}];
|
||||
string preferred_language = 5 [(validate.rules).string = {max_len: 10}];
|
||||
zitadel.user.v1.Gender gender = 6;
|
||||
}
|
||||
message Email {
|
||||
string email = 1 [(validate.rules).string.email = true]; //TODO: check if no value is allowed
|
||||
bool is_email_verified = 2;
|
||||
}
|
||||
message Phone {
|
||||
// has to be a global number
|
||||
string phone = 1 [(validate.rules).string = {min_len: 1, max_len: 50, prefix: "+"}];
|
||||
bool is_phone_verified = 2;
|
||||
}
|
||||
message HashedPassword{
|
||||
string value = 1;
|
||||
string algorithm = 2;
|
||||
}
|
||||
|
||||
string user_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
|
||||
Profile profile = 2 [(validate.rules).message.required = true];
|
||||
Email email = 3 [(validate.rules).message.required = true];
|
||||
Phone phone = 4;
|
||||
string password = 5;
|
||||
HashedPassword hashed_password = 6;
|
||||
bool password_change_required = 7;
|
||||
bool request_passwordless_registration = 8;
|
||||
|
||||
string otp_code = 9;
|
||||
}
|
||||
|
||||
|
||||
message DataProject {
|
||||
string project_id = 1;
|
||||
zitadel.management.v1.AddProjectRequest project = 2;
|
||||
}
|
||||
message DataAPIApplication {
|
||||
string app_id = 1;
|
||||
zitadel.management.v1.AddAPIAppRequest app = 2;
|
||||
}
|
||||
message DataOIDCApplication {
|
||||
string app_id = 1;
|
||||
zitadel.management.v1.AddOIDCAppRequest app = 2;
|
||||
}
|
||||
message DataHumanUser {
|
||||
string user_id = 1;
|
||||
zitadel.management.v1.ImportHumanUserRequest user = 2;
|
||||
}
|
||||
message DataMachineUser {
|
||||
string user_id = 1;
|
||||
zitadel.management.v1.AddMachineUserRequest user = 2;
|
||||
}
|
||||
message DataAction {
|
||||
string action_id = 1;
|
||||
zitadel.management.v1.CreateActionRequest action = 2;
|
||||
}
|
||||
message DataProjectGrant {
|
||||
string grant_id = 1;
|
||||
zitadel.management.v1.AddProjectGrantRequest project_grant = 2;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user