mirror of
https://github.com/zitadel/zitadel.git
synced 2025-05-30 07:58:21 +00:00
feat: invite user link (#8578)
# Which Problems Are Solved As an administrator I want to be able to invite users to my application with the API V2, some user data I will already prefil, the user should add the authentication method themself (password, passkey, sso). # How the Problems Are Solved - A user can now be created with a email explicitly set to false. - If a user has no verified email and no authentication method, an `InviteCode` can be created through the User V2 API. - the code can be returned or sent through email - additionally `URLTemplate` and an `ApplicatioName` can provided for the email - The code can be resent and verified through the User V2 API - The V1 login allows users to verify and resend the code and set a password (analog user initialization) - The message text for the user invitation can be customized # Additional Changes - `verifyUserPasskeyCode` directly uses `crypto.VerifyCode` (instead of `verifyEncryptedCode`) - `verifyEncryptedCode` is removed (unnecessarily queried for the code generator) # Additional Context - closes #8310 - TODO: login V2 will have to implement invite flow: https://github.com/zitadel/typescript/issues/166
This commit is contained in:
parent
02c78a19c6
commit
a07b2f4677
@ -712,6 +712,13 @@ DefaultInstance:
|
|||||||
IncludeUpperLetters: false # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_OTPEMAIL_INCLUDEUPPERLETTERS
|
IncludeUpperLetters: false # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_OTPEMAIL_INCLUDEUPPERLETTERS
|
||||||
IncludeDigits: true # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_OTPEMAIL_INCLUDEDIGITS
|
IncludeDigits: true # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_OTPEMAIL_INCLUDEDIGITS
|
||||||
IncludeSymbols: false # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_OTPEMAIL_INCLUDESYMBOLS
|
IncludeSymbols: false # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_OTPEMAIL_INCLUDESYMBOLS
|
||||||
|
InviteCode:
|
||||||
|
Length: 6 # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_INITIALIZEUSERCODE_LENGTH
|
||||||
|
Expiry: "72h" # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_INITIALIZEUSERCODE_EXPIRY
|
||||||
|
IncludeLowerLetters: false # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_INITIALIZEUSERCODE_INCLUDELOWERLETTERS
|
||||||
|
IncludeUpperLetters: true # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_INITIALIZEUSERCODE_INCLUDEUPPERLETTERS
|
||||||
|
IncludeDigits: true # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_INITIALIZEUSERCODE_INCLUDEDIGITS
|
||||||
|
IncludeSymbols: false # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_INITIALIZEUSERCODE_INCLUDESYMBOLS
|
||||||
PasswordComplexityPolicy:
|
PasswordComplexityPolicy:
|
||||||
MinLength: 8 # ZITADEL_DEFAULTINSTANCE_PASSWORDCOMPLEXITYPOLICY_MINLENGTH
|
MinLength: 8 # ZITADEL_DEFAULTINSTANCE_PASSWORDCOMPLEXITYPOLICY_MINLENGTH
|
||||||
HasLowercase: true # ZITADEL_DEFAULTINSTANCE_PASSWORDCOMPLEXITYPOLICY_HASLOWERCASE
|
HasLowercase: true # ZITADEL_DEFAULTINSTANCE_PASSWORDCOMPLEXITYPOLICY_HASLOWERCASE
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
GetDefaultInitMessageTextRequest as AdminGetDefaultInitMessageTextRequest,
|
GetDefaultInitMessageTextRequest as AdminGetDefaultInitMessageTextRequest,
|
||||||
GetDefaultPasswordChangeMessageTextRequest as AdminGetDefaultPasswordChangeMessageTextRequest,
|
GetDefaultPasswordChangeMessageTextRequest as AdminGetDefaultPasswordChangeMessageTextRequest,
|
||||||
GetDefaultPasswordlessRegistrationMessageTextRequest as AdminGetDefaultPasswordlessRegistrationMessageTextRequest,
|
GetDefaultPasswordlessRegistrationMessageTextRequest as AdminGetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||||
|
GetDefaultInviteUserMessageTextRequest as AdminGetDefaultInviteUserMessageTextRequest,
|
||||||
GetDefaultPasswordResetMessageTextRequest as AdminGetDefaultPasswordResetMessageTextRequest,
|
GetDefaultPasswordResetMessageTextRequest as AdminGetDefaultPasswordResetMessageTextRequest,
|
||||||
GetDefaultVerifyEmailMessageTextRequest as AdminGetDefaultVerifyEmailMessageTextRequest,
|
GetDefaultVerifyEmailMessageTextRequest as AdminGetDefaultVerifyEmailMessageTextRequest,
|
||||||
GetDefaultVerifyEmailOTPMessageTextRequest as AdminGetDefaultVerifyEmailOTPMessageTextRequest,
|
GetDefaultVerifyEmailOTPMessageTextRequest as AdminGetDefaultVerifyEmailOTPMessageTextRequest,
|
||||||
@ -16,6 +17,7 @@ import {
|
|||||||
SetDefaultInitMessageTextRequest,
|
SetDefaultInitMessageTextRequest,
|
||||||
SetDefaultPasswordChangeMessageTextRequest,
|
SetDefaultPasswordChangeMessageTextRequest,
|
||||||
SetDefaultPasswordlessRegistrationMessageTextRequest,
|
SetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||||
|
SetDefaultInviteUserMessageTextRequest,
|
||||||
SetDefaultPasswordResetMessageTextRequest,
|
SetDefaultPasswordResetMessageTextRequest,
|
||||||
SetDefaultVerifyEmailMessageTextRequest,
|
SetDefaultVerifyEmailMessageTextRequest,
|
||||||
SetDefaultVerifyEmailOTPMessageTextRequest,
|
SetDefaultVerifyEmailOTPMessageTextRequest,
|
||||||
@ -27,6 +29,7 @@ import {
|
|||||||
GetCustomInitMessageTextRequest,
|
GetCustomInitMessageTextRequest,
|
||||||
GetCustomPasswordChangeMessageTextRequest,
|
GetCustomPasswordChangeMessageTextRequest,
|
||||||
GetCustomPasswordlessRegistrationMessageTextRequest,
|
GetCustomPasswordlessRegistrationMessageTextRequest,
|
||||||
|
GetCustomInviteUserMessageTextRequest,
|
||||||
GetCustomPasswordResetMessageTextRequest,
|
GetCustomPasswordResetMessageTextRequest,
|
||||||
GetCustomVerifyEmailMessageTextRequest,
|
GetCustomVerifyEmailMessageTextRequest,
|
||||||
GetCustomVerifyEmailOTPMessageTextRequest,
|
GetCustomVerifyEmailOTPMessageTextRequest,
|
||||||
@ -36,6 +39,7 @@ import {
|
|||||||
GetDefaultInitMessageTextRequest,
|
GetDefaultInitMessageTextRequest,
|
||||||
GetDefaultPasswordChangeMessageTextRequest,
|
GetDefaultPasswordChangeMessageTextRequest,
|
||||||
GetDefaultPasswordlessRegistrationMessageTextRequest,
|
GetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||||
|
GetDefaultInviteUserMessageTextRequest,
|
||||||
GetDefaultPasswordResetMessageTextRequest,
|
GetDefaultPasswordResetMessageTextRequest,
|
||||||
GetDefaultVerifyEmailMessageTextRequest,
|
GetDefaultVerifyEmailMessageTextRequest,
|
||||||
GetDefaultVerifyEmailOTPMessageTextRequest,
|
GetDefaultVerifyEmailOTPMessageTextRequest,
|
||||||
@ -45,6 +49,7 @@ import {
|
|||||||
SetCustomInitMessageTextRequest,
|
SetCustomInitMessageTextRequest,
|
||||||
SetCustomPasswordChangeMessageTextRequest,
|
SetCustomPasswordChangeMessageTextRequest,
|
||||||
SetCustomPasswordlessRegistrationMessageTextRequest,
|
SetCustomPasswordlessRegistrationMessageTextRequest,
|
||||||
|
SetCustomInviteUserMessageTextRequest,
|
||||||
SetCustomPasswordResetMessageTextRequest,
|
SetCustomPasswordResetMessageTextRequest,
|
||||||
SetCustomVerifyEmailMessageTextRequest,
|
SetCustomVerifyEmailMessageTextRequest,
|
||||||
SetCustomVerifyEmailOTPMessageTextRequest,
|
SetCustomVerifyEmailOTPMessageTextRequest,
|
||||||
@ -73,6 +78,7 @@ enum MESSAGETYPES {
|
|||||||
PASSWORDCHANGE = 'PC',
|
PASSWORDCHANGE = 'PC',
|
||||||
VERIFYSMSOTP = 'VSO',
|
VERIFYSMSOTP = 'VSO',
|
||||||
VERIFYEMAILOTP = 'VEO',
|
VERIFYEMAILOTP = 'VEO',
|
||||||
|
INVITEUSER = 'IU',
|
||||||
}
|
}
|
||||||
|
|
||||||
const REQUESTMAP = {
|
const REQUESTMAP = {
|
||||||
@ -226,6 +232,23 @@ const REQUESTMAP = {
|
|||||||
req.setText(map.text ?? '');
|
req.setText(map.text ?? '');
|
||||||
req.setTitle(map.title ?? '');
|
req.setTitle(map.title ?? '');
|
||||||
|
|
||||||
|
return req;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[MESSAGETYPES.INVITEUSER]: {
|
||||||
|
get: new GetCustomInviteUserMessageTextRequest(),
|
||||||
|
set: new SetCustomInviteUserMessageTextRequest(),
|
||||||
|
getDefault: new GetDefaultInviteUserMessageTextRequest(),
|
||||||
|
setFcn: (map: Partial<SetCustomInviteUserMessageTextRequest.AsObject>): SetCustomInviteUserMessageTextRequest => {
|
||||||
|
const req = new SetCustomInviteUserMessageTextRequest();
|
||||||
|
req.setButtonText(map.buttonText ?? '');
|
||||||
|
req.setFooterText(map.footerText ?? '');
|
||||||
|
req.setGreeting(map.greeting ?? '');
|
||||||
|
req.setPreHeader(map.preHeader ?? '');
|
||||||
|
req.setSubject(map.subject ?? '');
|
||||||
|
req.setText(map.text ?? '');
|
||||||
|
req.setTitle(map.title ?? '');
|
||||||
|
|
||||||
return req;
|
return req;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -371,6 +394,22 @@ const REQUESTMAP = {
|
|||||||
req.setText(map.text ?? '');
|
req.setText(map.text ?? '');
|
||||||
req.setTitle(map.title ?? '');
|
req.setTitle(map.title ?? '');
|
||||||
|
|
||||||
|
return req;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[MESSAGETYPES.INVITEUSER]: {
|
||||||
|
get: new AdminGetDefaultInviteUserMessageTextRequest(),
|
||||||
|
set: new SetDefaultInviteUserMessageTextRequest(),
|
||||||
|
setFcn: (map: Partial<SetDefaultInviteUserMessageTextRequest.AsObject>): SetDefaultInviteUserMessageTextRequest => {
|
||||||
|
const req = new SetDefaultInviteUserMessageTextRequest();
|
||||||
|
req.setButtonText(map.buttonText ?? '');
|
||||||
|
req.setFooterText(map.footerText ?? '');
|
||||||
|
req.setGreeting(map.greeting ?? '');
|
||||||
|
req.setPreHeader(map.preHeader ?? '');
|
||||||
|
req.setSubject(map.subject ?? '');
|
||||||
|
req.setText(map.text ?? '');
|
||||||
|
req.setTitle(map.title ?? '');
|
||||||
|
|
||||||
return req;
|
return req;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -540,6 +579,21 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
|||||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.loginnames', value: '{{.LoginNames}}' },
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.loginnames', value: '{{.LoginNames}}' },
|
||||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.changedate', value: '{{.ChangeDate}}' },
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.changedate', value: '{{.ChangeDate}}' },
|
||||||
],
|
],
|
||||||
|
[MESSAGETYPES.INVITEUSER]: [
|
||||||
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.preferredLoginName', value: '{{.PreferredLoginName}}' },
|
||||||
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.username', value: '{{.UserName}}' },
|
||||||
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.firstname', value: '{{.FirstName}}' },
|
||||||
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastname', value: '{{.LastName}}' },
|
||||||
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.nickName', value: '{{.NickName}}' },
|
||||||
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.displayName', value: '{{.DisplayName}}' },
|
||||||
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastEmail', value: '{{.LastEmail}}' },
|
||||||
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.verifiedEmail', value: '{{.VerifiedEmail}}' },
|
||||||
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastPhone', value: '{{.LastPhone}}' },
|
||||||
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.verifiedPhone', value: '{{.VerifiedPhone}}' },
|
||||||
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.loginnames', value: '{{.LoginNames}}' },
|
||||||
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.changedate', value: '{{.ChangeDate}}' },
|
||||||
|
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.applicationName', value: '{{.ApplicationName}}' },
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
public language: string = 'en';
|
public language: string = 'en';
|
||||||
@ -599,6 +653,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
|||||||
return this.stripEmail(this.service.getDefaultPasswordlessRegistrationMessageText(req));
|
return this.stripEmail(this.service.getDefaultPasswordlessRegistrationMessageText(req));
|
||||||
case MESSAGETYPES.PASSWORDCHANGE:
|
case MESSAGETYPES.PASSWORDCHANGE:
|
||||||
return this.stripEmail(this.service.getDefaultPasswordChangeMessageText(req));
|
return this.stripEmail(this.service.getDefaultPasswordChangeMessageText(req));
|
||||||
|
case MESSAGETYPES.INVITEUSER:
|
||||||
|
return this.stripEmail(this.service.getDefaultInviteUserMessageText(req));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -622,6 +678,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
|||||||
return this.stripEmail(this.service.getCustomPasswordlessRegistrationMessageText(req));
|
return this.stripEmail(this.service.getCustomPasswordlessRegistrationMessageText(req));
|
||||||
case MESSAGETYPES.PASSWORDCHANGE:
|
case MESSAGETYPES.PASSWORDCHANGE:
|
||||||
return this.stripEmail(this.service.getCustomPasswordChangeMessageText(req));
|
return this.stripEmail(this.service.getCustomPasswordChangeMessageText(req));
|
||||||
|
case MESSAGETYPES.INVITEUSER:
|
||||||
|
return this.stripEmail(this.service.getCustomInviteUserMessageText(req));
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -690,6 +748,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
case MESSAGETYPES.PASSWORDCHANGE:
|
case MESSAGETYPES.PASSWORDCHANGE:
|
||||||
return handler((this.service as ManagementService).setCustomPasswordChangeMessageText(this.updateRequest));
|
return handler((this.service as ManagementService).setCustomPasswordChangeMessageText(this.updateRequest));
|
||||||
|
case MESSAGETYPES.INVITEUSER:
|
||||||
|
return handler((this.service as ManagementService).setCustomInviteUserMessageText(this.updateRequest));
|
||||||
}
|
}
|
||||||
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||||
switch (this.currentType) {
|
switch (this.currentType) {
|
||||||
@ -711,6 +771,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
|||||||
return handler((this.service as AdminService).setDefaultPasswordlessRegistrationMessageText(this.updateRequest));
|
return handler((this.service as AdminService).setDefaultPasswordlessRegistrationMessageText(this.updateRequest));
|
||||||
case MESSAGETYPES.PASSWORDCHANGE:
|
case MESSAGETYPES.PASSWORDCHANGE:
|
||||||
return handler((this.service as AdminService).setDefaultPasswordChangeMessageText(this.updateRequest));
|
return handler((this.service as AdminService).setDefaultPasswordChangeMessageText(this.updateRequest));
|
||||||
|
case MESSAGETYPES.INVITEUSER:
|
||||||
|
return handler((this.service as AdminService).setDefaultInviteUserMessageText(this.updateRequest));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -763,6 +825,8 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
|||||||
return handler(this.service.resetCustomPasswordlessRegistrationMessageTextToDefault(this.language));
|
return handler(this.service.resetCustomPasswordlessRegistrationMessageTextToDefault(this.language));
|
||||||
case MESSAGETYPES.PASSWORDCHANGE:
|
case MESSAGETYPES.PASSWORDCHANGE:
|
||||||
return handler(this.service.resetCustomPasswordChangeMessageTextToDefault(this.language));
|
return handler(this.service.resetCustomPasswordChangeMessageTextToDefault(this.language));
|
||||||
|
case MESSAGETYPES.INVITEUSER:
|
||||||
|
return handler(this.service.resetCustomInviteUserMessageTextToDefault(this.language));
|
||||||
default:
|
default:
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,8 @@ import {
|
|||||||
GetCustomPasswordChangeMessageTextResponse,
|
GetCustomPasswordChangeMessageTextResponse,
|
||||||
GetCustomPasswordlessRegistrationMessageTextRequest,
|
GetCustomPasswordlessRegistrationMessageTextRequest,
|
||||||
GetCustomPasswordlessRegistrationMessageTextResponse,
|
GetCustomPasswordlessRegistrationMessageTextResponse,
|
||||||
|
GetCustomInviteUserMessageTextRequest,
|
||||||
|
GetCustomInviteUserMessageTextResponse,
|
||||||
GetCustomPasswordResetMessageTextRequest,
|
GetCustomPasswordResetMessageTextRequest,
|
||||||
GetCustomPasswordResetMessageTextResponse,
|
GetCustomPasswordResetMessageTextResponse,
|
||||||
GetCustomVerifyEmailMessageTextRequest,
|
GetCustomVerifyEmailMessageTextRequest,
|
||||||
@ -96,6 +98,8 @@ import {
|
|||||||
GetDefaultPasswordChangeMessageTextResponse,
|
GetDefaultPasswordChangeMessageTextResponse,
|
||||||
GetDefaultPasswordlessRegistrationMessageTextRequest,
|
GetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||||
GetDefaultPasswordlessRegistrationMessageTextResponse,
|
GetDefaultPasswordlessRegistrationMessageTextResponse,
|
||||||
|
GetDefaultInviteUserMessageTextRequest,
|
||||||
|
GetDefaultInviteUserMessageTextResponse,
|
||||||
GetDefaultPasswordResetMessageTextRequest,
|
GetDefaultPasswordResetMessageTextRequest,
|
||||||
GetDefaultPasswordResetMessageTextResponse,
|
GetDefaultPasswordResetMessageTextResponse,
|
||||||
GetDefaultVerifyEmailMessageTextRequest,
|
GetDefaultVerifyEmailMessageTextRequest,
|
||||||
@ -224,6 +228,8 @@ import {
|
|||||||
SetDefaultPasswordChangeMessageTextResponse,
|
SetDefaultPasswordChangeMessageTextResponse,
|
||||||
SetDefaultPasswordlessRegistrationMessageTextRequest,
|
SetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||||
SetDefaultPasswordlessRegistrationMessageTextResponse,
|
SetDefaultPasswordlessRegistrationMessageTextResponse,
|
||||||
|
SetDefaultInviteUserMessageTextRequest,
|
||||||
|
SetDefaultInviteUserMessageTextResponse,
|
||||||
SetDefaultPasswordResetMessageTextRequest,
|
SetDefaultPasswordResetMessageTextRequest,
|
||||||
SetDefaultPasswordResetMessageTextResponse,
|
SetDefaultPasswordResetMessageTextResponse,
|
||||||
SetDefaultVerifyEmailMessageTextRequest,
|
SetDefaultVerifyEmailMessageTextRequest,
|
||||||
@ -311,6 +317,8 @@ import {
|
|||||||
ResetCustomPasswordChangeMessageTextToDefaultResponse,
|
ResetCustomPasswordChangeMessageTextToDefaultResponse,
|
||||||
ResetCustomPasswordlessRegistrationMessageTextToDefaultRequest,
|
ResetCustomPasswordlessRegistrationMessageTextToDefaultRequest,
|
||||||
ResetCustomPasswordlessRegistrationMessageTextToDefaultResponse,
|
ResetCustomPasswordlessRegistrationMessageTextToDefaultResponse,
|
||||||
|
ResetCustomInviteUserMessageTextToDefaultRequest,
|
||||||
|
ResetCustomInviteUserMessageTextToDefaultResponse,
|
||||||
ResetCustomPasswordResetMessageTextToDefaultRequest,
|
ResetCustomPasswordResetMessageTextToDefaultRequest,
|
||||||
ResetCustomPasswordResetMessageTextToDefaultResponse,
|
ResetCustomPasswordResetMessageTextToDefaultResponse,
|
||||||
ResetCustomVerifyEmailMessageTextToDefaultRequest,
|
ResetCustomVerifyEmailMessageTextToDefaultRequest,
|
||||||
@ -722,6 +730,32 @@ export class AdminService {
|
|||||||
return this.grpcService.admin.resetCustomPasswordChangeMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
return this.grpcService.admin.resetCustomPasswordChangeMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getDefaultInviteUserMessageText(
|
||||||
|
req: GetDefaultInviteUserMessageTextRequest,
|
||||||
|
): Promise<GetDefaultInviteUserMessageTextResponse.AsObject> {
|
||||||
|
return this.grpcService.admin.getDefaultInviteUserMessageText(req, null).then((resp) => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCustomInviteUserMessageText(
|
||||||
|
req: GetCustomInviteUserMessageTextRequest,
|
||||||
|
): Promise<GetCustomInviteUserMessageTextResponse.AsObject> {
|
||||||
|
return this.grpcService.admin.getCustomInviteUserMessageText(req, null).then((resp) => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public setDefaultInviteUserMessageText(
|
||||||
|
req: SetDefaultInviteUserMessageTextRequest,
|
||||||
|
): Promise<SetDefaultInviteUserMessageTextResponse.AsObject> {
|
||||||
|
return this.grpcService.admin.setDefaultInviteUserMessageText(req, null).then((resp) => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public resetCustomInviteUserMessageTextToDefault(
|
||||||
|
lang: string,
|
||||||
|
): Promise<ResetCustomInviteUserMessageTextToDefaultResponse.AsObject> {
|
||||||
|
const req = new ResetCustomInviteUserMessageTextToDefaultRequest();
|
||||||
|
req.setLanguage(lang);
|
||||||
|
return this.grpcService.admin.resetCustomInviteUserMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
public SetUpOrg(org: SetUpOrgRequest.Org, human: SetUpOrgRequest.Human): Promise<SetUpOrgResponse.AsObject> {
|
public SetUpOrg(org: SetUpOrgRequest.Org, human: SetUpOrgRequest.Human): Promise<SetUpOrgResponse.AsObject> {
|
||||||
const req = new SetUpOrgRequest();
|
const req = new SetUpOrgRequest();
|
||||||
|
|
||||||
|
@ -133,6 +133,8 @@ import {
|
|||||||
GetCustomLoginTextsResponse,
|
GetCustomLoginTextsResponse,
|
||||||
GetCustomPasswordChangeMessageTextRequest,
|
GetCustomPasswordChangeMessageTextRequest,
|
||||||
GetCustomPasswordChangeMessageTextResponse,
|
GetCustomPasswordChangeMessageTextResponse,
|
||||||
|
GetCustomInviteUserMessageTextRequest,
|
||||||
|
GetCustomInviteUserMessageTextResponse,
|
||||||
GetCustomPasswordlessRegistrationMessageTextRequest,
|
GetCustomPasswordlessRegistrationMessageTextRequest,
|
||||||
GetCustomPasswordlessRegistrationMessageTextResponse,
|
GetCustomPasswordlessRegistrationMessageTextResponse,
|
||||||
GetCustomPasswordResetMessageTextRequest,
|
GetCustomPasswordResetMessageTextRequest,
|
||||||
@ -155,6 +157,8 @@ import {
|
|||||||
GetDefaultLoginTextsResponse,
|
GetDefaultLoginTextsResponse,
|
||||||
GetDefaultPasswordChangeMessageTextRequest,
|
GetDefaultPasswordChangeMessageTextRequest,
|
||||||
GetDefaultPasswordChangeMessageTextResponse,
|
GetDefaultPasswordChangeMessageTextResponse,
|
||||||
|
GetDefaultInviteUserMessageTextRequest,
|
||||||
|
GetDefaultInviteUserMessageTextResponse,
|
||||||
GetDefaultPasswordComplexityPolicyRequest,
|
GetDefaultPasswordComplexityPolicyRequest,
|
||||||
GetDefaultPasswordComplexityPolicyResponse,
|
GetDefaultPasswordComplexityPolicyResponse,
|
||||||
GetDefaultPasswordlessRegistrationMessageTextRequest,
|
GetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||||
@ -386,6 +390,8 @@ import {
|
|||||||
ResetCustomLoginTextsToDefaultResponse,
|
ResetCustomLoginTextsToDefaultResponse,
|
||||||
ResetCustomPasswordChangeMessageTextToDefaultRequest,
|
ResetCustomPasswordChangeMessageTextToDefaultRequest,
|
||||||
ResetCustomPasswordChangeMessageTextToDefaultResponse,
|
ResetCustomPasswordChangeMessageTextToDefaultResponse,
|
||||||
|
ResetCustomInviteUserMessageTextToDefaultRequest,
|
||||||
|
ResetCustomInviteUserMessageTextToDefaultResponse,
|
||||||
ResetCustomPasswordlessRegistrationMessageTextToDefaultRequest,
|
ResetCustomPasswordlessRegistrationMessageTextToDefaultRequest,
|
||||||
ResetCustomPasswordlessRegistrationMessageTextToDefaultResponse,
|
ResetCustomPasswordlessRegistrationMessageTextToDefaultResponse,
|
||||||
ResetCustomPasswordResetMessageTextToDefaultRequest,
|
ResetCustomPasswordResetMessageTextToDefaultRequest,
|
||||||
@ -423,6 +429,8 @@ import {
|
|||||||
SetCustomLoginTextsResponse,
|
SetCustomLoginTextsResponse,
|
||||||
SetCustomPasswordChangeMessageTextRequest,
|
SetCustomPasswordChangeMessageTextRequest,
|
||||||
SetCustomPasswordChangeMessageTextResponse,
|
SetCustomPasswordChangeMessageTextResponse,
|
||||||
|
SetCustomInviteUserMessageTextRequest,
|
||||||
|
SetCustomInviteUserMessageTextResponse,
|
||||||
SetCustomPasswordlessRegistrationMessageTextRequest,
|
SetCustomPasswordlessRegistrationMessageTextRequest,
|
||||||
SetCustomPasswordlessRegistrationMessageTextResponse,
|
SetCustomPasswordlessRegistrationMessageTextResponse,
|
||||||
SetCustomPasswordResetMessageTextRequest,
|
SetCustomPasswordResetMessageTextRequest,
|
||||||
@ -804,6 +812,32 @@ export class ManagementService {
|
|||||||
return this.grpcService.mgmt.resetCustomPasswordChangeMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
return this.grpcService.mgmt.resetCustomPasswordChangeMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getDefaultInviteUserMessageText(
|
||||||
|
req: GetDefaultInviteUserMessageTextRequest,
|
||||||
|
): Promise<GetDefaultInviteUserMessageTextResponse.AsObject> {
|
||||||
|
return this.grpcService.mgmt.getDefaultInviteUserMessageText(req, null).then((resp) => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCustomInviteUserMessageText(
|
||||||
|
req: GetCustomInviteUserMessageTextRequest,
|
||||||
|
): Promise<GetCustomInviteUserMessageTextResponse.AsObject> {
|
||||||
|
return this.grpcService.mgmt.getCustomInviteUserMessageText(req, null).then((resp) => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public setCustomInviteUserMessageText(
|
||||||
|
req: SetCustomInviteUserMessageTextRequest,
|
||||||
|
): Promise<SetCustomInviteUserMessageTextResponse.AsObject> {
|
||||||
|
return this.grpcService.mgmt.setCustomInviteUserMessageCustomText(req, null).then((resp) => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public resetCustomInviteUserMessageTextToDefault(
|
||||||
|
lang: string,
|
||||||
|
): Promise<ResetCustomInviteUserMessageTextToDefaultResponse.AsObject> {
|
||||||
|
const req = new ResetCustomInviteUserMessageTextToDefaultRequest();
|
||||||
|
req.setLanguage(lang);
|
||||||
|
return this.grpcService.mgmt.resetCustomInviteUserMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
public updateUserName(userId: string, username: string): Promise<UpdateUserNameResponse.AsObject> {
|
public updateUserName(userId: string, username: string): Promise<UpdateUserNameResponse.AsObject> {
|
||||||
const req = new UpdateUserNameRequest();
|
const req = new UpdateUserNameRequest();
|
||||||
req.setUserId(userId);
|
req.setUserId(userId);
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "Когато потребител промени своя имейл адрес, той ще получи имейл с връзка за верифициране на новия адрес.",
|
"VE": "Когато потребител промени своя имейл адрес, той ще получи имейл с връзка за верифициране на новия адрес.",
|
||||||
"VP": "Когато потребител промени своя телефонен номер, той ще получи SMS с код за верификация на новия номер.",
|
"VP": "Когато потребител промени своя телефонен номер, той ще получи SMS с код за верификация на новия номер.",
|
||||||
"VEO": "Когато потребител добави метод за One-Time Password чрез имейл, трябва да го активира, като въведе код, изпратен на неговия имейл адрес.",
|
"VEO": "Когато потребител добави метод за One-Time Password чрез имейл, трябва да го активира, като въведе код, изпратен на неговия имейл адрес.",
|
||||||
"VSO": "Когато потребител добави метод за One-Time Password чрез SMS, трябва да го активира, като въведе код, изпратен на неговия телефонен номер."
|
"VSO": "Когато потребител добави метод за One-Time Password чрез SMS, трябва да го активира, като въведе код, изпратен на неговия телефонен номер.",
|
||||||
|
"IU": "Когато се създаде покана за потребител, те ще получат имейл с връзка за задаване на своя метод за удостоверяване."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1666,7 +1667,8 @@
|
|||||||
"PR": "Нулиране на парола",
|
"PR": "Нулиране на парола",
|
||||||
"DC": "Заявка за домейн",
|
"DC": "Заявка за домейн",
|
||||||
"PL": "Без парола",
|
"PL": "Без парола",
|
||||||
"PC": "Промяна на паролата"
|
"PC": "Промяна на паролата",
|
||||||
|
"IU": "Покана за потребител"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Първо име",
|
"firstname": "Първо име",
|
||||||
@ -1686,7 +1688,8 @@
|
|||||||
"tempUsername": "Временно потребителско име",
|
"tempUsername": "Временно потребителско име",
|
||||||
"otp": "Еднократна парола",
|
"otp": "Еднократна парола",
|
||||||
"verifyUrl": "URL за потвърждаване на еднократна парола",
|
"verifyUrl": "URL за потвърждаване на еднократна парола",
|
||||||
"expiry": "Изтичане"
|
"expiry": "Изтичане",
|
||||||
|
"applicationName": "Името на приложението"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "Персонализираните текстове са запазени."
|
"UPDATED": "Персонализираните текстове са запазени."
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "Když uživatel změní svou e-mailovou adresu, obdrží e-mail s odkazem na ověření nové adresy.",
|
"VE": "Když uživatel změní svou e-mailovou adresu, obdrží e-mail s odkazem na ověření nové adresy.",
|
||||||
"VP": "Když uživatel změní své telefonní číslo, obdrží SMS s kódem pro ověření nového čísla.",
|
"VP": "Když uživatel změní své telefonní číslo, obdrží SMS s kódem pro ověření nového čísla.",
|
||||||
"VEO": "Když uživatel přidá metodu jednorázového hesla přes e-mail, musí ji aktivovat zadáním kódu poslaného na jeho e-mailovou adresu.",
|
"VEO": "Když uživatel přidá metodu jednorázového hesla přes e-mail, musí ji aktivovat zadáním kódu poslaného na jeho e-mailovou adresu.",
|
||||||
"VSO": "Když uživatel přidá metodu jednorázového hesla přes SMS, musí ji aktivovat zadáním kódu poslaného na jeho telefonní číslo."
|
"VSO": "Když uživatel přidá metodu jednorázového hesla přes SMS, musí ji aktivovat zadáním kódu poslaného na jeho telefonní číslo.",
|
||||||
|
"IU": "Když se vytvoří pozvánka pro uživatele, obdrží e-mail s odkazem na nastavení své metody ověřování."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1667,7 +1668,8 @@
|
|||||||
"PR": "Reset hesla",
|
"PR": "Reset hesla",
|
||||||
"DC": "Nárok na doménu",
|
"DC": "Nárok na doménu",
|
||||||
"PL": "Bezheslový",
|
"PL": "Bezheslový",
|
||||||
"PC": "Změna hesla"
|
"PC": "Změna hesla",
|
||||||
|
"IU": "Pozvat uživatele"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Křestní jméno",
|
"firstname": "Křestní jméno",
|
||||||
@ -1687,7 +1689,8 @@
|
|||||||
"tempUsername": "Dočasné uživatelské jméno",
|
"tempUsername": "Dočasné uživatelské jméno",
|
||||||
"otp": "Jednorázové heslo",
|
"otp": "Jednorázové heslo",
|
||||||
"verifyUrl": "Ověřovací URL jednorázového hesla",
|
"verifyUrl": "Ověřovací URL jednorázového hesla",
|
||||||
"expiry": "Expirace"
|
"expiry": "Expirace",
|
||||||
|
"applicationName": "Název aplikace"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "Vlastní texty uloženy."
|
"UPDATED": "Vlastní texty uloženy."
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "Wenn ein Benutzer seine E-Mail-Adresse ändert, erhält er eine E-Mail mit einem Link zur Verifizierung der neuen Adresse.",
|
"VE": "Wenn ein Benutzer seine E-Mail-Adresse ändert, erhält er eine E-Mail mit einem Link zur Verifizierung der neuen Adresse.",
|
||||||
"VP": "Wenn ein Benutzer seine Telefonnummer ändert, erhält er eine SMS mit einem Code zur Verifizierung der neuen Nummer.",
|
"VP": "Wenn ein Benutzer seine Telefonnummer ändert, erhält er eine SMS mit einem Code zur Verifizierung der neuen Nummer.",
|
||||||
"VEO": "Wenn ein Benutzer eine Einmalpasswort-Methode per E-Mail hinzufügt, muss er sie aktivieren, indem er einen Code eingibt, der an seine E-Mail-Adresse gesendet wurde.",
|
"VEO": "Wenn ein Benutzer eine Einmalpasswort-Methode per E-Mail hinzufügt, muss er sie aktivieren, indem er einen Code eingibt, der an seine E-Mail-Adresse gesendet wurde.",
|
||||||
"VSO": "Wenn ein Benutzer eine Einmalpasswort-Methode per SMS hinzufügt, muss er sie aktivieren, indem er einen Code eingibt, der an seine Telefonnummer gesendet wurde."
|
"VSO": "Wenn ein Benutzer eine Einmalpasswort-Methode per SMS hinzufügt, muss er sie aktivieren, indem er einen Code eingibt, der an seine Telefonnummer gesendet wurde.",
|
||||||
|
"IU": "Wenn ein Benutzer-Einladungscode erstellt wird, erhält er eine E-Mail mit einem Link zur Einstellung seiner Authentifizierungsmethode."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1667,7 +1668,8 @@
|
|||||||
"PR": "Passwort Wiederherstellung",
|
"PR": "Passwort Wiederherstellung",
|
||||||
"DC": "Domainbeanspruchung",
|
"DC": "Domainbeanspruchung",
|
||||||
"PL": "Passwortlos",
|
"PL": "Passwortlos",
|
||||||
"PC": "Passwordwechsel"
|
"PC": "Passwordwechsel",
|
||||||
|
"IU": "Benutzer einladen"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Vorname",
|
"firstname": "Vorname",
|
||||||
@ -1687,7 +1689,8 @@
|
|||||||
"tempUsername": "Temp. Username",
|
"tempUsername": "Temp. Username",
|
||||||
"otp": "Einmalpasswort",
|
"otp": "Einmalpasswort",
|
||||||
"verifyUrl": "URL zur Überprüfung des Einmalpassworts",
|
"verifyUrl": "URL zur Überprüfung des Einmalpassworts",
|
||||||
"expiry": "Ablauf"
|
"expiry": "Ablauf",
|
||||||
|
"applicationName": "Anwendungsname"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "Benutzerdefinierte Texte gespeichert."
|
"UPDATED": "Benutzerdefinierte Texte gespeichert."
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "When a user changes their email address, they will receive an email with a link to verify the new address.",
|
"VE": "When a user changes their email address, they will receive an email with a link to verify the new address.",
|
||||||
"VP": "When a user changes their phone number, they will receive an SMS with a code to verify the new number.",
|
"VP": "When a user changes their phone number, they will receive an SMS with a code to verify the new number.",
|
||||||
"VEO": "When a user adds a One-Time Password via email method, they need to activate it by entering a code sent to their email address.",
|
"VEO": "When a user adds a One-Time Password via email method, they need to activate it by entering a code sent to their email address.",
|
||||||
"VSO": "When a user adds a One-Time Password via SMS method, they need to activate it by entering a code sent to their phone number."
|
"VSO": "When a user adds a One-Time Password via SMS method, they need to activate it by entering a code sent to their phone number.",
|
||||||
|
"IU": "When a user invite code is created, they will receive an email with a link to set their authentication method."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1667,7 +1668,8 @@
|
|||||||
"PR": "Password Reset",
|
"PR": "Password Reset",
|
||||||
"DC": "Domain Claim",
|
"DC": "Domain Claim",
|
||||||
"PL": "Passwordless",
|
"PL": "Passwordless",
|
||||||
"PC": "Password Change"
|
"PC": "Password Change",
|
||||||
|
"IU": "Invite User"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Given name",
|
"firstname": "Given name",
|
||||||
@ -1687,7 +1689,8 @@
|
|||||||
"tempUsername": "Temp username",
|
"tempUsername": "Temp username",
|
||||||
"otp": "One-time password",
|
"otp": "One-time password",
|
||||||
"verifyUrl": "Verify One-time-password URL",
|
"verifyUrl": "Verify One-time-password URL",
|
||||||
"expiry": "Expiry"
|
"expiry": "Expiry",
|
||||||
|
"applicationName": "Application name"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "Custom Texts saved."
|
"UPDATED": "Custom Texts saved."
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "Cuando un usuario cambia su dirección de correo electrónico, recibirá un correo electrónico con un enlace para verificar la nueva dirección.",
|
"VE": "Cuando un usuario cambia su dirección de correo electrónico, recibirá un correo electrónico con un enlace para verificar la nueva dirección.",
|
||||||
"VP": "Cuando un usuario cambia su número de teléfono, recibirá un SMS con un código para verificar el nuevo número.",
|
"VP": "Cuando un usuario cambia su número de teléfono, recibirá un SMS con un código para verificar el nuevo número.",
|
||||||
"VEO": "Cuando un usuario agrega una Contraseña de Un Solo Uso mediante correo electrónico, necesita activarla ingresando un código enviado a su dirección de correo electrónico.",
|
"VEO": "Cuando un usuario agrega una Contraseña de Un Solo Uso mediante correo electrónico, necesita activarla ingresando un código enviado a su dirección de correo electrónico.",
|
||||||
"VSO": "Cuando un usuario agrega una Contraseña de Un Solo Uso mediante SMS, necesita activarla ingresando un código enviado a su número de teléfono."
|
"VSO": "Cuando un usuario agrega una Contraseña de Un Solo Uso mediante SMS, necesita activarla ingresando un código enviado a su número de teléfono.",
|
||||||
|
"IU": "Cuando se crea un código de invitación de usuario, recibirán un correo electrónico con un enlace para configurar su método de autenticación."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1668,7 +1669,8 @@
|
|||||||
"PR": "Restablecimiento de contraseña",
|
"PR": "Restablecimiento de contraseña",
|
||||||
"DC": "Reclamar un dominio",
|
"DC": "Reclamar un dominio",
|
||||||
"PL": "Acceso sin contraseña",
|
"PL": "Acceso sin contraseña",
|
||||||
"PC": "Cambio de contraseña"
|
"PC": "Cambio de contraseña",
|
||||||
|
"IU": "Invitar usuario"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Nombre",
|
"firstname": "Nombre",
|
||||||
@ -1688,7 +1690,8 @@
|
|||||||
"tempUsername": "Nombre de usuario temporal",
|
"tempUsername": "Nombre de usuario temporal",
|
||||||
"otp": "Contraseña de un solo uso",
|
"otp": "Contraseña de un solo uso",
|
||||||
"verifyUrl": "URL para verificar la contraseña de un solo uso",
|
"verifyUrl": "URL para verificar la contraseña de un solo uso",
|
||||||
"expiry": "Expiración"
|
"expiry": "Expiración",
|
||||||
|
"applicationName": "Nombre de la aplicación"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "Textos personalizados guardados."
|
"UPDATED": "Textos personalizados guardados."
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "Lorsqu'un utilisateur change son adresse e-mail, il recevra un e-mail avec un lien pour vérifier la nouvelle adresse.",
|
"VE": "Lorsqu'un utilisateur change son adresse e-mail, il recevra un e-mail avec un lien pour vérifier la nouvelle adresse.",
|
||||||
"VP": "Lorsqu'un utilisateur change son numéro de téléphone, il recevra un SMS avec un code pour vérifier le nouveau numéro.",
|
"VP": "Lorsqu'un utilisateur change son numéro de téléphone, il recevra un SMS avec un code pour vérifier le nouveau numéro.",
|
||||||
"VEO": "Lorsqu'un utilisateur ajoute un Mot de Passe à Usage Unique via e-mail, il doit l'activer en entrant un code envoyé à son adresse e-mail.",
|
"VEO": "Lorsqu'un utilisateur ajoute un Mot de Passe à Usage Unique via e-mail, il doit l'activer en entrant un code envoyé à son adresse e-mail.",
|
||||||
"VSO": "Lorsqu'un utilisateur ajoute un Mot de Passe à Usage Unique via SMS, il doit l'activer en entrant un code envoyé à son numéro de téléphone."
|
"VSO": "Lorsqu'un utilisateur ajoute un Mot de Passe à Usage Unique via SMS, il doit l'activer en entrant un code envoyé à son numéro de téléphone.",
|
||||||
|
"IU": "Lorsqu'un code d'invitation d'utilisateur est créé, il recevra un e-mail avec un lien pour configurer sa méthode d'authentification."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1667,7 +1668,8 @@
|
|||||||
"PR": "Réinitialisation du mot de passe",
|
"PR": "Réinitialisation du mot de passe",
|
||||||
"DC": "Réclamation de domaine",
|
"DC": "Réclamation de domaine",
|
||||||
"PL": "Sans mot de passe",
|
"PL": "Sans mot de passe",
|
||||||
"PC": "Modification du mot de passe"
|
"PC": "Modification du mot de passe",
|
||||||
|
"IU": "Inviter un utilisateur"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Prénom",
|
"firstname": "Prénom",
|
||||||
@ -1687,7 +1689,8 @@
|
|||||||
"tempUsername": "Nom d'utilisateur temporaire",
|
"tempUsername": "Nom d'utilisateur temporaire",
|
||||||
"otp": "Mot de passe à usage unique",
|
"otp": "Mot de passe à usage unique",
|
||||||
"verifyUrl": "URL pour vérifier le mot de passe à usage unique",
|
"verifyUrl": "URL pour vérifier le mot de passe à usage unique",
|
||||||
"expiry": "Expiration"
|
"expiry": "Expiration",
|
||||||
|
"applicationName": "Nom de l'application"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "Textes personnalisés enregistrés."
|
"UPDATED": "Textes personnalisés enregistrés."
|
||||||
|
@ -185,7 +185,8 @@
|
|||||||
"VE": "Saat pengguna mengubah alamat emailnya, mereka akan menerima email berisi tautan untuk memverifikasi alamat baru.",
|
"VE": "Saat pengguna mengubah alamat emailnya, mereka akan menerima email berisi tautan untuk memverifikasi alamat baru.",
|
||||||
"VP": "Saat pengguna mengganti nomor teleponnya, mereka akan menerima SMS berisi kode untuk memverifikasi nomor baru.",
|
"VP": "Saat pengguna mengganti nomor teleponnya, mereka akan menerima SMS berisi kode untuk memverifikasi nomor baru.",
|
||||||
"VEO": "Ketika pengguna menambahkan Kata Sandi Sekali Pakai melalui metode email, mereka perlu mengaktifkannya dengan memasukkan kode yang dikirimkan ke alamat email mereka.",
|
"VEO": "Ketika pengguna menambahkan Kata Sandi Sekali Pakai melalui metode email, mereka perlu mengaktifkannya dengan memasukkan kode yang dikirimkan ke alamat email mereka.",
|
||||||
"VSO": "Ketika pengguna menambahkan One-Time Password melalui metode SMS, mereka perlu mengaktifkannya dengan memasukkan kode yang dikirimkan ke nomor telepon mereka."
|
"VSO": "Ketika pengguna menambahkan One-Time Password melalui metode SMS, mereka perlu mengaktifkannya dengan memasukkan kode yang dikirimkan ke nomor telepon mereka.",
|
||||||
|
"IU": "Ketika kode undangan pengguna dibuat, mereka akan menerima email dengan tautan untuk mengatur metode otentikasi mereka."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1533,7 +1534,8 @@
|
|||||||
"PR": "Reset Kata Sandi",
|
"PR": "Reset Kata Sandi",
|
||||||
"DC": "Klaim Domain",
|
"DC": "Klaim Domain",
|
||||||
"PL": "Tanpa kata sandi",
|
"PL": "Tanpa kata sandi",
|
||||||
"PC": "Perubahan Kata Sandi"
|
"PC": "Perubahan Kata Sandi",
|
||||||
|
"IU": "Mengundang Pengguna"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Nama yang diberikan",
|
"firstname": "Nama yang diberikan",
|
||||||
@ -1553,7 +1555,8 @@
|
|||||||
"tempUsername": "Nama pengguna sementara",
|
"tempUsername": "Nama pengguna sementara",
|
||||||
"otp": "Kata sandi satu kali",
|
"otp": "Kata sandi satu kali",
|
||||||
"verifyUrl": "Verifikasi URL kata sandi satu kali",
|
"verifyUrl": "Verifikasi URL kata sandi satu kali",
|
||||||
"expiry": "Kedaluwarsa"
|
"expiry": "Kedaluwarsa",
|
||||||
|
"applicationName": "Nama aplikasi"
|
||||||
},
|
},
|
||||||
"TOAST": { "UPDATED": "Teks Khusus disimpan." }
|
"TOAST": { "UPDATED": "Teks Khusus disimpan." }
|
||||||
},
|
},
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "Quando un utente cambia il suo indirizzo email, riceverà un'email con un link per verificare il nuovo indirizzo.",
|
"VE": "Quando un utente cambia il suo indirizzo email, riceverà un'email con un link per verificare il nuovo indirizzo.",
|
||||||
"VP": "Quando un utente cambia il suo numero di telefono, riceverà un SMS con un codice per verificare il nuovo numero.",
|
"VP": "Quando un utente cambia il suo numero di telefono, riceverà un SMS con un codice per verificare il nuovo numero.",
|
||||||
"VEO": "Quando un utente aggiunge una Password Monouso tramite metodo email, deve attivarla inserendo un codice inviato al suo indirizzo email.",
|
"VEO": "Quando un utente aggiunge una Password Monouso tramite metodo email, deve attivarla inserendo un codice inviato al suo indirizzo email.",
|
||||||
"VSO": "Quando un utente aggiunge una Password Monouso tramite metodo SMS, deve attivarla inserendo un codice inviato al suo numero di telefono."
|
"VSO": "Quando un utente aggiunge una Password Monouso tramite metodo SMS, deve attivarla inserendo un codice inviato al suo numero di telefono.",
|
||||||
|
"IU": "Quando viene creato un codice di invito per un utente, riceverà un'e-mail con un collegamento per impostare il suo metodo di autenticazione."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1667,7 +1668,8 @@
|
|||||||
"PR": "Ripristino della password",
|
"PR": "Ripristino della password",
|
||||||
"DC": "Rivendicazione del dominio",
|
"DC": "Rivendicazione del dominio",
|
||||||
"PL": "Autenticazione Passwordless",
|
"PL": "Autenticazione Passwordless",
|
||||||
"PC": "Cambiamento della password"
|
"PC": "Cambiamento della password",
|
||||||
|
"IU": "Invita utente"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Nome",
|
"firstname": "Nome",
|
||||||
@ -1687,7 +1689,8 @@
|
|||||||
"tempUsername": "Nome utente temporaneo",
|
"tempUsername": "Nome utente temporaneo",
|
||||||
"otp": "Password monouso",
|
"otp": "Password monouso",
|
||||||
"verifyUrl": "URL per verificare la password monouso",
|
"verifyUrl": "URL per verificare la password monouso",
|
||||||
"expiry": "Scadenza"
|
"expiry": "Scadenza",
|
||||||
|
"applicationName": "Nome dell'applicazione"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "Testi personalizzati salvati."
|
"UPDATED": "Testi personalizzati salvati."
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "ユーザーがメールアドレスを変更すると、新しいアドレスを検証するリンクが記載されたメールを受け取ります。",
|
"VE": "ユーザーがメールアドレスを変更すると、新しいアドレスを検証するリンクが記載されたメールを受け取ります。",
|
||||||
"VP": "ユーザーが電話番号を変更すると、新しい番号を検証するコードが記載されたSMSを受け取ります。",
|
"VP": "ユーザーが電話番号を変更すると、新しい番号を検証するコードが記載されたSMSを受け取ります。",
|
||||||
"VEO": "ユーザーがメール経由でワンタイムパスワードの方法を追加すると、それをアクティブにするためにメールに送信されたコードを入力する必要があります。",
|
"VEO": "ユーザーがメール経由でワンタイムパスワードの方法を追加すると、それをアクティブにするためにメールに送信されたコードを入力する必要があります。",
|
||||||
"VSO": "ユーザーがSMS経由でワンタイムパスワードの方法を追加すると、それをアクティブにするために電話番号に送信されたコードを入力する必要があります。"
|
"VSO": "ユーザーがSMS経由でワンタイムパスワードの方法を追加すると、それをアクティブにするために電話番号に送信されたコードを入力する必要があります。",
|
||||||
|
"IU": "ユーザー招待コードが作成されると、認証方法を設定するためのリンクを含むメールが送信されます。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1683,7 +1684,8 @@
|
|||||||
"tempUsername": "一時ユーザー名",
|
"tempUsername": "一時ユーザー名",
|
||||||
"otp": "ワンタイムパスワード",
|
"otp": "ワンタイムパスワード",
|
||||||
"verifyUrl": "ワンタイムパスワードを確認するURL",
|
"verifyUrl": "ワンタイムパスワードを確認するURL",
|
||||||
"expiry": "有効期限"
|
"expiry": "有効期限",
|
||||||
|
"applicationName": "アプリケーション名"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "カスタムテキストが保存されました。"
|
"UPDATED": "カスタムテキストが保存されました。"
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "Кога корисник ја менува својата е-маил адреса, тој ќе добие е-маил со врска за верификација на новата адреса.",
|
"VE": "Кога корисник ја менува својата е-маил адреса, тој ќе добие е-маил со врска за верификација на новата адреса.",
|
||||||
"VP": "Кога корисник ја менува својата телефонска бројка, тој ќе добие SMS со код за верификација на новиот број.",
|
"VP": "Кога корисник ја менува својата телефонска бројка, тој ќе добие SMS со код за верификација на новиот број.",
|
||||||
"VEO": "Кога корисник додава метод за еднократна лозинка преку е-маил, потребно е да го активира со внесување на кодот испратен на нивната е-маил адреса.",
|
"VEO": "Кога корисник додава метод за еднократна лозинка преку е-маил, потребно е да го активира со внесување на кодот испратен на нивната е-маил адреса.",
|
||||||
"VSO": "Кога корисник додава метод за еднократна лозинка преку SMS, потребно е да го активира со внесување на кодот испратен на нивниот телефонски број."
|
"VSO": "Кога корисник додава метод за еднократна лозинка преку SMS, потребно е да го активира со внесување на кодот испратен на нивниот телефонски број.",
|
||||||
|
"IU": "Кога се создаде покана за корисникот, тие ќе добијат имејл со врска за поставување на нивниот метод за автентикација."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1668,7 +1669,8 @@
|
|||||||
"PR": "Ресетирање на лозинка",
|
"PR": "Ресетирање на лозинка",
|
||||||
"DC": "Зафатница на домен",
|
"DC": "Зафатница на домен",
|
||||||
"PL": "Лозинка без лозинка",
|
"PL": "Лозинка без лозинка",
|
||||||
"PC": "Промена на лозинка"
|
"PC": "Промена на лозинка",
|
||||||
|
"IU": "Покана за корисникот"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Име",
|
"firstname": "Име",
|
||||||
@ -1688,7 +1690,8 @@
|
|||||||
"tempUsername": "Привремено корисничко име",
|
"tempUsername": "Привремено корисничко име",
|
||||||
"otp": "Еднократна лозинка",
|
"otp": "Еднократна лозинка",
|
||||||
"verifyUrl": "URL за потврдување на еднократна лозинка",
|
"verifyUrl": "URL за потврдување на еднократна лозинка",
|
||||||
"expiry": "Истекување"
|
"expiry": "Истекување",
|
||||||
|
"applicationName": "Името на апликацијата"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "Прилагодените текстови се зачувани."
|
"UPDATED": "Прилагодените текстови се зачувани."
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "Wanneer een gebruiker zijn e-mailadres wijzigt, ontvangt hij een e-mail met een link om het nieuwe adres te verifiëren.",
|
"VE": "Wanneer een gebruiker zijn e-mailadres wijzigt, ontvangt hij een e-mail met een link om het nieuwe adres te verifiëren.",
|
||||||
"VP": "Wanneer een gebruiker zijn telefoonnummer wijzigt, ontvangt hij een SMS met een code om het nieuwe nummer te verifiëren.",
|
"VP": "Wanneer een gebruiker zijn telefoonnummer wijzigt, ontvangt hij een SMS met een code om het nieuwe nummer te verifiëren.",
|
||||||
"VEO": "Wanneer een gebruiker een eenmalig wachtwoord via e-mailmethode toevoegt, moeten ze dit activeren door een code in te voeren die naar hun e-mailadres is verzonden.",
|
"VEO": "Wanneer een gebruiker een eenmalig wachtwoord via e-mailmethode toevoegt, moeten ze dit activeren door een code in te voeren die naar hun e-mailadres is verzonden.",
|
||||||
"VSO": "Wanneer een gebruiker een eenmalig wachtwoord via SMS-methode toevoegt, moeten ze dit activeren door een code in te voeren die naar hun telefoonnummer is verzonden."
|
"VSO": "Wanneer een gebruiker een eenmalig wachtwoord via SMS-methode toevoegt, moeten ze dit activeren door een code in te voeren die naar hun telefoonnummer is verzonden.",
|
||||||
|
"IU": "Wanneer een uitnodigingscode voor gebruikers wordt gemaakt, ontvangt de gebruiker een e-mail met een link om zijn verificatiemethode in te stellen."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1666,7 +1667,8 @@
|
|||||||
"PR": "Wachtwoord Reset",
|
"PR": "Wachtwoord Reset",
|
||||||
"DC": "Domein Claim",
|
"DC": "Domein Claim",
|
||||||
"PL": "Wachtwoordloos",
|
"PL": "Wachtwoordloos",
|
||||||
"PC": "Wachtwoord Verandering"
|
"PC": "Wachtwoord Verandering",
|
||||||
|
"IU": "Gebruiker uitnodigen"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Voornaam",
|
"firstname": "Voornaam",
|
||||||
@ -1686,7 +1688,8 @@
|
|||||||
"tempUsername": "Tijdelijke gebruikersnaam",
|
"tempUsername": "Tijdelijke gebruikersnaam",
|
||||||
"otp": "Eenmalig wachtwoord",
|
"otp": "Eenmalig wachtwoord",
|
||||||
"verifyUrl": "Verifieer Eenmalig-wachtwoord URL",
|
"verifyUrl": "Verifieer Eenmalig-wachtwoord URL",
|
||||||
"expiry": "Vervaldatum"
|
"expiry": "Vervaldatum",
|
||||||
|
"applicationName": "Toepassingsnaam"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "Aangepaste Teksten opgeslagen."
|
"UPDATED": "Aangepaste Teksten opgeslagen."
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "Gdy użytkownik zmieni swój adres e-mail, otrzyma e-mail z linkiem do weryfikacji nowego adresu.",
|
"VE": "Gdy użytkownik zmieni swój adres e-mail, otrzyma e-mail z linkiem do weryfikacji nowego adresu.",
|
||||||
"VP": "Gdy użytkownik zmieni swój numer telefonu, otrzyma SMS z kodem do weryfikacji nowego numeru.",
|
"VP": "Gdy użytkownik zmieni swój numer telefonu, otrzyma SMS z kodem do weryfikacji nowego numeru.",
|
||||||
"VEO": "Gdy użytkownik doda metodę jednorazowego hasła przez e-mail, musi ją aktywować, wprowadzając kod wysłany na jego adres e-mail.",
|
"VEO": "Gdy użytkownik doda metodę jednorazowego hasła przez e-mail, musi ją aktywować, wprowadzając kod wysłany na jego adres e-mail.",
|
||||||
"VSO": "Gdy użytkownik doda metodę jednorazowego hasła przez SMS, musi ją aktywować, wprowadzając kod wysłany na jego numer telefonu."
|
"VSO": "Gdy użytkownik doda metodę jednorazowego hasła przez SMS, musi ją aktywować, wprowadzając kod wysłany na jego numer telefonu.",
|
||||||
|
"IU": "Kiedy zostanie utworzony kod zaproszenia użytkownika, otrzyma on e-mail z linkiem do ustawienia swojej metody uwierzytelniania."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1666,7 +1667,8 @@
|
|||||||
"PR": "Resetowanie hasła",
|
"PR": "Resetowanie hasła",
|
||||||
"DC": "Rejestracja domeny",
|
"DC": "Rejestracja domeny",
|
||||||
"PL": "Bez hasła",
|
"PL": "Bez hasła",
|
||||||
"PC": "Zmiana hasła"
|
"PC": "Zmiana hasła",
|
||||||
|
"IU": "Zaproś użytkownika"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Imię",
|
"firstname": "Imię",
|
||||||
@ -1686,7 +1688,8 @@
|
|||||||
"tempUsername": "Tymczasowa nazwa użytkownika",
|
"tempUsername": "Tymczasowa nazwa użytkownika",
|
||||||
"otp": "Hasło jednorazowe",
|
"otp": "Hasło jednorazowe",
|
||||||
"verifyUrl": "URL do weryfikacji hasła jednorazowego",
|
"verifyUrl": "URL do weryfikacji hasła jednorazowego",
|
||||||
"expiry": "Wygaśnięcie"
|
"expiry": "Wygaśnięcie",
|
||||||
|
"applicationName": "Nazwa aplikacji"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "Teksty niestandardowe zapisane."
|
"UPDATED": "Teksty niestandardowe zapisane."
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "Quando um usuário muda seu endereço de e-mail, ele receberá um e-mail com um link para verificar o novo endereço.",
|
"VE": "Quando um usuário muda seu endereço de e-mail, ele receberá um e-mail com um link para verificar o novo endereço.",
|
||||||
"VP": "Quando um usuário muda seu número de telefone, ele receberá um SMS com um código para verificar o novo número.",
|
"VP": "Quando um usuário muda seu número de telefone, ele receberá um SMS com um código para verificar o novo número.",
|
||||||
"VEO": "Quando um usuário adiciona um método de Senha Única via e-mail, ele precisa ativá-lo inserindo um código enviado para seu endereço de e-mail.",
|
"VEO": "Quando um usuário adiciona um método de Senha Única via e-mail, ele precisa ativá-lo inserindo um código enviado para seu endereço de e-mail.",
|
||||||
"VSO": "Quando um usuário adiciona um método de Senha Única via SMS, ele precisa ativá-lo inserindo um código enviado para seu número de telefone."
|
"VSO": "Quando um usuário adiciona um método de Senha Única via SMS, ele precisa ativá-lo inserindo um código enviado para seu número de telefone.",
|
||||||
|
"IU": "Quando um código de convite de usuário é criado, eles receberão um e-mail com um link para configurar seu método de autenticação."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1668,7 +1669,8 @@
|
|||||||
"PR": "Redefinição de Senha",
|
"PR": "Redefinição de Senha",
|
||||||
"DC": "Reivindicação de Domínio",
|
"DC": "Reivindicação de Domínio",
|
||||||
"PL": "Sem senha",
|
"PL": "Sem senha",
|
||||||
"PC": "Alteração de Senha"
|
"PC": "Alteração de Senha",
|
||||||
|
"IU": "Convidar usuário"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Nome próprio",
|
"firstname": "Nome próprio",
|
||||||
@ -1688,7 +1690,8 @@
|
|||||||
"tempUsername": "Nome de usuário temporário",
|
"tempUsername": "Nome de usuário temporário",
|
||||||
"otp": "Senha de uso único",
|
"otp": "Senha de uso único",
|
||||||
"verifyUrl": "URL para verificar a senha de uso único",
|
"verifyUrl": "URL para verificar a senha de uso único",
|
||||||
"expiry": "Data de expiração"
|
"expiry": "Data de expiração",
|
||||||
|
"applicationName": "Nome do aplicativo"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "Textos personalizados salvos."
|
"UPDATED": "Textos personalizados salvos."
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "Когда пользователь меняет свой адрес электронной почты, он получает электронное письмо со ссылкой для подтверждения нового адреса.",
|
"VE": "Когда пользователь меняет свой адрес электронной почты, он получает электронное письмо со ссылкой для подтверждения нового адреса.",
|
||||||
"VP": "Когда пользователь меняет свой телефонный номер, он получает SMS с кодом для подтверждения нового номера.",
|
"VP": "Когда пользователь меняет свой телефонный номер, он получает SMS с кодом для подтверждения нового номера.",
|
||||||
"VEO": "Когда пользователь добавляет метод одноразового пароля по электронной почте, ему необходимо активировать его, введя код, отправленный на его адрес электронной почты.",
|
"VEO": "Когда пользователь добавляет метод одноразового пароля по электронной почте, ему необходимо активировать его, введя код, отправленный на его адрес электронной почты.",
|
||||||
"VSO": "Когда пользователь добавляет метод одноразового пароля по SMS, ему необходимо активировать его, введя код, отправленный на его телефонный номер."
|
"VSO": "Когда пользователь добавляет метод одноразового пароля по SMS, ему необходимо активировать его, введя код, отправленный на его телефонный номер.",
|
||||||
|
"IU": "Когда создается код приглашения пользователя, он получит электронное письмо со ссылкой для настройки своего метода аутентификации."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1735,7 +1736,8 @@
|
|||||||
"PR": "Восстановление пароля",
|
"PR": "Восстановление пароля",
|
||||||
"DC": "Утверждение домена",
|
"DC": "Утверждение домена",
|
||||||
"PL": "Без пароля",
|
"PL": "Без пароля",
|
||||||
"PC": "Изменение пароля"
|
"PC": "Изменение пароля",
|
||||||
|
"IU": "Пригласить пользователя"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Имя",
|
"firstname": "Имя",
|
||||||
@ -1755,7 +1757,8 @@
|
|||||||
"tempUsername": "Временное имя пользователя",
|
"tempUsername": "Временное имя пользователя",
|
||||||
"otp": "Одноразовый пароль",
|
"otp": "Одноразовый пароль",
|
||||||
"verifyUrl": "Проверка URL-адреса с одноразовым паролем",
|
"verifyUrl": "Проверка URL-адреса с одноразовым паролем",
|
||||||
"expiry": "Срок действия"
|
"expiry": "Срок действия",
|
||||||
|
"applicationName": "Имя приложения"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "Тексты сохранены."
|
"UPDATED": "Тексты сохранены."
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "När en användare ändrar sin e-postadress, kommer de att få ett mail med en länk för att verifiera den nya adressen.",
|
"VE": "När en användare ändrar sin e-postadress, kommer de att få ett mail med en länk för att verifiera den nya adressen.",
|
||||||
"VP": "När en användare ändrar sitt telefonnummer, kommer de att få ett SMS med en kod för att verifiera det nya numret.",
|
"VP": "När en användare ändrar sitt telefonnummer, kommer de att få ett SMS med en kod för att verifiera det nya numret.",
|
||||||
"VEO": "När en användare lägger till en engångslösenord via e-postmetod, måste de aktivera den genom att ange en kod som skickas till deras e-postadress.",
|
"VEO": "När en användare lägger till en engångslösenord via e-postmetod, måste de aktivera den genom att ange en kod som skickas till deras e-postadress.",
|
||||||
"VSO": "När en användare lägger till en engångslösenord via SMS-metod, måste de aktivera den genom att ange en kod som skickas till deras telefonnummer."
|
"VSO": "När en användare lägger till en engångslösenord via SMS-metod, måste de aktivera den genom att ange en kod som skickas till deras telefonnummer.",
|
||||||
|
"IU": "När en inbjudningskod för användare skapas, får de ett e-mail med en länk för att ställa in sin autentiseringsmetod."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1671,7 +1672,8 @@
|
|||||||
"PR": "Återställ Lösenord",
|
"PR": "Återställ Lösenord",
|
||||||
"DC": "Domänkrav",
|
"DC": "Domänkrav",
|
||||||
"PL": "lösenordsfri",
|
"PL": "lösenordsfri",
|
||||||
"PC": "Lösenordsändring"
|
"PC": "Lösenordsändring",
|
||||||
|
"IU": "Bjud in användare"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "Förnamn",
|
"firstname": "Förnamn",
|
||||||
@ -1691,7 +1693,8 @@
|
|||||||
"tempUsername": "Tillfälligt användarnamn",
|
"tempUsername": "Tillfälligt användarnamn",
|
||||||
"otp": "Engångslösenord",
|
"otp": "Engångslösenord",
|
||||||
"verifyUrl": "Verifiera Engångslösenord URL",
|
"verifyUrl": "Verifiera Engångslösenord URL",
|
||||||
"expiry": "Utgångsdatum"
|
"expiry": "Utgångsdatum",
|
||||||
|
"applicationName": "Applikationsnamn"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "Anpassade Texter sparade."
|
"UPDATED": "Anpassade Texter sparade."
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
"VE": "当用户更改其电子邮件地址时,他们将收到一封带有验证新地址链接的电子邮件。",
|
"VE": "当用户更改其电子邮件地址时,他们将收到一封带有验证新地址链接的电子邮件。",
|
||||||
"VP": "当用户更改其电话号码时,他们将收到一条带有验证新号码的代码的短信。",
|
"VP": "当用户更改其电话号码时,他们将收到一条带有验证新号码的代码的短信。",
|
||||||
"VEO": "当用户通过电子邮件方式添加一次性密码时,他们需要通过输入发送到其电子邮件地址的代码来激活它。",
|
"VEO": "当用户通过电子邮件方式添加一次性密码时,他们需要通过输入发送到其电子邮件地址的代码来激活它。",
|
||||||
"VSO": "当用户通过短信方式添加一次性密码时,他们需要通过输入发送到其电话号码的代码来激活它。"
|
"VSO": "当用户通过短信方式添加一次性密码时,他们需要通过输入发送到其电话号码的代码来激活它。",
|
||||||
|
"IU": "当创建用户邀请代码时,他们将收到一封包含设置其身份验证方法的链接的电子邮件。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LOGIN_TEXTS": {
|
"LOGIN_TEXTS": {
|
||||||
@ -1666,7 +1667,8 @@
|
|||||||
"PR": "重置密码",
|
"PR": "重置密码",
|
||||||
"DC": "域名声明",
|
"DC": "域名声明",
|
||||||
"PL": "无密码身份验证",
|
"PL": "无密码身份验证",
|
||||||
"PC": "修改密码"
|
"PC": "修改密码",
|
||||||
|
"IU": "邀请用户"
|
||||||
},
|
},
|
||||||
"CHIPS": {
|
"CHIPS": {
|
||||||
"firstname": "名",
|
"firstname": "名",
|
||||||
@ -1686,7 +1688,8 @@
|
|||||||
"tempUsername": "临时用户名",
|
"tempUsername": "临时用户名",
|
||||||
"otp": "一次性密码",
|
"otp": "一次性密码",
|
||||||
"verifyUrl": "验证一次性密码的URL",
|
"verifyUrl": "验证一次性密码的URL",
|
||||||
"expiry": "过期时间"
|
"expiry": "过期时间",
|
||||||
|
"applicationName": "应用程序名称"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"UPDATED": "自定义文本已保存。"
|
"UPDATED": "自定义文本已保存。"
|
||||||
|
@ -396,6 +396,54 @@ func (s *Server) ResetCustomPasswordChangeMessageTextToDefault(ctx context.Conte
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetDefaultInviteUserMessageText(ctx context.Context, req *admin_pb.GetDefaultInviteUserMessageTextRequest) (*admin_pb.GetDefaultInviteUserMessageTextResponse, error) {
|
||||||
|
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.InviteUserMessageType, req.Language)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &admin_pb.GetDefaultInviteUserMessageTextResponse{
|
||||||
|
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetCustomInviteUserMessageText(ctx context.Context, req *admin_pb.GetCustomInviteUserMessageTextRequest) (*admin_pb.GetCustomInviteUserMessageTextResponse, error) {
|
||||||
|
msg, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, authz.GetInstance(ctx).InstanceID(), domain.InviteUserMessageType, req.Language, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &admin_pb.GetCustomInviteUserMessageTextResponse{
|
||||||
|
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) SetDefaultInviteUserMessageText(ctx context.Context, req *admin_pb.SetDefaultInviteUserMessageTextRequest) (*admin_pb.SetDefaultInviteUserMessageTextResponse, error) {
|
||||||
|
result, err := s.command.SetDefaultMessageText(ctx, authz.GetInstance(ctx).InstanceID(), SetInviteUserCustomTextToDomain(req))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &admin_pb.SetDefaultInviteUserMessageTextResponse{
|
||||||
|
Details: object.ChangeToDetailsPb(
|
||||||
|
result.Sequence,
|
||||||
|
result.EventDate,
|
||||||
|
result.ResourceOwner,
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ResetCustomInviteUserMessageTextToDefault(ctx context.Context, req *admin_pb.ResetCustomInviteUserMessageTextToDefaultRequest) (*admin_pb.ResetCustomInviteUserMessageTextToDefaultResponse, error) {
|
||||||
|
result, err := s.command.RemoveInstanceMessageTexts(ctx, domain.InviteUserMessageType, language.Make(req.Language))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &admin_pb.ResetCustomInviteUserMessageTextToDefaultResponse{
|
||||||
|
Details: object.ChangeToDetailsPb(
|
||||||
|
result.Sequence,
|
||||||
|
result.EventDate,
|
||||||
|
result.ResourceOwner,
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) GetDefaultPasswordlessRegistrationMessageText(ctx context.Context, req *admin_pb.GetDefaultPasswordlessRegistrationMessageTextRequest) (*admin_pb.GetDefaultPasswordlessRegistrationMessageTextResponse, error) {
|
func (s *Server) GetDefaultPasswordlessRegistrationMessageText(ctx context.Context, req *admin_pb.GetDefaultPasswordlessRegistrationMessageTextRequest) (*admin_pb.GetDefaultPasswordlessRegistrationMessageTextResponse, error) {
|
||||||
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.PasswordlessRegistrationMessageType, req.Language)
|
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.PasswordlessRegistrationMessageType, req.Language)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -122,6 +122,21 @@ func SetPasswordChangeCustomTextToDomain(msg *admin_pb.SetDefaultPasswordChangeM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetInviteUserCustomTextToDomain(msg *admin_pb.SetDefaultInviteUserMessageTextRequest) *domain.CustomMessageText {
|
||||||
|
langTag := language.Make(msg.Language)
|
||||||
|
return &domain.CustomMessageText{
|
||||||
|
MessageTextType: domain.InviteUserMessageType,
|
||||||
|
Language: langTag,
|
||||||
|
Title: msg.Title,
|
||||||
|
PreHeader: msg.PreHeader,
|
||||||
|
Subject: msg.Subject,
|
||||||
|
Greeting: msg.Greeting,
|
||||||
|
Text: msg.Text,
|
||||||
|
ButtonText: msg.ButtonText,
|
||||||
|
FooterText: msg.FooterText,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func SetPasswordlessRegistrationCustomTextToDomain(msg *admin_pb.SetDefaultPasswordlessRegistrationMessageTextRequest) *domain.CustomMessageText {
|
func SetPasswordlessRegistrationCustomTextToDomain(msg *admin_pb.SetDefaultPasswordlessRegistrationMessageTextRequest) *domain.CustomMessageText {
|
||||||
langTag := language.Make(msg.Language)
|
langTag := language.Make(msg.Language)
|
||||||
return &domain.CustomMessageText{
|
return &domain.CustomMessageText{
|
||||||
|
@ -793,6 +793,7 @@ func importResources(ctx context.Context, s *Server, errors *[]*admin_pb.ImportD
|
|||||||
importVerifyPhoneMessageTexts(ctx, s, errors, org)
|
importVerifyPhoneMessageTexts(ctx, s, errors, org)
|
||||||
importDomainClaimedMessageTexts(ctx, s, errors, org)
|
importDomainClaimedMessageTexts(ctx, s, errors, org)
|
||||||
importPasswordlessRegistrationMessageTexts(ctx, s, errors, org)
|
importPasswordlessRegistrationMessageTexts(ctx, s, errors, org)
|
||||||
|
importInviteUserMessageTexts(ctx, s, errors, org)
|
||||||
if err := importHumanUsers(ctx, s, errors, successOrg, org, count, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessInitCode); err != nil {
|
if err := importHumanUsers(ctx, s, errors, successOrg, org, count, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessInitCode); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -975,6 +976,21 @@ func importPasswordlessRegistrationMessageTexts(ctx context.Context, s *Server,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func importInviteUserMessageTexts(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, org *admin_pb.DataOrg) {
|
||||||
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
|
defer func() { span.End() }()
|
||||||
|
|
||||||
|
if org.PasswordlessRegistrationMessages == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, message := range org.GetInviteUserMessages() {
|
||||||
|
_, err := s.command.SetOrgMessageText(ctx, authz.GetCtxData(ctx).OrgID, management.SetInviteUserCustomTextToDomain(message))
|
||||||
|
if err != nil {
|
||||||
|
*errors = append(*errors, &admin_pb.ImportDataError{Type: "invite_user_messages", Id: org.GetOrgId() + "_" + message.Language, Message: err.Error()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func importOrg2(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, success *admin_pb.ImportDataSuccess, count *counts, org *admin_pb.DataOrg) (err error) {
|
func importOrg2(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, success *admin_pb.ImportDataSuccess, count *counts, org *admin_pb.DataOrg) (err error) {
|
||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
@ -1236,6 +1252,7 @@ func (s *Server) dataOrgsV1ToDataOrgs(ctx context.Context, dataOrgs *v1_pb.Impor
|
|||||||
VerifyPhoneMessages: orgV1.GetVerifyPhoneMessages(),
|
VerifyPhoneMessages: orgV1.GetVerifyPhoneMessages(),
|
||||||
DomainClaimedMessages: orgV1.GetDomainClaimedMessages(),
|
DomainClaimedMessages: orgV1.GetDomainClaimedMessages(),
|
||||||
PasswordlessRegistrationMessages: orgV1.GetPasswordlessRegistrationMessages(),
|
PasswordlessRegistrationMessages: orgV1.GetPasswordlessRegistrationMessages(),
|
||||||
|
InviteUserMessages: orgV1.GetInviteUserMessages(),
|
||||||
OidcIdps: orgV1.GetOidcIdps(),
|
OidcIdps: orgV1.GetOidcIdps(),
|
||||||
JwtIdps: orgV1.GetJwtIdps(),
|
JwtIdps: orgV1.GetJwtIdps(),
|
||||||
UserLinks: orgV1.GetUserLinks(),
|
UserLinks: orgV1.GetUserLinks(),
|
||||||
|
@ -396,6 +396,54 @@ func (s *Server) ResetCustomPasswordChangeMessageTextToDefault(ctx context.Conte
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetCustomInviteUserMessageText(ctx context.Context, req *mgmt_pb.GetCustomInviteUserMessageTextRequest) (*mgmt_pb.GetCustomInviteUserMessageTextResponse, error) {
|
||||||
|
msg, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, authz.GetCtxData(ctx).OrgID, domain.InviteUserMessageType, req.Language, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mgmt_pb.GetCustomInviteUserMessageTextResponse{
|
||||||
|
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetDefaultInviteUserMessageText(ctx context.Context, req *mgmt_pb.GetDefaultInviteUserMessageTextRequest) (*mgmt_pb.GetDefaultInviteUserMessageTextResponse, error) {
|
||||||
|
msg, err := s.query.IAMMessageTextByTypeAndLanguage(ctx, domain.InviteUserMessageType, req.Language)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mgmt_pb.GetDefaultInviteUserMessageTextResponse{
|
||||||
|
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) SetCustomInviteUserMessageCustomText(ctx context.Context, req *mgmt_pb.SetCustomInviteUserMessageTextRequest) (*mgmt_pb.SetCustomInviteUserMessageTextResponse, error) {
|
||||||
|
result, err := s.command.SetOrgMessageText(ctx, authz.GetCtxData(ctx).OrgID, SetInviteUserCustomTextToDomain(req))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mgmt_pb.SetCustomInviteUserMessageTextResponse{
|
||||||
|
Details: object.ChangeToDetailsPb(
|
||||||
|
result.Sequence,
|
||||||
|
result.EventDate,
|
||||||
|
result.ResourceOwner,
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ResetCustomInviteUserMessageTextToDefault(ctx context.Context, req *mgmt_pb.ResetCustomInviteUserMessageTextToDefaultRequest) (*mgmt_pb.ResetCustomInviteUserMessageTextToDefaultResponse, error) {
|
||||||
|
result, err := s.command.RemoveOrgMessageTexts(ctx, authz.GetCtxData(ctx).OrgID, domain.InviteUserMessageType, language.Make(req.Language))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mgmt_pb.ResetCustomInviteUserMessageTextToDefaultResponse{
|
||||||
|
Details: object.ChangeToDetailsPb(
|
||||||
|
result.Sequence,
|
||||||
|
result.EventDate,
|
||||||
|
result.ResourceOwner,
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) GetCustomPasswordlessRegistrationMessageText(ctx context.Context, req *mgmt_pb.GetCustomPasswordlessRegistrationMessageTextRequest) (*mgmt_pb.GetCustomPasswordlessRegistrationMessageTextResponse, error) {
|
func (s *Server) GetCustomPasswordlessRegistrationMessageText(ctx context.Context, req *mgmt_pb.GetCustomPasswordlessRegistrationMessageTextRequest) (*mgmt_pb.GetCustomPasswordlessRegistrationMessageTextResponse, error) {
|
||||||
msg, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, authz.GetCtxData(ctx).OrgID, domain.PasswordlessRegistrationMessageType, req.Language, false)
|
msg, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, authz.GetCtxData(ctx).OrgID, domain.PasswordlessRegistrationMessageType, req.Language, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -122,6 +122,21 @@ func SetPasswordChangeCustomTextToDomain(msg *mgmt_pb.SetCustomPasswordChangeMes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetInviteUserCustomTextToDomain(msg *mgmt_pb.SetCustomInviteUserMessageTextRequest) *domain.CustomMessageText {
|
||||||
|
langTag := language.Make(msg.Language)
|
||||||
|
return &domain.CustomMessageText{
|
||||||
|
MessageTextType: domain.InviteUserMessageType,
|
||||||
|
Language: langTag,
|
||||||
|
Title: msg.Title,
|
||||||
|
PreHeader: msg.PreHeader,
|
||||||
|
Subject: msg.Subject,
|
||||||
|
Greeting: msg.Greeting,
|
||||||
|
Text: msg.Text,
|
||||||
|
ButtonText: msg.ButtonText,
|
||||||
|
FooterText: msg.FooterText,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func SetPasswordlessRegistrationCustomTextToDomain(msg *mgmt_pb.SetCustomPasswordlessRegistrationMessageTextRequest) *domain.CustomMessageText {
|
func SetPasswordlessRegistrationCustomTextToDomain(msg *mgmt_pb.SetCustomPasswordlessRegistrationMessageTextRequest) *domain.CustomMessageText {
|
||||||
langTag := language.Make(msg.Language)
|
langTag := language.Make(msg.Language)
|
||||||
return &domain.CustomMessageText{
|
return &domain.CustomMessageText{
|
||||||
|
@ -2437,3 +2437,310 @@ func TestServer_ListAuthenticationMethodTypes(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServer_CreateInviteCode(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
req *user.CreateInviteCodeRequest
|
||||||
|
prepare func(request *user.CreateInviteCodeRequest) error
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *user.CreateInviteCodeResponse
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "create, not existing",
|
||||||
|
args: args{
|
||||||
|
CTX,
|
||||||
|
&user.CreateInviteCodeRequest{
|
||||||
|
UserId: "notexisting",
|
||||||
|
},
|
||||||
|
func(request *user.CreateInviteCodeRequest) error { return nil },
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "create, ok",
|
||||||
|
args: args{
|
||||||
|
ctx: CTX,
|
||||||
|
req: &user.CreateInviteCodeRequest{},
|
||||||
|
prepare: func(request *user.CreateInviteCodeRequest) error {
|
||||||
|
resp := Instance.CreateHumanUser(CTX)
|
||||||
|
request.UserId = resp.GetUserId()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &user.CreateInviteCodeResponse{
|
||||||
|
Details: &object.Details{
|
||||||
|
ChangeDate: timestamppb.Now(),
|
||||||
|
ResourceOwner: Instance.DefaultOrg.Id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "create, invalid template",
|
||||||
|
args: args{
|
||||||
|
ctx: CTX,
|
||||||
|
req: &user.CreateInviteCodeRequest{
|
||||||
|
Verification: &user.CreateInviteCodeRequest_SendCode{
|
||||||
|
SendCode: &user.SendInviteCode{
|
||||||
|
UrlTemplate: gu.Ptr("{{"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prepare: func(request *user.CreateInviteCodeRequest) error {
|
||||||
|
resp := Instance.CreateHumanUser(CTX)
|
||||||
|
request.UserId = resp.GetUserId()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "create, valid template",
|
||||||
|
args: args{
|
||||||
|
ctx: CTX,
|
||||||
|
req: &user.CreateInviteCodeRequest{
|
||||||
|
Verification: &user.CreateInviteCodeRequest_SendCode{
|
||||||
|
SendCode: &user.SendInviteCode{
|
||||||
|
UrlTemplate: gu.Ptr("https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}"),
|
||||||
|
ApplicationName: gu.Ptr("TestApp"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prepare: func(request *user.CreateInviteCodeRequest) error {
|
||||||
|
resp := Instance.CreateHumanUser(CTX)
|
||||||
|
request.UserId = resp.GetUserId()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &user.CreateInviteCodeResponse{
|
||||||
|
Details: &object.Details{
|
||||||
|
ChangeDate: timestamppb.Now(),
|
||||||
|
ResourceOwner: Instance.DefaultOrg.Id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "create, return code, ok",
|
||||||
|
args: args{
|
||||||
|
ctx: CTX,
|
||||||
|
req: &user.CreateInviteCodeRequest{
|
||||||
|
Verification: &user.CreateInviteCodeRequest_ReturnCode{
|
||||||
|
ReturnCode: &user.ReturnInviteCode{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prepare: func(request *user.CreateInviteCodeRequest) error {
|
||||||
|
resp := Instance.CreateHumanUser(CTX)
|
||||||
|
request.UserId = resp.GetUserId()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &user.CreateInviteCodeResponse{
|
||||||
|
Details: &object.Details{
|
||||||
|
ChangeDate: timestamppb.Now(),
|
||||||
|
ResourceOwner: Instance.DefaultOrg.Id,
|
||||||
|
},
|
||||||
|
InviteCode: gu.Ptr("something"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := tt.args.prepare(tt.args.req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
got, err := Client.CreateInviteCode(tt.args.ctx, tt.args.req)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
integration.AssertDetails(t, tt.want, got)
|
||||||
|
if tt.want.GetInviteCode() != "" {
|
||||||
|
assert.NotEmpty(t, got.GetInviteCode())
|
||||||
|
} else {
|
||||||
|
assert.Empty(t, got.GetInviteCode())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServer_ResendInviteCode(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
req *user.ResendInviteCodeRequest
|
||||||
|
prepare func(request *user.ResendInviteCodeRequest) error
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *user.ResendInviteCodeResponse
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "user not existing",
|
||||||
|
args: args{
|
||||||
|
CTX,
|
||||||
|
&user.ResendInviteCodeRequest{
|
||||||
|
UserId: "notexisting",
|
||||||
|
},
|
||||||
|
func(request *user.ResendInviteCodeRequest) error { return nil },
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code not existing",
|
||||||
|
args: args{
|
||||||
|
ctx: CTX,
|
||||||
|
req: &user.ResendInviteCodeRequest{},
|
||||||
|
prepare: func(request *user.ResendInviteCodeRequest) error {
|
||||||
|
resp := Instance.CreateHumanUser(CTX)
|
||||||
|
request.UserId = resp.GetUserId()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code not sent before",
|
||||||
|
args: args{
|
||||||
|
ctx: CTX,
|
||||||
|
req: &user.ResendInviteCodeRequest{},
|
||||||
|
prepare: func(request *user.ResendInviteCodeRequest) error {
|
||||||
|
userResp := Instance.CreateHumanUser(CTX)
|
||||||
|
request.UserId = userResp.GetUserId()
|
||||||
|
Instance.CreateInviteCode(CTX, userResp.GetUserId())
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "resend, ok",
|
||||||
|
args: args{
|
||||||
|
ctx: CTX,
|
||||||
|
req: &user.ResendInviteCodeRequest{},
|
||||||
|
prepare: func(request *user.ResendInviteCodeRequest) error {
|
||||||
|
resp := Instance.CreateHumanUser(CTX)
|
||||||
|
request.UserId = resp.GetUserId()
|
||||||
|
_, err := Instance.Client.UserV2.CreateInviteCode(CTX, &user.CreateInviteCodeRequest{
|
||||||
|
UserId: resp.GetUserId(),
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &user.ResendInviteCodeResponse{
|
||||||
|
Details: &object.Details{
|
||||||
|
ChangeDate: timestamppb.Now(),
|
||||||
|
ResourceOwner: Instance.DefaultOrg.Id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := tt.args.prepare(tt.args.req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
got, err := Client.ResendInviteCode(tt.args.ctx, tt.args.req)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
integration.AssertDetails(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServer_VerifyInviteCode(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
req *user.VerifyInviteCodeRequest
|
||||||
|
prepare func(request *user.VerifyInviteCodeRequest) error
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *user.VerifyInviteCodeResponse
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "user not existing",
|
||||||
|
args: args{
|
||||||
|
CTX,
|
||||||
|
&user.VerifyInviteCodeRequest{
|
||||||
|
UserId: "notexisting",
|
||||||
|
},
|
||||||
|
func(request *user.VerifyInviteCodeRequest) error { return nil },
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code not existing",
|
||||||
|
args: args{
|
||||||
|
ctx: CTX,
|
||||||
|
req: &user.VerifyInviteCodeRequest{},
|
||||||
|
prepare: func(request *user.VerifyInviteCodeRequest) error {
|
||||||
|
resp := Instance.CreateHumanUser(CTX)
|
||||||
|
request.UserId = resp.GetUserId()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid code",
|
||||||
|
args: args{
|
||||||
|
ctx: CTX,
|
||||||
|
req: &user.VerifyInviteCodeRequest{
|
||||||
|
VerificationCode: "invalid",
|
||||||
|
},
|
||||||
|
prepare: func(request *user.VerifyInviteCodeRequest) error {
|
||||||
|
userResp := Instance.CreateHumanUser(CTX)
|
||||||
|
request.UserId = userResp.GetUserId()
|
||||||
|
Instance.CreateInviteCode(CTX, userResp.GetUserId())
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "verify, ok",
|
||||||
|
args: args{
|
||||||
|
ctx: CTX,
|
||||||
|
req: &user.VerifyInviteCodeRequest{},
|
||||||
|
prepare: func(request *user.VerifyInviteCodeRequest) error {
|
||||||
|
userResp := Instance.CreateHumanUser(CTX)
|
||||||
|
request.UserId = userResp.GetUserId()
|
||||||
|
codeResp := Instance.CreateInviteCode(CTX, userResp.GetUserId())
|
||||||
|
request.VerificationCode = codeResp.GetInviteCode()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &user.VerifyInviteCodeResponse{
|
||||||
|
Details: &object.Details{
|
||||||
|
ChangeDate: timestamppb.Now(),
|
||||||
|
ResourceOwner: Instance.DefaultOrg.Id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := tt.args.prepare(tt.args.req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
got, err := Client.VerifyInviteCode(tt.args.ctx, tt.args.req)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
integration.AssertDetails(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -45,14 +45,6 @@ func AddUserRequestToAddHuman(req *user.AddHumanUserRequest) (*command.AddHuman,
|
|||||||
if username == "" {
|
if username == "" {
|
||||||
username = req.GetEmail().GetEmail()
|
username = req.GetEmail().GetEmail()
|
||||||
}
|
}
|
||||||
var urlTemplate string
|
|
||||||
if req.GetEmail().GetSendCode() != nil {
|
|
||||||
urlTemplate = req.GetEmail().GetSendCode().GetUrlTemplate()
|
|
||||||
// test the template execution so the async notification will not fail because of it and the user won't realize
|
|
||||||
if err := domain.RenderConfirmURLTemplate(io.Discard, urlTemplate, req.GetUserId(), "code", "orgID"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
passwordChangeRequired := req.GetPassword().GetChangeRequired() || req.GetHashedPassword().GetChangeRequired()
|
passwordChangeRequired := req.GetPassword().GetChangeRequired() || req.GetHashedPassword().GetChangeRequired()
|
||||||
metadata := make([]*command.AddMetadataEntry, len(req.Metadata))
|
metadata := make([]*command.AddMetadataEntry, len(req.Metadata))
|
||||||
for i, metadataEntry := range req.Metadata {
|
for i, metadataEntry := range req.Metadata {
|
||||||
@ -69,6 +61,10 @@ func AddUserRequestToAddHuman(req *user.AddHumanUserRequest) (*command.AddHuman,
|
|||||||
DisplayName: link.GetUserName(),
|
DisplayName: link.GetUserName(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
email, err := addUserRequestEmailToCommand(req.GetEmail())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return &command.AddHuman{
|
return &command.AddHuman{
|
||||||
ID: req.GetUserId(),
|
ID: req.GetUserId(),
|
||||||
Username: username,
|
Username: username,
|
||||||
@ -76,12 +72,7 @@ func AddUserRequestToAddHuman(req *user.AddHumanUserRequest) (*command.AddHuman,
|
|||||||
LastName: req.GetProfile().GetFamilyName(),
|
LastName: req.GetProfile().GetFamilyName(),
|
||||||
NickName: req.GetProfile().GetNickName(),
|
NickName: req.GetProfile().GetNickName(),
|
||||||
DisplayName: req.GetProfile().GetDisplayName(),
|
DisplayName: req.GetProfile().GetDisplayName(),
|
||||||
Email: command.Email{
|
Email: email,
|
||||||
Address: domain.EmailAddress(req.GetEmail().GetEmail()),
|
|
||||||
Verified: req.GetEmail().GetIsVerified(),
|
|
||||||
ReturnCode: req.GetEmail().GetReturnCode() != nil,
|
|
||||||
URLTemplate: urlTemplate,
|
|
||||||
},
|
|
||||||
Phone: command.Phone{
|
Phone: command.Phone{
|
||||||
Number: domain.PhoneNumber(req.GetPhone().GetPhone()),
|
Number: domain.PhoneNumber(req.GetPhone().GetPhone()),
|
||||||
Verified: req.GetPhone().GetIsVerified(),
|
Verified: req.GetPhone().GetIsVerified(),
|
||||||
@ -100,6 +91,25 @@ func AddUserRequestToAddHuman(req *user.AddHumanUserRequest) (*command.AddHuman,
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addUserRequestEmailToCommand(email *user.SetHumanEmail) (command.Email, error) {
|
||||||
|
address := domain.EmailAddress(email.GetEmail())
|
||||||
|
switch v := email.GetVerification().(type) {
|
||||||
|
case *user.SetHumanEmail_ReturnCode:
|
||||||
|
return command.Email{Address: address, ReturnCode: true}, nil
|
||||||
|
case *user.SetHumanEmail_SendCode:
|
||||||
|
urlTemplate := v.SendCode.GetUrlTemplate()
|
||||||
|
// test the template execution so the async notification will not fail because of it and the user won't realize
|
||||||
|
if err := domain.RenderConfirmURLTemplate(io.Discard, urlTemplate, "userID", "code", "orgID"); err != nil {
|
||||||
|
return command.Email{}, err
|
||||||
|
}
|
||||||
|
return command.Email{Address: address, URLTemplate: urlTemplate}, nil
|
||||||
|
case *user.SetHumanEmail_IsVerified:
|
||||||
|
return command.Email{Address: address, Verified: v.IsVerified, NoEmailVerification: true}, nil
|
||||||
|
default:
|
||||||
|
return command.Email{Address: address}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func genderToDomain(gender user.Gender) domain.Gender {
|
func genderToDomain(gender user.Gender) domain.Gender {
|
||||||
switch gender {
|
switch gender {
|
||||||
case user.Gender_GENDER_UNSPECIFIED:
|
case user.Gender_GENDER_UNSPECIFIED:
|
||||||
@ -617,3 +627,54 @@ func authMethodTypeToPb(methodType domain.UserAuthMethodType) user.Authenticatio
|
|||||||
return user.AuthenticationMethodType_AUTHENTICATION_METHOD_TYPE_UNSPECIFIED
|
return user.AuthenticationMethodType_AUTHENTICATION_METHOD_TYPE_UNSPECIFIED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) CreateInviteCode(ctx context.Context, req *user.CreateInviteCodeRequest) (*user.CreateInviteCodeResponse, error) {
|
||||||
|
invite, err := createInviteCodeRequestToCommand(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
details, code, err := s.command.CreateInviteCode(ctx, invite)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &user.CreateInviteCodeResponse{
|
||||||
|
Details: object.DomainToDetailsPb(details),
|
||||||
|
InviteCode: code,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ResendInviteCode(ctx context.Context, req *user.ResendInviteCodeRequest) (*user.ResendInviteCodeResponse, error) {
|
||||||
|
details, err := s.command.ResendInviteCode(ctx, req.GetUserId(), "", "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &user.ResendInviteCodeResponse{
|
||||||
|
Details: object.DomainToDetailsPb(details),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) VerifyInviteCode(ctx context.Context, req *user.VerifyInviteCodeRequest) (*user.VerifyInviteCodeResponse, error) {
|
||||||
|
details, err := s.command.VerifyInviteCode(ctx, req.GetUserId(), req.GetVerificationCode())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &user.VerifyInviteCodeResponse{
|
||||||
|
Details: object.DomainToDetailsPb(details),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createInviteCodeRequestToCommand(req *user.CreateInviteCodeRequest) (*command.CreateUserInvite, error) {
|
||||||
|
switch v := req.GetVerification().(type) {
|
||||||
|
case *user.CreateInviteCodeRequest_SendCode:
|
||||||
|
urlTemplate := v.SendCode.GetUrlTemplate()
|
||||||
|
// test the template execution so the async notification will not fail because of it and the user won't realize
|
||||||
|
if err := domain.RenderConfirmURLTemplate(io.Discard, urlTemplate, req.GetUserId(), "code", "orgID"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &command.CreateUserInvite{UserID: req.GetUserId(), URLTemplate: urlTemplate, ApplicationName: v.SendCode.GetApplicationName()}, nil
|
||||||
|
case *user.CreateInviteCodeRequest_ReturnCode:
|
||||||
|
return &command.CreateUserInvite{UserID: req.GetUserId(), ReturnCode: true}, nil
|
||||||
|
default:
|
||||||
|
return &command.CreateUserInvite{UserID: req.GetUserId()}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
154
internal/api/ui/login/invite_user_handler.go
Normal file
154
internal/api/ui/login/invite_user_handler.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
package login
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
queryInviteUserCode = "code"
|
||||||
|
queryInviteUserUserID = "userID"
|
||||||
|
queryInviteUserLoginName = "loginname"
|
||||||
|
|
||||||
|
tmplInviteUser = "inviteuser"
|
||||||
|
)
|
||||||
|
|
||||||
|
type inviteUserFormData struct {
|
||||||
|
Code string `schema:"code"`
|
||||||
|
LoginName string `schema:"loginname"`
|
||||||
|
Password string `schema:"password"`
|
||||||
|
PasswordConfirm string `schema:"passwordconfirm"`
|
||||||
|
UserID string `schema:"userID"`
|
||||||
|
OrgID string `schema:"orgID"`
|
||||||
|
Resend bool `schema:"resend"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type inviteUserData struct {
|
||||||
|
baseData
|
||||||
|
profileData
|
||||||
|
Code string
|
||||||
|
LoginName string
|
||||||
|
UserID string
|
||||||
|
MinLength uint64
|
||||||
|
HasUppercase string
|
||||||
|
HasLowercase string
|
||||||
|
HasNumber string
|
||||||
|
HasSymbol string
|
||||||
|
}
|
||||||
|
|
||||||
|
func InviteUserLink(origin, userID, loginName, code, orgID string, authRequestID string) string {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set(queryInviteUserUserID, userID)
|
||||||
|
v.Set(queryInviteUserLoginName, loginName)
|
||||||
|
v.Set(queryInviteUserCode, code)
|
||||||
|
v.Set(queryOrgID, orgID)
|
||||||
|
v.Set(QueryAuthRequestID, authRequestID)
|
||||||
|
return externalLink(origin) + EndpointInviteUser + "?" + v.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Login) handleInviteUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
authReq := l.checkOptionalAuthRequestOfEmailLinks(r)
|
||||||
|
userID := r.FormValue(queryInviteUserUserID)
|
||||||
|
orgID := r.FormValue(queryOrgID)
|
||||||
|
code := r.FormValue(queryInviteUserCode)
|
||||||
|
loginName := r.FormValue(queryInviteUserLoginName)
|
||||||
|
l.renderInviteUser(w, r, authReq, userID, orgID, loginName, code, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Login) handleInviteUserCheck(w http.ResponseWriter, r *http.Request) {
|
||||||
|
data := new(inviteUserFormData)
|
||||||
|
authReq, err := l.getAuthRequestAndParseData(r, data)
|
||||||
|
if err != nil {
|
||||||
|
l.renderError(w, r, nil, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Resend {
|
||||||
|
l.resendUserInvite(w, r, authReq, data.UserID, data.OrgID, data.LoginName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.checkUserInviteCode(w, r, authReq, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Login) checkUserInviteCode(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *inviteUserFormData) {
|
||||||
|
if data.Password != data.PasswordConfirm {
|
||||||
|
err := zerrors.ThrowInvalidArgument(nil, "VIEW-KJS3h", "Errors.User.Password.ConfirmationWrong")
|
||||||
|
l.renderInviteUser(w, r, authReq, data.UserID, data.OrgID, data.LoginName, data.Code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userOrgID := ""
|
||||||
|
if authReq != nil {
|
||||||
|
userOrgID = authReq.UserOrgID
|
||||||
|
}
|
||||||
|
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||||
|
_, err := l.command.VerifyInviteCodeSetPassword(setUserContext(r.Context(), data.UserID, userOrgID), data.UserID, data.Code, data.Password, userAgentID)
|
||||||
|
if err != nil {
|
||||||
|
l.renderInviteUser(w, r, authReq, data.UserID, data.OrgID, data.LoginName, "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if authReq == nil {
|
||||||
|
l.defaultRedirect(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.renderNextStep(w, r, authReq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Login) resendUserInvite(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID, orgID, loginName string) {
|
||||||
|
var userOrgID, authRequestID string
|
||||||
|
if authReq != nil {
|
||||||
|
userOrgID = authReq.UserOrgID
|
||||||
|
authRequestID = authReq.ID
|
||||||
|
}
|
||||||
|
_, err := l.command.ResendInviteCode(setUserContext(r.Context(), userID, userOrgID), userID, userOrgID, authRequestID)
|
||||||
|
l.renderInviteUser(w, r, authReq, userID, orgID, loginName, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Login) renderInviteUser(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID, orgID, loginName string, code string, err error) {
|
||||||
|
var errID, errMessage string
|
||||||
|
if err != nil {
|
||||||
|
errID, errMessage = l.getErrorMessage(r, err)
|
||||||
|
}
|
||||||
|
if authReq != nil {
|
||||||
|
userID = authReq.UserID
|
||||||
|
orgID = authReq.UserOrgID
|
||||||
|
}
|
||||||
|
|
||||||
|
translator := l.getTranslator(r.Context(), authReq)
|
||||||
|
data := inviteUserData{
|
||||||
|
baseData: l.getBaseData(r, authReq, translator, "InviteUser.Title", "InviteUser.Description", errID, errMessage),
|
||||||
|
profileData: l.getProfileData(authReq),
|
||||||
|
UserID: userID,
|
||||||
|
Code: code,
|
||||||
|
}
|
||||||
|
// if the user clicked on the link in the mail, we need to make sure the loginName is rendered
|
||||||
|
if authReq == nil {
|
||||||
|
data.LoginName = loginName
|
||||||
|
data.UserName = loginName
|
||||||
|
}
|
||||||
|
policy := l.getPasswordComplexityPolicyByUserID(r, userID)
|
||||||
|
if policy != nil {
|
||||||
|
data.MinLength = policy.MinLength
|
||||||
|
if policy.HasUppercase {
|
||||||
|
data.HasUppercase = UpperCaseRegex
|
||||||
|
}
|
||||||
|
if policy.HasLowercase {
|
||||||
|
data.HasLowercase = LowerCaseRegex
|
||||||
|
}
|
||||||
|
if policy.HasSymbol {
|
||||||
|
data.HasSymbol = SymbolRegex
|
||||||
|
}
|
||||||
|
if policy.HasNumber {
|
||||||
|
data.HasNumber = NumberRegex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if authReq == nil {
|
||||||
|
if err == nil {
|
||||||
|
l.customTexts(r.Context(), translator, orgID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplInviteUser], data, nil)
|
||||||
|
}
|
@ -68,6 +68,7 @@ func CreateRenderer(pathPrefix string, staticStorage static.Storage, cookieName
|
|||||||
tmplInitPasswordDone: "init_password_done.html",
|
tmplInitPasswordDone: "init_password_done.html",
|
||||||
tmplInitUser: "init_user.html",
|
tmplInitUser: "init_user.html",
|
||||||
tmplInitUserDone: "init_user_done.html",
|
tmplInitUserDone: "init_user_done.html",
|
||||||
|
tmplInviteUser: "invite_user.html",
|
||||||
tmplPasswordResetDone: "password_reset_done.html",
|
tmplPasswordResetDone: "password_reset_done.html",
|
||||||
tmplChangePassword: "change_password.html",
|
tmplChangePassword: "change_password.html",
|
||||||
tmplChangePasswordDone: "change_password_done.html",
|
tmplChangePasswordDone: "change_password_done.html",
|
||||||
@ -193,6 +194,9 @@ func CreateRenderer(pathPrefix string, staticStorage static.Storage, cookieName
|
|||||||
"initUserUrl": func() string {
|
"initUserUrl": func() string {
|
||||||
return path.Join(r.pathPrefix, EndpointInitUser)
|
return path.Join(r.pathPrefix, EndpointInitUser)
|
||||||
},
|
},
|
||||||
|
"inviteUserUrl": func() string {
|
||||||
|
return path.Join(r.pathPrefix, EndpointInviteUser)
|
||||||
|
},
|
||||||
"changePasswordUrl": func() string {
|
"changePasswordUrl": func() string {
|
||||||
return path.Join(r.pathPrefix, EndpointChangePassword)
|
return path.Join(r.pathPrefix, EndpointChangePassword)
|
||||||
},
|
},
|
||||||
@ -329,6 +333,8 @@ func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq *
|
|||||||
l.renderInternalError(w, r, authReq, zerrors.ThrowPreconditionFailed(nil, "APP-asb43", "Errors.User.GrantRequired"))
|
l.renderInternalError(w, r, authReq, zerrors.ThrowPreconditionFailed(nil, "APP-asb43", "Errors.User.GrantRequired"))
|
||||||
case *domain.ProjectRequiredStep:
|
case *domain.ProjectRequiredStep:
|
||||||
l.renderInternalError(w, r, authReq, zerrors.ThrowPreconditionFailed(nil, "APP-m92d", "Errors.User.ProjectRequired"))
|
l.renderInternalError(w, r, authReq, zerrors.ThrowPreconditionFailed(nil, "APP-m92d", "Errors.User.ProjectRequired"))
|
||||||
|
case *domain.VerifyInviteStep:
|
||||||
|
l.renderInviteUser(w, r, authReq, "", "", "", "", nil)
|
||||||
default:
|
default:
|
||||||
l.renderInternalError(w, r, authReq, zerrors.ThrowInternal(nil, "APP-ds3QF", "step no possible"))
|
l.renderInternalError(w, r, authReq, zerrors.ThrowInternal(nil, "APP-ds3QF", "step no possible"))
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ const (
|
|||||||
EndpointChangePassword = "/password/change"
|
EndpointChangePassword = "/password/change"
|
||||||
EndpointPasswordReset = "/password/reset"
|
EndpointPasswordReset = "/password/reset"
|
||||||
EndpointInitUser = "/user/init"
|
EndpointInitUser = "/user/init"
|
||||||
|
EndpointInviteUser = "/user/invite"
|
||||||
EndpointMFAVerify = "/mfa/verify"
|
EndpointMFAVerify = "/mfa/verify"
|
||||||
EndpointMFAPrompt = "/mfa/prompt"
|
EndpointMFAPrompt = "/mfa/prompt"
|
||||||
EndpointMFAInitVerify = "/mfa/init/verify"
|
EndpointMFAInitVerify = "/mfa/init/verify"
|
||||||
@ -94,6 +95,8 @@ func CreateRouter(login *Login, interceptors ...mux.MiddlewareFunc) *mux.Router
|
|||||||
router.HandleFunc(EndpointPasswordReset, login.handlePasswordReset).Methods(http.MethodGet)
|
router.HandleFunc(EndpointPasswordReset, login.handlePasswordReset).Methods(http.MethodGet)
|
||||||
router.HandleFunc(EndpointInitUser, login.handleInitUser).Methods(http.MethodGet)
|
router.HandleFunc(EndpointInitUser, login.handleInitUser).Methods(http.MethodGet)
|
||||||
router.HandleFunc(EndpointInitUser, login.handleInitUserCheck).Methods(http.MethodPost)
|
router.HandleFunc(EndpointInitUser, login.handleInitUserCheck).Methods(http.MethodPost)
|
||||||
|
router.HandleFunc(EndpointInviteUser, login.handleInviteUser).Methods(http.MethodGet)
|
||||||
|
router.HandleFunc(EndpointInviteUser, login.handleInviteUserCheck).Methods(http.MethodPost)
|
||||||
router.HandleFunc(EndpointMFAVerify, login.handleMFAVerify).Methods(http.MethodPost)
|
router.HandleFunc(EndpointMFAVerify, login.handleMFAVerify).Methods(http.MethodPost)
|
||||||
router.HandleFunc(EndpointMFAPrompt, login.handleMFAPromptSelection).Methods(http.MethodGet)
|
router.HandleFunc(EndpointMFAPrompt, login.handleMFAPromptSelection).Methods(http.MethodGet)
|
||||||
router.HandleFunc(EndpointMFAPrompt, login.handleMFAPrompt).Methods(http.MethodPost)
|
router.HandleFunc(EndpointMFAPrompt, login.handleMFAPrompt).Methods(http.MethodPost)
|
||||||
|
@ -81,6 +81,14 @@ InitUserDone:
|
|||||||
Description: Имейлът е потвърден и паролата е успешно зададена
|
Description: Имейлът е потвърден и паролата е успешно зададена
|
||||||
NextButtonText: следващия
|
NextButtonText: следващия
|
||||||
CancelButtonText: анулиране
|
CancelButtonText: анулиране
|
||||||
|
InviteUser:
|
||||||
|
Title: Активиране на потребителя
|
||||||
|
Description: Проверете своя имейл с кода по-долу и задайте паролата си.
|
||||||
|
CodeLabel: Код
|
||||||
|
NewPasswordLabel: Нова парола
|
||||||
|
NewPasswordConfirm: Потвърди парола
|
||||||
|
NextButtonText: Напред
|
||||||
|
ResendButtonText: Изпрати отново код
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: 2-факторна настройка
|
Title: 2-факторна настройка
|
||||||
Description: >-
|
Description: >-
|
||||||
|
@ -86,6 +86,15 @@ InitUserDone:
|
|||||||
NextButtonText: Další
|
NextButtonText: Další
|
||||||
CancelButtonText: Zrušit
|
CancelButtonText: Zrušit
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: Aktivace uživatele
|
||||||
|
Description: Ověřte svůj e-mail pomocí níže uvedeného kódu a nastavte si heslo.
|
||||||
|
CodeLabel: Kód
|
||||||
|
NewPasswordLabel: Nové heslo
|
||||||
|
NewPasswordConfirm: Potvrďte heslo
|
||||||
|
NextButtonText: Další
|
||||||
|
ResendButtonText: Odeslat kód znovu
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: Nastavení 2-faktorové autentizace
|
Title: Nastavení 2-faktorové autentizace
|
||||||
Description: 2-faktorová autentizace vám poskytuje další zabezpečení pro váš uživatelský účet. Tím je zajištěno, že k vašemu účtu máte přístup pouze vy.
|
Description: 2-faktorová autentizace vám poskytuje další zabezpečení pro váš uživatelský účet. Tím je zajištěno, že k vašemu účtu máte přístup pouze vy.
|
||||||
|
@ -86,6 +86,15 @@ InitUserDone:
|
|||||||
NextButtonText: Weiter
|
NextButtonText: Weiter
|
||||||
CancelButtonText: Abbrechen
|
CancelButtonText: Abbrechen
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: Benutzer aktivieren
|
||||||
|
Description: Bestätige deine E-Mail-Adresse mit dem unten stehenden Code und lege dein Passwort fest.
|
||||||
|
CodeLabel: Code
|
||||||
|
NewPasswordLabel: Neues Passwort
|
||||||
|
NewPasswordConfirm: Passwort bestätigen
|
||||||
|
NextButtonText: Weiter
|
||||||
|
ResendButtonText: Code erneut senden
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: Zweitfaktor hinzufügen
|
Title: Zweitfaktor hinzufügen
|
||||||
Description: Die Zwei-Faktor-Authentifizierung gibt dir eine zusätzliche Sicherheit für dein Benutzerkonto. Damit stellst du sicher, dass nur du Zugriff auf dein Konto hast.
|
Description: Die Zwei-Faktor-Authentifizierung gibt dir eine zusätzliche Sicherheit für dein Benutzerkonto. Damit stellst du sicher, dass nur du Zugriff auf dein Konto hast.
|
||||||
|
@ -86,6 +86,15 @@ InitUserDone:
|
|||||||
NextButtonText: Next
|
NextButtonText: Next
|
||||||
CancelButtonText: Cancel
|
CancelButtonText: Cancel
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: Activate User
|
||||||
|
Description: Verify your e-mail with the code below and set your password.
|
||||||
|
CodeLabel: Code
|
||||||
|
NewPasswordLabel: New Password
|
||||||
|
NewPasswordConfirm: Confirm Password
|
||||||
|
NextButtonText: Next
|
||||||
|
ResendButtonText: Resend Code
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: 2-Factor Setup
|
Title: 2-Factor Setup
|
||||||
Description: 2-factor authentication gives you an additional security for your user account. This ensures that only you have access to your account.
|
Description: 2-factor authentication gives you an additional security for your user account. This ensures that only you have access to your account.
|
||||||
|
@ -86,6 +86,15 @@ InitUserDone:
|
|||||||
NextButtonText: siguiente
|
NextButtonText: siguiente
|
||||||
CancelButtonText: cancelar
|
CancelButtonText: cancelar
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: Activar usuario
|
||||||
|
Description: Verifica tu email con el siguiente código y establece tu contraseña.
|
||||||
|
CodeLabel: Código
|
||||||
|
NewPasswordLabel: Nueva contraseña
|
||||||
|
NewPasswordConfirm: Confirmar contraseña
|
||||||
|
NextButtonText: siguiente
|
||||||
|
ResendButtonText: reenviar código
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: Configuración de doble factor
|
Title: Configuración de doble factor
|
||||||
Description: La autenticación de doble factor te proporciona seguridad adicional para tu cuenta de usuario. Ésta asegura que solo tú tienes acceso a tu cuenta.
|
Description: La autenticación de doble factor te proporciona seguridad adicional para tu cuenta de usuario. Ésta asegura que solo tú tienes acceso a tu cuenta.
|
||||||
|
@ -86,6 +86,15 @@ InitUserDone:
|
|||||||
NextButtonText: Suivant
|
NextButtonText: Suivant
|
||||||
CancelButtonText: Annuler
|
CancelButtonText: Annuler
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: Activer l'utilisateur
|
||||||
|
Description: Vérifiez votre e-mail avec le code ci-dessous et définissez votre mot de passe.
|
||||||
|
CodeLabel: Code
|
||||||
|
NewPasswordLabel: Nouveau mot de passe
|
||||||
|
NewPasswordConfirm: Confirmer le mot de passe
|
||||||
|
NextButtonText: Suivant
|
||||||
|
ResendButtonText: Renvoyer le code
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: Configuration authentification à 2 facteurs
|
Title: Configuration authentification à 2 facteurs
|
||||||
Description: L'authentification authentification à 2 facteurs vous offre une sécurité supplémentaire pour votre compte d'utilisateur. Vous êtes ainsi assuré d'être le seul à avoir accès à votre compte.
|
Description: L'authentification authentification à 2 facteurs vous offre une sécurité supplémentaire pour votre compte d'utilisateur. Vous êtes ainsi assuré d'être le seul à avoir accès à votre compte.
|
||||||
|
@ -76,6 +76,14 @@ InitUserDone:
|
|||||||
Description: Email terverifikasi dan Kata Sandi berhasil ditetapkan
|
Description: Email terverifikasi dan Kata Sandi berhasil ditetapkan
|
||||||
NextButtonText: Berikutnya
|
NextButtonText: Berikutnya
|
||||||
CancelButtonText: Membatalkan
|
CancelButtonText: Membatalkan
|
||||||
|
InviteUser:
|
||||||
|
Title: Aktifkan Pengguna
|
||||||
|
Description: Verifikasi email Anda dengan kode di bawah ini dan atur kata sandi Anda.
|
||||||
|
CodeLabel: Kode
|
||||||
|
NewPasswordLabel: Kata Sandi Baru
|
||||||
|
NewPasswordConfirm: Konfirmasi Kata Sandi
|
||||||
|
NextButtonText: Selanjutnya
|
||||||
|
ResendButtonText: Kirim Ulang Kode
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: Pengaturan 2 Faktor
|
Title: Pengaturan 2 Faktor
|
||||||
Description: Otentikasi 2 faktor memberi Anda keamanan tambahan untuk akun pengguna Anda.
|
Description: Otentikasi 2 faktor memberi Anda keamanan tambahan untuk akun pengguna Anda.
|
||||||
|
@ -86,6 +86,15 @@ InitUserDone:
|
|||||||
NextButtonText: Avanti
|
NextButtonText: Avanti
|
||||||
CancelButtonText: annulla
|
CancelButtonText: annulla
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: Attiva utente
|
||||||
|
Description: Verifica la tua email con il codice seguente e imposta la tua password.
|
||||||
|
CodeLabel: Codice
|
||||||
|
NewPasswordLabel: Nuova password
|
||||||
|
NewPasswordConfirm: Conferma password
|
||||||
|
NextButtonText: Avanti
|
||||||
|
ResendButtonText: Reinvia codice
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: Impostazione a 2 fattori
|
Title: Impostazione a 2 fattori
|
||||||
Description: L'autenticazione a due fattori offre un'ulteriore sicurezza al vostro account utente. Questo garantisce che solo voi possiate accedere al vostro account.
|
Description: L'autenticazione a due fattori offre un'ulteriore sicurezza al vostro account utente. Questo garantisce che solo voi possiate accedere al vostro account.
|
||||||
|
@ -79,6 +79,15 @@ InitUserDone:
|
|||||||
NextButtonText: 次へ
|
NextButtonText: 次へ
|
||||||
CancelButtonText: キャンセル
|
CancelButtonText: キャンセル
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: ユーザーの有効化
|
||||||
|
Description: 下のコードでメールアドレスを確認し、パスワードを設定してください。
|
||||||
|
CodeLabel: コード
|
||||||
|
NewPasswordLabel: 新しいパスワード
|
||||||
|
NewPasswordConfirm: パスワードの確認
|
||||||
|
NextButtonText: 次へ
|
||||||
|
ResendButtonText: コードを再送信
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: 二要素認証のセットアップ
|
Title: 二要素認証のセットアップ
|
||||||
Description: 二要素認証でアカウントのセキュリティを強化します。
|
Description: 二要素認証でアカウントのセキュリティを強化します。
|
||||||
|
@ -86,6 +86,15 @@ InitUserDone:
|
|||||||
NextButtonText: следно
|
NextButtonText: следно
|
||||||
CancelButtonText: откажи
|
CancelButtonText: откажи
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: Активирање на корисникот
|
||||||
|
Description: Проверете го вашиот имејл со кодот подолу и поставете ја вашата лозинка.
|
||||||
|
CodeLabel: Код
|
||||||
|
NewPasswordLabel: Нова лозинка
|
||||||
|
NewPasswordConfirm: Потврди лозинка
|
||||||
|
NextButtonText: Следно
|
||||||
|
ResendButtonText: Повторно испрати код
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: Подесување на 2-факторска автентикација
|
Title: Подесување на 2-факторска автентикација
|
||||||
Description: 2-факторската автентикација ви дава дополнителна безбедност за вашата корисничка сметка. Ова обезбедува само вие да имате пристап до вашата сметка.
|
Description: 2-факторската автентикација ви дава дополнителна безбедност за вашата корисничка сметка. Ова обезбедува само вие да имате пристап до вашата сметка.
|
||||||
|
@ -86,6 +86,15 @@ InitUserDone:
|
|||||||
NextButtonText: Volgende
|
NextButtonText: Volgende
|
||||||
CancelButtonText: Annuleren
|
CancelButtonText: Annuleren
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: Gebruiker activeren
|
||||||
|
Description: Verifieer uw e-mail met de onderstaande code en stel uw wachtwoord in.
|
||||||
|
CodeLabel: Code
|
||||||
|
NewPasswordLabel: Nieuw wachtwoord
|
||||||
|
NewPasswordConfirm: Wachtwoord bevestigen
|
||||||
|
NextButtonText: Volgende
|
||||||
|
ResendButtonText: Code opnieuw verzenden
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: 2-Factor Setup
|
Title: 2-Factor Setup
|
||||||
Description: 2-factor authenticatie geeft u extra beveiliging voor uw gebruikersaccount. Hierdoor bent u de enige die toegang heeft tot uw account.
|
Description: 2-factor authenticatie geeft u extra beveiliging voor uw gebruikersaccount. Hierdoor bent u de enige die toegang heeft tot uw account.
|
||||||
|
@ -86,6 +86,15 @@ InitUserDone:
|
|||||||
NextButtonText: dalej
|
NextButtonText: dalej
|
||||||
CancelButtonText: anuluj
|
CancelButtonText: anuluj
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: Aktywuj użytkownika
|
||||||
|
Description: Zweryfikuj swój adres e-mail za pomocą poniższego kodu i ustaw swoje hasło.
|
||||||
|
CodeLabel: Kod
|
||||||
|
NewPasswordLabel: Nowe hasło
|
||||||
|
NewPasswordConfirm: Potwierdź hasło
|
||||||
|
NextButtonText: Dalej
|
||||||
|
ResendButtonText: Wyślij ponownie kod
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: Konfiguracja 2-etapowego uwierzytelniania
|
Title: Konfiguracja 2-etapowego uwierzytelniania
|
||||||
Description: 2-etapowe uwierzytelnianie daje Ci dodatkową ochronę dla Twojego konta użytkownika. Dzięki temu masz pewność, że tylko Ty masz dostęp do swojego konta.
|
Description: 2-etapowe uwierzytelnianie daje Ci dodatkową ochronę dla Twojego konta użytkownika. Dzięki temu masz pewność, że tylko Ty masz dostęp do swojego konta.
|
||||||
|
@ -86,6 +86,15 @@ InitUserDone:
|
|||||||
NextButtonText: próximo
|
NextButtonText: próximo
|
||||||
CancelButtonText: cancelar
|
CancelButtonText: cancelar
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: Ativar usuário
|
||||||
|
Description: Verifique seu e-mail com o código abaixo e defina sua senha.
|
||||||
|
CodeLabel: Código
|
||||||
|
NewPasswordLabel: Nova senha
|
||||||
|
NewPasswordConfirm: Confirmar senha
|
||||||
|
NextButtonText: Próximo
|
||||||
|
ResendButtonText: Reenviar código
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: Configuração de 2 fatores
|
Title: Configuração de 2 fatores
|
||||||
Description: A autenticação de 2 fatores fornece uma segurança adicional para sua conta de usuário. Isso garante que apenas você tenha acesso à sua conta.
|
Description: A autenticação de 2 fatores fornece uma segurança adicional para sua conta de usuário. Isso garante que apenas você tenha acesso à sua conta.
|
||||||
|
@ -85,6 +85,15 @@ InitUserDone:
|
|||||||
NextButtonText: далее
|
NextButtonText: далее
|
||||||
CancelButtonText: отмена
|
CancelButtonText: отмена
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: Активировать пользователя
|
||||||
|
Description: Проверьте свой адрес электронной почты с помощью кода ниже и установите свой пароль.
|
||||||
|
CodeLabel: Код
|
||||||
|
NewPasswordLabel: Новый пароль
|
||||||
|
NewPasswordConfirm: Подтвердить пароль
|
||||||
|
NextButtonText: Далее
|
||||||
|
ResendButtonText: Отправить код повторно
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: Установка двухфакторной аутентификации
|
Title: Установка двухфакторной аутентификации
|
||||||
Description: Двухфакторная аутентификация обеспечивает дополнительную защиту вашей учётной записи.
|
Description: Двухфакторная аутентификация обеспечивает дополнительную защиту вашей учётной записи.
|
||||||
|
@ -86,6 +86,15 @@ InitUserDone:
|
|||||||
NextButtonText: Fortsätt
|
NextButtonText: Fortsätt
|
||||||
CancelButtonText: Avbryt
|
CancelButtonText: Avbryt
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: Aktivera användare
|
||||||
|
Description: Verifiera din e-post med koden nedan och sätt ditt lösenord.
|
||||||
|
CodeLabel: Kod
|
||||||
|
NewPasswordLabel: Nytt lösenord
|
||||||
|
NewPasswordConfirm: Bekräfta lösenord
|
||||||
|
NextButtonText: Nästa
|
||||||
|
ResendButtonText: Skicka koden igen
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: tvåfaktorinställningar
|
Title: tvåfaktorinställningar
|
||||||
Description: 2-factor-identifiering ökar säkerheten för ditt konto. Enbart du som har tillgång till enheten kan logga in.
|
Description: 2-factor-identifiering ökar säkerheten för ditt konto. Enbart du som har tillgång till enheten kan logga in.
|
||||||
|
@ -86,6 +86,15 @@ InitUserDone:
|
|||||||
NextButtonText: 继续
|
NextButtonText: 继续
|
||||||
CancelButtonText: 取消
|
CancelButtonText: 取消
|
||||||
|
|
||||||
|
InviteUser:
|
||||||
|
Title: 激活用户
|
||||||
|
Description: 使用以下代码验证您的电子邮件并设置您的密码。
|
||||||
|
CodeLabel: 代码
|
||||||
|
NewPasswordLabel: 新密码
|
||||||
|
NewPasswordConfirm: 确认密码
|
||||||
|
NextButtonText: 下一步
|
||||||
|
ResendButtonText: 重新发送代码
|
||||||
|
|
||||||
InitMFAPrompt:
|
InitMFAPrompt:
|
||||||
Title: 两步验证设置
|
Title: 两步验证设置
|
||||||
Description: 两步验证为您的账户提供了额外的安全保障。这确保只有你能访问你的账户。
|
Description: 两步验证为您的账户提供了额外的安全保障。这确保只有你能访问你的账户。
|
||||||
|
63
internal/api/ui/login/static/templates/invite_user.html
Normal file
63
internal/api/ui/login/static/templates/invite_user.html
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
{{template "main-top" .}}
|
||||||
|
|
||||||
|
<div class="lgn-head">
|
||||||
|
<h1>{{t "InviteUser.Title"}}</h1>
|
||||||
|
|
||||||
|
{{ template "user-profile" . }}
|
||||||
|
|
||||||
|
<p>{{t "InviteUser.Description"}}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form action="{{ inviteUserUrl }}" method="POST">
|
||||||
|
|
||||||
|
{{ .CSRF }}
|
||||||
|
|
||||||
|
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||||
|
<input type="hidden" name="userID" value="{{ .UserID }}" />
|
||||||
|
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||||
|
<input type="text" name="loginName" value="{{if .DisplayLoginNameSuffix}}{{.LoginName}}{{else}}{{.UserName}}{{end}}" autocomplete="username" class="hidden" />
|
||||||
|
|
||||||
|
<div class="fields">
|
||||||
|
<div class="field">
|
||||||
|
<label class="lgn-label" for="code">{{t "InviteUser.CodeLabel"}}</label>
|
||||||
|
<input class="lgn-input" {{if .ErrMessage}}shake {{end}} type="text" id="code" name="code" value="{{.Code}}" autocomplete="one-time-code" autofocus
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="lgn-label" for="password">{{t "InviteUser.NewPasswordLabel"}}</label>
|
||||||
|
<input data-minlength="{{ .MinLength }}" data-has-uppercase="{{ .HasUppercase }}"
|
||||||
|
data-has-lowercase="{{ .HasLowercase }}" data-has-number="{{ .HasNumber }}"
|
||||||
|
data-has-symbol="{{ .HasSymbol }}" class="lgn-input" type="password" id="password" name="password"
|
||||||
|
autocomplete="new-password" autofocus required>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="lgn-label" for="passwordconfirm">{{t "InviteUser.NewPasswordConfirm"}}</label>
|
||||||
|
<input class="lgn-input" type="password" id="passwordconfirm" name="passwordconfirm"
|
||||||
|
autocomplete="new-password" autofocus required>
|
||||||
|
{{ template "password-complexity-policy-description" . }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ template "error-message" .}}
|
||||||
|
|
||||||
|
<div class="lgn-actions lgn-reverse-order">
|
||||||
|
<!-- position element in header -->
|
||||||
|
<a class="lgn-icon-button lgn-left-action" href="{{ loginUrl }}">
|
||||||
|
<i class="lgn-icon-arrow-left-solid"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button type="submit" id="init-button" name="resend" value="false"
|
||||||
|
class="lgn-primary lgn-raised-button">{{t "InviteUser.NextButtonText"}}</button>
|
||||||
|
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
|
||||||
|
<button type="submit" name="resend" value="true" class="lgn-stroked-button" formnovalidate>{{t "InviteUser.ResendButtonText"}}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script src="{{ resourceUrl "scripts/form_submit.js" }}"></script>
|
||||||
|
<script src="{{ resourceUrl "scripts/password_policy_check.js" }}"></script>
|
||||||
|
<script src="{{ resourceUrl "scripts/init_password_check.js" }}"></script>
|
||||||
|
|
||||||
|
{{template "main-bottom" .}}
|
@ -106,6 +106,7 @@ type idpUserLinksProvider interface {
|
|||||||
type userEventProvider interface {
|
type userEventProvider interface {
|
||||||
UserEventsByID(ctx context.Context, id string, changeDate time.Time, eventTypes []eventstore.EventType) ([]eventstore.Event, error)
|
UserEventsByID(ctx context.Context, id string, changeDate time.Time, eventTypes []eventstore.EventType) ([]eventstore.Event, error)
|
||||||
PasswordCodeExists(ctx context.Context, userID string) (exists bool, err error)
|
PasswordCodeExists(ctx context.Context, userID string) (exists bool, err error)
|
||||||
|
InviteCodeExists(ctx context.Context, userID string) (exists bool, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type userCommandProvider interface {
|
type userCommandProvider interface {
|
||||||
@ -1254,8 +1255,18 @@ func (repo *AuthRequestRepo) firstFactorChecked(ctx context.Context, request *do
|
|||||||
|
|
||||||
if user.PasswordInitRequired {
|
if user.PasswordInitRequired {
|
||||||
if !user.IsEmailVerified {
|
if !user.IsEmailVerified {
|
||||||
|
// If the user was created through the user resource API,
|
||||||
|
// they can either have an invite code...
|
||||||
|
exists, err := repo.UserEventProvider.InviteCodeExists(ctx, user.ID)
|
||||||
|
logging.WithFields("userID", user.ID).OnError(err).Error("unable to check if invite code exists")
|
||||||
|
if err == nil && exists {
|
||||||
|
return &domain.VerifyInviteStep{}
|
||||||
|
}
|
||||||
|
// or were created with an explicit email verification mail
|
||||||
return &domain.VerifyEMailStep{InitPassword: true}
|
return &domain.VerifyEMailStep{InitPassword: true}
|
||||||
}
|
}
|
||||||
|
// If they were created with a verified mail, they might have never received mail to set their password,
|
||||||
|
// e.g. when created through a user resource API. In this case we'll just create and send one now.
|
||||||
exists, err := repo.UserEventProvider.PasswordCodeExists(ctx, user.ID)
|
exists, err := repo.UserEventProvider.PasswordCodeExists(ctx, user.ID)
|
||||||
logging.WithFields("userID", user.ID).OnError(err).Error("unable to check if password code exists")
|
logging.WithFields("userID", user.ID).OnError(err).Error("unable to check if password code exists")
|
||||||
if err == nil && !exists {
|
if err == nil && !exists {
|
||||||
|
@ -110,8 +110,9 @@ func (m *mockViewNoUser) UserByID(context.Context, string, string) (*user_view_m
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mockEventUser struct {
|
type mockEventUser struct {
|
||||||
Events []eventstore.Event
|
Events []eventstore.Event
|
||||||
CodeExists bool
|
PwCodeExists bool
|
||||||
|
InvitationCodeExists bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockEventUser) UserEventsByID(ctx context.Context, id string, changeDate time.Time, types []eventstore.EventType) ([]eventstore.Event, error) {
|
func (m *mockEventUser) UserEventsByID(ctx context.Context, id string, changeDate time.Time, types []eventstore.EventType) ([]eventstore.Event, error) {
|
||||||
@ -119,7 +120,11 @@ func (m *mockEventUser) UserEventsByID(ctx context.Context, id string, changeDat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockEventUser) PasswordCodeExists(ctx context.Context, userID string) (bool, error) {
|
func (m *mockEventUser) PasswordCodeExists(ctx context.Context, userID string) (bool, error) {
|
||||||
return m.CodeExists, nil
|
return m.PwCodeExists, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockEventUser) InviteCodeExists(ctx context.Context, userID string) (bool, error) {
|
||||||
|
return m.InvitationCodeExists, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockEventUser) GetLatestUserSessionSequence(ctx context.Context, instanceID string) (*query.CurrentState, error) {
|
func (m *mockEventUser) GetLatestUserSessionSequence(ctx context.Context, instanceID string) (*query.CurrentState, error) {
|
||||||
@ -140,6 +145,10 @@ func (m *mockEventErrUser) PasswordCodeExists(ctx context.Context, userID string
|
|||||||
return false, zerrors.ThrowInternal(nil, "id", "internal error")
|
return false, zerrors.ThrowInternal(nil, "id", "internal error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockEventErrUser) InviteCodeExists(ctx context.Context, userID string) (bool, error) {
|
||||||
|
return false, zerrors.ThrowInternal(nil, "id", "internal error")
|
||||||
|
}
|
||||||
|
|
||||||
type mockViewUser struct {
|
type mockViewUser struct {
|
||||||
InitRequired bool
|
InitRequired bool
|
||||||
PasswordInitRequired bool
|
PasswordInitRequired bool
|
||||||
@ -1019,6 +1028,36 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
|||||||
[]domain.NextStep{&domain.VerifyEMailStep{}},
|
[]domain.NextStep{&domain.VerifyEMailStep{}},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"password not set (email not verified), invite code exists, invite step",
|
||||||
|
fields{
|
||||||
|
userSessionViewProvider: &mockViewUserSession{},
|
||||||
|
userViewProvider: &mockViewUser{
|
||||||
|
PasswordInitRequired: true,
|
||||||
|
},
|
||||||
|
userEventProvider: &mockEventUser{
|
||||||
|
InvitationCodeExists: true,
|
||||||
|
},
|
||||||
|
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||||
|
policy: &query.LockoutPolicy{
|
||||||
|
ShowFailures: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||||
|
idpUserLinksProvider: &mockIDPUserLinks{},
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
&domain.AuthRequest{
|
||||||
|
UserID: "UserID",
|
||||||
|
LoginPolicy: &domain.LoginPolicy{
|
||||||
|
AllowUsernamePassword: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
[]domain.NextStep{&domain.VerifyInviteStep{}},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"password not set (email not verified), init password step",
|
"password not set (email not verified), init password step",
|
||||||
fields{
|
fields{
|
||||||
@ -1056,7 +1095,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
|||||||
IsEmailVerified: true,
|
IsEmailVerified: true,
|
||||||
},
|
},
|
||||||
userEventProvider: &mockEventUser{
|
userEventProvider: &mockEventUser{
|
||||||
CodeExists: true,
|
PwCodeExists: true,
|
||||||
},
|
},
|
||||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||||
policy: &query.LockoutPolicy{
|
policy: &query.LockoutPolicy{
|
||||||
@ -1088,7 +1127,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
|||||||
IsEmailVerified: true,
|
IsEmailVerified: true,
|
||||||
},
|
},
|
||||||
userEventProvider: &mockEventUser{
|
userEventProvider: &mockEventUser{
|
||||||
CodeExists: false,
|
PwCodeExists: false,
|
||||||
},
|
},
|
||||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||||
policy: &query.LockoutPolicy{
|
policy: &query.LockoutPolicy{
|
||||||
|
@ -93,3 +93,41 @@ func (repo *UserRepo) PasswordCodeExists(ctx context.Context, userID string) (ex
|
|||||||
}
|
}
|
||||||
return model.exists, nil
|
return model.exists, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type inviteCodeCheck struct {
|
||||||
|
userID string
|
||||||
|
|
||||||
|
exists bool
|
||||||
|
events int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *inviteCodeCheck) Reduce() error {
|
||||||
|
p.exists = p.events > 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *inviteCodeCheck) AppendEvents(events ...eventstore.Event) {
|
||||||
|
p.events += len(events)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *inviteCodeCheck) Query() *eventstore.SearchQueryBuilder {
|
||||||
|
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||||
|
AddQuery().
|
||||||
|
AggregateTypes(user.AggregateType).
|
||||||
|
AggregateIDs(p.userID).
|
||||||
|
EventTypes(
|
||||||
|
user.HumanInviteCodeAddedType,
|
||||||
|
user.HumanInviteCodeSentType).
|
||||||
|
Builder()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) InviteCodeExists(ctx context.Context, userID string) (exists bool, err error) {
|
||||||
|
model := &inviteCodeCheck{
|
||||||
|
userID: userID,
|
||||||
|
}
|
||||||
|
err = repo.Eventstore.FilterToQueryReducer(ctx, model)
|
||||||
|
if err != nil {
|
||||||
|
return false, zerrors.ThrowPermissionDenied(err, "EVENT-GJ2os", "Errors.Internal")
|
||||||
|
}
|
||||||
|
return model.exists, nil
|
||||||
|
}
|
||||||
|
@ -41,14 +41,6 @@ func newEncryptedCodeWithDefaultConfig(ctx context.Context, filter preparation.F
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyEncryptedCode(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.EncryptionAlgorithm, creation time.Time, expiry time.Duration, crypted *crypto.CryptoValue, plain string) error {
|
|
||||||
gen, _, err := encryptedCodeGenerator(ctx, filter, typ, alg, emptyConfig)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return crypto.VerifyCode(creation, expiry, crypted, plain, gen.Alg())
|
|
||||||
}
|
|
||||||
|
|
||||||
func encryptedCodeGenerator(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.EncryptionAlgorithm, defaultConfig *crypto.GeneratorConfig) (crypto.Generator, *crypto.GeneratorConfig, error) {
|
func encryptedCodeGenerator(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.EncryptionAlgorithm, defaultConfig *crypto.GeneratorConfig) (crypto.Generator, *crypto.GeneratorConfig, error) {
|
||||||
config, err := cryptoGeneratorConfigWithDefault(ctx, filter, typ, defaultConfig)
|
config, err := cryptoGeneratorConfigWithDefault(ctx, filter, typ, defaultConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -123,78 +123,6 @@ func Test_newCryptoCode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_verifyCryptoCode(t *testing.T) {
|
|
||||||
es := eventstoreExpect(t, expectFilter(
|
|
||||||
eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)),
|
|
||||||
))
|
|
||||||
code, err := newEncryptedCode(context.Background(), es.Filter, domain.SecretGeneratorTypeVerifyEmailCode, crypto.CreateMockEncryptionAlg(gomock.NewController(t))) //nolint:staticcheck
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
typ domain.SecretGeneratorType
|
|
||||||
alg crypto.EncryptionAlgorithm
|
|
||||||
expiry time.Duration
|
|
||||||
crypted *crypto.CryptoValue
|
|
||||||
plain string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
eventsore *eventstore.Eventstore
|
|
||||||
args args
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "filter config error",
|
|
||||||
eventsore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)),
|
|
||||||
args: args{
|
|
||||||
typ: domain.SecretGeneratorTypeVerifyEmailCode,
|
|
||||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
|
||||||
expiry: code.Expiry,
|
|
||||||
crypted: code.Crypted,
|
|
||||||
plain: code.Plain,
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "success",
|
|
||||||
eventsore: eventstoreExpect(t, expectFilter(
|
|
||||||
eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)),
|
|
||||||
)),
|
|
||||||
args: args{
|
|
||||||
typ: domain.SecretGeneratorTypeVerifyEmailCode,
|
|
||||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
|
||||||
expiry: code.Expiry,
|
|
||||||
crypted: code.Crypted,
|
|
||||||
plain: code.Plain,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "wrong plain",
|
|
||||||
eventsore: eventstoreExpect(t, expectFilter(
|
|
||||||
eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)),
|
|
||||||
)),
|
|
||||||
args: args{
|
|
||||||
typ: domain.SecretGeneratorTypeVerifyEmailCode,
|
|
||||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
|
||||||
expiry: code.Expiry,
|
|
||||||
crypted: code.Crypted,
|
|
||||||
plain: "wrong",
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
err := verifyEncryptedCode(context.Background(), tt.eventsore.Filter, tt.args.typ, tt.args.alg, time.Now(), tt.args.expiry, tt.args.crypted, tt.args.plain) //nolint:staticcheck
|
|
||||||
if tt.wantErr {
|
|
||||||
assert.Error(t, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_cryptoCodeGenerator(t *testing.T) {
|
func Test_cryptoCodeGenerator(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
typ domain.SecretGeneratorType
|
typ domain.SecretGeneratorType
|
||||||
|
@ -12,6 +12,9 @@ type Email struct {
|
|||||||
Address domain.EmailAddress
|
Address domain.EmailAddress
|
||||||
Verified bool
|
Verified bool
|
||||||
|
|
||||||
|
// NoEmailVerification is used Verified field is false
|
||||||
|
NoEmailVerification bool
|
||||||
|
|
||||||
// ReturnCode is used if the Verified field is false
|
// ReturnCode is used if the Verified field is false
|
||||||
ReturnCode bool
|
ReturnCode bool
|
||||||
|
|
||||||
|
@ -145,6 +145,7 @@ type SecretGenerators struct {
|
|||||||
DomainVerification *crypto.GeneratorConfig
|
DomainVerification *crypto.GeneratorConfig
|
||||||
OTPSMS *crypto.GeneratorConfig
|
OTPSMS *crypto.GeneratorConfig
|
||||||
OTPEmail *crypto.GeneratorConfig
|
OTPEmail *crypto.GeneratorConfig
|
||||||
|
InviteCode *crypto.GeneratorConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type ZitadelConfig struct {
|
type ZitadelConfig struct {
|
||||||
|
@ -388,6 +388,10 @@ func (c *Commands) newUserInitCode(ctx context.Context, filter preparation.Filte
|
|||||||
return c.newEncryptedCode(ctx, filter, domain.SecretGeneratorTypeInitCode, alg)
|
return c.newEncryptedCode(ctx, filter, domain.SecretGeneratorTypeInitCode, alg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Commands) newUserInviteCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*EncryptedCode, error) {
|
||||||
|
return c.newEncryptedCodeWithDefault(ctx, filter, domain.SecretGeneratorTypeInviteCode, alg, c.defaultSecretGenerators.InviteCode)
|
||||||
|
}
|
||||||
|
|
||||||
func userWriteModelByID(ctx context.Context, filter preparation.FilterToQueryReducer, userID, resourceOwner string) (*UserWriteModel, error) {
|
func userWriteModelByID(ctx context.Context, filter preparation.FilterToQueryReducer, userID, resourceOwner string) (*UserWriteModel, error) {
|
||||||
user := NewUserWriteModel(userID, resourceOwner)
|
user := NewUserWriteModel(userID, resourceOwner)
|
||||||
events, err := filter(ctx, user.Query())
|
events, err := filter(ctx, user.Query())
|
||||||
|
@ -284,6 +284,9 @@ func (c *Commands) addHumanCommandEmail(ctx context.Context, filter preparation.
|
|||||||
}
|
}
|
||||||
return append(cmds, user.NewHumanInitialCodeAddedEvent(ctx, &a.Aggregate, initCode.Crypted, initCode.Expiry, human.AuthRequestID)), nil
|
return append(cmds, user.NewHumanInitialCodeAddedEvent(ctx, &a.Aggregate, initCode.Crypted, initCode.Expiry, human.AuthRequestID)), nil
|
||||||
}
|
}
|
||||||
|
if human.Email.NoEmailVerification {
|
||||||
|
return cmds, nil
|
||||||
|
}
|
||||||
if !human.Email.Verified {
|
if !human.Email.Verified {
|
||||||
emailCode, err := c.newEmailCode(ctx, filter, codeAlg)
|
emailCode, err := c.newEmailCode(ctx, filter, codeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -626,6 +626,67 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
|||||||
wantEmailCode: "emailCode",
|
wantEmailCode: "emailCode",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "add human (with password and unverified email), ok (no email code)",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: expectEventstore(
|
||||||
|
expectFilter(),
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||||
|
&user.NewAggregate("user1", "org1").Aggregate,
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
newAddHumanEvent("$plain$x$password", false, true, "", AllowedLanguage),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||||
|
userPasswordHasher: mockPasswordHasher("x"),
|
||||||
|
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||||
|
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
human: &AddHuman{
|
||||||
|
Username: "username",
|
||||||
|
Password: "password",
|
||||||
|
FirstName: "firstname",
|
||||||
|
LastName: "lastname",
|
||||||
|
Email: Email{
|
||||||
|
Address: "email@test.ch",
|
||||||
|
Verified: false,
|
||||||
|
NoEmailVerification: true,
|
||||||
|
},
|
||||||
|
PreferredLanguage: AllowedLanguage,
|
||||||
|
},
|
||||||
|
secretGenerator: GetMockSecretGenerator(t),
|
||||||
|
allowInitMail: false,
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.ObjectDetails{
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
wantID: "user1",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "add human email verified, ok",
|
name: "add human email verified, ok",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/repository/user"
|
"github.com/zitadel/zitadel/internal/repository/user"
|
||||||
@ -122,6 +124,10 @@ func UserAggregateFromWriteModel(wm *eventstore.WriteModel) *eventstore.Aggregat
|
|||||||
return eventstore.AggregateFromWriteModel(wm, user.AggregateType, user.AggregateVersion)
|
return eventstore.AggregateFromWriteModel(wm, user.AggregateType, user.AggregateVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UserAggregateFromWriteModelCtx(ctx context.Context, wm *eventstore.WriteModel) *eventstore.Aggregate {
|
||||||
|
return eventstore.AggregateFromWriteModelCtx(ctx, wm, user.AggregateType, user.AggregateVersion)
|
||||||
|
}
|
||||||
|
|
||||||
func isUserStateExists(state domain.UserState) bool {
|
func isUserStateExists(state domain.UserState) bool {
|
||||||
return !hasUserState(state, domain.UserStateDeleted, domain.UserStateUnspecified)
|
return !hasUserState(state, domain.UserStateDeleted, domain.UserStateUnspecified)
|
||||||
}
|
}
|
||||||
|
193
internal/command/user_v2_invite.go
Normal file
193
internal/command/user_v2_invite.go
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
|
"github.com/zitadel/zitadel/internal/repository/user"
|
||||||
|
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||||
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateUserInvite struct {
|
||||||
|
UserID string
|
||||||
|
URLTemplate string
|
||||||
|
ReturnCode bool
|
||||||
|
ApplicationName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Commands) CreateInviteCode(ctx context.Context, invite *CreateUserInvite) (details *domain.ObjectDetails, returnCode *string, err error) {
|
||||||
|
invite.UserID = strings.TrimSpace(invite.UserID)
|
||||||
|
if invite.UserID == "" {
|
||||||
|
return nil, nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-4jio3", "Errors.User.UserIDMissing")
|
||||||
|
}
|
||||||
|
wm, err := c.userInviteCodeWriteModel(ctx, invite.UserID, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err := c.checkPermission(ctx, domain.PermissionUserWrite, wm.ResourceOwner, wm.AggregateID); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if !wm.UserState.Exists() {
|
||||||
|
return nil, nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Wgvn4", "Errors.User.NotFound")
|
||||||
|
}
|
||||||
|
if !wm.CreationAllowed() {
|
||||||
|
return nil, nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-EF34g", "Errors.User.AlreadyInitialised")
|
||||||
|
}
|
||||||
|
code, err := c.newUserInviteCode(ctx, c.eventstore.Filter, c.userEncryption) //nolint
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
err = c.pushAppendAndReduce(ctx, wm, user.NewHumanInviteCodeAddedEvent(
|
||||||
|
ctx,
|
||||||
|
UserAggregateFromWriteModelCtx(ctx, &wm.WriteModel),
|
||||||
|
code.Crypted,
|
||||||
|
code.Expiry,
|
||||||
|
invite.URLTemplate,
|
||||||
|
invite.ReturnCode,
|
||||||
|
invite.ApplicationName,
|
||||||
|
"",
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if invite.ReturnCode {
|
||||||
|
returnCode = &code.Plain
|
||||||
|
}
|
||||||
|
return writeModelToObjectDetails(&wm.WriteModel), returnCode, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResendInviteCode resends the invite mail with a new code and an optional authRequestID.
|
||||||
|
// It will reuse the applicationName from the previous code.
|
||||||
|
func (c *Commands) ResendInviteCode(ctx context.Context, userID, resourceOwner, authRequestID string) (objectDetails *domain.ObjectDetails, err error) {
|
||||||
|
if userID == "" {
|
||||||
|
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-2n8vs", "Errors.User.UserIDMissing")
|
||||||
|
}
|
||||||
|
|
||||||
|
existingCode, err := c.userInviteCodeWriteModel(ctx, userID, resourceOwner)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if authz.GetCtxData(ctx).UserID != userID {
|
||||||
|
if err := c.checkPermission(ctx, domain.PermissionUserWrite, existingCode.ResourceOwner, userID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !existingCode.UserState.Exists() {
|
||||||
|
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-H3b2a", "Errors.User.NotFound")
|
||||||
|
}
|
||||||
|
if !existingCode.CreationAllowed() {
|
||||||
|
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Gg42s", "Errors.User.AlreadyInitialised")
|
||||||
|
}
|
||||||
|
if existingCode.InviteCode == nil || existingCode.CodeReturned {
|
||||||
|
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Wr3gq", "Errors.User.Code.NotFound")
|
||||||
|
}
|
||||||
|
code, err := c.newUserInviteCode(ctx, c.eventstore.Filter, c.userEncryption) //nolint
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if authRequestID == "" {
|
||||||
|
authRequestID = existingCode.AuthRequestID
|
||||||
|
}
|
||||||
|
err = c.pushAppendAndReduce(ctx, existingCode,
|
||||||
|
user.NewHumanInviteCodeAddedEvent(
|
||||||
|
ctx,
|
||||||
|
UserAggregateFromWriteModelCtx(ctx, &existingCode.WriteModel),
|
||||||
|
code.Crypted,
|
||||||
|
code.Expiry,
|
||||||
|
existingCode.URLTemplate,
|
||||||
|
false,
|
||||||
|
existingCode.ApplicationName,
|
||||||
|
authRequestID,
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return writeModelToObjectDetails(&existingCode.WriteModel), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Commands) InviteCodeSent(ctx context.Context, userID, orgID string) (err error) {
|
||||||
|
if userID == "" {
|
||||||
|
return zerrors.ThrowInvalidArgument(nil, "COMMAND-Sgf31", "Errors.User.UserIDMissing")
|
||||||
|
}
|
||||||
|
existingCode, err := c.userInviteCodeWriteModel(ctx, userID, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !existingCode.UserState.Exists() {
|
||||||
|
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-HN34a", "Errors.User.NotFound")
|
||||||
|
}
|
||||||
|
if existingCode.InviteCode == nil || existingCode.CodeReturned {
|
||||||
|
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-Wr3gq", "Errors.User.Code.NotFound")
|
||||||
|
}
|
||||||
|
userAgg := UserAggregateFromWriteModelCtx(ctx, &existingCode.WriteModel)
|
||||||
|
_, err = c.eventstore.Push(ctx, user.NewHumanInviteCodeSentEvent(ctx, userAgg))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Commands) VerifyInviteCode(ctx context.Context, userID, code string) (details *domain.ObjectDetails, err error) {
|
||||||
|
return c.VerifyInviteCodeSetPassword(ctx, userID, code, "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Commands) VerifyInviteCodeSetPassword(ctx context.Context, userID, code, password, userAgentID string) (details *domain.ObjectDetails, err error) {
|
||||||
|
if userID == "" {
|
||||||
|
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-Gk3f2", "Errors.User.UserIDMissing")
|
||||||
|
}
|
||||||
|
wm, err := c.userInviteCodeWriteModel(ctx, userID, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !wm.UserState.Exists() {
|
||||||
|
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-F5g2h", "Errors.User.NotFound")
|
||||||
|
}
|
||||||
|
userAgg := UserAggregateFromWriteModelCtx(ctx, &wm.WriteModel)
|
||||||
|
err = crypto.VerifyCode(wm.InviteCodeCreationDate, wm.InviteCodeExpiry, wm.InviteCode, code, c.userEncryption)
|
||||||
|
if err != nil {
|
||||||
|
_, err = c.eventstore.Push(ctx, user.NewHumanInviteCheckFailedEvent(ctx, userAgg))
|
||||||
|
logging.WithFields("userID", userAgg.ID).OnError(err).Error("NewHumanInviteCheckFailedEvent push failed")
|
||||||
|
return nil, zerrors.ThrowInvalidArgument(err, "COMMAND-Wgn4q", "Errors.User.Code.Invalid")
|
||||||
|
}
|
||||||
|
commands := []eventstore.Command{
|
||||||
|
user.NewHumanInviteCheckSucceededEvent(ctx, userAgg),
|
||||||
|
user.NewHumanEmailVerifiedEvent(ctx, userAgg),
|
||||||
|
}
|
||||||
|
if password != "" {
|
||||||
|
passwordCommand, err := c.setPasswordCommand(
|
||||||
|
ctx,
|
||||||
|
userAgg,
|
||||||
|
wm.UserState,
|
||||||
|
password,
|
||||||
|
"",
|
||||||
|
userAgentID,
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
commands = append(commands, passwordCommand)
|
||||||
|
}
|
||||||
|
err = c.pushAppendAndReduce(ctx, wm, commands...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return writeModelToObjectDetails(&wm.WriteModel), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Commands) userInviteCodeWriteModel(ctx context.Context, userID, orgID string) (writeModel *UserV2InviteWriteModel, err error) {
|
||||||
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
|
writeModel = newUserV2InviteWriteModel(userID, orgID)
|
||||||
|
err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return writeModel, nil
|
||||||
|
}
|
141
internal/command/user_v2_invite_model.go
Normal file
141
internal/command/user_v2_invite_model.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
|
"github.com/zitadel/zitadel/internal/repository/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserV2InviteWriteModel struct {
|
||||||
|
eventstore.WriteModel
|
||||||
|
|
||||||
|
InviteCode *crypto.CryptoValue
|
||||||
|
InviteCodeCreationDate time.Time
|
||||||
|
InviteCodeExpiry time.Duration
|
||||||
|
InviteCheckFailureCount uint8
|
||||||
|
|
||||||
|
ApplicationName string
|
||||||
|
AuthRequestID string
|
||||||
|
URLTemplate string
|
||||||
|
CodeReturned bool
|
||||||
|
EmailVerified bool
|
||||||
|
AuthMethodSet bool
|
||||||
|
|
||||||
|
UserState domain.UserState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wm *UserV2InviteWriteModel) CreationAllowed() bool {
|
||||||
|
return !wm.EmailVerified && !wm.AuthMethodSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUserV2InviteWriteModel(userID, orgID string) *UserV2InviteWriteModel {
|
||||||
|
return &UserV2InviteWriteModel{
|
||||||
|
WriteModel: eventstore.WriteModel{
|
||||||
|
AggregateID: userID,
|
||||||
|
ResourceOwner: orgID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wm *UserV2InviteWriteModel) Reduce() error {
|
||||||
|
for _, event := range wm.Events {
|
||||||
|
switch e := event.(type) {
|
||||||
|
case *user.HumanAddedEvent:
|
||||||
|
wm.UserState = domain.UserStateActive
|
||||||
|
wm.AuthMethodSet = crypto.SecretOrEncodedHash(e.Secret, e.EncodedHash) != ""
|
||||||
|
wm.EmptyInviteCode()
|
||||||
|
wm.ApplicationName = ""
|
||||||
|
wm.AuthRequestID = ""
|
||||||
|
case *user.HumanRegisteredEvent:
|
||||||
|
wm.UserState = domain.UserStateActive
|
||||||
|
wm.AuthMethodSet = crypto.SecretOrEncodedHash(e.Secret, e.EncodedHash) != ""
|
||||||
|
wm.EmptyInviteCode()
|
||||||
|
wm.ApplicationName = ""
|
||||||
|
wm.AuthRequestID = ""
|
||||||
|
case *user.HumanInviteCodeAddedEvent:
|
||||||
|
wm.SetInviteCode(e.Code, e.Expiry, e.CreationDate())
|
||||||
|
wm.URLTemplate = e.URLTemplate
|
||||||
|
wm.CodeReturned = e.CodeReturned
|
||||||
|
wm.ApplicationName = e.ApplicationName
|
||||||
|
wm.AuthRequestID = e.AuthRequestID
|
||||||
|
case *user.HumanInviteCheckSucceededEvent:
|
||||||
|
wm.EmptyInviteCode()
|
||||||
|
case *user.HumanInviteCheckFailedEvent:
|
||||||
|
wm.InviteCheckFailureCount++
|
||||||
|
if wm.InviteCheckFailureCount >= 3 { //TODO: config?
|
||||||
|
wm.UserState = domain.UserStateDeleted
|
||||||
|
}
|
||||||
|
case *user.HumanEmailVerifiedEvent:
|
||||||
|
wm.EmailVerified = true
|
||||||
|
wm.EmptyInviteCode()
|
||||||
|
case *user.UserLockedEvent:
|
||||||
|
wm.UserState = domain.UserStateLocked
|
||||||
|
case *user.UserUnlockedEvent:
|
||||||
|
wm.UserState = domain.UserStateActive
|
||||||
|
case *user.UserDeactivatedEvent:
|
||||||
|
wm.UserState = domain.UserStateInactive
|
||||||
|
case *user.UserReactivatedEvent:
|
||||||
|
wm.UserState = domain.UserStateActive
|
||||||
|
case *user.UserRemovedEvent:
|
||||||
|
wm.UserState = domain.UserStateDeleted
|
||||||
|
case *user.HumanPasswordChangedEvent:
|
||||||
|
wm.AuthMethodSet = true
|
||||||
|
case *user.UserIDPLinkAddedEvent:
|
||||||
|
wm.AuthMethodSet = true
|
||||||
|
case *user.HumanPasswordlessVerifiedEvent:
|
||||||
|
wm.AuthMethodSet = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wm.WriteModel.Reduce()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wm *UserV2InviteWriteModel) SetInviteCode(code *crypto.CryptoValue, expiry time.Duration, creationDate time.Time) {
|
||||||
|
wm.InviteCode = code
|
||||||
|
wm.InviteCodeExpiry = expiry
|
||||||
|
wm.InviteCodeCreationDate = creationDate
|
||||||
|
wm.InviteCheckFailureCount = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wm *UserV2InviteWriteModel) EmptyInviteCode() {
|
||||||
|
wm.InviteCode = nil
|
||||||
|
wm.InviteCodeExpiry = 0
|
||||||
|
wm.InviteCodeCreationDate = time.Time{}
|
||||||
|
wm.InviteCheckFailureCount = 0
|
||||||
|
}
|
||||||
|
func (wm *UserV2InviteWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||||
|
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||||
|
AddQuery().
|
||||||
|
AggregateTypes(user.AggregateType).
|
||||||
|
AggregateIDs(wm.AggregateID).
|
||||||
|
EventTypes(
|
||||||
|
user.UserV1AddedType,
|
||||||
|
user.HumanAddedType,
|
||||||
|
user.UserV1RegisteredType,
|
||||||
|
user.HumanRegisteredType,
|
||||||
|
user.HumanInviteCodeAddedType,
|
||||||
|
user.HumanInviteCheckSucceededType,
|
||||||
|
user.HumanInviteCheckFailedType,
|
||||||
|
user.UserV1EmailVerifiedType,
|
||||||
|
user.HumanEmailVerifiedType,
|
||||||
|
user.UserLockedType,
|
||||||
|
user.UserUnlockedType,
|
||||||
|
user.UserDeactivatedType,
|
||||||
|
user.UserReactivatedType,
|
||||||
|
user.UserRemovedType,
|
||||||
|
user.HumanPasswordChangedType,
|
||||||
|
user.UserV1PasswordChangedType,
|
||||||
|
user.UserIDPLinkAddedType,
|
||||||
|
user.HumanPasswordlessTokenVerifiedType,
|
||||||
|
).Builder()
|
||||||
|
if wm.ResourceOwner != "" {
|
||||||
|
query.ResourceOwner(wm.ResourceOwner)
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wm *UserV2InviteWriteModel) Aggregate() *user.Aggregate {
|
||||||
|
return user.NewAggregate(wm.AggregateID, wm.ResourceOwner)
|
||||||
|
}
|
1207
internal/command/user_v2_invite_test.go
Normal file
1207
internal/command/user_v2_invite_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -48,7 +48,7 @@ func (c *Commands) verifyUserPasskeyCode(ctx context.Context, userID, resourceOw
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = verifyEncryptedCode(ctx, c.eventstore.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg, wm.ChangeDate, wm.Expiration, wm.CryptoCode, code) //nolint:staticcheck
|
err = crypto.VerifyCode(wm.ChangeDate, wm.Expiration, wm.CryptoCode, code, alg)
|
||||||
if err != nil || wm.State != domain.PasswordlessInitCodeStateActive {
|
if err != nil || wm.State != domain.PasswordlessInitCodeStateActive {
|
||||||
c.verifyUserPasskeyCodeFailed(ctx, wm)
|
c.verifyUserPasskeyCodeFailed(ctx, wm)
|
||||||
return nil, zerrors.ThrowInvalidArgument(err, "COMMAND-Eeb2a", "Errors.User.Code.Invalid")
|
return nil, zerrors.ThrowInvalidArgument(err, "COMMAND-Eeb2a", "Errors.User.Code.Invalid")
|
||||||
|
@ -143,7 +143,7 @@ func TestCommands_RegisterUserPasskeyWithCode(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
idGenerator id.Generator
|
idGenerator id.Generator
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
@ -163,7 +163,7 @@ func TestCommands_RegisterUserPasskeyWithCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "code verification error",
|
name: "code verification error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusherWithCreationDateNow(
|
eventFromEventPusherWithCreationDateNow(
|
||||||
user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(),
|
user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(),
|
||||||
@ -174,7 +174,6 @@ func TestCommands_RegisterUserPasskeyWithCode(t *testing.T) {
|
|||||||
user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"),
|
user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))),
|
|
||||||
expectPush(
|
expectPush(
|
||||||
user.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, "123"),
|
user.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, "123"),
|
||||||
),
|
),
|
||||||
@ -192,7 +191,7 @@ func TestCommands_RegisterUserPasskeyWithCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "code verification ok, get human passwordless error",
|
name: "code verification ok, get human passwordless error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusherWithCreationDateNow(
|
eventFromEventPusherWithCreationDateNow(
|
||||||
user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(),
|
user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(),
|
||||||
@ -203,7 +202,6 @@ func TestCommands_RegisterUserPasskeyWithCode(t *testing.T) {
|
|||||||
user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"),
|
user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))),
|
|
||||||
expectFilterError(io.ErrClosedPipe),
|
expectFilterError(io.ErrClosedPipe),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -220,7 +218,7 @@ func TestCommands_RegisterUserPasskeyWithCode(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
c := &Commands{
|
c := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
idGenerator: tt.fields.idGenerator,
|
idGenerator: tt.fields.idGenerator,
|
||||||
webauthnConfig: webauthnConfig,
|
webauthnConfig: webauthnConfig,
|
||||||
}
|
}
|
||||||
@ -242,7 +240,7 @@ func TestCommands_verifyUserPasskeyCode(t *testing.T) {
|
|||||||
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
||||||
|
|
||||||
type fields struct {
|
type fields struct {
|
||||||
eventstore *eventstore.Eventstore
|
eventstore func(*testing.T) *eventstore.Eventstore
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
userID string
|
userID string
|
||||||
@ -260,7 +258,7 @@ func TestCommands_verifyUserPasskeyCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "filter error",
|
name: "filter error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilterError(io.ErrClosedPipe),
|
expectFilterError(io.ErrClosedPipe),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -274,7 +272,7 @@ func TestCommands_verifyUserPasskeyCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "code verification error",
|
name: "code verification error",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusherWithCreationDateNow(
|
eventFromEventPusherWithCreationDateNow(
|
||||||
user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(),
|
user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(),
|
||||||
@ -285,7 +283,6 @@ func TestCommands_verifyUserPasskeyCode(t *testing.T) {
|
|||||||
user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"),
|
user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))),
|
|
||||||
expectPush(
|
expectPush(
|
||||||
user.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, "123"),
|
user.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, "123"),
|
||||||
),
|
),
|
||||||
@ -302,7 +299,7 @@ func TestCommands_verifyUserPasskeyCode(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "success",
|
name: "success",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
eventstore: eventstoreExpect(t,
|
eventstore: expectEventstore(
|
||||||
expectFilter(
|
expectFilter(
|
||||||
eventFromEventPusherWithCreationDateNow(
|
eventFromEventPusherWithCreationDateNow(
|
||||||
user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(),
|
user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(),
|
||||||
@ -313,7 +310,6 @@ func TestCommands_verifyUserPasskeyCode(t *testing.T) {
|
|||||||
user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"),
|
user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))),
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
@ -328,7 +324,7 @@ func TestCommands_verifyUserPasskeyCode(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
c := &Commands{
|
c := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore(t),
|
||||||
}
|
}
|
||||||
got, err := c.verifyUserPasskeyCode(ctx, tt.args.userID, tt.args.resourceOwner, tt.args.codeID, tt.args.code, alg)
|
got, err := c.verifyUserPasskeyCode(ctx, tt.args.userID, tt.args.resourceOwner, tt.args.codeID, tt.args.code, alg)
|
||||||
require.ErrorIs(t, err, tt.wantErr)
|
require.ErrorIs(t, err, tt.wantErr)
|
||||||
|
@ -17,6 +17,7 @@ const (
|
|||||||
DomainClaimedMessageType = "DomainClaimed"
|
DomainClaimedMessageType = "DomainClaimed"
|
||||||
PasswordlessRegistrationMessageType = "PasswordlessRegistration"
|
PasswordlessRegistrationMessageType = "PasswordlessRegistration"
|
||||||
PasswordChangeMessageType = "PasswordChange"
|
PasswordChangeMessageType = "PasswordChange"
|
||||||
|
InviteUserMessageType = "InviteUser"
|
||||||
MessageTitle = "Title"
|
MessageTitle = "Title"
|
||||||
MessagePreHeader = "PreHeader"
|
MessagePreHeader = "PreHeader"
|
||||||
MessageSubject = "Subject"
|
MessageSubject = "Subject"
|
||||||
@ -26,16 +27,6 @@ const (
|
|||||||
MessageFooterText = "Footer"
|
MessageFooterText = "Footer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MessageTexts struct {
|
|
||||||
InitCode CustomMessageText
|
|
||||||
PasswordReset CustomMessageText
|
|
||||||
VerifyEmail CustomMessageText
|
|
||||||
VerifyPhone CustomMessageText
|
|
||||||
DomainClaimed CustomMessageText
|
|
||||||
PasswordlessRegistration CustomMessageText
|
|
||||||
PasswordChange CustomMessageText
|
|
||||||
}
|
|
||||||
|
|
||||||
type CustomMessageText struct {
|
type CustomMessageText struct {
|
||||||
models.ObjectRoot
|
models.ObjectRoot
|
||||||
|
|
||||||
@ -71,5 +62,6 @@ func IsMessageTextType(textType string) bool {
|
|||||||
textType == VerifyEmailOTPMessageType ||
|
textType == VerifyEmailOTPMessageType ||
|
||||||
textType == DomainClaimedMessageType ||
|
textType == DomainClaimedMessageType ||
|
||||||
textType == PasswordlessRegistrationMessageType ||
|
textType == PasswordlessRegistrationMessageType ||
|
||||||
textType == PasswordChangeMessageType
|
textType == PasswordChangeMessageType ||
|
||||||
|
textType == InviteUserMessageType
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ const (
|
|||||||
NextStepProjectRequired
|
NextStepProjectRequired
|
||||||
NextStepRedirectToExternalIDP
|
NextStepRedirectToExternalIDP
|
||||||
NextStepLoginSucceeded
|
NextStepLoginSucceeded
|
||||||
|
NextStepVerifyInvite
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginStep struct{}
|
type LoginStep struct{}
|
||||||
@ -191,3 +192,9 @@ type LoginSucceededStep struct{}
|
|||||||
func (s *LoginSucceededStep) Type() NextStepType {
|
func (s *LoginSucceededStep) Type() NextStepType {
|
||||||
return NextStepLoginSucceeded
|
return NextStepLoginSucceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VerifyInviteStep struct{}
|
||||||
|
|
||||||
|
func (s *VerifyInviteStep) Type() NextStepType {
|
||||||
|
return NextStepVerifyInvite
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@ const (
|
|||||||
SecretGeneratorTypeAppSecret
|
SecretGeneratorTypeAppSecret
|
||||||
SecretGeneratorTypeOTPSMS
|
SecretGeneratorTypeOTPSMS
|
||||||
SecretGeneratorTypeOTPEmail
|
SecretGeneratorTypeOTPEmail
|
||||||
|
SecretGeneratorTypeInviteCode
|
||||||
|
|
||||||
secretGeneratorTypeCount
|
secretGeneratorTypeCount
|
||||||
)
|
)
|
||||||
|
@ -4,14 +4,11 @@ package domain
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const _SecretGeneratorTypeName = "unspecifiedinit_codeverify_email_codeverify_phone_codeverify_domainpassword_reset_codepasswordless_init_codeapp_secretotpsmsotp_emailsecret_generator_type_count"
|
const _SecretGeneratorTypeName = "unspecifiedinit_codeverify_email_codeverify_phone_codeverify_domainpassword_reset_codepasswordless_init_codeapp_secretotpsmsotp_emailinvite_codesecret_generator_type_count"
|
||||||
|
|
||||||
var _SecretGeneratorTypeIndex = [...]uint8{0, 11, 20, 37, 54, 67, 86, 108, 118, 124, 133, 160}
|
var _SecretGeneratorTypeIndex = [...]uint8{0, 11, 20, 37, 54, 67, 86, 108, 118, 124, 133, 144, 171}
|
||||||
|
|
||||||
const _SecretGeneratorTypeLowerName = "unspecifiedinit_codeverify_email_codeverify_phone_codeverify_domainpassword_reset_codepasswordless_init_codeapp_secretotpsmsotp_emailsecret_generator_type_count"
|
|
||||||
|
|
||||||
func (i SecretGeneratorType) String() string {
|
func (i SecretGeneratorType) String() string {
|
||||||
if i < 0 || i >= SecretGeneratorType(len(_SecretGeneratorTypeIndex)-1) {
|
if i < 0 || i >= SecretGeneratorType(len(_SecretGeneratorTypeIndex)-1) {
|
||||||
@ -20,62 +17,21 @@ func (i SecretGeneratorType) String() string {
|
|||||||
return _SecretGeneratorTypeName[_SecretGeneratorTypeIndex[i]:_SecretGeneratorTypeIndex[i+1]]
|
return _SecretGeneratorTypeName[_SecretGeneratorTypeIndex[i]:_SecretGeneratorTypeIndex[i+1]]
|
||||||
}
|
}
|
||||||
|
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
var _SecretGeneratorTypeValues = []SecretGeneratorType{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
|
||||||
// Re-run the stringer command to generate them again.
|
|
||||||
func _SecretGeneratorTypeNoOp() {
|
|
||||||
var x [1]struct{}
|
|
||||||
_ = x[SecretGeneratorTypeUnspecified-(0)]
|
|
||||||
_ = x[SecretGeneratorTypeInitCode-(1)]
|
|
||||||
_ = x[SecretGeneratorTypeVerifyEmailCode-(2)]
|
|
||||||
_ = x[SecretGeneratorTypeVerifyPhoneCode-(3)]
|
|
||||||
_ = x[SecretGeneratorTypeVerifyDomain-(4)]
|
|
||||||
_ = x[SecretGeneratorTypePasswordResetCode-(5)]
|
|
||||||
_ = x[SecretGeneratorTypePasswordlessInitCode-(6)]
|
|
||||||
_ = x[SecretGeneratorTypeAppSecret-(7)]
|
|
||||||
_ = x[SecretGeneratorTypeOTPSMS-(8)]
|
|
||||||
_ = x[SecretGeneratorTypeOTPEmail-(9)]
|
|
||||||
_ = x[secretGeneratorTypeCount-(10)]
|
|
||||||
}
|
|
||||||
|
|
||||||
var _SecretGeneratorTypeValues = []SecretGeneratorType{SecretGeneratorTypeUnspecified, SecretGeneratorTypeInitCode, SecretGeneratorTypeVerifyEmailCode, SecretGeneratorTypeVerifyPhoneCode, SecretGeneratorTypeVerifyDomain, SecretGeneratorTypePasswordResetCode, SecretGeneratorTypePasswordlessInitCode, SecretGeneratorTypeAppSecret, SecretGeneratorTypeOTPSMS, SecretGeneratorTypeOTPEmail, secretGeneratorTypeCount}
|
|
||||||
|
|
||||||
var _SecretGeneratorTypeNameToValueMap = map[string]SecretGeneratorType{
|
var _SecretGeneratorTypeNameToValueMap = map[string]SecretGeneratorType{
|
||||||
_SecretGeneratorTypeName[0:11]: SecretGeneratorTypeUnspecified,
|
_SecretGeneratorTypeName[0:11]: 0,
|
||||||
_SecretGeneratorTypeLowerName[0:11]: SecretGeneratorTypeUnspecified,
|
_SecretGeneratorTypeName[11:20]: 1,
|
||||||
_SecretGeneratorTypeName[11:20]: SecretGeneratorTypeInitCode,
|
_SecretGeneratorTypeName[20:37]: 2,
|
||||||
_SecretGeneratorTypeLowerName[11:20]: SecretGeneratorTypeInitCode,
|
_SecretGeneratorTypeName[37:54]: 3,
|
||||||
_SecretGeneratorTypeName[20:37]: SecretGeneratorTypeVerifyEmailCode,
|
_SecretGeneratorTypeName[54:67]: 4,
|
||||||
_SecretGeneratorTypeLowerName[20:37]: SecretGeneratorTypeVerifyEmailCode,
|
_SecretGeneratorTypeName[67:86]: 5,
|
||||||
_SecretGeneratorTypeName[37:54]: SecretGeneratorTypeVerifyPhoneCode,
|
_SecretGeneratorTypeName[86:108]: 6,
|
||||||
_SecretGeneratorTypeLowerName[37:54]: SecretGeneratorTypeVerifyPhoneCode,
|
_SecretGeneratorTypeName[108:118]: 7,
|
||||||
_SecretGeneratorTypeName[54:67]: SecretGeneratorTypeVerifyDomain,
|
_SecretGeneratorTypeName[118:124]: 8,
|
||||||
_SecretGeneratorTypeLowerName[54:67]: SecretGeneratorTypeVerifyDomain,
|
_SecretGeneratorTypeName[124:133]: 9,
|
||||||
_SecretGeneratorTypeName[67:86]: SecretGeneratorTypePasswordResetCode,
|
_SecretGeneratorTypeName[133:144]: 10,
|
||||||
_SecretGeneratorTypeLowerName[67:86]: SecretGeneratorTypePasswordResetCode,
|
_SecretGeneratorTypeName[144:171]: 11,
|
||||||
_SecretGeneratorTypeName[86:108]: SecretGeneratorTypePasswordlessInitCode,
|
|
||||||
_SecretGeneratorTypeLowerName[86:108]: SecretGeneratorTypePasswordlessInitCode,
|
|
||||||
_SecretGeneratorTypeName[108:118]: SecretGeneratorTypeAppSecret,
|
|
||||||
_SecretGeneratorTypeLowerName[108:118]: SecretGeneratorTypeAppSecret,
|
|
||||||
_SecretGeneratorTypeName[118:124]: SecretGeneratorTypeOTPSMS,
|
|
||||||
_SecretGeneratorTypeLowerName[118:124]: SecretGeneratorTypeOTPSMS,
|
|
||||||
_SecretGeneratorTypeName[124:133]: SecretGeneratorTypeOTPEmail,
|
|
||||||
_SecretGeneratorTypeLowerName[124:133]: SecretGeneratorTypeOTPEmail,
|
|
||||||
_SecretGeneratorTypeName[133:160]: secretGeneratorTypeCount,
|
|
||||||
_SecretGeneratorTypeLowerName[133:160]: secretGeneratorTypeCount,
|
|
||||||
}
|
|
||||||
|
|
||||||
var _SecretGeneratorTypeNames = []string{
|
|
||||||
_SecretGeneratorTypeName[0:11],
|
|
||||||
_SecretGeneratorTypeName[11:20],
|
|
||||||
_SecretGeneratorTypeName[20:37],
|
|
||||||
_SecretGeneratorTypeName[37:54],
|
|
||||||
_SecretGeneratorTypeName[54:67],
|
|
||||||
_SecretGeneratorTypeName[67:86],
|
|
||||||
_SecretGeneratorTypeName[86:108],
|
|
||||||
_SecretGeneratorTypeName[108:118],
|
|
||||||
_SecretGeneratorTypeName[118:124],
|
|
||||||
_SecretGeneratorTypeName[124:133],
|
|
||||||
_SecretGeneratorTypeName[133:160],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecretGeneratorTypeString retrieves an enum value from the enum constants string name.
|
// SecretGeneratorTypeString retrieves an enum value from the enum constants string name.
|
||||||
@ -84,10 +40,6 @@ func SecretGeneratorTypeString(s string) (SecretGeneratorType, error) {
|
|||||||
if val, ok := _SecretGeneratorTypeNameToValueMap[s]; ok {
|
if val, ok := _SecretGeneratorTypeNameToValueMap[s]; ok {
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, ok := _SecretGeneratorTypeNameToValueMap[strings.ToLower(s)]; ok {
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
return 0, fmt.Errorf("%s does not belong to SecretGeneratorType values", s)
|
return 0, fmt.Errorf("%s does not belong to SecretGeneratorType values", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,13 +48,6 @@ func SecretGeneratorTypeValues() []SecretGeneratorType {
|
|||||||
return _SecretGeneratorTypeValues
|
return _SecretGeneratorTypeValues
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecretGeneratorTypeStrings returns a slice of all String values of the enum
|
|
||||||
func SecretGeneratorTypeStrings() []string {
|
|
||||||
strs := make([]string, len(_SecretGeneratorTypeNames))
|
|
||||||
copy(strs, _SecretGeneratorTypeNames)
|
|
||||||
return strs
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsASecretGeneratorType returns "true" if the value is listed in the enum definition. "false" otherwise
|
// IsASecretGeneratorType returns "true" if the value is listed in the enum definition. "false" otherwise
|
||||||
func (i SecretGeneratorType) IsASecretGeneratorType() bool {
|
func (i SecretGeneratorType) IsASecretGeneratorType() bool {
|
||||||
for _, v := range _SecretGeneratorTypeValues {
|
for _, v := range _SecretGeneratorTypeValues {
|
||||||
|
@ -775,3 +775,12 @@ func (i *Instance) CreateSchemaUser(ctx context.Context, orgID string, schemaID
|
|||||||
logging.OnError(err).Fatal("create user")
|
logging.OnError(err).Fatal("create user")
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Instance) CreateInviteCode(ctx context.Context, userID string) *user_v2.CreateInviteCodeResponse {
|
||||||
|
user, err := i.Client.UserV2.CreateInviteCode(ctx, &user_v2.CreateInviteCodeRequest{
|
||||||
|
UserId: userID,
|
||||||
|
Verification: &user_v2.CreateInviteCodeRequest_ReturnCode{ReturnCode: &user_v2.ReturnInviteCode{}},
|
||||||
|
})
|
||||||
|
logging.OnError(err).Fatal("create invite code")
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
@ -19,6 +19,7 @@ type Commands interface {
|
|||||||
HumanPasswordlessInitCodeSent(ctx context.Context, userID, resourceOwner, codeID string) error
|
HumanPasswordlessInitCodeSent(ctx context.Context, userID, resourceOwner, codeID string) error
|
||||||
PasswordChangeSent(ctx context.Context, orgID, userID string) error
|
PasswordChangeSent(ctx context.Context, orgID, userID string) error
|
||||||
HumanPhoneVerificationCodeSent(ctx context.Context, orgID, userID string) error
|
HumanPhoneVerificationCodeSent(ctx context.Context, orgID, userID string) error
|
||||||
|
InviteCodeSent(ctx context.Context, orgID, userID string) error
|
||||||
UsageNotificationSent(ctx context.Context, dueEvent *quota.NotificationDueEvent) error
|
UsageNotificationSent(ctx context.Context, dueEvent *quota.NotificationDueEvent) error
|
||||||
MilestonePushed(ctx context.Context, msType milestone.Type, endpoints []string, primaryDomain string) error
|
MilestonePushed(ctx context.Context, msType milestone.Type, endpoints []string, primaryDomain string) error
|
||||||
}
|
}
|
||||||
|
@ -18,30 +18,30 @@ import (
|
|||||||
gomock "go.uber.org/mock/gomock"
|
gomock "go.uber.org/mock/gomock"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockCommands is a mock of Commands interface.
|
// MockCommands is a mock of Commands interface
|
||||||
type MockCommands struct {
|
type MockCommands struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
recorder *MockCommandsMockRecorder
|
recorder *MockCommandsMockRecorder
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockCommandsMockRecorder is the mock recorder for MockCommands.
|
// MockCommandsMockRecorder is the mock recorder for MockCommands
|
||||||
type MockCommandsMockRecorder struct {
|
type MockCommandsMockRecorder struct {
|
||||||
mock *MockCommands
|
mock *MockCommands
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMockCommands creates a new mock instance.
|
// NewMockCommands creates a new mock instance
|
||||||
func NewMockCommands(ctrl *gomock.Controller) *MockCommands {
|
func NewMockCommands(ctrl *gomock.Controller) *MockCommands {
|
||||||
mock := &MockCommands{ctrl: ctrl}
|
mock := &MockCommands{ctrl: ctrl}
|
||||||
mock.recorder = &MockCommandsMockRecorder{mock}
|
mock.recorder = &MockCommandsMockRecorder{mock}
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
func (m *MockCommands) EXPECT() *MockCommandsMockRecorder {
|
func (m *MockCommands) EXPECT() *MockCommandsMockRecorder {
|
||||||
return m.recorder
|
return m.recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
// HumanEmailVerificationCodeSent mocks base method.
|
// HumanEmailVerificationCodeSent mocks base method
|
||||||
func (m *MockCommands) HumanEmailVerificationCodeSent(arg0 context.Context, arg1, arg2 string) error {
|
func (m *MockCommands) HumanEmailVerificationCodeSent(arg0 context.Context, arg1, arg2 string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "HumanEmailVerificationCodeSent", arg0, arg1, arg2)
|
ret := m.ctrl.Call(m, "HumanEmailVerificationCodeSent", arg0, arg1, arg2)
|
||||||
@ -49,13 +49,13 @@ func (m *MockCommands) HumanEmailVerificationCodeSent(arg0 context.Context, arg1
|
|||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// HumanEmailVerificationCodeSent indicates an expected call of HumanEmailVerificationCodeSent.
|
// HumanEmailVerificationCodeSent indicates an expected call of HumanEmailVerificationCodeSent
|
||||||
func (mr *MockCommandsMockRecorder) HumanEmailVerificationCodeSent(arg0, arg1, arg2 any) *gomock.Call {
|
func (mr *MockCommandsMockRecorder) HumanEmailVerificationCodeSent(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanEmailVerificationCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanEmailVerificationCodeSent), arg0, arg1, arg2)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanEmailVerificationCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanEmailVerificationCodeSent), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HumanInitCodeSent mocks base method.
|
// HumanInitCodeSent mocks base method
|
||||||
func (m *MockCommands) HumanInitCodeSent(arg0 context.Context, arg1, arg2 string) error {
|
func (m *MockCommands) HumanInitCodeSent(arg0 context.Context, arg1, arg2 string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "HumanInitCodeSent", arg0, arg1, arg2)
|
ret := m.ctrl.Call(m, "HumanInitCodeSent", arg0, arg1, arg2)
|
||||||
@ -63,13 +63,13 @@ func (m *MockCommands) HumanInitCodeSent(arg0 context.Context, arg1, arg2 string
|
|||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// HumanInitCodeSent indicates an expected call of HumanInitCodeSent.
|
// HumanInitCodeSent indicates an expected call of HumanInitCodeSent
|
||||||
func (mr *MockCommandsMockRecorder) HumanInitCodeSent(arg0, arg1, arg2 any) *gomock.Call {
|
func (mr *MockCommandsMockRecorder) HumanInitCodeSent(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanInitCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanInitCodeSent), arg0, arg1, arg2)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanInitCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanInitCodeSent), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HumanOTPEmailCodeSent mocks base method.
|
// HumanOTPEmailCodeSent mocks base method
|
||||||
func (m *MockCommands) HumanOTPEmailCodeSent(arg0 context.Context, arg1, arg2 string) error {
|
func (m *MockCommands) HumanOTPEmailCodeSent(arg0 context.Context, arg1, arg2 string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "HumanOTPEmailCodeSent", arg0, arg1, arg2)
|
ret := m.ctrl.Call(m, "HumanOTPEmailCodeSent", arg0, arg1, arg2)
|
||||||
@ -77,13 +77,13 @@ func (m *MockCommands) HumanOTPEmailCodeSent(arg0 context.Context, arg1, arg2 st
|
|||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// HumanOTPEmailCodeSent indicates an expected call of HumanOTPEmailCodeSent.
|
// HumanOTPEmailCodeSent indicates an expected call of HumanOTPEmailCodeSent
|
||||||
func (mr *MockCommandsMockRecorder) HumanOTPEmailCodeSent(arg0, arg1, arg2 any) *gomock.Call {
|
func (mr *MockCommandsMockRecorder) HumanOTPEmailCodeSent(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanOTPEmailCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanOTPEmailCodeSent), arg0, arg1, arg2)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanOTPEmailCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanOTPEmailCodeSent), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HumanOTPSMSCodeSent mocks base method.
|
// HumanOTPSMSCodeSent mocks base method
|
||||||
func (m *MockCommands) HumanOTPSMSCodeSent(arg0 context.Context, arg1, arg2 string) error {
|
func (m *MockCommands) HumanOTPSMSCodeSent(arg0 context.Context, arg1, arg2 string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "HumanOTPSMSCodeSent", arg0, arg1, arg2)
|
ret := m.ctrl.Call(m, "HumanOTPSMSCodeSent", arg0, arg1, arg2)
|
||||||
@ -91,13 +91,13 @@ func (m *MockCommands) HumanOTPSMSCodeSent(arg0 context.Context, arg1, arg2 stri
|
|||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// HumanOTPSMSCodeSent indicates an expected call of HumanOTPSMSCodeSent.
|
// HumanOTPSMSCodeSent indicates an expected call of HumanOTPSMSCodeSent
|
||||||
func (mr *MockCommandsMockRecorder) HumanOTPSMSCodeSent(arg0, arg1, arg2 any) *gomock.Call {
|
func (mr *MockCommandsMockRecorder) HumanOTPSMSCodeSent(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanOTPSMSCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanOTPSMSCodeSent), arg0, arg1, arg2)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanOTPSMSCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanOTPSMSCodeSent), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HumanPasswordlessInitCodeSent mocks base method.
|
// HumanPasswordlessInitCodeSent mocks base method
|
||||||
func (m *MockCommands) HumanPasswordlessInitCodeSent(arg0 context.Context, arg1, arg2, arg3 string) error {
|
func (m *MockCommands) HumanPasswordlessInitCodeSent(arg0 context.Context, arg1, arg2, arg3 string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "HumanPasswordlessInitCodeSent", arg0, arg1, arg2, arg3)
|
ret := m.ctrl.Call(m, "HumanPasswordlessInitCodeSent", arg0, arg1, arg2, arg3)
|
||||||
@ -105,13 +105,13 @@ func (m *MockCommands) HumanPasswordlessInitCodeSent(arg0 context.Context, arg1,
|
|||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// HumanPasswordlessInitCodeSent indicates an expected call of HumanPasswordlessInitCodeSent.
|
// HumanPasswordlessInitCodeSent indicates an expected call of HumanPasswordlessInitCodeSent
|
||||||
func (mr *MockCommandsMockRecorder) HumanPasswordlessInitCodeSent(arg0, arg1, arg2, arg3 any) *gomock.Call {
|
func (mr *MockCommandsMockRecorder) HumanPasswordlessInitCodeSent(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanPasswordlessInitCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanPasswordlessInitCodeSent), arg0, arg1, arg2, arg3)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanPasswordlessInitCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanPasswordlessInitCodeSent), arg0, arg1, arg2, arg3)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HumanPhoneVerificationCodeSent mocks base method.
|
// HumanPhoneVerificationCodeSent mocks base method
|
||||||
func (m *MockCommands) HumanPhoneVerificationCodeSent(arg0 context.Context, arg1, arg2 string) error {
|
func (m *MockCommands) HumanPhoneVerificationCodeSent(arg0 context.Context, arg1, arg2 string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "HumanPhoneVerificationCodeSent", arg0, arg1, arg2)
|
ret := m.ctrl.Call(m, "HumanPhoneVerificationCodeSent", arg0, arg1, arg2)
|
||||||
@ -119,13 +119,27 @@ func (m *MockCommands) HumanPhoneVerificationCodeSent(arg0 context.Context, arg1
|
|||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// HumanPhoneVerificationCodeSent indicates an expected call of HumanPhoneVerificationCodeSent.
|
// HumanPhoneVerificationCodeSent indicates an expected call of HumanPhoneVerificationCodeSent
|
||||||
func (mr *MockCommandsMockRecorder) HumanPhoneVerificationCodeSent(arg0, arg1, arg2 any) *gomock.Call {
|
func (mr *MockCommandsMockRecorder) HumanPhoneVerificationCodeSent(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanPhoneVerificationCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanPhoneVerificationCodeSent), arg0, arg1, arg2)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanPhoneVerificationCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanPhoneVerificationCodeSent), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MilestonePushed mocks base method.
|
// InviteCodeSent mocks base method
|
||||||
|
func (m *MockCommands) InviteCodeSent(arg0 context.Context, arg1, arg2 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "InviteCodeSent", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// InviteCodeSent indicates an expected call of InviteCodeSent
|
||||||
|
func (mr *MockCommandsMockRecorder) InviteCodeSent(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InviteCodeSent", reflect.TypeOf((*MockCommands)(nil).InviteCodeSent), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MilestonePushed mocks base method
|
||||||
func (m *MockCommands) MilestonePushed(arg0 context.Context, arg1 milestone.Type, arg2 []string, arg3 string) error {
|
func (m *MockCommands) MilestonePushed(arg0 context.Context, arg1 milestone.Type, arg2 []string, arg3 string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "MilestonePushed", arg0, arg1, arg2, arg3)
|
ret := m.ctrl.Call(m, "MilestonePushed", arg0, arg1, arg2, arg3)
|
||||||
@ -133,13 +147,13 @@ func (m *MockCommands) MilestonePushed(arg0 context.Context, arg1 milestone.Type
|
|||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// MilestonePushed indicates an expected call of MilestonePushed.
|
// MilestonePushed indicates an expected call of MilestonePushed
|
||||||
func (mr *MockCommandsMockRecorder) MilestonePushed(arg0, arg1, arg2, arg3 any) *gomock.Call {
|
func (mr *MockCommandsMockRecorder) MilestonePushed(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MilestonePushed", reflect.TypeOf((*MockCommands)(nil).MilestonePushed), arg0, arg1, arg2, arg3)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MilestonePushed", reflect.TypeOf((*MockCommands)(nil).MilestonePushed), arg0, arg1, arg2, arg3)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OTPEmailSent mocks base method.
|
// OTPEmailSent mocks base method
|
||||||
func (m *MockCommands) OTPEmailSent(arg0 context.Context, arg1, arg2 string) error {
|
func (m *MockCommands) OTPEmailSent(arg0 context.Context, arg1, arg2 string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "OTPEmailSent", arg0, arg1, arg2)
|
ret := m.ctrl.Call(m, "OTPEmailSent", arg0, arg1, arg2)
|
||||||
@ -147,13 +161,13 @@ func (m *MockCommands) OTPEmailSent(arg0 context.Context, arg1, arg2 string) err
|
|||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// OTPEmailSent indicates an expected call of OTPEmailSent.
|
// OTPEmailSent indicates an expected call of OTPEmailSent
|
||||||
func (mr *MockCommandsMockRecorder) OTPEmailSent(arg0, arg1, arg2 any) *gomock.Call {
|
func (mr *MockCommandsMockRecorder) OTPEmailSent(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OTPEmailSent", reflect.TypeOf((*MockCommands)(nil).OTPEmailSent), arg0, arg1, arg2)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OTPEmailSent", reflect.TypeOf((*MockCommands)(nil).OTPEmailSent), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OTPSMSSent mocks base method.
|
// OTPSMSSent mocks base method
|
||||||
func (m *MockCommands) OTPSMSSent(arg0 context.Context, arg1, arg2 string) error {
|
func (m *MockCommands) OTPSMSSent(arg0 context.Context, arg1, arg2 string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "OTPSMSSent", arg0, arg1, arg2)
|
ret := m.ctrl.Call(m, "OTPSMSSent", arg0, arg1, arg2)
|
||||||
@ -161,13 +175,13 @@ func (m *MockCommands) OTPSMSSent(arg0 context.Context, arg1, arg2 string) error
|
|||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// OTPSMSSent indicates an expected call of OTPSMSSent.
|
// OTPSMSSent indicates an expected call of OTPSMSSent
|
||||||
func (mr *MockCommandsMockRecorder) OTPSMSSent(arg0, arg1, arg2 any) *gomock.Call {
|
func (mr *MockCommandsMockRecorder) OTPSMSSent(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OTPSMSSent", reflect.TypeOf((*MockCommands)(nil).OTPSMSSent), arg0, arg1, arg2)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OTPSMSSent", reflect.TypeOf((*MockCommands)(nil).OTPSMSSent), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PasswordChangeSent mocks base method.
|
// PasswordChangeSent mocks base method
|
||||||
func (m *MockCommands) PasswordChangeSent(arg0 context.Context, arg1, arg2 string) error {
|
func (m *MockCommands) PasswordChangeSent(arg0 context.Context, arg1, arg2 string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "PasswordChangeSent", arg0, arg1, arg2)
|
ret := m.ctrl.Call(m, "PasswordChangeSent", arg0, arg1, arg2)
|
||||||
@ -175,13 +189,13 @@ func (m *MockCommands) PasswordChangeSent(arg0 context.Context, arg1, arg2 strin
|
|||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// PasswordChangeSent indicates an expected call of PasswordChangeSent.
|
// PasswordChangeSent indicates an expected call of PasswordChangeSent
|
||||||
func (mr *MockCommandsMockRecorder) PasswordChangeSent(arg0, arg1, arg2 any) *gomock.Call {
|
func (mr *MockCommandsMockRecorder) PasswordChangeSent(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordChangeSent", reflect.TypeOf((*MockCommands)(nil).PasswordChangeSent), arg0, arg1, arg2)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordChangeSent", reflect.TypeOf((*MockCommands)(nil).PasswordChangeSent), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PasswordCodeSent mocks base method.
|
// PasswordCodeSent mocks base method
|
||||||
func (m *MockCommands) PasswordCodeSent(arg0 context.Context, arg1, arg2 string) error {
|
func (m *MockCommands) PasswordCodeSent(arg0 context.Context, arg1, arg2 string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "PasswordCodeSent", arg0, arg1, arg2)
|
ret := m.ctrl.Call(m, "PasswordCodeSent", arg0, arg1, arg2)
|
||||||
@ -189,13 +203,13 @@ func (m *MockCommands) PasswordCodeSent(arg0 context.Context, arg1, arg2 string)
|
|||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// PasswordCodeSent indicates an expected call of PasswordCodeSent.
|
// PasswordCodeSent indicates an expected call of PasswordCodeSent
|
||||||
func (mr *MockCommandsMockRecorder) PasswordCodeSent(arg0, arg1, arg2 any) *gomock.Call {
|
func (mr *MockCommandsMockRecorder) PasswordCodeSent(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordCodeSent", reflect.TypeOf((*MockCommands)(nil).PasswordCodeSent), arg0, arg1, arg2)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordCodeSent", reflect.TypeOf((*MockCommands)(nil).PasswordCodeSent), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UsageNotificationSent mocks base method.
|
// UsageNotificationSent mocks base method
|
||||||
func (m *MockCommands) UsageNotificationSent(arg0 context.Context, arg1 *quota.NotificationDueEvent) error {
|
func (m *MockCommands) UsageNotificationSent(arg0 context.Context, arg1 *quota.NotificationDueEvent) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "UsageNotificationSent", arg0, arg1)
|
ret := m.ctrl.Call(m, "UsageNotificationSent", arg0, arg1)
|
||||||
@ -203,13 +217,13 @@ func (m *MockCommands) UsageNotificationSent(arg0 context.Context, arg1 *quota.N
|
|||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// UsageNotificationSent indicates an expected call of UsageNotificationSent.
|
// UsageNotificationSent indicates an expected call of UsageNotificationSent
|
||||||
func (mr *MockCommandsMockRecorder) UsageNotificationSent(arg0, arg1 any) *gomock.Call {
|
func (mr *MockCommandsMockRecorder) UsageNotificationSent(arg0, arg1 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UsageNotificationSent", reflect.TypeOf((*MockCommands)(nil).UsageNotificationSent), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UsageNotificationSent", reflect.TypeOf((*MockCommands)(nil).UsageNotificationSent), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserDomainClaimedSent mocks base method.
|
// UserDomainClaimedSent mocks base method
|
||||||
func (m *MockCommands) UserDomainClaimedSent(arg0 context.Context, arg1, arg2 string) error {
|
func (m *MockCommands) UserDomainClaimedSent(arg0 context.Context, arg1, arg2 string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "UserDomainClaimedSent", arg0, arg1, arg2)
|
ret := m.ctrl.Call(m, "UserDomainClaimedSent", arg0, arg1, arg2)
|
||||||
@ -217,8 +231,8 @@ func (m *MockCommands) UserDomainClaimedSent(arg0 context.Context, arg1, arg2 st
|
|||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserDomainClaimedSent indicates an expected call of UserDomainClaimedSent.
|
// UserDomainClaimedSent indicates an expected call of UserDomainClaimedSent
|
||||||
func (mr *MockCommandsMockRecorder) UserDomainClaimedSent(arg0, arg1, arg2 any) *gomock.Call {
|
func (mr *MockCommandsMockRecorder) UserDomainClaimedSent(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserDomainClaimedSent", reflect.TypeOf((*MockCommands)(nil).UserDomainClaimedSent), arg0, arg1, arg2)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserDomainClaimedSent", reflect.TypeOf((*MockCommands)(nil).UserDomainClaimedSent), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,10 @@ func (u *userNotifier) Reducers() []handler.AggregateReducer {
|
|||||||
Event: user.HumanOTPEmailCodeAddedType,
|
Event: user.HumanOTPEmailCodeAddedType,
|
||||||
Reduce: u.reduceOTPEmailCodeAdded,
|
Reduce: u.reduceOTPEmailCodeAdded,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Event: user.HumanInviteCodeAddedType,
|
||||||
|
Reduce: u.reduceInviteCodeAdded,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -718,6 +722,61 @@ func (u *userNotifier) reducePhoneCodeAdded(event eventstore.Event) (*handler.St
|
|||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *userNotifier) reduceInviteCodeAdded(event eventstore.Event) (*handler.Statement, error) {
|
||||||
|
e, ok := event.(*user.HumanInviteCodeAddedEvent)
|
||||||
|
if !ok {
|
||||||
|
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Eeg3s", "reduce.wrong.event.type %s", user.HumanInviteCodeAddedType)
|
||||||
|
}
|
||||||
|
if e.CodeReturned {
|
||||||
|
return handler.NewNoOpStatement(e), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||||
|
ctx := HandlerContext(event.Aggregate())
|
||||||
|
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||||
|
user.HumanInviteCodeAddedType, user.HumanInviteCodeSentType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if alreadyHandled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
code, err := crypto.DecryptString(e.Code, u.queries.UserDataCrypto)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
template, err := u.queries.MailTemplateByOrg(ctx, e.Aggregate().ResourceOwner, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.InviteUserMessageType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, err = u.queries.Origin(ctx, e)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
notify := types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e)
|
||||||
|
err = notify.SendInviteCode(ctx, notifyUser, code, e.ApplicationName, e.URLTemplate, e.AuthRequestID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return u.commands.InviteCodeSent(ctx, e.Aggregate().ID, e.Aggregate().ResourceOwner)
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (u *userNotifier) checkIfCodeAlreadyHandledOrExpired(ctx context.Context, event eventstore.Event, expiry time.Duration, data map[string]interface{}, eventTypes ...eventstore.EventType) (bool, error) {
|
func (u *userNotifier) checkIfCodeAlreadyHandledOrExpired(ctx context.Context, event eventstore.Event, expiry time.Duration, data map[string]interface{}, eventTypes ...eventstore.EventType) (bool, error) {
|
||||||
if event.CreatedAt().Add(expiry).Before(time.Now().UTC()) {
|
if event.CreatedAt().Add(expiry).Before(time.Now().UTC()) {
|
||||||
return true, nil
|
return true, nil
|
||||||
|
@ -69,3 +69,10 @@ PasswordChange:
|
|||||||
Паролата на вашия потребител е променена, ако тази промяна не е направена от
|
Паролата на вашия потребител е променена, ако тази промяна не е направена от
|
||||||
вас, моля, незабавно нулирайте паролата си.
|
вас, моля, незабавно нулирайте паролата си.
|
||||||
ButtonText: Влизам
|
ButtonText: Влизам
|
||||||
|
InviteUser:
|
||||||
|
Title: Покана за {{.ApplicationName}}
|
||||||
|
PreHeader: Покана за {{.ApplicationName}}
|
||||||
|
Subject: Покана за {{.ApplicationName}}
|
||||||
|
Greeting: 'Здравейте {{.DisplayName}},'
|
||||||
|
Text: Вашият потребител е бил поканен за {{.ApplicationName}}. Моля, кликнете върху бутона по-долу, за да завършите процеса на покана. Ако не сте поискали този имейл, моля, игнорирайте го.
|
||||||
|
ButtonText: Приеми поканата
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: Dobrý den, {{.DisplayName}},
|
Greeting: Dobrý den, {{.DisplayName}},
|
||||||
Text: Heslo vašeho uživatele bylo změněno. Pokud tato změna nebyla provedena Vámi pak doporučujeme okamžitě resetovat/změnit vaše heslo.
|
Text: Heslo vašeho uživatele bylo změněno. Pokud tato změna nebyla provedena Vámi pak doporučujeme okamžitě resetovat/změnit vaše heslo.
|
||||||
ButtonText: Přihlásit se
|
ButtonText: Přihlásit se
|
||||||
|
InviteUser:
|
||||||
|
Title: Pozvánka do {{.ApplicationName}}
|
||||||
|
PreHeader: Pozvánka do {{.ApplicationName}}
|
||||||
|
Subject: Pozvánka do {{.ApplicationName}}
|
||||||
|
Greeting: Dobrý den, {{.DisplayName}},
|
||||||
|
Text: Váš uživatel byl pozván do {{.ApplicationName}}. Klikněte prosím na tlačítko níže, abyste dokončili proces pozvání. Pokud jste o tento e-mail nepožádali, prosím, ignorujte ho.
|
||||||
|
ButtonText: Přijmout pozvání
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: Hallo {{.DisplayName}},
|
Greeting: Hallo {{.DisplayName}},
|
||||||
Text: Dein Passwort wurde geändert. Wenn diese Änderung nicht von dir gemacht wurde, empfehlen wir das sofortige Zurücksetzen deines Passworts.
|
Text: Dein Passwort wurde geändert. Wenn diese Änderung nicht von dir gemacht wurde, empfehlen wir das sofortige Zurücksetzen deines Passworts.
|
||||||
ButtonText: Login
|
ButtonText: Login
|
||||||
|
InviteUser:
|
||||||
|
Title: Einladung zu {{.ApplicationName}}
|
||||||
|
PreHeader: Einladung zu {{.ApplicationName}}
|
||||||
|
Subject: Einladung zu {{.ApplicationName}}
|
||||||
|
Greeting: Hallo {{.DisplayName}},
|
||||||
|
Text: Ihr Benutzer wurde zu {{.ApplicationName}} eingeladen. Bitte klicken Sie auf die Schaltfläche unten, um den Einladungsprozess abzuschließen. Wenn Sie diese E-Mail nicht angefordert haben, ignorieren Sie sie bitte.
|
||||||
|
ButtonText: Einladung annehmen
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: Hello {{.DisplayName}},
|
Greeting: Hello {{.DisplayName}},
|
||||||
Text: The password of your user has changed. If this change was not done by you, please be advised to immediately reset your password.
|
Text: The password of your user has changed. If this change was not done by you, please be advised to immediately reset your password.
|
||||||
ButtonText: Login
|
ButtonText: Login
|
||||||
|
InviteUser:
|
||||||
|
Title: Invitation to {{.ApplicationName}}
|
||||||
|
PreHeader: Invitation to {{.ApplicationName}}
|
||||||
|
Subject: Invitation to {{.ApplicationName}}
|
||||||
|
Greeting: Hello {{.DisplayName}},
|
||||||
|
Text: Your user has been invited to {{.ApplicationName}}. Please click the button below to finish the invite process. If you didn't ask for this mail, please ignore it.
|
||||||
|
ButtonText: Accept invite
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: Hola {{.DisplayName}},
|
Greeting: Hola {{.DisplayName}},
|
||||||
Text: La contraseña de tu usuario ha sido cambiada, si este cambio no fue hecho por ti, por favor proceder a restablecer inmediatamente tu contraseña.
|
Text: La contraseña de tu usuario ha sido cambiada, si este cambio no fue hecho por ti, por favor proceder a restablecer inmediatamente tu contraseña.
|
||||||
ButtonText: Iniciar sesión
|
ButtonText: Iniciar sesión
|
||||||
|
InviteUser:
|
||||||
|
Title: Invitación a {{.ApplicationName}}
|
||||||
|
PreHeader: Invitación a {{.ApplicationName}}
|
||||||
|
Subject: Invitación a {{.ApplicationName}}
|
||||||
|
Greeting: Hola {{.DisplayName}},
|
||||||
|
Text: Tu usuario ha sido invitado a {{.ApplicationName}}. Haz clic en el botón de abajo para finalizar el proceso de invitación. Si no solicitaste este correo electrónico, por favor ignóralo.
|
||||||
|
ButtonText: Aceptar invitación
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: Bonjour {{.DisplayName}},
|
Greeting: Bonjour {{.DisplayName}},
|
||||||
Text: Le mot de passe de votre utilisateur a changé, si ce changement n'a pas été fait par vous, nous vous conseillons de réinitialiser immédiatement votre mot de passe.
|
Text: Le mot de passe de votre utilisateur a changé, si ce changement n'a pas été fait par vous, nous vous conseillons de réinitialiser immédiatement votre mot de passe.
|
||||||
ButtonText: Login
|
ButtonText: Login
|
||||||
|
InviteUser:
|
||||||
|
Title: Invitation à {{.ApplicationName}}
|
||||||
|
PreHeader: Invitation à {{.ApplicationName}}
|
||||||
|
Subject: Invitation à {{.ApplicationName}}
|
||||||
|
Greeting: Bonjour {{.DisplayName}},
|
||||||
|
Text: Votre utilisateur a été invité à {{.ApplicationName}}. Veuillez cliquer sur le bouton ci-dessous pour terminer le processus d'invitation. Si vous n'avez pas demandé cet e-mail, veuillez l'ignorer.
|
||||||
|
ButtonText: Accepter l'invitation
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: 'Halo {{.DisplayName}},'
|
Greeting: 'Halo {{.DisplayName}},'
|
||||||
Text: 'Kata sandi pengguna Anda telah berubah. '
|
Text: 'Kata sandi pengguna Anda telah berubah. '
|
||||||
ButtonText: Login
|
ButtonText: Login
|
||||||
|
InviteUser:
|
||||||
|
Title: Undangan ke {{.ApplicationName}}
|
||||||
|
PreHeader: Undangan ke {{.ApplicationName}}
|
||||||
|
Subject: Undangan ke {{.ApplicationName}}
|
||||||
|
Greeting: 'Halo {{.DisplayName}},'
|
||||||
|
Text: Pengguna Anda telah diundang ke {{.ApplicationName}}. Silakan klik tombol di bawah ini untuk menyelesaikan proses undangan. Jika Anda tidak meminta email ini, harap abaikan.
|
||||||
|
ButtonText: Terima undangan
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: Ciao {{.DisplayName}},
|
Greeting: Ciao {{.DisplayName}},
|
||||||
Text: La password del vostro utente è cambiata; se questa modifica non è stata fatta da voi, vi consigliamo di reimpostare immediatamente la vostra password.
|
Text: La password del vostro utente è cambiata; se questa modifica non è stata fatta da voi, vi consigliamo di reimpostare immediatamente la vostra password.
|
||||||
ButtonText: Login
|
ButtonText: Login
|
||||||
|
InviteUser:
|
||||||
|
Title: Invito a {{.ApplicationName}}
|
||||||
|
PreHeader: Invito a {{.ApplicationName}}
|
||||||
|
Subject: Invito a {{.ApplicationName}}
|
||||||
|
Greeting: 'Ciao {{.DisplayName}},'
|
||||||
|
Text: Il tuo utente è stato invitato a {{.ApplicationName}}. Clicca sul pulsante qui sotto per completare il processo di invito. Se non hai richiesto questa email, ignorala.
|
||||||
|
ButtonText: Accetta invito
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: こんにちは {{.DisplayName}} さん、
|
Greeting: こんにちは {{.DisplayName}} さん、
|
||||||
Text: ユーザーのパスワードが変更されました。この変更があなたによって行われなかった場合は、すぐにパスワードをリセットすることをお勧めします。
|
Text: ユーザーのパスワードが変更されました。この変更があなたによって行われなかった場合は、すぐにパスワードをリセットすることをお勧めします。
|
||||||
ButtonText: ログイン
|
ButtonText: ログイン
|
||||||
|
InviteUser:
|
||||||
|
Title: '{{.ApplicationName}}への招待'
|
||||||
|
PreHeader: '{{.ApplicationName}}への招待'
|
||||||
|
Subject: '{{.ApplicationName}}への招待'
|
||||||
|
Greeting: こんにちは {{.DisplayName}} さん、
|
||||||
|
Text: あなたのユーザーは{{.ApplicationName}}に招待されました。下のボタンをクリックして、招待プロセスを完了してください。このメールをリクエストしていない場合は、無視してください。
|
||||||
|
ButtonText: 招待を受け入れる
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: Здраво {{.DisplayName}},
|
Greeting: Здраво {{.DisplayName}},
|
||||||
Text: Лозинката на вашиот корисник е променета. Ако оваа промена не е извршена од вас, ве молиме веднаш ресетирајте ја вашата лозинка.
|
Text: Лозинката на вашиот корисник е променета. Ако оваа промена не е извршена од вас, ве молиме веднаш ресетирајте ја вашата лозинка.
|
||||||
ButtonText: Најава
|
ButtonText: Најава
|
||||||
|
InviteUser:
|
||||||
|
Title: Покана за {{.ApplicationName}}
|
||||||
|
PreHeader: Покана за {{.ApplicationName}}
|
||||||
|
Subject: Покана за {{.ApplicationName}}
|
||||||
|
Greeting: Здраво {{.DisplayName}},
|
||||||
|
Text: Вашиот корисник е бил поканет за {{.ApplicationName}}. Ве молиме кликнете на копчето подолу за да го завршите процесот на покана. Ако не сте побарале овој мејл, ве молиме игнорирајте го.
|
||||||
|
ButtonText: Прифати покана
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: Hallo {{.DisplayName}},
|
Greeting: Hallo {{.DisplayName}},
|
||||||
Text: Het wachtwoord van uw gebruiker is veranderd. Als deze wijziging niet door u is gedaan, wordt u geadviseerd om direct uw wachtwoord te resetten.
|
Text: Het wachtwoord van uw gebruiker is veranderd. Als deze wijziging niet door u is gedaan, wordt u geadviseerd om direct uw wachtwoord te resetten.
|
||||||
ButtonText: Inloggen
|
ButtonText: Inloggen
|
||||||
|
InviteUser:
|
||||||
|
Title: Uitnodiging voor {{.ApplicationName}}
|
||||||
|
PreHeader: Uitnodiging voor {{.ApplicationName}}
|
||||||
|
Subject: Uitnodiging voor {{.ApplicationName}}
|
||||||
|
Greeting: Hallo {{.DisplayName}},
|
||||||
|
Text: Uw gebruiker is uitgenodigd voor {{.ApplicationName}}. Klik op de onderstaande knop om het uitnodigingsproces te voltooien. Als u deze e-mail niet hebt aangevraagd, negeer deze dan.
|
||||||
|
ButtonText: Uitnodiging accepteren
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: Witaj {{.DisplayName}},
|
Greeting: Witaj {{.DisplayName}},
|
||||||
Text: Hasło Twojego użytkownika zostało zmienione, jeśli ta zmiana nie została dokonana przez Ciebie, zalecamy natychmiastowe zresetowanie hasła.
|
Text: Hasło Twojego użytkownika zostało zmienione, jeśli ta zmiana nie została dokonana przez Ciebie, zalecamy natychmiastowe zresetowanie hasła.
|
||||||
ButtonText: Zaloguj się
|
ButtonText: Zaloguj się
|
||||||
|
InviteUser:
|
||||||
|
Title: Zaproszenie do {{.ApplicationName}}
|
||||||
|
PreHeader: Zaproszenie do {{.ApplicationName}}
|
||||||
|
Subject: Zaproszenie do {{.ApplicationName}}
|
||||||
|
Greeting: Witaj {{.DisplayName}},
|
||||||
|
Text: Twój użytkownik został zaproszony do {{.ApplicationName}}. Kliknij poniższy przycisk, aby zakończyć proces zaproszenia. Jeśli nie zażądałeś tego e-maila, zignoruj go.
|
||||||
|
ButtonText: Akceptuj zaproszenie
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: Olá {{.DisplayName}},
|
Greeting: Olá {{.DisplayName}},
|
||||||
Text: A senha do seu usuário foi alterada. Se esta alteração não foi feita por você, recomendamos que você redefina sua senha imediatamente.
|
Text: A senha do seu usuário foi alterada. Se esta alteração não foi feita por você, recomendamos que você redefina sua senha imediatamente.
|
||||||
ButtonText: Fazer login
|
ButtonText: Fazer login
|
||||||
|
InviteUser:
|
||||||
|
Title: Convite para {{.ApplicationName}}
|
||||||
|
PreHeader: Convite para {{.ApplicationName}}
|
||||||
|
Subject: Convite para {{.ApplicationName}}
|
||||||
|
Greeting: Olá {{.DisplayName}},
|
||||||
|
Text: Seu usuário foi convidado para {{.ApplicationName}}. Clique no botão abaixo para concluir o processo de convite. Se você não solicitou este e-mail, por favor, ignore-o.
|
||||||
|
ButtonText: Aceitar convite
|
@ -2,28 +2,28 @@ InitCode:
|
|||||||
Title: Регистрация пользователя
|
Title: Регистрация пользователя
|
||||||
PreHeader: Регистрация пользователя
|
PreHeader: Регистрация пользователя
|
||||||
Subject: Регистрация пользователя
|
Subject: Регистрация пользователя
|
||||||
Greeting: Здравствуйте {{.FirstName}} {{.LastName}},
|
Greeting: Здравствуйте {{.DisplayName}},
|
||||||
Text: Используйте логин {{.PreferredLoginName}} для входа. Пожалуйста, нажмите кнопку ниже для завершения процесса регистрации. (Код {{.Code}}) Если вы не запрашивали это письмо, пожалуйста, проигнорируйте его.
|
Text: Используйте логин {{.PreferredLoginName}} для входа. Пожалуйста, нажмите кнопку ниже для завершения процесса регистрации. (Код {{.Code}}) Если вы не запрашивали это письмо, пожалуйста, проигнорируйте его.
|
||||||
ButtonText: Завершить регистрацию
|
ButtonText: Завершить регистрацию
|
||||||
PasswordReset:
|
PasswordReset:
|
||||||
Title: Сброс пароля
|
Title: Сброс пароля
|
||||||
PreHeader: Сброс пароля
|
PreHeader: Сброс пароля
|
||||||
Subject: Сброс пароля
|
Subject: Сброс пароля
|
||||||
Greeting: Здравствуйте {{.FirstName}} {{.LastName}},
|
Greeting: Здравствуйте {{.DisplayName}},
|
||||||
Text: Мы получили запрос на сброс пароля. Пожалуйста, нажмите кнопку ниже для сброса вашего пароля. (Код {{.Code}}) Если вы не запрашивали это письмо, пожалуйста, проигнорируйте его.
|
Text: Мы получили запрос на сброс пароля. Пожалуйста, нажмите кнопку ниже для сброса вашего пароля. (Код {{.Code}}) Если вы не запрашивали это письмо, пожалуйста, проигнорируйте его.
|
||||||
ButtonText: Сбросить пароль
|
ButtonText: Сбросить пароль
|
||||||
VerifyEmail:
|
VerifyEmail:
|
||||||
Title: Подтверждение email
|
Title: Подтверждение email
|
||||||
PreHeader: Подтверждение email
|
PreHeader: Подтверждение email
|
||||||
Subject: Подтверждение email
|
Subject: Подтверждение email
|
||||||
Greeting: Здравствуйте {{.FirstName}} {{.LastName}},
|
Greeting: Здравствуйте {{.DisplayName}},
|
||||||
Text: Добавлен новый email. Пожалуйста, нажмите кнопку ниже для подтверждения вашего email. (Код {{.Code}}) Если вы не запрашивали это письмо, пожалуйста, проигнорируйте его.
|
Text: Добавлен новый email. Пожалуйста, нажмите кнопку ниже для подтверждения вашего email. (Код {{.Code}}) Если вы не запрашивали это письмо, пожалуйста, проигнорируйте его.
|
||||||
ButtonText: Подтвердить email
|
ButtonText: Подтвердить email
|
||||||
VerifyPhone:
|
VerifyPhone:
|
||||||
Title: Подтверждение телефона
|
Title: Подтверждение телефона
|
||||||
PreHeader: Подтверждение телефона
|
PreHeader: Подтверждение телефона
|
||||||
Subject: Подтверждение телефона
|
Subject: Подтверждение телефона
|
||||||
Greeting: Здравствуйте {{.FirstName}} {{.LastName}},
|
Greeting: Здравствуйте {{.DisplayName}},
|
||||||
Text: Добавлен новый номер телефона. Пожалуйста, используйте следующий код, чтобы подтвердить его. Код {{.Code}}
|
Text: Добавлен новый номер телефона. Пожалуйста, используйте следующий код, чтобы подтвердить его. Код {{.Code}}
|
||||||
ButtonText: Подтвердить телефон
|
ButtonText: Подтвердить телефон
|
||||||
VerifyEmailOTP:
|
VerifyEmailOTP:
|
||||||
@ -42,20 +42,27 @@ DomainClaimed:
|
|||||||
Title: Утверждение домена
|
Title: Утверждение домена
|
||||||
PreHeader: Изменение email / логина
|
PreHeader: Изменение email / логина
|
||||||
Subject: Домен был утвержден
|
Subject: Домен был утвержден
|
||||||
Greeting: Здравствуйте {{.FirstName}} {{.LastName}},
|
Greeting: Здравствуйте {{.DisplayName}},
|
||||||
Text: Домен {{.Domain}} был утвержден организацией. Ваш текущий пользователь {{.Username}} не является частью этой организации. Вам необходимо изменить свой email при входе в систему. Мы создали временный логин ({{.TempUsername}}) для входа.
|
Text: Домен {{.Domain}} был утвержден организацией. Ваш текущий пользователь {{.Username}} не является частью этой организации. Вам необходимо изменить свой email при входе в систему. Мы создали временный логин ({{.TempUsername}}) для входа.
|
||||||
ButtonText: Вход
|
ButtonText: Вход
|
||||||
PasswordlessRegistration:
|
PasswordlessRegistration:
|
||||||
Title: Добавление входа без пароля
|
Title: Добавление входа без пароля
|
||||||
PreHeader: Добавление входа без пароля
|
PreHeader: Добавление входа без пароля
|
||||||
Subject: Добавление входа без пароля
|
Subject: Добавление входа без пароля
|
||||||
Greeting: Здравствуйте {{.FirstName}} {{.LastName}},
|
Greeting: Здравствуйте {{.DisplayName}},
|
||||||
Text: Мы получили запрос на добавление токена для входа без пароля. Пожалуйста, используйте кнопку ниже, чтобы добавить свой токен или устройство для входа без пароля.
|
Text: Мы получили запрос на добавление токена для входа без пароля. Пожалуйста, используйте кнопку ниже, чтобы добавить свой токен или устройство для входа без пароля.
|
||||||
ButtonText: Добавить вход без пароля
|
ButtonText: Добавить вход без пароля
|
||||||
PasswordChange:
|
PasswordChange:
|
||||||
Title: Смена пароля пользователя
|
Title: Смена пароля пользователя
|
||||||
PreHeader: Смена пароля
|
PreHeader: Смена пароля
|
||||||
Subject: Пароль пользователя изменен
|
Subject: Пароль пользователя изменен
|
||||||
Greeting: Здравствуйте {{.FirstName}} {{.LastName}},
|
Greeting: Здравствуйте {{.DisplayName}},
|
||||||
Text: Пароль пользователя был изменен. Если это изменение сделано не вами, советуем немедленно сбросить пароль.
|
Text: Пароль пользователя был изменен. Если это изменение сделано не вами, советуем немедленно сбросить пароль.
|
||||||
ButtonText: Вход
|
ButtonText: Вход
|
||||||
|
InviteUser:
|
||||||
|
Title: Приглашение в {{.ApplicationName}}
|
||||||
|
PreHeader: Приглашение в {{.ApplicationName}}
|
||||||
|
Subject: Приглашение в {{.ApplicationName}}
|
||||||
|
Greeting: Здравствуйте, {{.DisplayName}},
|
||||||
|
Text: Ваш пользователь был приглашен в {{.ApplicationName}}. Пожалуйста, нажмите кнопку ниже, чтобы завершить процесс приглашения. Если вы не запрашивали это письмо, пожалуйста, игнорируйте его.
|
||||||
|
ButtonText: Принять приглашение
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: Hej {{.DisplayName}},
|
Greeting: Hej {{.DisplayName}},
|
||||||
Text: Lösenordet för din användare har ändrats. Om denna ändring inte gjordes av dig, vänligen återställ ditt lösenord omedelbart.
|
Text: Lösenordet för din användare har ändrats. Om denna ändring inte gjordes av dig, vänligen återställ ditt lösenord omedelbart.
|
||||||
ButtonText: Logga in
|
ButtonText: Logga in
|
||||||
|
InviteUser:
|
||||||
|
Title: Inbjudan till {{.ApplicationName}}
|
||||||
|
PreHeader: Inbjudan till {{.ApplicationName}}
|
||||||
|
Subject: Inbjudan till {{.ApplicationName}}
|
||||||
|
Greeting: Hej {{.DisplayName}},
|
||||||
|
Text: Din användare har blivit inbjuden till {{.ApplicationName}}. Klicka på knappen nedan för att slutföra inbjudansprocessen. Om du inte har begärt detta e-postmeddelande, ignorera det.
|
||||||
|
ButtonText: Acceptera inbjudan
|
@ -59,3 +59,10 @@ PasswordChange:
|
|||||||
Greeting: 你好 {{.DisplayName}},
|
Greeting: 你好 {{.DisplayName}},
|
||||||
Text: 您的用户的密码已经改变,如果这个改变不是由您做的,请注意立即重新设置您的密码。
|
Text: 您的用户的密码已经改变,如果这个改变不是由您做的,请注意立即重新设置您的密码。
|
||||||
ButtonText: 登录
|
ButtonText: 登录
|
||||||
|
InviteUser:
|
||||||
|
Title: '{{.ApplicationName}}邀请'
|
||||||
|
PreHeader: '{{.ApplicationName}}邀请'
|
||||||
|
Subject: '{{.ApplicationName}}邀请'
|
||||||
|
Greeting: 您好,{{.DisplayName}},
|
||||||
|
Text: 您的用户已被邀请加入{{.ApplicationName}}。请点击下面的按钮完成邀请过程。如果您没有请求此邮件,请忽略它。
|
||||||
|
ButtonText: 接受邀请
|
31
internal/notification/types/invite_code.go
Normal file
31
internal/notification/types/invite_code.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
http_utils "github.com/zitadel/zitadel/internal/api/http"
|
||||||
|
"github.com/zitadel/zitadel/internal/api/ui/login"
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (notify Notify) SendInviteCode(ctx context.Context, user *query.NotifyUser, code, applicationName, urlTmpl, authRequestID string) error {
|
||||||
|
var url string
|
||||||
|
if applicationName == "" {
|
||||||
|
applicationName = "ZITADEL"
|
||||||
|
}
|
||||||
|
if urlTmpl == "" {
|
||||||
|
url = login.InviteUserLink(http_utils.DomainContext(ctx).Origin(), user.ID, user.PreferredLoginName, code, user.ResourceOwner, authRequestID)
|
||||||
|
} else {
|
||||||
|
var buf strings.Builder
|
||||||
|
if err := domain.RenderConfirmURLTemplate(&buf, urlTmpl, user.ID, code, user.ResourceOwner); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
url = buf.String()
|
||||||
|
}
|
||||||
|
args := make(map[string]interface{})
|
||||||
|
args["Code"] = code
|
||||||
|
args["ApplicationName"] = applicationName
|
||||||
|
return notify(url, args, domain.InviteUserMessageType, true)
|
||||||
|
}
|
@ -33,6 +33,7 @@ type MessageTexts struct {
|
|||||||
DomainClaimed MessageText
|
DomainClaimed MessageText
|
||||||
PasswordlessRegistration MessageText
|
PasswordlessRegistration MessageText
|
||||||
PasswordChange MessageText
|
PasswordChange MessageText
|
||||||
|
InviteUser MessageText
|
||||||
}
|
}
|
||||||
|
|
||||||
type MessageText struct {
|
type MessageText struct {
|
||||||
@ -346,6 +347,8 @@ func (m *MessageTexts) GetMessageTextByType(msgType string) *MessageText {
|
|||||||
return &m.PasswordlessRegistration
|
return &m.PasswordlessRegistration
|
||||||
case domain.PasswordChangeMessageType:
|
case domain.PasswordChangeMessageType:
|
||||||
return &m.PasswordChange
|
return &m.PasswordChange
|
||||||
|
case domain.InviteUserMessageType:
|
||||||
|
return &m.InviteUser
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -272,7 +272,8 @@ func isMessageTemplate(template string) bool {
|
|||||||
template == domain.VerifyEmailOTPMessageType ||
|
template == domain.VerifyEmailOTPMessageType ||
|
||||||
template == domain.DomainClaimedMessageType ||
|
template == domain.DomainClaimedMessageType ||
|
||||||
template == domain.PasswordlessRegistrationMessageType ||
|
template == domain.PasswordlessRegistrationMessageType ||
|
||||||
template == domain.PasswordChangeMessageType
|
template == domain.PasswordChangeMessageType ||
|
||||||
|
template == domain.InviteUserMessageType
|
||||||
}
|
}
|
||||||
func isTitle(key string) bool {
|
func isTitle(key string) bool {
|
||||||
return key == domain.MessageTitle
|
return key == domain.MessageTitle
|
||||||
|
@ -137,4 +137,8 @@ func init() {
|
|||||||
eventstore.RegisterFilterEventMapper(AggregateType, MachineSecretCheckSucceededType, MachineSecretCheckSucceededEventMapper)
|
eventstore.RegisterFilterEventMapper(AggregateType, MachineSecretCheckSucceededType, MachineSecretCheckSucceededEventMapper)
|
||||||
eventstore.RegisterFilterEventMapper(AggregateType, MachineSecretCheckFailedType, MachineSecretCheckFailedEventMapper)
|
eventstore.RegisterFilterEventMapper(AggregateType, MachineSecretCheckFailedType, MachineSecretCheckFailedEventMapper)
|
||||||
eventstore.RegisterFilterEventMapper(AggregateType, MachineSecretHashUpdatedType, eventstore.GenericEventMapper[MachineSecretHashUpdatedEvent])
|
eventstore.RegisterFilterEventMapper(AggregateType, MachineSecretHashUpdatedType, eventstore.GenericEventMapper[MachineSecretHashUpdatedEvent])
|
||||||
|
eventstore.RegisterFilterEventMapper(AggregateType, HumanInviteCodeAddedType, eventstore.GenericEventMapper[HumanInviteCodeAddedEvent])
|
||||||
|
eventstore.RegisterFilterEventMapper(AggregateType, HumanInviteCodeSentType, eventstore.GenericEventMapper[HumanInviteCodeSentEvent])
|
||||||
|
eventstore.RegisterFilterEventMapper(AggregateType, HumanInviteCheckSucceededType, eventstore.GenericEventMapper[HumanInviteCheckSucceededEvent])
|
||||||
|
eventstore.RegisterFilterEventMapper(AggregateType, HumanInviteCheckFailedType, eventstore.GenericEventMapper[HumanInviteCheckFailedEvent])
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,10 @@ const (
|
|||||||
HumanInitialCodeSentType = humanEventPrefix + "initialization.code.sent"
|
HumanInitialCodeSentType = humanEventPrefix + "initialization.code.sent"
|
||||||
HumanInitializedCheckSucceededType = humanEventPrefix + "initialization.check.succeeded"
|
HumanInitializedCheckSucceededType = humanEventPrefix + "initialization.check.succeeded"
|
||||||
HumanInitializedCheckFailedType = humanEventPrefix + "initialization.check.failed"
|
HumanInitializedCheckFailedType = humanEventPrefix + "initialization.check.failed"
|
||||||
|
HumanInviteCodeAddedType = humanEventPrefix + "invite.code.added"
|
||||||
|
HumanInviteCodeSentType = humanEventPrefix + "invite.code.sent"
|
||||||
|
HumanInviteCheckSucceededType = humanEventPrefix + "invite.check.succeeded"
|
||||||
|
HumanInviteCheckFailedType = humanEventPrefix + "invite.check.failed"
|
||||||
HumanSignedOutType = humanEventPrefix + "signed.out"
|
HumanSignedOutType = humanEventPrefix + "signed.out"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -379,6 +383,137 @@ func HumanInitializedCheckFailedEventMapper(event eventstore.Event) (eventstore.
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HumanInviteCodeAddedEvent struct {
|
||||||
|
*eventstore.BaseEvent `json:"-"`
|
||||||
|
Code *crypto.CryptoValue `json:"code,omitempty"`
|
||||||
|
Expiry time.Duration `json:"expiry,omitempty"`
|
||||||
|
TriggeredAtOrigin string `json:"triggerOrigin,omitempty"`
|
||||||
|
URLTemplate string `json:"urlTemplate,omitempty"`
|
||||||
|
CodeReturned bool `json:"codeReturned,omitempty"`
|
||||||
|
ApplicationName string `json:"applicationName,omitempty"`
|
||||||
|
AuthRequestID string `json:"authRequestID,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HumanInviteCodeAddedEvent) SetBaseEvent(b *eventstore.BaseEvent) {
|
||||||
|
e.BaseEvent = b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HumanInviteCodeAddedEvent) Payload() interface{} {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HumanInviteCodeAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HumanInviteCodeAddedEvent) TriggerOrigin() string {
|
||||||
|
return e.TriggeredAtOrigin
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHumanInviteCodeAddedEvent(
|
||||||
|
ctx context.Context,
|
||||||
|
aggregate *eventstore.Aggregate,
|
||||||
|
code *crypto.CryptoValue,
|
||||||
|
expiry time.Duration,
|
||||||
|
urlTemplate string,
|
||||||
|
codeReturned bool,
|
||||||
|
applicationName string,
|
||||||
|
authRequestID string,
|
||||||
|
) *HumanInviteCodeAddedEvent {
|
||||||
|
return &HumanInviteCodeAddedEvent{
|
||||||
|
BaseEvent: eventstore.NewBaseEventForPush(
|
||||||
|
ctx,
|
||||||
|
aggregate,
|
||||||
|
HumanInviteCodeAddedType,
|
||||||
|
),
|
||||||
|
Code: code,
|
||||||
|
Expiry: expiry,
|
||||||
|
TriggeredAtOrigin: http.DomainContext(ctx).Origin(),
|
||||||
|
URLTemplate: urlTemplate,
|
||||||
|
CodeReturned: codeReturned,
|
||||||
|
ApplicationName: applicationName,
|
||||||
|
AuthRequestID: authRequestID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type HumanInviteCodeSentEvent struct {
|
||||||
|
*eventstore.BaseEvent `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HumanInviteCodeSentEvent) SetBaseEvent(b *eventstore.BaseEvent) {
|
||||||
|
e.BaseEvent = b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HumanInviteCodeSentEvent) Payload() interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HumanInviteCodeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHumanInviteCodeSentEvent(ctx context.Context, aggregate *eventstore.Aggregate) *HumanInviteCodeSentEvent {
|
||||||
|
return &HumanInviteCodeSentEvent{
|
||||||
|
BaseEvent: eventstore.NewBaseEventForPush(
|
||||||
|
ctx,
|
||||||
|
aggregate,
|
||||||
|
HumanInviteCodeSentType,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type HumanInviteCheckSucceededEvent struct {
|
||||||
|
*eventstore.BaseEvent `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HumanInviteCheckSucceededEvent) SetBaseEvent(b *eventstore.BaseEvent) {
|
||||||
|
e.BaseEvent = b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HumanInviteCheckSucceededEvent) Payload() interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HumanInviteCheckSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHumanInviteCheckSucceededEvent(ctx context.Context, aggregate *eventstore.Aggregate) *HumanInviteCheckSucceededEvent {
|
||||||
|
return &HumanInviteCheckSucceededEvent{
|
||||||
|
BaseEvent: eventstore.NewBaseEventForPush(
|
||||||
|
ctx,
|
||||||
|
aggregate,
|
||||||
|
HumanInviteCheckSucceededType,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type HumanInviteCheckFailedEvent struct {
|
||||||
|
*eventstore.BaseEvent `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HumanInviteCheckFailedEvent) SetBaseEvent(b *eventstore.BaseEvent) {
|
||||||
|
e.BaseEvent = b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HumanInviteCheckFailedEvent) Payload() interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HumanInviteCheckFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHumanInviteCheckFailedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *HumanInviteCheckFailedEvent {
|
||||||
|
return &HumanInviteCheckFailedEvent{
|
||||||
|
BaseEvent: eventstore.NewBaseEventForPush(
|
||||||
|
ctx,
|
||||||
|
aggregate,
|
||||||
|
HumanInviteCheckFailedType,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type HumanSignedOutEvent struct {
|
type HumanSignedOutEvent struct {
|
||||||
eventstore.BaseEvent `json:"-"`
|
eventstore.BaseEvent `json:"-"`
|
||||||
|
|
||||||
|
@ -704,6 +704,13 @@ EventTypes:
|
|||||||
check:
|
check:
|
||||||
succeeded: Проверката за инициализация е успешна
|
succeeded: Проверката за инициализация е успешна
|
||||||
failed: Проверката на инициализацията е неуспешна
|
failed: Проверката на инициализацията е неуспешна
|
||||||
|
invite:
|
||||||
|
code:
|
||||||
|
added: Генериран е код за покана
|
||||||
|
sent: Изпратен е код за покана
|
||||||
|
check:
|
||||||
|
succeeded: Проверката на поканата е успешна
|
||||||
|
failed: Проверката на поканата е неуспешна
|
||||||
username:
|
username:
|
||||||
reserved: Потребителското име е запазено
|
reserved: Потребителското име е запазено
|
||||||
released: Потребителското име е освободено
|
released: Потребителското име е освободено
|
||||||
|
@ -685,6 +685,13 @@ EventTypes:
|
|||||||
check:
|
check:
|
||||||
succeeded: Kontrola inicializace byla úspěšná
|
succeeded: Kontrola inicializace byla úspěšná
|
||||||
failed: Kontrola inicializace selhala
|
failed: Kontrola inicializace selhala
|
||||||
|
invite:
|
||||||
|
code:
|
||||||
|
added: Vygenerován pozvánkový kód
|
||||||
|
sent: Pozvánkový kód byl odeslán
|
||||||
|
check:
|
||||||
|
succeeded: Kontrola pozvánky byla úspěšná
|
||||||
|
failed: Kontrola pozvánky selhala
|
||||||
username:
|
username:
|
||||||
reserved: Uživatelské jméno rezervováno
|
reserved: Uživatelské jméno rezervováno
|
||||||
released: Uživatelské jméno uvolněno
|
released: Uživatelské jméno uvolněno
|
||||||
|
@ -687,6 +687,13 @@ EventTypes:
|
|||||||
check:
|
check:
|
||||||
succeeded: Benutzerinitialisierung erfolgreich
|
succeeded: Benutzerinitialisierung erfolgreich
|
||||||
failed: Benutzerinitialisierung fehlgeschlagen
|
failed: Benutzerinitialisierung fehlgeschlagen
|
||||||
|
invite:
|
||||||
|
code:
|
||||||
|
added: Einladungscode generiert
|
||||||
|
sent: Einladungscode gesendet
|
||||||
|
check:
|
||||||
|
succeeded: Einladungsprüfung erfolgreich
|
||||||
|
failed: Einladungsprüfung fehlgeschlagen
|
||||||
username:
|
username:
|
||||||
reserved: Benutzername reserviert
|
reserved: Benutzername reserviert
|
||||||
released: Benutzername freigegeben
|
released: Benutzername freigegeben
|
||||||
|
@ -687,6 +687,13 @@ EventTypes:
|
|||||||
check:
|
check:
|
||||||
succeeded: Initialization check succeeded
|
succeeded: Initialization check succeeded
|
||||||
failed: Initialization check failed
|
failed: Initialization check failed
|
||||||
|
invite:
|
||||||
|
code:
|
||||||
|
added: Invitation code generated
|
||||||
|
sent: Invitation code sent
|
||||||
|
check:
|
||||||
|
succeeded: Invitation check succeeded
|
||||||
|
failed: Invitation check failed
|
||||||
username:
|
username:
|
||||||
reserved: Username reserved
|
reserved: Username reserved
|
||||||
released: Username released
|
released: Username released
|
||||||
|
@ -687,6 +687,13 @@ EventTypes:
|
|||||||
check:
|
check:
|
||||||
succeeded: Comprobación exitosa de la inicialización
|
succeeded: Comprobación exitosa de la inicialización
|
||||||
failed: Fallo en la comprobación de la inicialización
|
failed: Fallo en la comprobación de la inicialización
|
||||||
|
invite:
|
||||||
|
code:
|
||||||
|
added: Código de invitación generado
|
||||||
|
sent: Código de invitación enviado
|
||||||
|
check:
|
||||||
|
succeeded: Comprobación de invitación correcta
|
||||||
|
failed: Comprobación de invitación fallida
|
||||||
username:
|
username:
|
||||||
reserved: Nombre de usuario reservado
|
reserved: Nombre de usuario reservado
|
||||||
released: Nombre de usuario liberado
|
released: Nombre de usuario liberado
|
||||||
|
@ -685,6 +685,13 @@ EventTypes:
|
|||||||
check:
|
check:
|
||||||
succeeded: Vérification de l'initialisation réussie
|
succeeded: Vérification de l'initialisation réussie
|
||||||
failed: La vérification de l'initialisation a échoué
|
failed: La vérification de l'initialisation a échoué
|
||||||
|
invite:
|
||||||
|
code:
|
||||||
|
added: Code d'invitation généré
|
||||||
|
sent: Code d'invitation envoyé
|
||||||
|
check:
|
||||||
|
succeeded: Vérification de l'invitation réussie
|
||||||
|
failed: Vérification de l'invitation échouée
|
||||||
username:
|
username:
|
||||||
reserved: Nom d'utilisateur réservé
|
reserved: Nom d'utilisateur réservé
|
||||||
released: Nom d'utilisateur libéré
|
released: Nom d'utilisateur libéré
|
||||||
|
@ -680,6 +680,13 @@ EventTypes:
|
|||||||
check:
|
check:
|
||||||
succeeded: Pemeriksaan inisialisasi berhasil
|
succeeded: Pemeriksaan inisialisasi berhasil
|
||||||
failed: Pemeriksaan inisialisasi gagal
|
failed: Pemeriksaan inisialisasi gagal
|
||||||
|
invite:
|
||||||
|
code:
|
||||||
|
added: Kode undangan dihasilkan
|
||||||
|
sent: Kode undangan dikirim
|
||||||
|
check:
|
||||||
|
succeeded: Pemeriksaan undangan berhasil
|
||||||
|
failed: Pemeriksaan undangan gagal
|
||||||
username:
|
username:
|
||||||
reserved: Nama pengguna dicadangkan
|
reserved: Nama pengguna dicadangkan
|
||||||
released: Nama pengguna dirilis
|
released: Nama pengguna dirilis
|
||||||
|
@ -686,6 +686,13 @@ EventTypes:
|
|||||||
check:
|
check:
|
||||||
succeeded: Controllo dell'inizializzazione riuscito
|
succeeded: Controllo dell'inizializzazione riuscito
|
||||||
failed: Controllo dell'inizializzazione fallito
|
failed: Controllo dell'inizializzazione fallito
|
||||||
|
invite:
|
||||||
|
code:
|
||||||
|
added: Codice invito generato
|
||||||
|
sent: Codice invito inviato
|
||||||
|
check:
|
||||||
|
succeeded: Controllo invito riuscito
|
||||||
|
failed: Controllo invito fallito
|
||||||
username:
|
username:
|
||||||
reserved: Nome utente riservato
|
reserved: Nome utente riservato
|
||||||
released: Nome utente rilasciato
|
released: Nome utente rilasciato
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user