mirror of
https://github.com/zitadel/zitadel.git
synced 2025-03-01 04:07:23 +00:00
feat: add default redirect uri and handling of unknown usernames (#3616)
* feat: add possibility to ignore username errors on first login screen * console changes * fix: handling of unknown usernames (#3445) * fix: handling of unknown usernames * fix: handle HideLoginNameSuffix on unknown users * feat: add default redirect uri on login policy (#3607) * feat: add default redirect uri on login policy * fix tests * feat: Console login policy default redirect (#3613) * console default redirect * placeholder * validate default redirect uri * allow empty default redirect uri Co-authored-by: Max Peintner <max@caos.ch> * remove wonrgly cherry picked migration Co-authored-by: Max Peintner <max@caos.ch>
This commit is contained in:
parent
f1fa74a2c0
commit
411d7c6c5c
@ -247,7 +247,9 @@ DefaultInstance:
|
|||||||
AllowExternalIDP: true
|
AllowExternalIDP: true
|
||||||
ForceMFA: false
|
ForceMFA: false
|
||||||
HidePasswordReset: false
|
HidePasswordReset: false
|
||||||
|
IgnoreUnknownUsernames: false
|
||||||
PasswordlessType: 1 #1: allowed 0: not allowed
|
PasswordlessType: 1 #1: allowed 0: not allowed
|
||||||
|
DefaultRedirectURI: #empty because we use the Console UI
|
||||||
PasswordCheckLifetime: 240h #10d
|
PasswordCheckLifetime: 240h #10d
|
||||||
ExternalLoginCheckLifetime: 240h #10d
|
ExternalLoginCheckLifetime: 240h #10d
|
||||||
MfaInitSkipLifetime: 720h #30d
|
MfaInitSkipLifetime: 720h #30d
|
||||||
|
@ -129,6 +129,19 @@
|
|||||||
</cnsl-info-section>
|
</cnsl-info-section>
|
||||||
</ng-template> -->
|
</ng-template> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="login-policy-row">
|
||||||
|
<mat-slide-toggle class="login-policy-toggle" color="primary" ngDefaultControl [(ngModel)]="loginData.ignoreUnknownUsernames">
|
||||||
|
{{ 'POLICY.DATA.IGNOREUNKNOWNUSERNAMES' | translate }}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="login-policy-row">
|
||||||
|
<cnsl-form-field class="form-field" label="Access Code" required="true">
|
||||||
|
<cnsl-label>{{ 'POLICY.DATA.DEFAULTREDIRECTURI' | translate }}</cnsl-label>
|
||||||
|
<input cnslInput placeholder="https://" [(ngModel)]="loginData.defaultRedirectUri" />
|
||||||
|
</cnsl-form-field>
|
||||||
|
</div>
|
||||||
</cnsl-card>
|
</cnsl-card>
|
||||||
|
|
||||||
<div class="login-policy-btn-container">
|
<div class="login-policy-btn-container">
|
||||||
|
@ -88,10 +88,12 @@ export class LoginPolicyComponent implements OnInit {
|
|||||||
mgmtreq.setForceMfa(this.loginData.forceMfa);
|
mgmtreq.setForceMfa(this.loginData.forceMfa);
|
||||||
mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
|
mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||||
mgmtreq.setHidePasswordReset(this.loginData.hidePasswordReset);
|
mgmtreq.setHidePasswordReset(this.loginData.hidePasswordReset);
|
||||||
|
mgmtreq.setIgnoreUnknownUsernames(this.loginData.ignoreUnknownUsernames);
|
||||||
|
mgmtreq.setDefaultRedirectUri(this.loginData.defaultRedirectUri);
|
||||||
|
|
||||||
// if(this.loginData.passwordCheckLifetime) {
|
// if(this.loginData.passwordCheckLifetime) {
|
||||||
// mgmtreq.setPasswordCheckLifetime(this.loginData.passwordCheckLifetime);
|
// mgmtreq.setPasswordCheckLifetime(this.loginData.passwordCheckLifetime);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if ((this.loginData as LoginPolicy.AsObject).isDefault) {
|
if ((this.loginData as LoginPolicy.AsObject).isDefault) {
|
||||||
return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq);
|
return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq);
|
||||||
} else {
|
} else {
|
||||||
@ -105,6 +107,8 @@ export class LoginPolicyComponent implements OnInit {
|
|||||||
adminreq.setForceMfa(this.loginData.forceMfa);
|
adminreq.setForceMfa(this.loginData.forceMfa);
|
||||||
adminreq.setPasswordlessType(this.loginData.passwordlessType);
|
adminreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||||
adminreq.setHidePasswordReset(this.loginData.hidePasswordReset);
|
adminreq.setHidePasswordReset(this.loginData.hidePasswordReset);
|
||||||
|
adminreq.setIgnoreUnknownUsernames(this.loginData.ignoreUnknownUsernames);
|
||||||
|
adminreq.setDefaultRedirectUri(this.loginData.defaultRedirectUri);
|
||||||
// adminreq.setPasswordCheckLifetime(this.loginData.passwordCheckLifetime);
|
// adminreq.setPasswordCheckLifetime(this.loginData.passwordCheckLifetime);
|
||||||
|
|
||||||
return (this.service as AdminService).updateLoginPolicy(adminreq);
|
return (this.service as AdminService).updateLoginPolicy(adminreq);
|
||||||
|
@ -1092,6 +1092,10 @@
|
|||||||
"HIDEPASSWORDRESET": "Passwort vergessen ausblenden",
|
"HIDEPASSWORDRESET": "Passwort vergessen ausblenden",
|
||||||
"HIDEPASSWORDRESET_DESC": "Ist die Option gewählt, ist es nicht möglich im Login das Passwort zurück zusetzen via Passwort vergessen Link.",
|
"HIDEPASSWORDRESET_DESC": "Ist die Option gewählt, ist es nicht möglich im Login das Passwort zurück zusetzen via Passwort vergessen Link.",
|
||||||
"HIDELOGINNAMESUFFIX": "Loginname Suffix ausblenden",
|
"HIDELOGINNAMESUFFIX": "Loginname Suffix ausblenden",
|
||||||
|
"IGNOREUNKNOWNUSERNAMES": "Unbekannte Usernamen ignorieren",
|
||||||
|
"IGNOREUNKNOWNUSERNAMES_DESC": "Ist die Option gewählt, wird der Passwort Schritt im Login auch angezeigt wenn der User nicht gefunden wurde. Dem Benutzer wird auf bei der Passwortprüfung nicht angezeigt ob der Username oder das Passwort falsch war.",
|
||||||
|
"DEFAULTREDIRECTURI": "Default Redirect URI",
|
||||||
|
"DEFAULTREDIRECTURI_DESC": "Definiert, wohin der Benutzer umgeleitet wird, wenn die Anmeldung ohne App-Kontext gestartet wurde (z. B. von Mail)",
|
||||||
"ERRORMSGPOPUP": "Fehler als Dialog Fenster",
|
"ERRORMSGPOPUP": "Fehler als Dialog Fenster",
|
||||||
"DISABLEWATERMARK": "Wasserzeichen ausblenden"
|
"DISABLEWATERMARK": "Wasserzeichen ausblenden"
|
||||||
},
|
},
|
||||||
|
@ -1092,6 +1092,10 @@
|
|||||||
"HIDEPASSWORDRESET": "Hide Password reset",
|
"HIDEPASSWORDRESET": "Hide Password reset",
|
||||||
"HIDEPASSWORDRESET_DESC": "If the option is selected, the user can't reset his password in the login process.",
|
"HIDEPASSWORDRESET_DESC": "If the option is selected, the user can't reset his password in the login process.",
|
||||||
"HIDELOGINNAMESUFFIX": "Hide Loginname suffix",
|
"HIDELOGINNAMESUFFIX": "Hide Loginname suffix",
|
||||||
|
"IGNOREUNKNOWNUSERNAMES": "Ignore unknown usernames",
|
||||||
|
"IGNOREUNKNOWNUSERNAMES_DESC": "If the option is selected, the password screen will be displayed in the login process even if the user was not found. The error on the password check will not disclose if the username or password was wrong.",
|
||||||
|
"DEFAULTREDIRECTURI": "Default Redirect URI",
|
||||||
|
"DEFAULTREDIRECTURI_DESC": "Defines where the user will be redirected to if the login has started without an app context (e.g. from mail)",
|
||||||
"ERRORMSGPOPUP": "Show Error in Dialog",
|
"ERRORMSGPOPUP": "Show Error in Dialog",
|
||||||
"DISABLEWATERMARK": "Hide Watermark"
|
"DISABLEWATERMARK": "Hide Watermark"
|
||||||
},
|
},
|
||||||
|
@ -1092,6 +1092,10 @@
|
|||||||
"HIDEPASSWORDRESET": "Nascondi ripristino della password",
|
"HIDEPASSWORDRESET": "Nascondi ripristino della password",
|
||||||
"HIDEPASSWORDRESET_DESC": "Se l'opzione \u00e8 selezionata, l'utente non pu\u00f2 resettare la sua password nel interfaccia login.",
|
"HIDEPASSWORDRESET_DESC": "Se l'opzione \u00e8 selezionata, l'utente non pu\u00f2 resettare la sua password nel interfaccia login.",
|
||||||
"HIDELOGINNAMESUFFIX": "Nascondi il suffisso del nome utente",
|
"HIDELOGINNAMESUFFIX": "Nascondi il suffisso del nome utente",
|
||||||
|
"IGNOREUNKNOWNUSERNAMES": "Ignora un nome utente sconosciuto",
|
||||||
|
"IGNOREUNKNOWNUSERNAMES_DESC": "Se l'opzione \u00e8 selezionata, l'inserimento della password viene mostrato anche se nessun utente è stato trovato. Nota che dopo il controllo della password, non viene mostrato se il nome utente o la password erano errati.",
|
||||||
|
"DEFAULTREDIRECTURI": "Default Redirect URI",
|
||||||
|
"DEFAULTREDIRECTURI_DESC": "Definisce dove verrà reindirizzato l'utente se l'accesso è stato avviato senza un contesto dell'app (ad es. dall' email)",
|
||||||
"ERRORMSGPOPUP": "Mostra l'errore nella finestra di dialogo",
|
"ERRORMSGPOPUP": "Mostra l'errore nella finestra di dialogo",
|
||||||
"DISABLEWATERMARK": "Nascondi la filigrana"
|
"DISABLEWATERMARK": "Nascondi la filigrana"
|
||||||
},
|
},
|
||||||
|
@ -3700,6 +3700,8 @@ This is an empty request
|
|||||||
| force_mfa | bool | - | |
|
| force_mfa | bool | - | |
|
||||||
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
|
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
|
||||||
| hide_password_reset | bool | - | |
|
| hide_password_reset | bool | - | |
|
||||||
|
| ignore_unknown_usernames | bool | - | |
|
||||||
|
| default_redirect_uri | string | - | |
|
||||||
| password_check_lifetime | google.protobuf.Duration | - | |
|
| password_check_lifetime | google.protobuf.Duration | - | |
|
||||||
| external_login_check_lifetime | google.protobuf.Duration | - | |
|
| external_login_check_lifetime | google.protobuf.Duration | - | |
|
||||||
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
||||||
|
@ -3039,6 +3039,8 @@ This is an empty request
|
|||||||
| force_mfa | bool | - | |
|
| force_mfa | bool | - | |
|
||||||
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
|
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
|
||||||
| hide_password_reset | bool | - | |
|
| hide_password_reset | bool | - | |
|
||||||
|
| ignore_unknown_usernames | bool | - | |
|
||||||
|
| default_redirect_uri | string | - | |
|
||||||
| password_check_lifetime | google.protobuf.Duration | - | |
|
| password_check_lifetime | google.protobuf.Duration | - | |
|
||||||
| external_login_check_lifetime | google.protobuf.Duration | - | |
|
| external_login_check_lifetime | google.protobuf.Duration | - | |
|
||||||
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
||||||
@ -7760,6 +7762,8 @@ This is an empty request
|
|||||||
| force_mfa | bool | - | |
|
| force_mfa | bool | - | |
|
||||||
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
|
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
|
||||||
| hide_password_reset | bool | - | |
|
| hide_password_reset | bool | - | |
|
||||||
|
| ignore_unknown_usernames | bool | - | |
|
||||||
|
| default_redirect_uri | string | - | |
|
||||||
| password_check_lifetime | google.protobuf.Duration | - | |
|
| password_check_lifetime | google.protobuf.Duration | - | |
|
||||||
| external_login_check_lifetime | google.protobuf.Duration | - | |
|
| external_login_check_lifetime | google.protobuf.Duration | - | |
|
||||||
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
||||||
|
@ -77,6 +77,8 @@ title: zitadel/policy.proto
|
|||||||
| passwordless_type | PasswordlessType | - | |
|
| passwordless_type | PasswordlessType | - | |
|
||||||
| is_default | bool | - | |
|
| is_default | bool | - | |
|
||||||
| hide_password_reset | bool | - | |
|
| hide_password_reset | bool | - | |
|
||||||
|
| ignore_unknown_usernames | bool | - | |
|
||||||
|
| default_redirect_uri | string | - | |
|
||||||
| password_check_lifetime | google.protobuf.Duration | - | |
|
| password_check_lifetime | google.protobuf.Duration | - | |
|
||||||
| external_login_check_lifetime | google.protobuf.Duration | - | |
|
| external_login_check_lifetime | google.protobuf.Duration | - | |
|
||||||
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
||||||
|
@ -16,6 +16,8 @@ func updateLoginPolicyToDomain(p *admin_pb.UpdateLoginPolicyRequest) *domain.Log
|
|||||||
ForceMFA: p.ForceMfa,
|
ForceMFA: p.ForceMfa,
|
||||||
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
|
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
|
||||||
HidePasswordReset: p.HidePasswordReset,
|
HidePasswordReset: p.HidePasswordReset,
|
||||||
|
IgnoreUnknownUsernames: p.IgnoreUnknownUsernames,
|
||||||
|
DefaultRedirectURI: p.DefaultRedirectUri,
|
||||||
PasswordCheckLifetime: p.PasswordCheckLifetime.AsDuration(),
|
PasswordCheckLifetime: p.PasswordCheckLifetime.AsDuration(),
|
||||||
ExternalLoginCheckLifetime: p.ExternalLoginCheckLifetime.AsDuration(),
|
ExternalLoginCheckLifetime: p.ExternalLoginCheckLifetime.AsDuration(),
|
||||||
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
|
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
|
||||||
|
@ -16,6 +16,8 @@ func addLoginPolicyToDomain(p *mgmt_pb.AddCustomLoginPolicyRequest) *domain.Logi
|
|||||||
ForceMFA: p.ForceMfa,
|
ForceMFA: p.ForceMfa,
|
||||||
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
|
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
|
||||||
HidePasswordReset: p.HidePasswordReset,
|
HidePasswordReset: p.HidePasswordReset,
|
||||||
|
IgnoreUnknownUsernames: p.IgnoreUnknownUsernames,
|
||||||
|
DefaultRedirectURI: p.DefaultRedirectUri,
|
||||||
PasswordCheckLifetime: p.PasswordCheckLifetime.AsDuration(),
|
PasswordCheckLifetime: p.PasswordCheckLifetime.AsDuration(),
|
||||||
ExternalLoginCheckLifetime: p.ExternalLoginCheckLifetime.AsDuration(),
|
ExternalLoginCheckLifetime: p.ExternalLoginCheckLifetime.AsDuration(),
|
||||||
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
|
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
|
||||||
@ -32,6 +34,8 @@ func updateLoginPolicyToDomain(p *mgmt_pb.UpdateCustomLoginPolicyRequest) *domai
|
|||||||
ForceMFA: p.ForceMfa,
|
ForceMFA: p.ForceMfa,
|
||||||
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
|
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
|
||||||
HidePasswordReset: p.HidePasswordReset,
|
HidePasswordReset: p.HidePasswordReset,
|
||||||
|
IgnoreUnknownUsernames: p.IgnoreUnknownUsernames,
|
||||||
|
DefaultRedirectURI: p.DefaultRedirectUri,
|
||||||
PasswordCheckLifetime: p.PasswordCheckLifetime.AsDuration(),
|
PasswordCheckLifetime: p.PasswordCheckLifetime.AsDuration(),
|
||||||
ExternalLoginCheckLifetime: p.ExternalLoginCheckLifetime.AsDuration(),
|
ExternalLoginCheckLifetime: p.ExternalLoginCheckLifetime.AsDuration(),
|
||||||
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
|
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package policy
|
package policy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/query"
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/object"
|
"github.com/zitadel/zitadel/pkg/grpc/object"
|
||||||
policy_pb "github.com/zitadel/zitadel/pkg/grpc/policy"
|
policy_pb "github.com/zitadel/zitadel/pkg/grpc/policy"
|
||||||
"google.golang.org/protobuf/types/known/durationpb"
|
|
||||||
timestamp_pb "google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ModelLoginPolicyToPb(policy *query.LoginPolicy) *policy_pb.LoginPolicy {
|
func ModelLoginPolicyToPb(policy *query.LoginPolicy) *policy_pb.LoginPolicy {
|
||||||
@ -18,6 +19,8 @@ func ModelLoginPolicyToPb(policy *query.LoginPolicy) *policy_pb.LoginPolicy {
|
|||||||
ForceMfa: policy.ForceMFA,
|
ForceMfa: policy.ForceMFA,
|
||||||
PasswordlessType: ModelPasswordlessTypeToPb(policy.PasswordlessType),
|
PasswordlessType: ModelPasswordlessTypeToPb(policy.PasswordlessType),
|
||||||
HidePasswordReset: policy.HidePasswordReset,
|
HidePasswordReset: policy.HidePasswordReset,
|
||||||
|
IgnoreUnknownUsernames: policy.IgnoreUnknownUsernames,
|
||||||
|
DefaultRedirectUri: policy.DefaultRedirectURI,
|
||||||
PasswordCheckLifetime: durationpb.New(policy.PasswordCheckLifetime),
|
PasswordCheckLifetime: durationpb.New(policy.PasswordCheckLifetime),
|
||||||
ExternalLoginCheckLifetime: durationpb.New(policy.ExternalLoginCheckLifetime),
|
ExternalLoginCheckLifetime: durationpb.New(policy.ExternalLoginCheckLifetime),
|
||||||
MfaInitSkipLifetime: durationpb.New(policy.MFAInitSkipLifetime),
|
MfaInitSkipLifetime: durationpb.New(policy.MFAInitSkipLifetime),
|
||||||
@ -25,8 +28,8 @@ func ModelLoginPolicyToPb(policy *query.LoginPolicy) *policy_pb.LoginPolicy {
|
|||||||
MultiFactorCheckLifetime: durationpb.New(policy.MultiFactorCheckLifetime),
|
MultiFactorCheckLifetime: durationpb.New(policy.MultiFactorCheckLifetime),
|
||||||
Details: &object.ObjectDetails{
|
Details: &object.ObjectDetails{
|
||||||
Sequence: policy.Sequence,
|
Sequence: policy.Sequence,
|
||||||
CreationDate: timestamp_pb.New(policy.CreationDate),
|
CreationDate: timestamppb.New(policy.CreationDate),
|
||||||
ChangeDate: timestamp_pb.New(policy.ChangeDate),
|
ChangeDate: timestamppb.New(policy.ChangeDate),
|
||||||
ResourceOwner: policy.OrgID,
|
ResourceOwner: policy.OrgID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ func (l *Login) handleExternalLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if authReq == nil {
|
if authReq == nil {
|
||||||
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
l.defaultRedirect(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l.handleIDP(w, r, authReq, data.IDPConfigID)
|
l.handleIDP(w, r, authReq, data.IDPConfigID)
|
||||||
|
@ -58,7 +58,7 @@ func (l *Login) handleExternalRegister(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if authReq == nil {
|
if authReq == nil {
|
||||||
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
l.defaultRedirect(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
idpConfig, err := l.getIDPConfigByID(r, data.IDPConfigID)
|
idpConfig, err := l.getIDPConfigByID(r, data.IDPConfigID)
|
||||||
|
@ -39,8 +39,8 @@ type initPasswordData struct {
|
|||||||
HasSymbol string
|
HasSymbol string
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitPasswordLink(origin, userID, code string) string {
|
func InitPasswordLink(origin, userID, code, orgID string) string {
|
||||||
return fmt.Sprintf("%s%s?userID=%s&code=%s", externalLink(origin), EndpointInitPassword, userID, code)
|
return fmt.Sprintf("%s%s?userID=%s&code=%s&orgID=%s", externalLink(origin), EndpointInitPassword, userID, code, orgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) handleInitPassword(w http.ResponseWriter, r *http.Request) {
|
func (l *Login) handleInitPassword(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -42,8 +42,8 @@ type initUserData struct {
|
|||||||
HasSymbol string
|
HasSymbol string
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitUserLink(origin, userID, code string, passwordSet bool) string {
|
func InitUserLink(origin, userID, code, orgID string, passwordSet bool) string {
|
||||||
return fmt.Sprintf("%s%s?userID=%s&code=%s&passwordset=%t", externalLink(origin), EndpointInitUser, userID, code, passwordSet)
|
return fmt.Sprintf("%s%s?userID=%s&code=%s&passwordset=%t&orgID=%s", externalLink(origin), EndpointInitUser, userID, code, passwordSet, orgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) handleInitUser(w http.ResponseWriter, r *http.Request) {
|
func (l *Login) handleInitUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -120,7 +120,7 @@ func (l *Login) jwtExtractionUserNotFound(w http.ResponseWriter, r *http.Request
|
|||||||
l.renderError(w, r, authReq, err)
|
l.renderError(w, r, authReq, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resourceOwner := l.getOrgID(authReq)
|
resourceOwner := l.getOrgID(r, authReq)
|
||||||
orgIamPolicy, err := l.getOrgDomainPolicy(r, resourceOwner)
|
orgIamPolicy, err := l.getOrgDomainPolicy(r, resourceOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderError(w, r, authReq, err)
|
l.renderError(w, r, authReq, err)
|
||||||
|
@ -3,6 +3,8 @@ package login
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/errors"
|
"github.com/zitadel/zitadel/internal/errors"
|
||||||
@ -10,6 +12,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
tmplLogin = "login"
|
tmplLogin = "login"
|
||||||
|
queryOrgID = "orgID"
|
||||||
)
|
)
|
||||||
|
|
||||||
type loginData struct {
|
type loginData struct {
|
||||||
@ -17,8 +20,8 @@ type loginData struct {
|
|||||||
Register bool `schema:"register"`
|
Register bool `schema:"register"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoginLink(origin string) string {
|
func LoginLink(origin, orgID string) string {
|
||||||
return externalLink(origin) + EndpointLogin
|
return externalLink(origin) + EndpointLogin + "?orgID=" + orgID
|
||||||
}
|
}
|
||||||
|
|
||||||
func externalLink(origin string) string {
|
func externalLink(origin string) string {
|
||||||
@ -32,12 +35,23 @@ func (l *Login) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if authReq == nil {
|
if authReq == nil {
|
||||||
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
l.defaultRedirect(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l.renderNextStep(w, r, authReq)
|
l.renderNextStep(w, r, authReq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Login) defaultRedirect(w http.ResponseWriter, r *http.Request) {
|
||||||
|
orgID := r.FormValue(queryOrgID)
|
||||||
|
policy, err := l.getLoginPolicy(r, orgID)
|
||||||
|
logging.OnError(err).WithField("orgID", orgID).Error("error loading login policy")
|
||||||
|
redirect := l.consolePath
|
||||||
|
if policy != nil && policy.DefaultRedirectURI != "" {
|
||||||
|
redirect = policy.DefaultRedirectURI
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, redirect, http.StatusFound)
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Login) handleLoginName(w http.ResponseWriter, r *http.Request) {
|
func (l *Login) handleLoginName(w http.ResponseWriter, r *http.Request) {
|
||||||
authReq, err := l.getAuthRequest(r)
|
authReq, err := l.getAuthRequest(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -27,8 +27,8 @@ type mailVerificationData struct {
|
|||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func MailVerificationLink(origin, userID, code string) string {
|
func MailVerificationLink(origin, userID, code, orgID string) string {
|
||||||
return fmt.Sprintf("%s%s?userID=%s&code=%s", externalLink(origin), EndpointMailVerification, userID, code)
|
return fmt.Sprintf("%s%s?userID=%s&code=%s&orgID=%s", externalLink(origin), EndpointMailVerification, userID, code, orgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) handleMailVerification(w http.ResponseWriter, r *http.Request) {
|
func (l *Login) handleMailVerification(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -40,6 +40,10 @@ func (l *Login) handlePasswordCheck(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
err = l.authRepo.VerifyPassword(setContext(r.Context(), authReq.UserOrgID), authReq.ID, authReq.UserID, authReq.UserOrgID, data.Password, authReq.AgentID, domain.BrowserInfoFromRequest(r))
|
err = l.authRepo.VerifyPassword(setContext(r.Context(), authReq.UserOrgID), authReq.ID, authReq.UserID, authReq.UserOrgID, data.Password, authReq.AgentID, domain.BrowserInfoFromRequest(r))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if authReq.LoginPolicy.IgnoreUnknownUsernames {
|
||||||
|
l.renderLogin(w, r, authReq, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
l.renderPassword(w, r, authReq, err)
|
l.renderPassword(w, r, authReq, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package login
|
package login
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
|
||||||
"github.com/zitadel/zitadel/internal/query"
|
|
||||||
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
|
"github.com/zitadel/zitadel/internal/errors"
|
||||||
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -29,6 +30,9 @@ func (l *Login) handlePasswordReset(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
passwordCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordResetCode, l.userCodeAlg)
|
passwordCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordResetCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if authReq.LoginPolicy.IgnoreUnknownUsernames && errors.IsNotFound(err) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
l.renderPasswordResetDone(w, r, authReq, err)
|
l.renderPasswordResetDone(w, r, authReq, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -21,3 +21,10 @@ func (l *Login) getOrgDomainPolicy(r *http.Request, orgID string) (*query.Domain
|
|||||||
func (l *Login) getIDPConfigByID(r *http.Request, idpConfigID string) (*iam_model.IDPConfigView, error) {
|
func (l *Login) getIDPConfigByID(r *http.Request, idpConfigID string) (*iam_model.IDPConfigView, error) {
|
||||||
return l.authRepo.GetIDPConfigByID(r.Context(), idpConfigID)
|
return l.authRepo.GetIDPConfigByID(r.Context(), idpConfigID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Login) getLoginPolicy(r *http.Request, orgID string) (*query.LoginPolicy, error) {
|
||||||
|
if orgID == "" {
|
||||||
|
return l.query.DefaultLoginPolicy(r.Context())
|
||||||
|
}
|
||||||
|
return l.query.LoginPolicyByID(r.Context(), orgID)
|
||||||
|
}
|
||||||
|
@ -90,7 +90,7 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if authRequest == nil {
|
if authRequest == nil {
|
||||||
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
l.defaultRedirect(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||||
|
@ -73,7 +73,7 @@ func (l *Login) handleRegisterOrgCheck(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if authRequest == nil {
|
if authRequest == nil {
|
||||||
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
l.defaultRedirect(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l.renderNextStep(w, r, authRequest)
|
l.renderNextStep(w, r, authRequest)
|
||||||
|
@ -342,8 +342,8 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title
|
|||||||
Theme: l.getTheme(r),
|
Theme: l.getTheme(r),
|
||||||
ThemeMode: l.getThemeMode(r),
|
ThemeMode: l.getThemeMode(r),
|
||||||
DarkMode: l.isDarkMode(r),
|
DarkMode: l.isDarkMode(r),
|
||||||
PrivateLabelingOrgID: l.getPrivateLabelingID(authz.GetInstance(r.Context()).InstanceID(), authReq),
|
PrivateLabelingOrgID: l.getPrivateLabelingID(r, authReq),
|
||||||
OrgID: l.getOrgID(authReq),
|
OrgID: l.getOrgID(r, authReq),
|
||||||
OrgName: l.getOrgName(authReq),
|
OrgName: l.getOrgName(authReq),
|
||||||
PrimaryDomain: l.getOrgPrimaryDomain(authReq),
|
PrimaryDomain: l.getOrgPrimaryDomain(authReq),
|
||||||
DisplayLoginNameSuffix: l.isDisplayLoginNameSuffix(authReq),
|
DisplayLoginNameSuffix: l.isDisplayLoginNameSuffix(authReq),
|
||||||
@ -361,6 +361,10 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title
|
|||||||
}
|
}
|
||||||
privacyPolicy = authReq.PrivacyPolicy
|
privacyPolicy = authReq.PrivacyPolicy
|
||||||
} else {
|
} else {
|
||||||
|
labelPolicy, _ := l.query.ActiveLabelPolicyByOrg(r.Context(), baseData.PrivateLabelingOrgID)
|
||||||
|
if labelPolicy != nil {
|
||||||
|
baseData.LabelPolicy = labelPolicy.ToDomain()
|
||||||
|
}
|
||||||
policy, err := l.query.DefaultPrivacyPolicy(r.Context())
|
policy, err := l.query.DefaultPrivacyPolicy(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return baseData
|
return baseData
|
||||||
@ -446,9 +450,9 @@ func (l *Login) isDarkMode(r *http.Request) bool {
|
|||||||
return strings.HasSuffix(cookie.Value, "dark")
|
return strings.HasSuffix(cookie.Value, "dark")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) getOrgID(authReq *domain.AuthRequest) string {
|
func (l *Login) getOrgID(r *http.Request, authReq *domain.AuthRequest) string {
|
||||||
if authReq == nil {
|
if authReq == nil {
|
||||||
return ""
|
return r.FormValue(queryOrgID)
|
||||||
}
|
}
|
||||||
if authReq.RequestedOrgID != "" {
|
if authReq.RequestedOrgID != "" {
|
||||||
return authReq.RequestedOrgID
|
return authReq.RequestedOrgID
|
||||||
@ -456,9 +460,12 @@ func (l *Login) getOrgID(authReq *domain.AuthRequest) string {
|
|||||||
return authReq.UserOrgID
|
return authReq.UserOrgID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) getPrivateLabelingID(instanceID string, authReq *domain.AuthRequest) string {
|
func (l *Login) getPrivateLabelingID(r *http.Request, authReq *domain.AuthRequest) string {
|
||||||
privateLabelingOrgID := instanceID
|
privateLabelingOrgID := authz.GetInstance(r.Context()).InstanceID()
|
||||||
if authReq == nil {
|
if authReq == nil {
|
||||||
|
if id := r.FormValue(queryOrgID); id != "" {
|
||||||
|
return id
|
||||||
|
}
|
||||||
return privateLabelingOrgID
|
return privateLabelingOrgID
|
||||||
}
|
}
|
||||||
if authReq.PrivateLabelingSetting != domain.PrivateLabelingSettingUnspecified {
|
if authReq.PrivateLabelingSetting != domain.PrivateLabelingSettingUnspecified {
|
||||||
|
@ -322,6 +322,8 @@ Errors:
|
|||||||
Empty: Passwort ist leer
|
Empty: Passwort ist leer
|
||||||
Invalid: Passwort ungültig
|
Invalid: Passwort ungültig
|
||||||
InvalidAndLocked: Password ist undgültig und Benutzer wurde gesperrt, melden Sie sich bei ihrem Administrator.
|
InvalidAndLocked: Password ist undgültig und Benutzer wurde gesperrt, melden Sie sich bei ihrem Administrator.
|
||||||
|
UsernameOrPassword:
|
||||||
|
Invalid: Username oder Passwort ist ungültig
|
||||||
PasswordComplexityPolicy:
|
PasswordComplexityPolicy:
|
||||||
NotFound: Passwort Policy konnte nicht gefunden werden
|
NotFound: Passwort Policy konnte nicht gefunden werden
|
||||||
MinLength: Passwort ist zu kurz
|
MinLength: Passwort ist zu kurz
|
||||||
|
@ -323,6 +323,8 @@ Errors:
|
|||||||
Empty: Password is empty
|
Empty: Password is empty
|
||||||
Invalid: Password is invalid
|
Invalid: Password is invalid
|
||||||
InvalidAndLocked: Password is invalid and user is locked, contact your administrator.
|
InvalidAndLocked: Password is invalid and user is locked, contact your administrator.
|
||||||
|
UsernameOrPassword:
|
||||||
|
Invalid: Username or Password is invalid
|
||||||
PasswordComplexityPolicy:
|
PasswordComplexityPolicy:
|
||||||
NotFound: Password policy not found
|
NotFound: Password policy not found
|
||||||
MinLength: Password is to short
|
MinLength: Password is to short
|
||||||
|
@ -323,6 +323,8 @@ Errors:
|
|||||||
Empty: La password è vuota
|
Empty: La password è vuota
|
||||||
Invalid: La password non è valida
|
Invalid: La password non è valida
|
||||||
InvalidAndLocked: La password non è valida e l'utente è bloccato, contatta il tuo amministratore.
|
InvalidAndLocked: La password non è valida e l'utente è bloccato, contatta il tuo amministratore.
|
||||||
|
UsernameOrPassword:
|
||||||
|
Invalid: Il nome utente o la password non sono validi
|
||||||
PasswordComplexityPolicy:
|
PasswordComplexityPolicy:
|
||||||
NotFound: Impostazioni della password non trovate
|
NotFound: Impostazioni della password non trovate
|
||||||
MinLength: La password è troppo corta
|
MinLength: La password è troppo corta
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||||
<input type="hidden" name="userID" value="{{ .UserID }}" />
|
<input type="hidden" name="userID" value="{{ .UserID }}" />
|
||||||
|
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||||
|
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
@ -12,11 +12,9 @@
|
|||||||
{{ .CSRF }}
|
{{ .CSRF }}
|
||||||
|
|
||||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||||
|
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||||
|
|
||||||
<div class="lgnactions">
|
<div class="lgn-actions">
|
||||||
<a class="lgn-stroked-button lgn-primary" href="{{ loginUrl }}">
|
|
||||||
{{t "InitPasswordDone.CancelButtonText"}}
|
|
||||||
</a>
|
|
||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
<button class="lgn-raised-button lgn-primary" type="submit">{{t "InitPasswordDone.NextButtonText"}}</button>
|
<button class="lgn-raised-button lgn-primary" type="submit">{{t "InitPasswordDone.NextButtonText"}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||||
<input type="hidden" name="userID" value="{{ .UserID }}" />
|
<input type="hidden" name="userID" value="{{ .UserID }}" />
|
||||||
<input type="hidden" name="passwordSet" value="{{ .PasswordSet }}" />
|
<input type="hidden" name="passwordSet" value="{{ .PasswordSet }}" />
|
||||||
|
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||||
|
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
{{ .CSRF }}
|
{{ .CSRF }}
|
||||||
|
|
||||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||||
|
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||||
|
|
||||||
<div class="lgn-actions lgn-reverse-order">
|
<div class="lgn-actions lgn-reverse-order">
|
||||||
<button class="lgn-raised-button lgn-primary" type="submit">{{t "InitUserDone.NextButtonText"}}</button>
|
<button class="lgn-raised-button lgn-primary" type="submit">{{t "InitUserDone.NextButtonText"}}</button>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||||
<input type="hidden" name="userID" value="{{ .UserID }}" />
|
<input type="hidden" name="userID" value="{{ .UserID }}" />
|
||||||
|
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||||
|
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<label class="lgn-label" for="code">{{t "EmailVerification.CodeLabel"}}</label>
|
<label class="lgn-label" for="code">{{t "EmailVerification.CodeLabel"}}</label>
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
{{ .CSRF }}
|
{{ .CSRF }}
|
||||||
|
|
||||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||||
|
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||||
|
|
||||||
<div class="lgn-actions">
|
<div class="lgn-actions">
|
||||||
<a class="lgn-stroked-button lgn-primary" href="{{ loginUrl }}">
|
<a class="lgn-stroked-button lgn-primary" href="{{ loginUrl }}">
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
{{ .CSRF }}
|
{{ .CSRF }}
|
||||||
|
|
||||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||||
|
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||||
|
|
||||||
<div class="lgn-actions">
|
<div class="lgn-actions">
|
||||||
{{if not .HideNextButton }}
|
{{if not .HideNextButton }}
|
||||||
|
@ -27,6 +27,8 @@ import (
|
|||||||
user_view_model "github.com/zitadel/zitadel/internal/user/repository/view/model"
|
user_view_model "github.com/zitadel/zitadel/internal/user/repository/view/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const unknownUserID = "UNKNOWN"
|
||||||
|
|
||||||
type AuthRequestRepo struct {
|
type AuthRequestRepo struct {
|
||||||
Command *command.Commands
|
Command *command.Commands
|
||||||
Query *query.Queries
|
Query *query.Queries
|
||||||
@ -284,7 +286,7 @@ func (repo *AuthRequestRepo) SelectUser(ctx context.Context, id, userID, userAge
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user, err := activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, repo.LockoutPolicyViewProvider, userID)
|
user, err := activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, repo.LockoutPolicyViewProvider, userID, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -304,13 +306,28 @@ func (repo *AuthRequestRepo) VerifyPassword(ctx context.Context, authReqID, user
|
|||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
request, err := repo.getAuthRequestEnsureUser(ctx, authReqID, userAgentID, userID)
|
request, err := repo.getAuthRequestEnsureUser(ctx, authReqID, userAgentID, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if isIgnoreUserNotFoundError(err, request) {
|
||||||
|
return errors.ThrowInvalidArgument(nil, "EVENT-SDe2f", "Errors.User.UsernameOrPassword.Invalid")
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
policy, err := repo.getLockoutPolicy(ctx, resourceOwner)
|
policy, err := repo.getLockoutPolicy(ctx, resourceOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return repo.Command.HumanCheckPassword(ctx, resourceOwner, userID, password, request.WithCurrentInfo(info), lockoutPolicyToDomain(policy))
|
err = repo.Command.HumanCheckPassword(ctx, resourceOwner, userID, password, request.WithCurrentInfo(info), lockoutPolicyToDomain(policy))
|
||||||
|
if isIgnoreUserInvalidPasswordError(err, request) {
|
||||||
|
return errors.ThrowInvalidArgument(nil, "EVENT-Jsf32", "Errors.User.UsernameOrPassword.Invalid")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIgnoreUserNotFoundError(err error, request *domain.AuthRequest) bool {
|
||||||
|
return request != nil && request.LoginPolicy != nil && request.LoginPolicy.IgnoreUnknownUsernames && errors.IsNotFound(err) && errors.Contains(err, "Errors.User.NotFound")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIgnoreUserInvalidPasswordError(err error, request *domain.AuthRequest) bool {
|
||||||
|
return request != nil && request.LoginPolicy != nil && request.LoginPolicy.IgnoreUnknownUsernames && errors.IsErrorInvalidArgument(err) && errors.Contains(err, "Errors.User.Password.Invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
func lockoutPolicyToDomain(policy *query.LockoutPolicy) *domain.LockoutPolicy {
|
func lockoutPolicyToDomain(policy *query.LockoutPolicy) *domain.LockoutPolicy {
|
||||||
@ -499,9 +516,9 @@ func (repo *AuthRequestRepo) getAuthRequestEnsureUser(ctx context.Context, authR
|
|||||||
if request.UserID != userID {
|
if request.UserID != userID {
|
||||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-GBH32", "Errors.User.NotMatchingUserID")
|
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-GBH32", "Errors.User.NotMatchingUserID")
|
||||||
}
|
}
|
||||||
_, err = activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, repo.LockoutPolicyViewProvider, request.UserID)
|
_, err = activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, repo.LockoutPolicyViewProvider, request.UserID, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return request, err
|
||||||
}
|
}
|
||||||
return request, nil
|
return request, nil
|
||||||
}
|
}
|
||||||
@ -613,8 +630,8 @@ func (repo *AuthRequestRepo) tryUsingOnlyUserSession(request *domain.AuthRequest
|
|||||||
|
|
||||||
func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *domain.AuthRequest, loginName string) (err error) {
|
func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *domain.AuthRequest, loginName string) (err error) {
|
||||||
user := new(user_view_model.UserView)
|
user := new(user_view_model.UserView)
|
||||||
if request.RequestedOrgID != "" {
|
|
||||||
preferredLoginName := loginName
|
preferredLoginName := loginName
|
||||||
|
if request.RequestedOrgID != "" {
|
||||||
if request.RequestedOrgID != "" {
|
if request.RequestedOrgID != "" {
|
||||||
preferredLoginName += "@" + request.RequestedPrimaryDomain
|
preferredLoginName += "@" + request.RequestedPrimaryDomain
|
||||||
}
|
}
|
||||||
@ -628,6 +645,15 @@ func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *domain
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if request.LoginPolicy.IgnoreUnknownUsernames {
|
||||||
|
if errors.IsNotFound(err) || (user != nil && user.State == int32(domain.UserStateInactive)) {
|
||||||
|
if request.LabelPolicy.HideLoginNameSuffix {
|
||||||
|
preferredLoginName = loginName
|
||||||
|
}
|
||||||
|
request.SetUserInfo(unknownUserID, preferredLoginName, preferredLoginName, preferredLoginName, "", request.RequestedOrgID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -675,6 +701,7 @@ func queryLoginPolicyToDomain(policy *query.LoginPolicy) *domain.LoginPolicy {
|
|||||||
MultiFactors: policy.MultiFactors,
|
MultiFactors: policy.MultiFactors,
|
||||||
PasswordlessType: policy.PasswordlessType,
|
PasswordlessType: policy.PasswordlessType,
|
||||||
HidePasswordReset: policy.HidePasswordReset,
|
HidePasswordReset: policy.HidePasswordReset,
|
||||||
|
IgnoreUnknownUsernames: policy.IgnoreUnknownUsernames,
|
||||||
PasswordCheckLifetime: policy.PasswordCheckLifetime,
|
PasswordCheckLifetime: policy.PasswordCheckLifetime,
|
||||||
ExternalLoginCheckLifetime: policy.ExternalLoginCheckLifetime,
|
ExternalLoginCheckLifetime: policy.ExternalLoginCheckLifetime,
|
||||||
MFAInitSkipLifetime: policy.MFAInitSkipLifetime,
|
MFAInitSkipLifetime: policy.MFAInitSkipLifetime,
|
||||||
@ -703,7 +730,7 @@ func (repo *AuthRequestRepo) checkExternalUserLogin(ctx context.Context, request
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user, err := activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, repo.LockoutPolicyViewProvider, externalIDP.UserID)
|
user, err := activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, repo.LockoutPolicyViewProvider, externalIDP.UserID, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -749,11 +776,13 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
|
|||||||
}
|
}
|
||||||
return steps, nil
|
return steps, nil
|
||||||
}
|
}
|
||||||
user, err := activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, repo.LockoutPolicyViewProvider, request.UserID)
|
user, err := activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, repo.LockoutPolicyViewProvider, request.UserID, request.LoginPolicy.IgnoreUnknownUsernames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if user.PreferredLoginName != "" {
|
||||||
request.LoginName = user.PreferredLoginName
|
request.LoginName = user.PreferredLoginName
|
||||||
|
}
|
||||||
userSession, err := userSessionByIDs(ctx, repo.UserSessionViewProvider, repo.UserEventProvider, request.AgentID, user)
|
userSession, err := userSessionByIDs(ctx, repo.UserSessionViewProvider, repo.UserEventProvider, request.AgentID, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1129,10 +1158,16 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
|
|||||||
return user_view_model.UserSessionToModel(&sessionCopy, provider.PrefixAvatarURL()), nil
|
return user_view_model.UserSessionToModel(&sessionCopy, provider.PrefixAvatarURL()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func activeUserByID(ctx context.Context, userViewProvider userViewProvider, userEventProvider userEventProvider, queries orgViewProvider, lockoutPolicyProvider lockoutPolicyViewProvider, userID string) (*user_model.UserView, error) {
|
func activeUserByID(ctx context.Context, userViewProvider userViewProvider, userEventProvider userEventProvider, queries orgViewProvider, lockoutPolicyProvider lockoutPolicyViewProvider, userID string, ignoreUnknownUsernames bool) (user *user_model.UserView, err error) {
|
||||||
// PLANNED: Check LockoutPolicy
|
// PLANNED: Check LockoutPolicy
|
||||||
user, err := userByID(ctx, userViewProvider, userEventProvider, userID)
|
user, err = userByID(ctx, userViewProvider, userEventProvider, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if ignoreUnknownUsernames && errors.IsNotFound(err) {
|
||||||
|
return &user_model.UserView{
|
||||||
|
ID: userID,
|
||||||
|
HumanView: &user_model.HumanView{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,7 +421,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
|||||||
userViewProvider: &mockViewNoUser{},
|
userViewProvider: &mockViewNoUser{},
|
||||||
userEventProvider: &mockEventUser{},
|
userEventProvider: &mockEventUser{},
|
||||||
},
|
},
|
||||||
args{&domain.AuthRequest{UserID: "UserID"}, false},
|
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
|
||||||
nil,
|
nil,
|
||||||
errors.IsNotFound,
|
errors.IsNotFound,
|
||||||
},
|
},
|
||||||
@ -443,7 +443,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
args{&domain.AuthRequest{UserID: "UserID"}, false},
|
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
|
||||||
nil,
|
nil,
|
||||||
errors.IsPreconditionFailed,
|
errors.IsPreconditionFailed,
|
||||||
},
|
},
|
||||||
@ -464,7 +464,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
args{&domain.AuthRequest{UserID: "UserID"}, false},
|
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
|
||||||
nil,
|
nil,
|
||||||
errors.IsPreconditionFailed,
|
errors.IsPreconditionFailed,
|
||||||
},
|
},
|
||||||
@ -480,7 +480,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
args{&domain.AuthRequest{UserID: "UserID"}, false},
|
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
|
||||||
nil,
|
nil,
|
||||||
errors.IsInternal,
|
errors.IsInternal,
|
||||||
},
|
},
|
||||||
@ -496,7 +496,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
args{&domain.AuthRequest{UserID: "UserID"}, false},
|
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
|
||||||
nil,
|
nil,
|
||||||
errors.IsPreconditionFailed,
|
errors.IsPreconditionFailed,
|
||||||
},
|
},
|
||||||
@ -532,7 +532,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
args{&domain.AuthRequest{UserID: "UserID"}, false},
|
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
|
||||||
nil,
|
nil,
|
||||||
errors.IsInternal,
|
errors.IsInternal,
|
||||||
},
|
},
|
||||||
@ -552,7 +552,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
args{&domain.AuthRequest{UserID: "UserID"}, false},
|
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
|
||||||
[]domain.NextStep{&domain.InitUserStep{
|
[]domain.NextStep{&domain.InitUserStep{
|
||||||
PasswordSet: true,
|
PasswordSet: true,
|
||||||
}},
|
}},
|
||||||
|
@ -68,7 +68,9 @@ type InstanceSetup struct {
|
|||||||
AllowExternalIDP bool
|
AllowExternalIDP bool
|
||||||
ForceMFA bool
|
ForceMFA bool
|
||||||
HidePasswordReset bool
|
HidePasswordReset bool
|
||||||
|
IgnoreUnknownUsername bool
|
||||||
PasswordlessType domain.PasswordlessType
|
PasswordlessType domain.PasswordlessType
|
||||||
|
DefaultRedirectURI string
|
||||||
PasswordCheckLifetime time.Duration
|
PasswordCheckLifetime time.Duration
|
||||||
ExternalLoginCheckLifetime time.Duration
|
ExternalLoginCheckLifetime time.Duration
|
||||||
MfaInitSkipLifetime time.Duration
|
MfaInitSkipLifetime time.Duration
|
||||||
@ -205,7 +207,9 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str
|
|||||||
setup.LoginPolicy.AllowExternalIDP,
|
setup.LoginPolicy.AllowExternalIDP,
|
||||||
setup.LoginPolicy.ForceMFA,
|
setup.LoginPolicy.ForceMFA,
|
||||||
setup.LoginPolicy.HidePasswordReset,
|
setup.LoginPolicy.HidePasswordReset,
|
||||||
|
setup.LoginPolicy.IgnoreUnknownUsername,
|
||||||
setup.LoginPolicy.PasswordlessType,
|
setup.LoginPolicy.PasswordlessType,
|
||||||
|
setup.LoginPolicy.DefaultRedirectURI,
|
||||||
setup.LoginPolicy.PasswordCheckLifetime,
|
setup.LoginPolicy.PasswordCheckLifetime,
|
||||||
setup.LoginPolicy.ExternalLoginCheckLifetime,
|
setup.LoginPolicy.ExternalLoginCheckLifetime,
|
||||||
setup.LoginPolicy.MfaInitSkipLifetime,
|
setup.LoginPolicy.MfaInitSkipLifetime,
|
||||||
|
@ -31,8 +31,10 @@ func writeModelToLoginPolicy(wm *LoginPolicyWriteModel) *domain.LoginPolicy {
|
|||||||
AllowRegister: wm.AllowRegister,
|
AllowRegister: wm.AllowRegister,
|
||||||
AllowExternalIDP: wm.AllowExternalIDP,
|
AllowExternalIDP: wm.AllowExternalIDP,
|
||||||
HidePasswordReset: wm.HidePasswordReset,
|
HidePasswordReset: wm.HidePasswordReset,
|
||||||
|
IgnoreUnknownUsernames: wm.IgnoreUnknownUsernames,
|
||||||
ForceMFA: wm.ForceMFA,
|
ForceMFA: wm.ForceMFA,
|
||||||
PasswordlessType: wm.PasswordlessType,
|
PasswordlessType: wm.PasswordlessType,
|
||||||
|
DefaultRedirectURI: wm.DefaultRedirectURI,
|
||||||
PasswordCheckLifetime: wm.PasswordCheckLifetime,
|
PasswordCheckLifetime: wm.PasswordCheckLifetime,
|
||||||
ExternalLoginCheckLifetime: wm.ExternalLoginCheckLifetime,
|
ExternalLoginCheckLifetime: wm.ExternalLoginCheckLifetime,
|
||||||
MFAInitSkipLifetime: wm.MFAInitSkipLifetime,
|
MFAInitSkipLifetime: wm.MFAInitSkipLifetime,
|
||||||
|
@ -17,7 +17,9 @@ func AddDefaultLoginPolicy(
|
|||||||
allowExternalIDP bool,
|
allowExternalIDP bool,
|
||||||
forceMFA bool,
|
forceMFA bool,
|
||||||
hidePasswordReset bool,
|
hidePasswordReset bool,
|
||||||
|
ignoreUnknownUsernames bool,
|
||||||
passwordlessType domain.PasswordlessType,
|
passwordlessType domain.PasswordlessType,
|
||||||
|
defaultRedirectURI string,
|
||||||
passwordCheckLifetime time.Duration,
|
passwordCheckLifetime time.Duration,
|
||||||
externalLoginCheckLifetime time.Duration,
|
externalLoginCheckLifetime time.Duration,
|
||||||
mfaInitSkipLifetime time.Duration,
|
mfaInitSkipLifetime time.Duration,
|
||||||
@ -34,7 +36,9 @@ func AddDefaultLoginPolicy(
|
|||||||
allowExternalIDP,
|
allowExternalIDP,
|
||||||
forceMFA,
|
forceMFA,
|
||||||
hidePasswordReset,
|
hidePasswordReset,
|
||||||
|
ignoreUnknownUsernames,
|
||||||
passwordlessType,
|
passwordlessType,
|
||||||
|
defaultRedirectURI,
|
||||||
passwordCheckLifetime,
|
passwordCheckLifetime,
|
||||||
externalLoginCheckLifetime,
|
externalLoginCheckLifetime,
|
||||||
mfaInitSkipLifetime,
|
mfaInitSkipLifetime,
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
@ -56,7 +57,9 @@ func (c *Commands) addDefaultLoginPolicy(ctx context.Context, instanceAgg *event
|
|||||||
policy.AllowExternalIDP,
|
policy.AllowExternalIDP,
|
||||||
policy.ForceMFA,
|
policy.ForceMFA,
|
||||||
policy.HidePasswordReset,
|
policy.HidePasswordReset,
|
||||||
|
policy.IgnoreUnknownUsernames,
|
||||||
policy.PasswordlessType,
|
policy.PasswordlessType,
|
||||||
|
policy.DefaultRedirectURI,
|
||||||
policy.PasswordCheckLifetime,
|
policy.PasswordCheckLifetime,
|
||||||
policy.ExternalLoginCheckLifetime,
|
policy.ExternalLoginCheckLifetime,
|
||||||
policy.MFAInitSkipLifetime,
|
policy.MFAInitSkipLifetime,
|
||||||
@ -83,6 +86,9 @@ func (c *Commands) ChangeDefaultLoginPolicy(ctx context.Context, policy *domain.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) changeDefaultLoginPolicy(ctx context.Context, instanceAgg *eventstore.Aggregate, existingPolicy *InstanceLoginPolicyWriteModel, policy *domain.LoginPolicy) (eventstore.Command, error) {
|
func (c *Commands) changeDefaultLoginPolicy(ctx context.Context, instanceAgg *eventstore.Aggregate, existingPolicy *InstanceLoginPolicyWriteModel, policy *domain.LoginPolicy) (eventstore.Command, error) {
|
||||||
|
if ok := domain.ValidateDefaultRedirectURI(policy.DefaultRedirectURI); !ok {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-SFdqd", "Errors.IAM.LoginPolicy.RedirectURIInvalid")
|
||||||
|
}
|
||||||
err := c.defaultLoginPolicyWriteModelByID(ctx, existingPolicy)
|
err := c.defaultLoginPolicyWriteModelByID(ctx, existingPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -97,12 +103,15 @@ func (c *Commands) changeDefaultLoginPolicy(ctx context.Context, instanceAgg *ev
|
|||||||
policy.AllowExternalIDP,
|
policy.AllowExternalIDP,
|
||||||
policy.ForceMFA,
|
policy.ForceMFA,
|
||||||
policy.HidePasswordReset,
|
policy.HidePasswordReset,
|
||||||
|
policy.IgnoreUnknownUsernames,
|
||||||
policy.PasswordlessType,
|
policy.PasswordlessType,
|
||||||
|
policy.DefaultRedirectURI,
|
||||||
policy.PasswordCheckLifetime,
|
policy.PasswordCheckLifetime,
|
||||||
policy.ExternalLoginCheckLifetime,
|
policy.ExternalLoginCheckLifetime,
|
||||||
policy.MFAInitSkipLifetime,
|
policy.MFAInitSkipLifetime,
|
||||||
policy.SecondFactorCheckLifetime,
|
policy.SecondFactorCheckLifetime,
|
||||||
policy.MultiFactorCheckLifetime)
|
policy.MultiFactorCheckLifetime,
|
||||||
|
)
|
||||||
if !hasChanged {
|
if !hasChanged {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "INSTANCE-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "INSTANCE-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
|
||||||
}
|
}
|
||||||
|
@ -65,8 +65,10 @@ func (wm *InstanceLoginPolicyWriteModel) NewChangedEvent(
|
|||||||
allowRegister,
|
allowRegister,
|
||||||
allowExternalIDP,
|
allowExternalIDP,
|
||||||
forceMFA,
|
forceMFA,
|
||||||
hidePasswordReset bool,
|
hidePasswordReset,
|
||||||
|
ignoreUnknownUsernames bool,
|
||||||
passwordlessType domain.PasswordlessType,
|
passwordlessType domain.PasswordlessType,
|
||||||
|
defaultRedirectURI string,
|
||||||
passwordCheckLifetime,
|
passwordCheckLifetime,
|
||||||
externalLoginCheckLifetime,
|
externalLoginCheckLifetime,
|
||||||
mfaInitSkipLifetime,
|
mfaInitSkipLifetime,
|
||||||
@ -93,6 +95,12 @@ func (wm *InstanceLoginPolicyWriteModel) NewChangedEvent(
|
|||||||
if wm.HidePasswordReset != hidePasswordReset {
|
if wm.HidePasswordReset != hidePasswordReset {
|
||||||
changes = append(changes, policy.ChangeHidePasswordReset(hidePasswordReset))
|
changes = append(changes, policy.ChangeHidePasswordReset(hidePasswordReset))
|
||||||
}
|
}
|
||||||
|
if wm.IgnoreUnknownUsernames != ignoreUnknownUsernames {
|
||||||
|
changes = append(changes, policy.ChangeIgnoreUnknownUsernames(ignoreUnknownUsernames))
|
||||||
|
}
|
||||||
|
if wm.DefaultRedirectURI != defaultRedirectURI {
|
||||||
|
changes = append(changes, policy.ChangeDefaultRedirectURI(defaultRedirectURI))
|
||||||
|
}
|
||||||
if wm.PasswordCheckLifetime != passwordCheckLifetime {
|
if wm.PasswordCheckLifetime != passwordCheckLifetime {
|
||||||
changes = append(changes, policy.ChangePasswordCheckLifetime(passwordCheckLifetime))
|
changes = append(changes, policy.ChangePasswordCheckLifetime(passwordCheckLifetime))
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,9 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
@ -90,7 +92,9 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"https://example.com/redirect",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -110,7 +114,9 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
|
|||||||
AllowExternalIDP: true,
|
AllowExternalIDP: true,
|
||||||
ForceMFA: true,
|
ForceMFA: true,
|
||||||
HidePasswordReset: true,
|
HidePasswordReset: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
PasswordCheckLifetime: time.Hour * 1,
|
PasswordCheckLifetime: time.Hour * 1,
|
||||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
MFAInitSkipLifetime: time.Hour * 3,
|
MFAInitSkipLifetime: time.Hour * 3,
|
||||||
@ -130,7 +136,9 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
|
|||||||
AllowExternalIDP: true,
|
AllowExternalIDP: true,
|
||||||
ForceMFA: true,
|
ForceMFA: true,
|
||||||
HidePasswordReset: true,
|
HidePasswordReset: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
PasswordCheckLifetime: time.Hour * 1,
|
PasswordCheckLifetime: time.Hour * 1,
|
||||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
MFAInitSkipLifetime: time.Hour * 3,
|
MFAInitSkipLifetime: time.Hour * 3,
|
||||||
@ -210,7 +218,9 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"https://example.com/redirect",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -229,7 +239,9 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
|
|||||||
AllowExternalIDP: true,
|
AllowExternalIDP: true,
|
||||||
ForceMFA: true,
|
ForceMFA: true,
|
||||||
HidePasswordReset: true,
|
HidePasswordReset: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
PasswordCheckLifetime: time.Hour * 1,
|
PasswordCheckLifetime: time.Hour * 1,
|
||||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
MFAInitSkipLifetime: time.Hour * 3,
|
MFAInitSkipLifetime: time.Hour * 3,
|
||||||
@ -256,7 +268,9 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"https://example.com/redirect",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -275,7 +289,9 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*10,
|
time.Hour*10,
|
||||||
time.Hour*20,
|
time.Hour*20,
|
||||||
time.Hour*30,
|
time.Hour*30,
|
||||||
@ -294,7 +310,9 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
|
|||||||
AllowExternalIDP: false,
|
AllowExternalIDP: false,
|
||||||
ForceMFA: false,
|
ForceMFA: false,
|
||||||
HidePasswordReset: false,
|
HidePasswordReset: false,
|
||||||
|
IgnoreUnknownUsernames: false,
|
||||||
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
||||||
|
DefaultRedirectURI: "",
|
||||||
PasswordCheckLifetime: time.Hour * 10,
|
PasswordCheckLifetime: time.Hour * 10,
|
||||||
ExternalLoginCheckLifetime: time.Hour * 20,
|
ExternalLoginCheckLifetime: time.Hour * 20,
|
||||||
MFAInitSkipLifetime: time.Hour * 30,
|
MFAInitSkipLifetime: time.Hour * 30,
|
||||||
@ -314,7 +332,9 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
|
|||||||
AllowExternalIDP: false,
|
AllowExternalIDP: false,
|
||||||
ForceMFA: false,
|
ForceMFA: false,
|
||||||
HidePasswordReset: false,
|
HidePasswordReset: false,
|
||||||
|
IgnoreUnknownUsernames: false,
|
||||||
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
||||||
|
DefaultRedirectURI: "",
|
||||||
PasswordCheckLifetime: time.Hour * 10,
|
PasswordCheckLifetime: time.Hour * 10,
|
||||||
ExternalLoginCheckLifetime: time.Hour * 20,
|
ExternalLoginCheckLifetime: time.Hour * 20,
|
||||||
MFAInitSkipLifetime: time.Hour * 30,
|
MFAInitSkipLifetime: time.Hour * 30,
|
||||||
@ -408,7 +428,9 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -444,7 +466,9 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -500,7 +524,9 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -638,7 +664,9 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -674,7 +702,9 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -723,7 +753,9 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -777,7 +809,9 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -839,7 +873,9 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -1392,8 +1428,9 @@ func TestCommandSide_RemoveMultiFactorDefaultLoginPolicy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allowUsernamePassword, allowExternalIDP, forceMFA, hidePasswordReset bool,
|
func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allowUsernamePassword, allowExternalIDP, forceMFA, hidePasswordReset, ignoreUnknownUsernames bool,
|
||||||
passwordlessType domain.PasswordlessType,
|
passwordlessType domain.PasswordlessType,
|
||||||
|
redirectURI string,
|
||||||
passwordLifetime, externalLoginLifetime, mfaInitSkipLifetime, secondFactorLifetime, multiFactorLifetime time.Duration) *instance.LoginPolicyChangedEvent {
|
passwordLifetime, externalLoginLifetime, mfaInitSkipLifetime, secondFactorLifetime, multiFactorLifetime time.Duration) *instance.LoginPolicyChangedEvent {
|
||||||
event, _ := instance.NewLoginPolicyChangedEvent(ctx,
|
event, _ := instance.NewLoginPolicyChangedEvent(ctx,
|
||||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||||
@ -1403,7 +1440,9 @@ func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allow
|
|||||||
policy.ChangeForceMFA(forceMFA),
|
policy.ChangeForceMFA(forceMFA),
|
||||||
policy.ChangeAllowUserNamePassword(allowUsernamePassword),
|
policy.ChangeAllowUserNamePassword(allowUsernamePassword),
|
||||||
policy.ChangeHidePasswordReset(hidePasswordReset),
|
policy.ChangeHidePasswordReset(hidePasswordReset),
|
||||||
|
policy.ChangeIgnoreUnknownUsernames(ignoreUnknownUsernames),
|
||||||
policy.ChangePasswordlessType(passwordlessType),
|
policy.ChangePasswordlessType(passwordlessType),
|
||||||
|
policy.ChangeDefaultRedirectURI(redirectURI),
|
||||||
policy.ChangePasswordCheckLifetime(passwordLifetime),
|
policy.ChangePasswordCheckLifetime(passwordLifetime),
|
||||||
policy.ChangeExternalLoginCheckLifetime(externalLoginLifetime),
|
policy.ChangeExternalLoginCheckLifetime(externalLoginLifetime),
|
||||||
policy.ChangeMFAInitSkipLifetime(mfaInitSkipLifetime),
|
policy.ChangeMFAInitSkipLifetime(mfaInitSkipLifetime),
|
||||||
|
@ -16,6 +16,9 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
|
|||||||
if resourceOwner == "" {
|
if resourceOwner == "" {
|
||||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Fn8ds", "Errors.ResourceOwnerMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Fn8ds", "Errors.ResourceOwnerMissing")
|
||||||
}
|
}
|
||||||
|
if ok := domain.ValidateDefaultRedirectURI(policy.DefaultRedirectURI); !ok {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-WSfdq", "Errors.Org.LoginPolicy.RedirectURIInvalid")
|
||||||
|
}
|
||||||
addedPolicy := NewOrgLoginPolicyWriteModel(resourceOwner)
|
addedPolicy := NewOrgLoginPolicyWriteModel(resourceOwner)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -36,7 +39,9 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
|
|||||||
policy.AllowExternalIDP,
|
policy.AllowExternalIDP,
|
||||||
policy.ForceMFA,
|
policy.ForceMFA,
|
||||||
policy.HidePasswordReset,
|
policy.HidePasswordReset,
|
||||||
|
policy.IgnoreUnknownUsernames,
|
||||||
policy.PasswordlessType,
|
policy.PasswordlessType,
|
||||||
|
policy.DefaultRedirectURI,
|
||||||
policy.PasswordCheckLifetime,
|
policy.PasswordCheckLifetime,
|
||||||
policy.ExternalLoginCheckLifetime,
|
policy.ExternalLoginCheckLifetime,
|
||||||
policy.MFAInitSkipLifetime,
|
policy.MFAInitSkipLifetime,
|
||||||
@ -76,6 +81,9 @@ func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string,
|
|||||||
if resourceOwner == "" {
|
if resourceOwner == "" {
|
||||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Mf9sf", "Errors.ResourceOwnerMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Mf9sf", "Errors.ResourceOwnerMissing")
|
||||||
}
|
}
|
||||||
|
if ok := domain.ValidateDefaultRedirectURI(policy.DefaultRedirectURI); !ok {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Sfd21", "Errors.Org.LoginPolicy.RedirectURIInvalid")
|
||||||
|
}
|
||||||
existingPolicy := NewOrgLoginPolicyWriteModel(resourceOwner)
|
existingPolicy := NewOrgLoginPolicyWriteModel(resourceOwner)
|
||||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -94,7 +102,9 @@ func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string,
|
|||||||
policy.AllowExternalIDP,
|
policy.AllowExternalIDP,
|
||||||
policy.ForceMFA,
|
policy.ForceMFA,
|
||||||
policy.HidePasswordReset,
|
policy.HidePasswordReset,
|
||||||
|
policy.IgnoreUnknownUsernames,
|
||||||
policy.PasswordlessType,
|
policy.PasswordlessType,
|
||||||
|
policy.DefaultRedirectURI,
|
||||||
policy.PasswordCheckLifetime,
|
policy.PasswordCheckLifetime,
|
||||||
policy.ExternalLoginCheckLifetime,
|
policy.ExternalLoginCheckLifetime,
|
||||||
policy.MFAInitSkipLifetime,
|
policy.MFAInitSkipLifetime,
|
||||||
|
@ -67,8 +67,10 @@ func (wm *OrgLoginPolicyWriteModel) NewChangedEvent(
|
|||||||
allowRegister,
|
allowRegister,
|
||||||
allowExternalIDP,
|
allowExternalIDP,
|
||||||
forceMFA,
|
forceMFA,
|
||||||
hidePasswordReset bool,
|
hidePasswordReset,
|
||||||
|
ignoreUnknownUsernames bool,
|
||||||
passwordlessType domain.PasswordlessType,
|
passwordlessType domain.PasswordlessType,
|
||||||
|
defaultRedirectURI string,
|
||||||
passwordCheckLifetime,
|
passwordCheckLifetime,
|
||||||
externalLoginCheckLifetime,
|
externalLoginCheckLifetime,
|
||||||
mfaInitSkipLifetime,
|
mfaInitSkipLifetime,
|
||||||
@ -92,6 +94,9 @@ func (wm *OrgLoginPolicyWriteModel) NewChangedEvent(
|
|||||||
if wm.HidePasswordReset != hidePasswordReset {
|
if wm.HidePasswordReset != hidePasswordReset {
|
||||||
changes = append(changes, policy.ChangeHidePasswordReset(hidePasswordReset))
|
changes = append(changes, policy.ChangeHidePasswordReset(hidePasswordReset))
|
||||||
}
|
}
|
||||||
|
if wm.IgnoreUnknownUsernames != ignoreUnknownUsernames {
|
||||||
|
changes = append(changes, policy.ChangeIgnoreUnknownUsernames(ignoreUnknownUsernames))
|
||||||
|
}
|
||||||
if wm.PasswordCheckLifetime != passwordCheckLifetime {
|
if wm.PasswordCheckLifetime != passwordCheckLifetime {
|
||||||
changes = append(changes, policy.ChangePasswordCheckLifetime(passwordCheckLifetime))
|
changes = append(changes, policy.ChangePasswordCheckLifetime(passwordCheckLifetime))
|
||||||
}
|
}
|
||||||
@ -110,6 +115,9 @@ func (wm *OrgLoginPolicyWriteModel) NewChangedEvent(
|
|||||||
if passwordlessType.Valid() && wm.PasswordlessType != passwordlessType {
|
if passwordlessType.Valid() && wm.PasswordlessType != passwordlessType {
|
||||||
changes = append(changes, policy.ChangePasswordlessType(passwordlessType))
|
changes = append(changes, policy.ChangePasswordlessType(passwordlessType))
|
||||||
}
|
}
|
||||||
|
if wm.DefaultRedirectURI != defaultRedirectURI {
|
||||||
|
changes = append(changes, policy.ChangeDefaultRedirectURI(defaultRedirectURI))
|
||||||
|
}
|
||||||
if len(changes) == 0 {
|
if len(changes) == 0 {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,9 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"https://example.com/redirect",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -96,7 +98,9 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
|||||||
AllowUsernamePassword: true,
|
AllowUsernamePassword: true,
|
||||||
AllowExternalIDP: true,
|
AllowExternalIDP: true,
|
||||||
ForceMFA: true,
|
ForceMFA: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
PasswordCheckLifetime: time.Hour * 1,
|
PasswordCheckLifetime: time.Hour * 1,
|
||||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
MFAInitSkipLifetime: time.Hour * 3,
|
MFAInitSkipLifetime: time.Hour * 3,
|
||||||
@ -124,7 +128,9 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"https://example.com/redirect",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -145,7 +151,9 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
|||||||
AllowExternalIDP: true,
|
AllowExternalIDP: true,
|
||||||
ForceMFA: true,
|
ForceMFA: true,
|
||||||
HidePasswordReset: true,
|
HidePasswordReset: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
PasswordCheckLifetime: time.Hour * 1,
|
PasswordCheckLifetime: time.Hour * 1,
|
||||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
MFAInitSkipLifetime: time.Hour * 3,
|
MFAInitSkipLifetime: time.Hour * 3,
|
||||||
@ -164,7 +172,9 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
|||||||
AllowExternalIDP: true,
|
AllowExternalIDP: true,
|
||||||
ForceMFA: true,
|
ForceMFA: true,
|
||||||
HidePasswordReset: true,
|
HidePasswordReset: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
PasswordCheckLifetime: time.Hour * 1,
|
PasswordCheckLifetime: time.Hour * 1,
|
||||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
MFAInitSkipLifetime: time.Hour * 3,
|
MFAInitSkipLifetime: time.Hour * 3,
|
||||||
@ -226,7 +236,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
|||||||
AllowUsernamePassword: true,
|
AllowUsernamePassword: true,
|
||||||
AllowExternalIDP: true,
|
AllowExternalIDP: true,
|
||||||
ForceMFA: true,
|
ForceMFA: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
res: res{
|
res: res{
|
||||||
@ -249,7 +261,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
|||||||
AllowUsernamePassword: true,
|
AllowUsernamePassword: true,
|
||||||
AllowExternalIDP: true,
|
AllowExternalIDP: true,
|
||||||
ForceMFA: true,
|
ForceMFA: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
res: res{
|
res: res{
|
||||||
@ -270,7 +284,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"https://example.com/redirect",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -290,7 +306,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
|||||||
AllowExternalIDP: true,
|
AllowExternalIDP: true,
|
||||||
ForceMFA: true,
|
ForceMFA: true,
|
||||||
HidePasswordReset: true,
|
HidePasswordReset: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
PasswordCheckLifetime: time.Hour * 1,
|
PasswordCheckLifetime: time.Hour * 1,
|
||||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
MFAInitSkipLifetime: time.Hour * 3,
|
MFAInitSkipLifetime: time.Hour * 3,
|
||||||
@ -316,7 +334,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"https://example.com/redirect",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -335,7 +355,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
&duration10,
|
&duration10,
|
||||||
&duration20,
|
&duration20,
|
||||||
&duration30,
|
&duration30,
|
||||||
@ -355,7 +377,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
|||||||
AllowUsernamePassword: false,
|
AllowUsernamePassword: false,
|
||||||
AllowExternalIDP: false,
|
AllowExternalIDP: false,
|
||||||
ForceMFA: false,
|
ForceMFA: false,
|
||||||
|
IgnoreUnknownUsernames: false,
|
||||||
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
||||||
|
DefaultRedirectURI: "",
|
||||||
PasswordCheckLifetime: time.Hour * 10,
|
PasswordCheckLifetime: time.Hour * 10,
|
||||||
ExternalLoginCheckLifetime: time.Hour * 20,
|
ExternalLoginCheckLifetime: time.Hour * 20,
|
||||||
MFAInitSkipLifetime: time.Hour * 30,
|
MFAInitSkipLifetime: time.Hour * 30,
|
||||||
@ -374,7 +398,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
|||||||
AllowExternalIDP: false,
|
AllowExternalIDP: false,
|
||||||
ForceMFA: false,
|
ForceMFA: false,
|
||||||
HidePasswordReset: false,
|
HidePasswordReset: false,
|
||||||
|
IgnoreUnknownUsernames: false,
|
||||||
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
||||||
|
DefaultRedirectURI: "",
|
||||||
PasswordCheckLifetime: time.Hour * 10,
|
PasswordCheckLifetime: time.Hour * 10,
|
||||||
ExternalLoginCheckLifetime: time.Hour * 20,
|
ExternalLoginCheckLifetime: time.Hour * 20,
|
||||||
MFAInitSkipLifetime: time.Hour * 30,
|
MFAInitSkipLifetime: time.Hour * 30,
|
||||||
@ -465,7 +491,9 @@ func TestCommandSide_RemoveLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -603,7 +631,9 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -642,7 +672,9 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -701,7 +733,9 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -863,7 +897,9 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -902,7 +938,9 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -953,7 +991,9 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -1011,7 +1051,9 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -1077,7 +1119,9 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -1694,8 +1738,9 @@ func TestCommandSide_RemoveMultiFactorLoginPolicy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLoginPolicyChangedEvent(ctx context.Context, orgID string, usernamePassword, register, externalIDP, mfa, passwordReset bool,
|
func newLoginPolicyChangedEvent(ctx context.Context, orgID string, usernamePassword, register, externalIDP, mfa, passwordReset, ignoreUnknownUsernames bool,
|
||||||
passwordlessType domain.PasswordlessType,
|
passwordlessType domain.PasswordlessType,
|
||||||
|
redirectURI string,
|
||||||
passwordLifetime, externalLoginLifetime, mfaInitSkipLifetime, secondFactorLifetime, multiFactorLifetime *time.Duration) *org.LoginPolicyChangedEvent {
|
passwordLifetime, externalLoginLifetime, mfaInitSkipLifetime, secondFactorLifetime, multiFactorLifetime *time.Duration) *org.LoginPolicyChangedEvent {
|
||||||
changes := []policy.LoginPolicyChanges{
|
changes := []policy.LoginPolicyChanges{
|
||||||
policy.ChangeAllowUserNamePassword(usernamePassword),
|
policy.ChangeAllowUserNamePassword(usernamePassword),
|
||||||
@ -1703,7 +1748,9 @@ func newLoginPolicyChangedEvent(ctx context.Context, orgID string, usernamePassw
|
|||||||
policy.ChangeAllowExternalIDP(externalIDP),
|
policy.ChangeAllowExternalIDP(externalIDP),
|
||||||
policy.ChangeForceMFA(mfa),
|
policy.ChangeForceMFA(mfa),
|
||||||
policy.ChangeHidePasswordReset(passwordReset),
|
policy.ChangeHidePasswordReset(passwordReset),
|
||||||
|
policy.ChangeIgnoreUnknownUsernames(ignoreUnknownUsernames),
|
||||||
policy.ChangePasswordlessType(passwordlessType),
|
policy.ChangePasswordlessType(passwordlessType),
|
||||||
|
policy.ChangeDefaultRedirectURI(redirectURI),
|
||||||
}
|
}
|
||||||
if passwordLifetime != nil {
|
if passwordLifetime != nil {
|
||||||
changes = append(changes, policy.ChangePasswordCheckLifetime(*passwordLifetime))
|
changes = append(changes, policy.ChangePasswordCheckLifetime(*passwordLifetime))
|
||||||
|
@ -16,7 +16,9 @@ type LoginPolicyWriteModel struct {
|
|||||||
AllowExternalIDP bool
|
AllowExternalIDP bool
|
||||||
ForceMFA bool
|
ForceMFA bool
|
||||||
HidePasswordReset bool
|
HidePasswordReset bool
|
||||||
|
IgnoreUnknownUsernames bool
|
||||||
PasswordlessType domain.PasswordlessType
|
PasswordlessType domain.PasswordlessType
|
||||||
|
DefaultRedirectURI string
|
||||||
PasswordCheckLifetime time.Duration
|
PasswordCheckLifetime time.Duration
|
||||||
ExternalLoginCheckLifetime time.Duration
|
ExternalLoginCheckLifetime time.Duration
|
||||||
MFAInitSkipLifetime time.Duration
|
MFAInitSkipLifetime time.Duration
|
||||||
@ -35,6 +37,8 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
|
|||||||
wm.ForceMFA = e.ForceMFA
|
wm.ForceMFA = e.ForceMFA
|
||||||
wm.PasswordlessType = e.PasswordlessType
|
wm.PasswordlessType = e.PasswordlessType
|
||||||
wm.HidePasswordReset = e.HidePasswordReset
|
wm.HidePasswordReset = e.HidePasswordReset
|
||||||
|
wm.IgnoreUnknownUsernames = e.IgnoreUnknownUsernames
|
||||||
|
wm.DefaultRedirectURI = e.DefaultRedirectURI
|
||||||
wm.PasswordCheckLifetime = e.PasswordCheckLifetime
|
wm.PasswordCheckLifetime = e.PasswordCheckLifetime
|
||||||
wm.ExternalLoginCheckLifetime = e.ExternalLoginCheckLifetime
|
wm.ExternalLoginCheckLifetime = e.ExternalLoginCheckLifetime
|
||||||
wm.MFAInitSkipLifetime = e.MFAInitSkipLifetime
|
wm.MFAInitSkipLifetime = e.MFAInitSkipLifetime
|
||||||
@ -57,9 +61,15 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
|
|||||||
if e.HidePasswordReset != nil {
|
if e.HidePasswordReset != nil {
|
||||||
wm.HidePasswordReset = *e.HidePasswordReset
|
wm.HidePasswordReset = *e.HidePasswordReset
|
||||||
}
|
}
|
||||||
|
if e.IgnoreUnknownUsernames != nil {
|
||||||
|
wm.IgnoreUnknownUsernames = *e.IgnoreUnknownUsernames
|
||||||
|
}
|
||||||
if e.PasswordlessType != nil {
|
if e.PasswordlessType != nil {
|
||||||
wm.PasswordlessType = *e.PasswordlessType
|
wm.PasswordlessType = *e.PasswordlessType
|
||||||
}
|
}
|
||||||
|
if e.DefaultRedirectURI != nil {
|
||||||
|
wm.DefaultRedirectURI = *e.DefaultRedirectURI
|
||||||
|
}
|
||||||
if e.PasswordCheckLifetime != nil {
|
if e.PasswordCheckLifetime != nil {
|
||||||
wm.PasswordCheckLifetime = *e.PasswordCheckLifetime
|
wm.PasswordCheckLifetime = *e.PasswordCheckLifetime
|
||||||
}
|
}
|
||||||
|
@ -1156,7 +1156,9 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -1191,7 +1193,9 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -1227,7 +1231,9 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -1279,7 +1285,9 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -1365,7 +1373,9 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -1458,7 +1468,9 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
|
@ -1687,7 +1687,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -1750,7 +1752,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -1813,7 +1817,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -1893,7 +1899,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -2031,7 +2039,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -2137,7 +2147,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -2237,7 +2249,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
@ -2359,7 +2373,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
domain.PasswordlessTypeNotAllowed,
|
domain.PasswordlessTypeNotAllowed,
|
||||||
|
"",
|
||||||
time.Hour*1,
|
time.Hour*1,
|
||||||
time.Hour*2,
|
time.Hour*2,
|
||||||
time.Hour*3,
|
time.Hour*3,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||||
@ -19,6 +20,8 @@ type LoginPolicy struct {
|
|||||||
MultiFactors []MultiFactorType
|
MultiFactors []MultiFactorType
|
||||||
PasswordlessType PasswordlessType
|
PasswordlessType PasswordlessType
|
||||||
HidePasswordReset bool
|
HidePasswordReset bool
|
||||||
|
IgnoreUnknownUsernames bool
|
||||||
|
DefaultRedirectURI string
|
||||||
PasswordCheckLifetime time.Duration
|
PasswordCheckLifetime time.Duration
|
||||||
ExternalLoginCheckLifetime time.Duration
|
ExternalLoginCheckLifetime time.Duration
|
||||||
MFAInitSkipLifetime time.Duration
|
MFAInitSkipLifetime time.Duration
|
||||||
@ -26,6 +29,24 @@ type LoginPolicy struct {
|
|||||||
MultiFactorCheckLifetime time.Duration
|
MultiFactorCheckLifetime time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ValidateDefaultRedirectURI(rawURL string) bool {
|
||||||
|
if rawURL == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
parsedURL, err := url.Parse(rawURL)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch parsedURL.Scheme {
|
||||||
|
case "":
|
||||||
|
return false
|
||||||
|
case "http", "https":
|
||||||
|
return parsedURL.Host != ""
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type IDPProvider struct {
|
type IDPProvider struct {
|
||||||
models.ObjectRoot
|
models.ObjectRoot
|
||||||
Type IdentityProviderType
|
Type IdentityProviderType
|
||||||
|
73
internal/domain/policy_login_test.go
Normal file
73
internal/domain/policy_login_test.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidateDefaultRedirectURI(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
rawURL string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"invalid url, false",
|
||||||
|
args{
|
||||||
|
rawURL: string('\n'),
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"empty schema, false",
|
||||||
|
args{
|
||||||
|
rawURL: "url",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"empty http host, false",
|
||||||
|
args{
|
||||||
|
rawURL: "http://",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"empty https host, false",
|
||||||
|
args{
|
||||||
|
rawURL: "https://",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"https, ok",
|
||||||
|
args{
|
||||||
|
rawURL: "https://test",
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"custom schema, ok",
|
||||||
|
args{
|
||||||
|
rawURL: "custom://",
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"empty url, ok",
|
||||||
|
args{
|
||||||
|
rawURL: "",
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert.Equalf(t, tt.want, ValidateDefaultRedirectURI(tt.args.rawURL), "ValidateDefaultRedirectURI(%v)", tt.args.rawURL)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,7 @@ type DomainClaimedData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SendDomainClaimed(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, username string, emailConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), colors *query.LabelPolicy, assetsPrefix string, origin string) error {
|
func SendDomainClaimed(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, username string, emailConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), colors *query.LabelPolicy, assetsPrefix string, origin string) error {
|
||||||
url := login.LoginLink(origin)
|
url := login.LoginLink(origin, user.ResourceOwner)
|
||||||
var args = mapNotifyUserToArgs(user)
|
var args = mapNotifyUserToArgs(user)
|
||||||
args["TempUsername"] = username
|
args["TempUsername"] = username
|
||||||
args["Domain"] = strings.Split(user.LastEmail, "@")[1]
|
args["Domain"] = strings.Split(user.LastEmail, "@")[1]
|
||||||
|
@ -26,7 +26,7 @@ func SendEmailVerificationCode(ctx context.Context, mailhtml string, translator
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
url := login.MailVerificationLink(origin, user.ID, codeString)
|
url := login.MailVerificationLink(origin, user.ID, codeString, user.ResourceOwner)
|
||||||
var args = mapNotifyUserToArgs(user)
|
var args = mapNotifyUserToArgs(user)
|
||||||
args["Code"] = codeString
|
args["Code"] = codeString
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ type UrlData struct {
|
|||||||
UserID string
|
UserID string
|
||||||
Code string
|
Code string
|
||||||
PasswordSet bool
|
PasswordSet bool
|
||||||
|
OrgID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendUserInitCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.InitUserCode, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix, origin string) error {
|
func SendUserInitCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.InitUserCode, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix, origin string) error {
|
||||||
@ -32,7 +33,7 @@ func SendUserInitCode(ctx context.Context, mailhtml string, translator *i18n.Tra
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
url := login.InitUserLink(origin, user.ID, codeString, user.PasswordSet)
|
url := login.InitUserLink(origin, user.ID, codeString, user.ResourceOwner, user.PasswordSet)
|
||||||
var args = mapNotifyUserToArgs(user)
|
var args = mapNotifyUserToArgs(user)
|
||||||
args["Code"] = codeString
|
args["Code"] = codeString
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ func SendPasswordCode(ctx context.Context, mailhtml string, translator *i18n.Tra
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
url := login.InitPasswordLink(origin, user.ID, codeString)
|
url := login.InitPasswordLink(origin, user.ID, codeString, user.ResourceOwner)
|
||||||
var args = mapNotifyUserToArgs(user)
|
var args = mapNotifyUserToArgs(user)
|
||||||
args["Code"] = codeString
|
args["Code"] = codeString
|
||||||
|
|
||||||
|
@ -307,3 +307,25 @@ func prepareLabelPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LabelPolicy,
|
|||||||
return policy, nil
|
return policy, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *LabelPolicy) ToDomain() *domain.LabelPolicy {
|
||||||
|
return &domain.LabelPolicy{
|
||||||
|
Default: p.IsDefault,
|
||||||
|
PrimaryColor: p.Light.PrimaryColor,
|
||||||
|
BackgroundColor: p.Light.BackgroundColor,
|
||||||
|
WarnColor: p.Light.WarnColor,
|
||||||
|
FontColor: p.Light.FontColor,
|
||||||
|
LogoURL: p.Light.LogoURL,
|
||||||
|
IconURL: p.Light.IconURL,
|
||||||
|
PrimaryColorDark: p.Dark.PrimaryColor,
|
||||||
|
BackgroundColorDark: p.Dark.BackgroundColor,
|
||||||
|
WarnColorDark: p.Dark.WarnColor,
|
||||||
|
FontColorDark: p.Dark.FontColor,
|
||||||
|
LogoDarkURL: p.Dark.LogoURL,
|
||||||
|
IconDarkURL: p.Dark.IconURL,
|
||||||
|
Font: p.FontURL,
|
||||||
|
HideLoginNameSuffix: p.HideLoginNameSuffix,
|
||||||
|
ErrorMsgPopup: p.ShouldErrorPopup,
|
||||||
|
DisableWatermark: p.WatermarkDisabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/errors"
|
"github.com/zitadel/zitadel/internal/errors"
|
||||||
"github.com/zitadel/zitadel/internal/query/projection"
|
"github.com/zitadel/zitadel/internal/query/projection"
|
||||||
@ -30,6 +29,8 @@ type LoginPolicy struct {
|
|||||||
PasswordlessType domain.PasswordlessType
|
PasswordlessType domain.PasswordlessType
|
||||||
IsDefault bool
|
IsDefault bool
|
||||||
HidePasswordReset bool
|
HidePasswordReset bool
|
||||||
|
IgnoreUnknownUsernames bool
|
||||||
|
DefaultRedirectURI string
|
||||||
PasswordCheckLifetime time.Duration
|
PasswordCheckLifetime time.Duration
|
||||||
ExternalLoginCheckLifetime time.Duration
|
ExternalLoginCheckLifetime time.Duration
|
||||||
MFAInitSkipLifetime time.Duration
|
MFAInitSkipLifetime time.Duration
|
||||||
@ -107,6 +108,14 @@ var (
|
|||||||
name: projection.LoginPolicyHidePWResetCol,
|
name: projection.LoginPolicyHidePWResetCol,
|
||||||
table: loginPolicyTable,
|
table: loginPolicyTable,
|
||||||
}
|
}
|
||||||
|
LoginPolicyColumnIgnoreUnknownUsernames = Column{
|
||||||
|
name: projection.IgnoreUnknownUsernames,
|
||||||
|
table: loginPolicyTable,
|
||||||
|
}
|
||||||
|
LoginPolicyColumnDefaultRedirectURI = Column{
|
||||||
|
name: projection.DefaultRedirectURI,
|
||||||
|
table: loginPolicyTable,
|
||||||
|
}
|
||||||
LoginPolicyColumnPasswordCheckLifetime = Column{
|
LoginPolicyColumnPasswordCheckLifetime = Column{
|
||||||
name: projection.PasswordCheckLifetimeCol,
|
name: projection.PasswordCheckLifetimeCol,
|
||||||
table: loginPolicyTable,
|
table: loginPolicyTable,
|
||||||
@ -284,6 +293,8 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
|
|||||||
LoginPolicyColumnPasswordlessType.identifier(),
|
LoginPolicyColumnPasswordlessType.identifier(),
|
||||||
LoginPolicyColumnIsDefault.identifier(),
|
LoginPolicyColumnIsDefault.identifier(),
|
||||||
LoginPolicyColumnHidePasswordReset.identifier(),
|
LoginPolicyColumnHidePasswordReset.identifier(),
|
||||||
|
LoginPolicyColumnIgnoreUnknownUsernames.identifier(),
|
||||||
|
LoginPolicyColumnDefaultRedirectURI.identifier(),
|
||||||
LoginPolicyColumnPasswordCheckLifetime.identifier(),
|
LoginPolicyColumnPasswordCheckLifetime.identifier(),
|
||||||
LoginPolicyColumnExternalLoginCheckLifetime.identifier(),
|
LoginPolicyColumnExternalLoginCheckLifetime.identifier(),
|
||||||
LoginPolicyColumnMFAInitSkipLifetime.identifier(),
|
LoginPolicyColumnMFAInitSkipLifetime.identifier(),
|
||||||
@ -294,6 +305,7 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
|
|||||||
p := new(LoginPolicy)
|
p := new(LoginPolicy)
|
||||||
secondFactors := pq.Int32Array{}
|
secondFactors := pq.Int32Array{}
|
||||||
multiFactors := pq.Int32Array{}
|
multiFactors := pq.Int32Array{}
|
||||||
|
defaultRedirectURI := sql.NullString{}
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&p.OrgID,
|
&p.OrgID,
|
||||||
&p.CreationDate,
|
&p.CreationDate,
|
||||||
@ -308,6 +320,8 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
|
|||||||
&p.PasswordlessType,
|
&p.PasswordlessType,
|
||||||
&p.IsDefault,
|
&p.IsDefault,
|
||||||
&p.HidePasswordReset,
|
&p.HidePasswordReset,
|
||||||
|
&p.IgnoreUnknownUsernames,
|
||||||
|
&defaultRedirectURI,
|
||||||
&p.PasswordCheckLifetime,
|
&p.PasswordCheckLifetime,
|
||||||
&p.ExternalLoginCheckLifetime,
|
&p.ExternalLoginCheckLifetime,
|
||||||
&p.MFAInitSkipLifetime,
|
&p.MFAInitSkipLifetime,
|
||||||
@ -320,7 +334,7 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
|
|||||||
}
|
}
|
||||||
return nil, errors.ThrowInternal(err, "QUERY-YcC53", "Errors.Internal")
|
return nil, errors.ThrowInternal(err, "QUERY-YcC53", "Errors.Internal")
|
||||||
}
|
}
|
||||||
|
p.DefaultRedirectURI = defaultRedirectURI.String
|
||||||
p.MultiFactors = make([]domain.MultiFactorType, len(multiFactors))
|
p.MultiFactors = make([]domain.MultiFactorType, len(multiFactors))
|
||||||
for i, mfa := range multiFactors {
|
for i, mfa := range multiFactors {
|
||||||
p.MultiFactors[i] = domain.MultiFactorType(mfa)
|
p.MultiFactors[i] = domain.MultiFactorType(mfa)
|
||||||
|
@ -44,6 +44,8 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
|||||||
` projections.login_policies.passwordless_type,`+
|
` projections.login_policies.passwordless_type,`+
|
||||||
` projections.login_policies.is_default,`+
|
` projections.login_policies.is_default,`+
|
||||||
` projections.login_policies.hide_password_reset,`+
|
` projections.login_policies.hide_password_reset,`+
|
||||||
|
` projections.login_policies.ignore_unknown_usernames,`+
|
||||||
|
` projections.login_policies.default_redirect_uri,`+
|
||||||
` projections.login_policies.password_check_lifetime,`+
|
` projections.login_policies.password_check_lifetime,`+
|
||||||
` projections.login_policies.external_login_check_lifetime,`+
|
` projections.login_policies.external_login_check_lifetime,`+
|
||||||
` projections.login_policies.mfa_init_skip_lifetime,`+
|
` projections.login_policies.mfa_init_skip_lifetime,`+
|
||||||
@ -80,6 +82,8 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
|||||||
` projections.login_policies.passwordless_type,`+
|
` projections.login_policies.passwordless_type,`+
|
||||||
` projections.login_policies.is_default,`+
|
` projections.login_policies.is_default,`+
|
||||||
` projections.login_policies.hide_password_reset,`+
|
` projections.login_policies.hide_password_reset,`+
|
||||||
|
` projections.login_policies.ignore_unknown_usernames,`+
|
||||||
|
` projections.login_policies.default_redirect_uri,`+
|
||||||
` projections.login_policies.password_check_lifetime,`+
|
` projections.login_policies.password_check_lifetime,`+
|
||||||
` projections.login_policies.external_login_check_lifetime,`+
|
` projections.login_policies.external_login_check_lifetime,`+
|
||||||
` projections.login_policies.mfa_init_skip_lifetime,`+
|
` projections.login_policies.mfa_init_skip_lifetime,`+
|
||||||
@ -100,6 +104,8 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
|||||||
"passwordless_type",
|
"passwordless_type",
|
||||||
"is_default",
|
"is_default",
|
||||||
"hide_password_reset",
|
"hide_password_reset",
|
||||||
|
"ignore_unknown_usernames",
|
||||||
|
"default_redirect_uri",
|
||||||
"password_check_lifetime",
|
"password_check_lifetime",
|
||||||
"external_login_check_lifetime",
|
"external_login_check_lifetime",
|
||||||
"mfa_init_skip_lifetime",
|
"mfa_init_skip_lifetime",
|
||||||
@ -120,6 +126,8 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
|||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
|
"https://example.com/redirect",
|
||||||
time.Hour * 2,
|
time.Hour * 2,
|
||||||
time.Hour * 2,
|
time.Hour * 2,
|
||||||
time.Hour * 2,
|
time.Hour * 2,
|
||||||
@ -142,6 +150,8 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
|||||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
IsDefault: true,
|
IsDefault: true,
|
||||||
HidePasswordReset: true,
|
HidePasswordReset: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
PasswordCheckLifetime: time.Hour * 2,
|
PasswordCheckLifetime: time.Hour * 2,
|
||||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
MFAInitSkipLifetime: time.Hour * 2,
|
MFAInitSkipLifetime: time.Hour * 2,
|
||||||
@ -167,6 +177,8 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
|||||||
` projections.login_policies.passwordless_type,`+
|
` projections.login_policies.passwordless_type,`+
|
||||||
` projections.login_policies.is_default,`+
|
` projections.login_policies.is_default,`+
|
||||||
` projections.login_policies.hide_password_reset,`+
|
` projections.login_policies.hide_password_reset,`+
|
||||||
|
` projections.login_policies.ignore_unknown_usernames,`+
|
||||||
|
` projections.login_policies.default_redirect_uri,`+
|
||||||
` projections.login_policies.password_check_lifetime,`+
|
` projections.login_policies.password_check_lifetime,`+
|
||||||
` projections.login_policies.external_login_check_lifetime,`+
|
` projections.login_policies.external_login_check_lifetime,`+
|
||||||
` projections.login_policies.mfa_init_skip_lifetime,`+
|
` projections.login_policies.mfa_init_skip_lifetime,`+
|
||||||
|
@ -29,6 +29,8 @@ const (
|
|||||||
LoginPolicyMFAsCol = "multi_factors"
|
LoginPolicyMFAsCol = "multi_factors"
|
||||||
LoginPolicyPasswordlessTypeCol = "passwordless_type"
|
LoginPolicyPasswordlessTypeCol = "passwordless_type"
|
||||||
LoginPolicyHidePWResetCol = "hide_password_reset"
|
LoginPolicyHidePWResetCol = "hide_password_reset"
|
||||||
|
IgnoreUnknownUsernames = "ignore_unknown_usernames"
|
||||||
|
DefaultRedirectURI = "default_redirect_uri"
|
||||||
PasswordCheckLifetimeCol = "password_check_lifetime"
|
PasswordCheckLifetimeCol = "password_check_lifetime"
|
||||||
ExternalLoginCheckLifetimeCol = "external_login_check_lifetime"
|
ExternalLoginCheckLifetimeCol = "external_login_check_lifetime"
|
||||||
MFAInitSkipLifetimeCol = "mfa_init_skip_lifetime"
|
MFAInitSkipLifetimeCol = "mfa_init_skip_lifetime"
|
||||||
@ -60,6 +62,8 @@ func NewLoginPolicyProjection(ctx context.Context, config crdb.StatementHandlerC
|
|||||||
crdb.NewColumn(LoginPolicyMFAsCol, crdb.ColumnTypeEnumArray, crdb.Nullable()),
|
crdb.NewColumn(LoginPolicyMFAsCol, crdb.ColumnTypeEnumArray, crdb.Nullable()),
|
||||||
crdb.NewColumn(LoginPolicyPasswordlessTypeCol, crdb.ColumnTypeEnum),
|
crdb.NewColumn(LoginPolicyPasswordlessTypeCol, crdb.ColumnTypeEnum),
|
||||||
crdb.NewColumn(LoginPolicyHidePWResetCol, crdb.ColumnTypeBool),
|
crdb.NewColumn(LoginPolicyHidePWResetCol, crdb.ColumnTypeBool),
|
||||||
|
crdb.NewColumn(IgnoreUnknownUsernames, crdb.ColumnTypeBool),
|
||||||
|
crdb.NewColumn(DefaultRedirectURI, crdb.ColumnTypeText, crdb.Nullable()),
|
||||||
crdb.NewColumn(PasswordCheckLifetimeCol, crdb.ColumnTypeInt64),
|
crdb.NewColumn(PasswordCheckLifetimeCol, crdb.ColumnTypeInt64),
|
||||||
crdb.NewColumn(ExternalLoginCheckLifetimeCol, crdb.ColumnTypeInt64),
|
crdb.NewColumn(ExternalLoginCheckLifetimeCol, crdb.ColumnTypeInt64),
|
||||||
crdb.NewColumn(MFAInitSkipLifetimeCol, crdb.ColumnTypeInt64),
|
crdb.NewColumn(MFAInitSkipLifetimeCol, crdb.ColumnTypeInt64),
|
||||||
@ -167,6 +171,8 @@ func (p *LoginPolicyProjection) reduceLoginPolicyAdded(event eventstore.Event) (
|
|||||||
handler.NewCol(LoginPolicyPasswordlessTypeCol, policyEvent.PasswordlessType),
|
handler.NewCol(LoginPolicyPasswordlessTypeCol, policyEvent.PasswordlessType),
|
||||||
handler.NewCol(LoginPolicyIsDefaultCol, isDefault),
|
handler.NewCol(LoginPolicyIsDefaultCol, isDefault),
|
||||||
handler.NewCol(LoginPolicyHidePWResetCol, policyEvent.HidePasswordReset),
|
handler.NewCol(LoginPolicyHidePWResetCol, policyEvent.HidePasswordReset),
|
||||||
|
handler.NewCol(IgnoreUnknownUsernames, policyEvent.IgnoreUnknownUsernames),
|
||||||
|
handler.NewCol(DefaultRedirectURI, policyEvent.DefaultRedirectURI),
|
||||||
handler.NewCol(PasswordCheckLifetimeCol, policyEvent.PasswordCheckLifetime),
|
handler.NewCol(PasswordCheckLifetimeCol, policyEvent.PasswordCheckLifetime),
|
||||||
handler.NewCol(ExternalLoginCheckLifetimeCol, policyEvent.ExternalLoginCheckLifetime),
|
handler.NewCol(ExternalLoginCheckLifetimeCol, policyEvent.ExternalLoginCheckLifetime),
|
||||||
handler.NewCol(MFAInitSkipLifetimeCol, policyEvent.MFAInitSkipLifetime),
|
handler.NewCol(MFAInitSkipLifetimeCol, policyEvent.MFAInitSkipLifetime),
|
||||||
@ -208,6 +214,12 @@ func (p *LoginPolicyProjection) reduceLoginPolicyChanged(event eventstore.Event)
|
|||||||
if policyEvent.HidePasswordReset != nil {
|
if policyEvent.HidePasswordReset != nil {
|
||||||
cols = append(cols, handler.NewCol(LoginPolicyHidePWResetCol, *policyEvent.HidePasswordReset))
|
cols = append(cols, handler.NewCol(LoginPolicyHidePWResetCol, *policyEvent.HidePasswordReset))
|
||||||
}
|
}
|
||||||
|
if policyEvent.IgnoreUnknownUsernames != nil {
|
||||||
|
cols = append(cols, handler.NewCol(IgnoreUnknownUsernames, *policyEvent.IgnoreUnknownUsernames))
|
||||||
|
}
|
||||||
|
if policyEvent.DefaultRedirectURI != nil {
|
||||||
|
cols = append(cols, handler.NewCol(DefaultRedirectURI, *policyEvent.DefaultRedirectURI))
|
||||||
|
}
|
||||||
if policyEvent.PasswordCheckLifetime != nil {
|
if policyEvent.PasswordCheckLifetime != nil {
|
||||||
cols = append(cols, handler.NewCol(PasswordCheckLifetimeCol, *policyEvent.PasswordCheckLifetime))
|
cols = append(cols, handler.NewCol(PasswordCheckLifetimeCol, *policyEvent.PasswordCheckLifetime))
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,9 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
|||||||
"allowExternalIdp": false,
|
"allowExternalIdp": false,
|
||||||
"forceMFA": false,
|
"forceMFA": false,
|
||||||
"hidePasswordReset": true,
|
"hidePasswordReset": true,
|
||||||
|
"ignoreUnknownUsernames": true,
|
||||||
"passwordlessType": 1,
|
"passwordlessType": 1,
|
||||||
|
"defaultRedirectURI": "https://example.com/redirect",
|
||||||
"passwordCheckLifetime": 10000000,
|
"passwordCheckLifetime": 10000000,
|
||||||
"externalLoginCheckLifetime": 10000000,
|
"externalLoginCheckLifetime": 10000000,
|
||||||
"mfaInitSkipLifetime": 10000000,
|
"mfaInitSkipLifetime": 10000000,
|
||||||
@ -53,7 +55,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "INSERT INTO projections.login_policies (aggregate_id, instance_id, creation_date, change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, is_default, hide_password_reset, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)",
|
expectedStmt: "INSERT INTO projections.login_policies (aggregate_id, instance_id, creation_date, change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, is_default, hide_password_reset, ignore_unknown_usernames, default_redirect_uri, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"agg-id",
|
"agg-id",
|
||||||
"instance-id",
|
"instance-id",
|
||||||
@ -67,6 +69,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
|||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
|
"https://example.com/redirect",
|
||||||
time.Millisecond * 10,
|
time.Millisecond * 10,
|
||||||
time.Millisecond * 10,
|
time.Millisecond * 10,
|
||||||
time.Millisecond * 10,
|
time.Millisecond * 10,
|
||||||
@ -91,7 +95,9 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
|||||||
"allowExternalIdp": true,
|
"allowExternalIdp": true,
|
||||||
"forceMFA": true,
|
"forceMFA": true,
|
||||||
"hidePasswordReset": true,
|
"hidePasswordReset": true,
|
||||||
|
"ignoreUnknownUsernames": true,
|
||||||
"passwordlessType": 1,
|
"passwordlessType": 1,
|
||||||
|
"defaultRedirectURI": "https://example.com/redirect",
|
||||||
"passwordCheckLifetime": 10000000,
|
"passwordCheckLifetime": 10000000,
|
||||||
"externalLoginCheckLifetime": 10000000,
|
"externalLoginCheckLifetime": 10000000,
|
||||||
"mfaInitSkipLifetime": 10000000,
|
"mfaInitSkipLifetime": 10000000,
|
||||||
@ -108,7 +114,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.login_policies SET (change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, hide_password_reset, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) WHERE (aggregate_id = $14)",
|
expectedStmt: "UPDATE projections.login_policies SET (change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, hide_password_reset, ignore_unknown_usernames, default_redirect_uri, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) WHERE (aggregate_id = $16)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
uint64(15),
|
uint64(15),
|
||||||
@ -118,6 +124,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
|
"https://example.com/redirect",
|
||||||
time.Millisecond * 10,
|
time.Millisecond * 10,
|
||||||
time.Millisecond * 10,
|
time.Millisecond * 10,
|
||||||
time.Millisecond * 10,
|
time.Millisecond * 10,
|
||||||
@ -298,7 +306,9 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
|||||||
"allowExternalIdp": false,
|
"allowExternalIdp": false,
|
||||||
"forceMFA": false,
|
"forceMFA": false,
|
||||||
"hidePasswordReset": true,
|
"hidePasswordReset": true,
|
||||||
|
"ignoreUnknownUsernames": true,
|
||||||
"passwordlessType": 1,
|
"passwordlessType": 1,
|
||||||
|
"defaultRedirectURI": "https://example.com/redirect",
|
||||||
"passwordCheckLifetime": 10000000,
|
"passwordCheckLifetime": 10000000,
|
||||||
"externalLoginCheckLifetime": 10000000,
|
"externalLoginCheckLifetime": 10000000,
|
||||||
"mfaInitSkipLifetime": 10000000,
|
"mfaInitSkipLifetime": 10000000,
|
||||||
@ -315,7 +325,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "INSERT INTO projections.login_policies (aggregate_id, instance_id, creation_date, change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, is_default, hide_password_reset, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)",
|
expectedStmt: "INSERT INTO projections.login_policies (aggregate_id, instance_id, creation_date, change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, is_default, hide_password_reset, ignore_unknown_usernames, default_redirect_uri, password_check_lifetime, external_login_check_lifetime, mfa_init_skip_lifetime, second_factor_check_lifetime, multi_factor_check_lifetime) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"agg-id",
|
"agg-id",
|
||||||
"instance-id",
|
"instance-id",
|
||||||
@ -329,6 +339,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
|||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
|
"https://example.com/redirect",
|
||||||
time.Millisecond * 10,
|
time.Millisecond * 10,
|
||||||
time.Millisecond * 10,
|
time.Millisecond * 10,
|
||||||
time.Millisecond * 10,
|
time.Millisecond * 10,
|
||||||
@ -353,7 +365,9 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
|||||||
"allowExternalIdp": true,
|
"allowExternalIdp": true,
|
||||||
"forceMFA": true,
|
"forceMFA": true,
|
||||||
"hidePasswordReset": true,
|
"hidePasswordReset": true,
|
||||||
"passwordlessType": 1
|
"ignoreUnknownUsernames": true,
|
||||||
|
"passwordlessType": 1,
|
||||||
|
"defaultRedirectURI": "https://example.com/redirect"
|
||||||
}`),
|
}`),
|
||||||
), instance.LoginPolicyChangedEventMapper),
|
), instance.LoginPolicyChangedEventMapper),
|
||||||
},
|
},
|
||||||
@ -365,7 +379,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
|||||||
executer: &testExecuter{
|
executer: &testExecuter{
|
||||||
executions: []execution{
|
executions: []execution{
|
||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.login_policies SET (change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, hide_password_reset) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (aggregate_id = $9)",
|
expectedStmt: "UPDATE projections.login_policies SET (change_date, sequence, allow_register, allow_username_password, allow_external_idps, force_mfa, passwordless_type, hide_password_reset, ignore_unknown_usernames, default_redirect_uri) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) WHERE (aggregate_id = $11)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
anyArg{},
|
anyArg{},
|
||||||
uint64(15),
|
uint64(15),
|
||||||
@ -375,6 +389,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
domain.PasswordlessTypeAllowed,
|
domain.PasswordlessTypeAllowed,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
|
"https://example.com/redirect",
|
||||||
"agg-id",
|
"agg-id",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -27,8 +27,10 @@ func NewLoginPolicyAddedEvent(
|
|||||||
allowRegister,
|
allowRegister,
|
||||||
allowExternalIDP,
|
allowExternalIDP,
|
||||||
forceMFA,
|
forceMFA,
|
||||||
hidePasswordReset bool,
|
hidePasswordReset,
|
||||||
|
ignoreUnknownUsernames bool,
|
||||||
passwordlessType domain.PasswordlessType,
|
passwordlessType domain.PasswordlessType,
|
||||||
|
defaultRedirectURI string,
|
||||||
passwordCheckLifetime,
|
passwordCheckLifetime,
|
||||||
externalLoginCheckLifetime,
|
externalLoginCheckLifetime,
|
||||||
mfaInitSkipLifetime,
|
mfaInitSkipLifetime,
|
||||||
@ -46,7 +48,9 @@ func NewLoginPolicyAddedEvent(
|
|||||||
allowExternalIDP,
|
allowExternalIDP,
|
||||||
forceMFA,
|
forceMFA,
|
||||||
hidePasswordReset,
|
hidePasswordReset,
|
||||||
|
ignoreUnknownUsernames,
|
||||||
passwordlessType,
|
passwordlessType,
|
||||||
|
defaultRedirectURI,
|
||||||
passwordCheckLifetime,
|
passwordCheckLifetime,
|
||||||
externalLoginCheckLifetime,
|
externalLoginCheckLifetime,
|
||||||
mfaInitSkipLifetime,
|
mfaInitSkipLifetime,
|
||||||
|
@ -28,8 +28,10 @@ func NewLoginPolicyAddedEvent(
|
|||||||
allowRegister,
|
allowRegister,
|
||||||
allowExternalIDP,
|
allowExternalIDP,
|
||||||
forceMFA,
|
forceMFA,
|
||||||
hidePasswordReset bool,
|
hidePasswordReset,
|
||||||
|
ignoreUnknownUsernames bool,
|
||||||
passwordlessType domain.PasswordlessType,
|
passwordlessType domain.PasswordlessType,
|
||||||
|
defaultRedirectURI string,
|
||||||
passwordCheckLifetime,
|
passwordCheckLifetime,
|
||||||
externalLoginCheckLifetime,
|
externalLoginCheckLifetime,
|
||||||
mfaInitSkipLifetime,
|
mfaInitSkipLifetime,
|
||||||
@ -47,7 +49,9 @@ func NewLoginPolicyAddedEvent(
|
|||||||
allowExternalIDP,
|
allowExternalIDP,
|
||||||
forceMFA,
|
forceMFA,
|
||||||
hidePasswordReset,
|
hidePasswordReset,
|
||||||
|
ignoreUnknownUsernames,
|
||||||
passwordlessType,
|
passwordlessType,
|
||||||
|
defaultRedirectURI,
|
||||||
passwordCheckLifetime,
|
passwordCheckLifetime,
|
||||||
externalLoginCheckLifetime,
|
externalLoginCheckLifetime,
|
||||||
mfaInitSkipLifetime,
|
mfaInitSkipLifetime,
|
||||||
|
@ -25,7 +25,9 @@ type LoginPolicyAddedEvent struct {
|
|||||||
AllowExternalIDP bool `json:"allowExternalIdp,omitempty"`
|
AllowExternalIDP bool `json:"allowExternalIdp,omitempty"`
|
||||||
ForceMFA bool `json:"forceMFA,omitempty"`
|
ForceMFA bool `json:"forceMFA,omitempty"`
|
||||||
HidePasswordReset bool `json:"hidePasswordReset,omitempty"`
|
HidePasswordReset bool `json:"hidePasswordReset,omitempty"`
|
||||||
|
IgnoreUnknownUsernames bool `json:"ignoreUnknownUsernames,omitempty"`
|
||||||
PasswordlessType domain.PasswordlessType `json:"passwordlessType,omitempty"`
|
PasswordlessType domain.PasswordlessType `json:"passwordlessType,omitempty"`
|
||||||
|
DefaultRedirectURI string `json:"defaultRedirectURI,omitempty"`
|
||||||
PasswordCheckLifetime time.Duration `json:"passwordCheckLifetime,omitempty"`
|
PasswordCheckLifetime time.Duration `json:"passwordCheckLifetime,omitempty"`
|
||||||
ExternalLoginCheckLifetime time.Duration `json:"externalLoginCheckLifetime,omitempty"`
|
ExternalLoginCheckLifetime time.Duration `json:"externalLoginCheckLifetime,omitempty"`
|
||||||
MFAInitSkipLifetime time.Duration `json:"mfaInitSkipLifetime,omitempty"`
|
MFAInitSkipLifetime time.Duration `json:"mfaInitSkipLifetime,omitempty"`
|
||||||
@ -47,8 +49,10 @@ func NewLoginPolicyAddedEvent(
|
|||||||
allowRegister,
|
allowRegister,
|
||||||
allowExternalIDP,
|
allowExternalIDP,
|
||||||
forceMFA,
|
forceMFA,
|
||||||
hidePasswordReset bool,
|
hidePasswordReset,
|
||||||
|
ignoreUnknownUsernames bool,
|
||||||
passwordlessType domain.PasswordlessType,
|
passwordlessType domain.PasswordlessType,
|
||||||
|
defaultRedirectURI string,
|
||||||
passwordCheckLifetime,
|
passwordCheckLifetime,
|
||||||
externalLoginCheckLifetime,
|
externalLoginCheckLifetime,
|
||||||
mfaInitSkipLifetime,
|
mfaInitSkipLifetime,
|
||||||
@ -63,6 +67,8 @@ func NewLoginPolicyAddedEvent(
|
|||||||
ForceMFA: forceMFA,
|
ForceMFA: forceMFA,
|
||||||
PasswordlessType: passwordlessType,
|
PasswordlessType: passwordlessType,
|
||||||
HidePasswordReset: hidePasswordReset,
|
HidePasswordReset: hidePasswordReset,
|
||||||
|
IgnoreUnknownUsernames: ignoreUnknownUsernames,
|
||||||
|
DefaultRedirectURI: defaultRedirectURI,
|
||||||
PasswordCheckLifetime: passwordCheckLifetime,
|
PasswordCheckLifetime: passwordCheckLifetime,
|
||||||
ExternalLoginCheckLifetime: externalLoginCheckLifetime,
|
ExternalLoginCheckLifetime: externalLoginCheckLifetime,
|
||||||
MFAInitSkipLifetime: mfaInitSkipLifetime,
|
MFAInitSkipLifetime: mfaInitSkipLifetime,
|
||||||
@ -92,7 +98,9 @@ type LoginPolicyChangedEvent struct {
|
|||||||
AllowExternalIDP *bool `json:"allowExternalIdp,omitempty"`
|
AllowExternalIDP *bool `json:"allowExternalIdp,omitempty"`
|
||||||
ForceMFA *bool `json:"forceMFA,omitempty"`
|
ForceMFA *bool `json:"forceMFA,omitempty"`
|
||||||
HidePasswordReset *bool `json:"hidePasswordReset,omitempty"`
|
HidePasswordReset *bool `json:"hidePasswordReset,omitempty"`
|
||||||
|
IgnoreUnknownUsernames *bool `json:"ignoreUnknownUsernames,omitempty"`
|
||||||
PasswordlessType *domain.PasswordlessType `json:"passwordlessType,omitempty"`
|
PasswordlessType *domain.PasswordlessType `json:"passwordlessType,omitempty"`
|
||||||
|
DefaultRedirectURI *string `json:"defaultRedirectURI,omitempty"`
|
||||||
PasswordCheckLifetime *time.Duration `json:"passwordCheckLifetime,omitempty"`
|
PasswordCheckLifetime *time.Duration `json:"passwordCheckLifetime,omitempty"`
|
||||||
ExternalLoginCheckLifetime *time.Duration `json:"externalLoginCheckLifetime,omitempty"`
|
ExternalLoginCheckLifetime *time.Duration `json:"externalLoginCheckLifetime,omitempty"`
|
||||||
MFAInitSkipLifetime *time.Duration `json:"mfaInitSkipLifetime,omitempty"`
|
MFAInitSkipLifetime *time.Duration `json:"mfaInitSkipLifetime,omitempty"`
|
||||||
@ -167,26 +175,43 @@ func ChangePasswordCheckLifetime(passwordCheckLifetime time.Duration) func(*Logi
|
|||||||
e.PasswordCheckLifetime = &passwordCheckLifetime
|
e.PasswordCheckLifetime = &passwordCheckLifetime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChangeExternalLoginCheckLifetime(externalLoginCheckLifetime time.Duration) func(*LoginPolicyChangedEvent) {
|
func ChangeExternalLoginCheckLifetime(externalLoginCheckLifetime time.Duration) func(*LoginPolicyChangedEvent) {
|
||||||
return func(e *LoginPolicyChangedEvent) {
|
return func(e *LoginPolicyChangedEvent) {
|
||||||
e.ExternalLoginCheckLifetime = &externalLoginCheckLifetime
|
e.ExternalLoginCheckLifetime = &externalLoginCheckLifetime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChangeMFAInitSkipLifetime(mfaInitSkipLifetime time.Duration) func(*LoginPolicyChangedEvent) {
|
func ChangeMFAInitSkipLifetime(mfaInitSkipLifetime time.Duration) func(*LoginPolicyChangedEvent) {
|
||||||
return func(e *LoginPolicyChangedEvent) {
|
return func(e *LoginPolicyChangedEvent) {
|
||||||
e.MFAInitSkipLifetime = &mfaInitSkipLifetime
|
e.MFAInitSkipLifetime = &mfaInitSkipLifetime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChangeSecondFactorCheckLifetime(secondFactorCheckLifetime time.Duration) func(*LoginPolicyChangedEvent) {
|
func ChangeSecondFactorCheckLifetime(secondFactorCheckLifetime time.Duration) func(*LoginPolicyChangedEvent) {
|
||||||
return func(e *LoginPolicyChangedEvent) {
|
return func(e *LoginPolicyChangedEvent) {
|
||||||
e.SecondFactorCheckLifetime = &secondFactorCheckLifetime
|
e.SecondFactorCheckLifetime = &secondFactorCheckLifetime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChangeMultiFactorCheckLifetime(multiFactorCheckLifetime time.Duration) func(*LoginPolicyChangedEvent) {
|
func ChangeMultiFactorCheckLifetime(multiFactorCheckLifetime time.Duration) func(*LoginPolicyChangedEvent) {
|
||||||
return func(e *LoginPolicyChangedEvent) {
|
return func(e *LoginPolicyChangedEvent) {
|
||||||
e.MultiFactorCheckLifetime = &multiFactorCheckLifetime
|
e.MultiFactorCheckLifetime = &multiFactorCheckLifetime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ChangeIgnoreUnknownUsernames(ignoreUnknownUsernames bool) func(*LoginPolicyChangedEvent) {
|
||||||
|
return func(e *LoginPolicyChangedEvent) {
|
||||||
|
e.IgnoreUnknownUsernames = &ignoreUnknownUsernames
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangeDefaultRedirectURI(defaultRedirectURI string) func(*LoginPolicyChangedEvent) {
|
||||||
|
return func(e *LoginPolicyChangedEvent) {
|
||||||
|
e.DefaultRedirectURI = &defaultRedirectURI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func LoginPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
func LoginPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||||
e := &LoginPolicyChangedEvent{
|
e := &LoginPolicyChangedEvent{
|
||||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||||
|
@ -176,6 +176,7 @@ Errors:
|
|||||||
LoginPolicy:
|
LoginPolicy:
|
||||||
NotFound: Login Policy konnte nicht gefunden werden
|
NotFound: Login Policy konnte nicht gefunden werden
|
||||||
Invalid: Login Policy ist ungültig
|
Invalid: Login Policy ist ungültig
|
||||||
|
RedirectURIInvalid: Default Redirect URI ist ungültig
|
||||||
NotExisting: Login Policy existiert nicht auf dieser Organisation
|
NotExisting: Login Policy existiert nicht auf dieser Organisation
|
||||||
AlreadyExists: Login Policy existiert bereits
|
AlreadyExists: Login Policy existiert bereits
|
||||||
IdpProviderAlreadyExisting: Idp Provider existiert bereits
|
IdpProviderAlreadyExisting: Idp Provider existiert bereits
|
||||||
@ -285,6 +286,7 @@ Errors:
|
|||||||
NotChanged: Default Login Policy wurde nicht verändert
|
NotChanged: Default Login Policy wurde nicht verändert
|
||||||
NotExisting: Default Login Policy existiert nicht
|
NotExisting: Default Login Policy existiert nicht
|
||||||
AlreadyExists: Default Login Policy existiert bereits
|
AlreadyExists: Default Login Policy existiert bereits
|
||||||
|
RedirectURIInvalid: Default Redirect URI ist ungültig
|
||||||
MFA:
|
MFA:
|
||||||
AlreadyExists: Multifaktor existiert bereits
|
AlreadyExists: Multifaktor existiert bereits
|
||||||
NotExisting: Multifaktor existiert nicht
|
NotExisting: Multifaktor existiert nicht
|
||||||
|
@ -176,7 +176,8 @@ Errors:
|
|||||||
LoginPolicy:
|
LoginPolicy:
|
||||||
NotFound: Login Policy not found
|
NotFound: Login Policy not found
|
||||||
Invalid: Login Policy is invalid
|
Invalid: Login Policy is invalid
|
||||||
NotExisting: Login Policy not existig
|
RedirectURIInvalid: Default Redirect URI is invalid
|
||||||
|
NotExisting: Login Policy not existing
|
||||||
AlreadyExists: Login Policy already exists
|
AlreadyExists: Login Policy already exists
|
||||||
IdpProviderAlreadyExisting: Idp Provider already existing
|
IdpProviderAlreadyExisting: Idp Provider already existing
|
||||||
IdpProviderNotExisting: Idp Provider not existing
|
IdpProviderNotExisting: Idp Provider not existing
|
||||||
@ -285,6 +286,7 @@ Errors:
|
|||||||
NotChanged: Default Login Policy has not been changed
|
NotChanged: Default Login Policy has not been changed
|
||||||
NotExisting: Default Login Policy not existig
|
NotExisting: Default Login Policy not existig
|
||||||
AlreadyExists: Default Login Policy already exists
|
AlreadyExists: Default Login Policy already exists
|
||||||
|
RedirectURIInvalid: Default Redirect URI is invalid
|
||||||
MFA:
|
MFA:
|
||||||
AlreadyExists: Multifactor already exists
|
AlreadyExists: Multifactor already exists
|
||||||
NotExisting: Multifactor not existing
|
NotExisting: Multifactor not existing
|
||||||
|
@ -176,6 +176,7 @@ Errors:
|
|||||||
LoginPolicy:
|
LoginPolicy:
|
||||||
NotFound: Impostazioni di accesso non trovati
|
NotFound: Impostazioni di accesso non trovati
|
||||||
Invalid: Impostazioni di accesso non sono validi
|
Invalid: Impostazioni di accesso non sono validi
|
||||||
|
RedirectURIInvalid: Default Redirect URI non valido
|
||||||
NotExisting: Impostazioni di accesso non esistenti
|
NotExisting: Impostazioni di accesso non esistenti
|
||||||
AlreadyExists: Impostazioni di accesso già esistenti
|
AlreadyExists: Impostazioni di accesso già esistenti
|
||||||
IdpProviderAlreadyExisting: IDP già esistente
|
IdpProviderAlreadyExisting: IDP già esistente
|
||||||
@ -283,6 +284,7 @@ Errors:
|
|||||||
NotChanged: Le impostazioni di accesso predefinite non sono state cambiate
|
NotChanged: Le impostazioni di accesso predefinite non sono state cambiate
|
||||||
NotExisting: Impostazioni di accesso predefinite non esistenti
|
NotExisting: Impostazioni di accesso predefinite non esistenti
|
||||||
AlreadyExists: Impostazioni di accesso predefinite già esistenti
|
AlreadyExists: Impostazioni di accesso predefinite già esistenti
|
||||||
|
RedirectURIInvalid: Default Redirect URI non valido
|
||||||
MFA:
|
MFA:
|
||||||
AlreadyExists: Multifattore già esistente
|
AlreadyExists: Multifattore già esistente
|
||||||
NotExisting: Multifattore non esistente
|
NotExisting: Multifattore non esistente
|
||||||
|
@ -3677,11 +3677,21 @@ message UpdateLoginPolicyRequest {
|
|||||||
description: "defines if password reset link should be shown in the login screen"
|
description: "defines if password reset link should be shown in the login screen"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
google.protobuf.Duration password_check_lifetime = 7;
|
bool ignore_unknown_usernames = 7 [
|
||||||
google.protobuf.Duration external_login_check_lifetime = 8;
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
google.protobuf.Duration mfa_init_skip_lifetime = 9;
|
description: "defines if unknown username on login screen directly return an error or always display the password screen"
|
||||||
google.protobuf.Duration second_factor_check_lifetime = 10;
|
}
|
||||||
google.protobuf.Duration multi_factor_check_lifetime = 11;
|
];
|
||||||
|
string default_redirect_uri = 8 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
description: "defines where the user will be redirected to if the login is started without app context (e.g. from mail)"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
google.protobuf.Duration password_check_lifetime = 9;
|
||||||
|
google.protobuf.Duration external_login_check_lifetime = 10;
|
||||||
|
google.protobuf.Duration mfa_init_skip_lifetime = 11;
|
||||||
|
google.protobuf.Duration second_factor_check_lifetime = 12;
|
||||||
|
google.protobuf.Duration multi_factor_check_lifetime = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpdateLoginPolicyResponse {
|
message UpdateLoginPolicyResponse {
|
||||||
|
@ -4344,11 +4344,21 @@ message AddCustomLoginPolicyRequest {
|
|||||||
bool force_mfa = 4;
|
bool force_mfa = 4;
|
||||||
zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}];
|
zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}];
|
||||||
bool hide_password_reset = 6;
|
bool hide_password_reset = 6;
|
||||||
google.protobuf.Duration password_check_lifetime = 7;
|
bool ignore_unknown_usernames = 7 [
|
||||||
google.protobuf.Duration external_login_check_lifetime = 8;
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
google.protobuf.Duration mfa_init_skip_lifetime = 9;
|
description: "defines if unknown username on login screen directly return an error or always display the password screen"
|
||||||
google.protobuf.Duration second_factor_check_lifetime = 10;
|
}
|
||||||
google.protobuf.Duration multi_factor_check_lifetime = 11;
|
];
|
||||||
|
string default_redirect_uri = 8 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
description: "defines where the user will be redirected to if the login is started without app context (e.g. from mail)"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
google.protobuf.Duration password_check_lifetime = 9;
|
||||||
|
google.protobuf.Duration external_login_check_lifetime = 10;
|
||||||
|
google.protobuf.Duration mfa_init_skip_lifetime = 11;
|
||||||
|
google.protobuf.Duration second_factor_check_lifetime = 12;
|
||||||
|
google.protobuf.Duration multi_factor_check_lifetime = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AddCustomLoginPolicyResponse {
|
message AddCustomLoginPolicyResponse {
|
||||||
@ -4362,11 +4372,21 @@ message UpdateCustomLoginPolicyRequest {
|
|||||||
bool force_mfa = 4;
|
bool force_mfa = 4;
|
||||||
zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}];
|
zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}];
|
||||||
bool hide_password_reset = 6;
|
bool hide_password_reset = 6;
|
||||||
google.protobuf.Duration password_check_lifetime = 7;
|
bool ignore_unknown_usernames = 7 [
|
||||||
google.protobuf.Duration external_login_check_lifetime = 8;
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
google.protobuf.Duration mfa_init_skip_lifetime = 9;
|
description: "defines if unknown username on login screen directly return an error or always display the password screen"
|
||||||
google.protobuf.Duration second_factor_check_lifetime = 10;
|
}
|
||||||
google.protobuf.Duration multi_factor_check_lifetime = 11;
|
];
|
||||||
|
string default_redirect_uri = 8 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
description: "defines where the user will be redirected to if the login is started without app context (e.g. from mail)"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
google.protobuf.Duration password_check_lifetime = 9;
|
||||||
|
google.protobuf.Duration external_login_check_lifetime = 10;
|
||||||
|
google.protobuf.Duration mfa_init_skip_lifetime = 11;
|
||||||
|
google.protobuf.Duration second_factor_check_lifetime = 12;
|
||||||
|
google.protobuf.Duration multi_factor_check_lifetime = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpdateCustomLoginPolicyResponse {
|
message UpdateCustomLoginPolicyResponse {
|
||||||
|
@ -149,11 +149,21 @@ message LoginPolicy {
|
|||||||
description: "defines if password reset link should be shown in the login screen"
|
description: "defines if password reset link should be shown in the login screen"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
google.protobuf.Duration password_check_lifetime = 9;
|
bool ignore_unknown_usernames = 9 [
|
||||||
google.protobuf.Duration external_login_check_lifetime = 10;
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
google.protobuf.Duration mfa_init_skip_lifetime = 11;
|
description: "defines if unknown username on login screen directly return an error or always display the password screen"
|
||||||
google.protobuf.Duration second_factor_check_lifetime = 12;
|
}
|
||||||
google.protobuf.Duration multi_factor_check_lifetime = 13;
|
];
|
||||||
|
string default_redirect_uri = 10 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
description: "defines where the user will be redirected to if the login is started without app context (e.g. from mail)"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
google.protobuf.Duration password_check_lifetime = 11;
|
||||||
|
google.protobuf.Duration external_login_check_lifetime = 12;
|
||||||
|
google.protobuf.Duration mfa_init_skip_lifetime = 13;
|
||||||
|
google.protobuf.Duration second_factor_check_lifetime = 14;
|
||||||
|
google.protobuf.Duration multi_factor_check_lifetime = 15;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user