mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 19:57:22 +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
|
||||
ForceMFA: false
|
||||
HidePasswordReset: false
|
||||
IgnoreUnknownUsernames: false
|
||||
PasswordlessType: 1 #1: allowed 0: not allowed
|
||||
DefaultRedirectURI: #empty because we use the Console UI
|
||||
PasswordCheckLifetime: 240h #10d
|
||||
ExternalLoginCheckLifetime: 240h #10d
|
||||
MfaInitSkipLifetime: 720h #30d
|
||||
|
@ -129,6 +129,19 @@
|
||||
</cnsl-info-section>
|
||||
</ng-template> -->
|
||||
</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>
|
||||
|
||||
<div class="login-policy-btn-container">
|
||||
|
@ -88,10 +88,12 @@ export class LoginPolicyComponent implements OnInit {
|
||||
mgmtreq.setForceMfa(this.loginData.forceMfa);
|
||||
mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||
mgmtreq.setHidePasswordReset(this.loginData.hidePasswordReset);
|
||||
mgmtreq.setIgnoreUnknownUsernames(this.loginData.ignoreUnknownUsernames);
|
||||
mgmtreq.setDefaultRedirectUri(this.loginData.defaultRedirectUri);
|
||||
|
||||
// if(this.loginData.passwordCheckLifetime) {
|
||||
// mgmtreq.setPasswordCheckLifetime(this.loginData.passwordCheckLifetime);
|
||||
// }
|
||||
|
||||
if ((this.loginData as LoginPolicy.AsObject).isDefault) {
|
||||
return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq);
|
||||
} else {
|
||||
@ -105,6 +107,8 @@ export class LoginPolicyComponent implements OnInit {
|
||||
adminreq.setForceMfa(this.loginData.forceMfa);
|
||||
adminreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||
adminreq.setHidePasswordReset(this.loginData.hidePasswordReset);
|
||||
adminreq.setIgnoreUnknownUsernames(this.loginData.ignoreUnknownUsernames);
|
||||
adminreq.setDefaultRedirectUri(this.loginData.defaultRedirectUri);
|
||||
// adminreq.setPasswordCheckLifetime(this.loginData.passwordCheckLifetime);
|
||||
|
||||
return (this.service as AdminService).updateLoginPolicy(adminreq);
|
||||
|
@ -1092,6 +1092,10 @@
|
||||
"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.",
|
||||
"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",
|
||||
"DISABLEWATERMARK": "Wasserzeichen ausblenden"
|
||||
},
|
||||
|
@ -1092,6 +1092,10 @@
|
||||
"HIDEPASSWORDRESET": "Hide Password reset",
|
||||
"HIDEPASSWORDRESET_DESC": "If the option is selected, the user can't reset his password in the login process.",
|
||||
"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",
|
||||
"DISABLEWATERMARK": "Hide Watermark"
|
||||
},
|
||||
|
@ -1092,6 +1092,10 @@
|
||||
"HIDEPASSWORDRESET": "Nascondi ripristino della password",
|
||||
"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",
|
||||
"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",
|
||||
"DISABLEWATERMARK": "Nascondi la filigrana"
|
||||
},
|
||||
|
@ -3700,6 +3700,8 @@ This is an empty request
|
||||
| force_mfa | bool | - | |
|
||||
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
|
||||
| hide_password_reset | bool | - | |
|
||||
| ignore_unknown_usernames | bool | - | |
|
||||
| default_redirect_uri | string | - | |
|
||||
| password_check_lifetime | google.protobuf.Duration | - | |
|
||||
| external_login_check_lifetime | google.protobuf.Duration | - | |
|
||||
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
||||
|
@ -3039,6 +3039,8 @@ This is an empty request
|
||||
| force_mfa | bool | - | |
|
||||
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
|
||||
| hide_password_reset | bool | - | |
|
||||
| ignore_unknown_usernames | bool | - | |
|
||||
| default_redirect_uri | string | - | |
|
||||
| password_check_lifetime | google.protobuf.Duration | - | |
|
||||
| external_login_check_lifetime | google.protobuf.Duration | - | |
|
||||
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
||||
@ -7760,6 +7762,8 @@ This is an empty request
|
||||
| force_mfa | bool | - | |
|
||||
| passwordless_type | zitadel.policy.v1.PasswordlessType | - | enum.defined_only: true<br /> |
|
||||
| hide_password_reset | bool | - | |
|
||||
| ignore_unknown_usernames | bool | - | |
|
||||
| default_redirect_uri | string | - | |
|
||||
| password_check_lifetime | google.protobuf.Duration | - | |
|
||||
| external_login_check_lifetime | google.protobuf.Duration | - | |
|
||||
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
||||
|
@ -77,6 +77,8 @@ title: zitadel/policy.proto
|
||||
| passwordless_type | PasswordlessType | - | |
|
||||
| is_default | bool | - | |
|
||||
| hide_password_reset | bool | - | |
|
||||
| ignore_unknown_usernames | bool | - | |
|
||||
| default_redirect_uri | string | - | |
|
||||
| password_check_lifetime | google.protobuf.Duration | - | |
|
||||
| external_login_check_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,
|
||||
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
|
||||
HidePasswordReset: p.HidePasswordReset,
|
||||
IgnoreUnknownUsernames: p.IgnoreUnknownUsernames,
|
||||
DefaultRedirectURI: p.DefaultRedirectUri,
|
||||
PasswordCheckLifetime: p.PasswordCheckLifetime.AsDuration(),
|
||||
ExternalLoginCheckLifetime: p.ExternalLoginCheckLifetime.AsDuration(),
|
||||
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
|
||||
|
@ -16,6 +16,8 @@ func addLoginPolicyToDomain(p *mgmt_pb.AddCustomLoginPolicyRequest) *domain.Logi
|
||||
ForceMFA: p.ForceMfa,
|
||||
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
|
||||
HidePasswordReset: p.HidePasswordReset,
|
||||
IgnoreUnknownUsernames: p.IgnoreUnknownUsernames,
|
||||
DefaultRedirectURI: p.DefaultRedirectUri,
|
||||
PasswordCheckLifetime: p.PasswordCheckLifetime.AsDuration(),
|
||||
ExternalLoginCheckLifetime: p.ExternalLoginCheckLifetime.AsDuration(),
|
||||
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
|
||||
@ -32,6 +34,8 @@ func updateLoginPolicyToDomain(p *mgmt_pb.UpdateCustomLoginPolicyRequest) *domai
|
||||
ForceMFA: p.ForceMfa,
|
||||
PasswordlessType: policy_grpc.PasswordlessTypeToDomain(p.PasswordlessType),
|
||||
HidePasswordReset: p.HidePasswordReset,
|
||||
IgnoreUnknownUsernames: p.IgnoreUnknownUsernames,
|
||||
DefaultRedirectURI: p.DefaultRedirectUri,
|
||||
PasswordCheckLifetime: p.PasswordCheckLifetime.AsDuration(),
|
||||
ExternalLoginCheckLifetime: p.ExternalLoginCheckLifetime.AsDuration(),
|
||||
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
|
||||
|
@ -1,12 +1,13 @@
|
||||
package policy
|
||||
|
||||
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/query"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/object"
|
||||
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 {
|
||||
@ -18,6 +19,8 @@ func ModelLoginPolicyToPb(policy *query.LoginPolicy) *policy_pb.LoginPolicy {
|
||||
ForceMfa: policy.ForceMFA,
|
||||
PasswordlessType: ModelPasswordlessTypeToPb(policy.PasswordlessType),
|
||||
HidePasswordReset: policy.HidePasswordReset,
|
||||
IgnoreUnknownUsernames: policy.IgnoreUnknownUsernames,
|
||||
DefaultRedirectUri: policy.DefaultRedirectURI,
|
||||
PasswordCheckLifetime: durationpb.New(policy.PasswordCheckLifetime),
|
||||
ExternalLoginCheckLifetime: durationpb.New(policy.ExternalLoginCheckLifetime),
|
||||
MfaInitSkipLifetime: durationpb.New(policy.MFAInitSkipLifetime),
|
||||
@ -25,8 +28,8 @@ func ModelLoginPolicyToPb(policy *query.LoginPolicy) *policy_pb.LoginPolicy {
|
||||
MultiFactorCheckLifetime: durationpb.New(policy.MultiFactorCheckLifetime),
|
||||
Details: &object.ObjectDetails{
|
||||
Sequence: policy.Sequence,
|
||||
CreationDate: timestamp_pb.New(policy.CreationDate),
|
||||
ChangeDate: timestamp_pb.New(policy.ChangeDate),
|
||||
CreationDate: timestamppb.New(policy.CreationDate),
|
||||
ChangeDate: timestamppb.New(policy.ChangeDate),
|
||||
ResourceOwner: policy.OrgID,
|
||||
},
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ func (l *Login) handleExternalLogin(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if authReq == nil {
|
||||
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
||||
l.defaultRedirect(w, r)
|
||||
return
|
||||
}
|
||||
l.handleIDP(w, r, authReq, data.IDPConfigID)
|
||||
|
@ -58,7 +58,7 @@ func (l *Login) handleExternalRegister(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if authReq == nil {
|
||||
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
||||
l.defaultRedirect(w, r)
|
||||
return
|
||||
}
|
||||
idpConfig, err := l.getIDPConfigByID(r, data.IDPConfigID)
|
||||
|
@ -39,8 +39,8 @@ type initPasswordData struct {
|
||||
HasSymbol string
|
||||
}
|
||||
|
||||
func InitPasswordLink(origin, userID, code string) string {
|
||||
return fmt.Sprintf("%s%s?userID=%s&code=%s", externalLink(origin), EndpointInitPassword, userID, code)
|
||||
func InitPasswordLink(origin, userID, code, orgID string) string {
|
||||
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) {
|
||||
|
@ -42,8 +42,8 @@ type initUserData struct {
|
||||
HasSymbol string
|
||||
}
|
||||
|
||||
func InitUserLink(origin, userID, code string, passwordSet bool) string {
|
||||
return fmt.Sprintf("%s%s?userID=%s&code=%s&passwordset=%t", externalLink(origin), EndpointInitUser, userID, code, passwordSet)
|
||||
func InitUserLink(origin, userID, code, orgID string, passwordSet bool) string {
|
||||
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) {
|
||||
|
@ -120,7 +120,7 @@ func (l *Login) jwtExtractionUserNotFound(w http.ResponseWriter, r *http.Request
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
}
|
||||
resourceOwner := l.getOrgID(authReq)
|
||||
resourceOwner := l.getOrgID(r, authReq)
|
||||
orgIamPolicy, err := l.getOrgDomainPolicy(r, resourceOwner)
|
||||
if err != nil {
|
||||
l.renderError(w, r, authReq, err)
|
||||
|
@ -3,13 +3,16 @@ package login
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
tmplLogin = "login"
|
||||
tmplLogin = "login"
|
||||
queryOrgID = "orgID"
|
||||
)
|
||||
|
||||
type loginData struct {
|
||||
@ -17,8 +20,8 @@ type loginData struct {
|
||||
Register bool `schema:"register"`
|
||||
}
|
||||
|
||||
func LoginLink(origin string) string {
|
||||
return externalLink(origin) + EndpointLogin
|
||||
func LoginLink(origin, orgID string) string {
|
||||
return externalLink(origin) + EndpointLogin + "?orgID=" + orgID
|
||||
}
|
||||
|
||||
func externalLink(origin string) string {
|
||||
@ -32,12 +35,23 @@ func (l *Login) handleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if authReq == nil {
|
||||
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
||||
l.defaultRedirect(w, r)
|
||||
return
|
||||
}
|
||||
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) {
|
||||
authReq, err := l.getAuthRequest(r)
|
||||
if err != nil {
|
||||
|
@ -27,8 +27,8 @@ type mailVerificationData struct {
|
||||
UserID string
|
||||
}
|
||||
|
||||
func MailVerificationLink(origin, userID, code string) string {
|
||||
return fmt.Sprintf("%s%s?userID=%s&code=%s", externalLink(origin), EndpointMailVerification, userID, code)
|
||||
func MailVerificationLink(origin, userID, code, orgID string) string {
|
||||
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) {
|
||||
|
@ -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))
|
||||
if err != nil {
|
||||
if authReq.LoginPolicy.IgnoreUnknownUsernames {
|
||||
l.renderLogin(w, r, authReq, err)
|
||||
return
|
||||
}
|
||||
l.renderPassword(w, r, authReq, err)
|
||||
return
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
|
||||
"net/http"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
)
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
if authReq.LoginPolicy.IgnoreUnknownUsernames && errors.IsNotFound(err) {
|
||||
err = nil
|
||||
}
|
||||
l.renderPasswordResetDone(w, r, authReq, err)
|
||||
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) {
|
||||
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
|
||||
}
|
||||
if authRequest == nil {
|
||||
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
||||
l.defaultRedirect(w, r)
|
||||
return
|
||||
}
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
|
@ -73,7 +73,7 @@ func (l *Login) handleRegisterOrgCheck(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if authRequest == nil {
|
||||
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
||||
l.defaultRedirect(w, r)
|
||||
return
|
||||
}
|
||||
l.renderNextStep(w, r, authRequest)
|
||||
|
@ -342,8 +342,8 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title
|
||||
Theme: l.getTheme(r),
|
||||
ThemeMode: l.getThemeMode(r),
|
||||
DarkMode: l.isDarkMode(r),
|
||||
PrivateLabelingOrgID: l.getPrivateLabelingID(authz.GetInstance(r.Context()).InstanceID(), authReq),
|
||||
OrgID: l.getOrgID(authReq),
|
||||
PrivateLabelingOrgID: l.getPrivateLabelingID(r, authReq),
|
||||
OrgID: l.getOrgID(r, authReq),
|
||||
OrgName: l.getOrgName(authReq),
|
||||
PrimaryDomain: l.getOrgPrimaryDomain(authReq),
|
||||
DisplayLoginNameSuffix: l.isDisplayLoginNameSuffix(authReq),
|
||||
@ -361,6 +361,10 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title
|
||||
}
|
||||
privacyPolicy = authReq.PrivacyPolicy
|
||||
} else {
|
||||
labelPolicy, _ := l.query.ActiveLabelPolicyByOrg(r.Context(), baseData.PrivateLabelingOrgID)
|
||||
if labelPolicy != nil {
|
||||
baseData.LabelPolicy = labelPolicy.ToDomain()
|
||||
}
|
||||
policy, err := l.query.DefaultPrivacyPolicy(r.Context())
|
||||
if err != nil {
|
||||
return baseData
|
||||
@ -446,9 +450,9 @@ func (l *Login) isDarkMode(r *http.Request) bool {
|
||||
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 {
|
||||
return ""
|
||||
return r.FormValue(queryOrgID)
|
||||
}
|
||||
if authReq.RequestedOrgID != "" {
|
||||
return authReq.RequestedOrgID
|
||||
@ -456,9 +460,12 @@ func (l *Login) getOrgID(authReq *domain.AuthRequest) string {
|
||||
return authReq.UserOrgID
|
||||
}
|
||||
|
||||
func (l *Login) getPrivateLabelingID(instanceID string, authReq *domain.AuthRequest) string {
|
||||
privateLabelingOrgID := instanceID
|
||||
func (l *Login) getPrivateLabelingID(r *http.Request, authReq *domain.AuthRequest) string {
|
||||
privateLabelingOrgID := authz.GetInstance(r.Context()).InstanceID()
|
||||
if authReq == nil {
|
||||
if id := r.FormValue(queryOrgID); id != "" {
|
||||
return id
|
||||
}
|
||||
return privateLabelingOrgID
|
||||
}
|
||||
if authReq.PrivateLabelingSetting != domain.PrivateLabelingSettingUnspecified {
|
||||
|
@ -322,6 +322,8 @@ Errors:
|
||||
Empty: Passwort ist leer
|
||||
Invalid: Passwort ungültig
|
||||
InvalidAndLocked: Password ist undgültig und Benutzer wurde gesperrt, melden Sie sich bei ihrem Administrator.
|
||||
UsernameOrPassword:
|
||||
Invalid: Username oder Passwort ist ungültig
|
||||
PasswordComplexityPolicy:
|
||||
NotFound: Passwort Policy konnte nicht gefunden werden
|
||||
MinLength: Passwort ist zu kurz
|
||||
|
@ -323,6 +323,8 @@ Errors:
|
||||
Empty: Password is empty
|
||||
Invalid: Password is invalid
|
||||
InvalidAndLocked: Password is invalid and user is locked, contact your administrator.
|
||||
UsernameOrPassword:
|
||||
Invalid: Username or Password is invalid
|
||||
PasswordComplexityPolicy:
|
||||
NotFound: Password policy not found
|
||||
MinLength: Password is to short
|
||||
|
@ -323,6 +323,8 @@ Errors:
|
||||
Empty: La password è vuota
|
||||
Invalid: La password non è valida
|
||||
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:
|
||||
NotFound: Impostazioni della password non trovate
|
||||
MinLength: La password è troppo corta
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||
<input type="hidden" name="userID" value="{{ .UserID }}" />
|
||||
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
@ -56,4 +57,4 @@
|
||||
<script src="{{ resourceUrl "scripts/init_password_check.js" }}"></script>
|
||||
|
||||
|
||||
{{template "main-bottom" .}}
|
||||
{{template "main-bottom" .}}
|
||||
|
@ -12,15 +12,13 @@
|
||||
{{ .CSRF }}
|
||||
|
||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||
|
||||
<div class="lgnactions">
|
||||
<a class="lgn-stroked-button lgn-primary" href="{{ loginUrl }}">
|
||||
{{t "InitPasswordDone.CancelButtonText"}}
|
||||
</a>
|
||||
<div class="lgn-actions">
|
||||
<span class="fill-space"></span>
|
||||
<button class="lgn-raised-button lgn-primary" type="submit">{{t "InitPasswordDone.NextButtonText"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
{{template "main-bottom" .}}
|
||||
{{template "main-bottom" .}}
|
||||
|
@ -15,6 +15,7 @@
|
||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||
<input type="hidden" name="userID" value="{{ .UserID }}" />
|
||||
<input type="hidden" name="passwordSet" value="{{ .PasswordSet }}" />
|
||||
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
@ -63,4 +64,4 @@
|
||||
<script src="{{ resourceUrl "scripts/init_password_check.js" }}"></script>
|
||||
{{ end }}
|
||||
|
||||
{{template "main-bottom" .}}
|
||||
{{template "main-bottom" .}}
|
||||
|
@ -13,6 +13,7 @@
|
||||
{{ .CSRF }}
|
||||
|
||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||
|
||||
<div class="lgn-actions lgn-reverse-order">
|
||||
<button class="lgn-raised-button lgn-primary" type="submit">{{t "InitUserDone.NextButtonText"}}</button>
|
||||
@ -24,4 +25,4 @@
|
||||
</form>
|
||||
|
||||
|
||||
{{template "main-bottom" .}}
|
||||
{{template "main-bottom" .}}
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||
<input type="hidden" name="userID" value="{{ .UserID }}" />
|
||||
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||
|
||||
<div class="fields">
|
||||
<label class="lgn-label" for="code">{{t "EmailVerification.CodeLabel"}}</label>
|
||||
@ -41,4 +42,4 @@
|
||||
<script src="{{ resourceUrl "scripts/form_submit.js" }}"></script>
|
||||
<script src="{{ resourceUrl "scripts/default_form_validation.js" }}"></script>
|
||||
|
||||
{{template "main-bottom" .}}
|
||||
{{template "main-bottom" .}}
|
||||
|
@ -12,6 +12,7 @@
|
||||
{{ .CSRF }}
|
||||
|
||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||
|
||||
<div class="lgn-actions">
|
||||
<a class="lgn-stroked-button lgn-primary" href="{{ loginUrl }}">
|
||||
@ -29,4 +30,4 @@
|
||||
</form>
|
||||
|
||||
|
||||
{{template "main-bottom" .}}
|
||||
{{template "main-bottom" .}}
|
||||
|
@ -16,6 +16,7 @@
|
||||
{{ .CSRF }}
|
||||
|
||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||
|
||||
<div class="lgn-actions">
|
||||
{{if not .HideNextButton }}
|
||||
|
@ -27,6 +27,8 @@ import (
|
||||
user_view_model "github.com/zitadel/zitadel/internal/user/repository/view/model"
|
||||
)
|
||||
|
||||
const unknownUserID = "UNKNOWN"
|
||||
|
||||
type AuthRequestRepo struct {
|
||||
Command *command.Commands
|
||||
Query *query.Queries
|
||||
@ -284,7 +286,7 @@ func (repo *AuthRequestRepo) SelectUser(ctx context.Context, id, userID, userAge
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -304,13 +306,28 @@ func (repo *AuthRequestRepo) VerifyPassword(ctx context.Context, authReqID, user
|
||||
defer func() { span.EndWithError(err) }()
|
||||
request, err := repo.getAuthRequestEnsureUser(ctx, authReqID, userAgentID, userID)
|
||||
if err != nil {
|
||||
if isIgnoreUserNotFoundError(err, request) {
|
||||
return errors.ThrowInvalidArgument(nil, "EVENT-SDe2f", "Errors.User.UsernameOrPassword.Invalid")
|
||||
}
|
||||
return err
|
||||
}
|
||||
policy, err := repo.getLockoutPolicy(ctx, resourceOwner)
|
||||
if err != nil {
|
||||
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 {
|
||||
@ -499,9 +516,9 @@ func (repo *AuthRequestRepo) getAuthRequestEnsureUser(ctx context.Context, authR
|
||||
if request.UserID != userID {
|
||||
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 {
|
||||
return nil, err
|
||||
return request, err
|
||||
}
|
||||
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) {
|
||||
user := new(user_view_model.UserView)
|
||||
preferredLoginName := loginName
|
||||
if request.RequestedOrgID != "" {
|
||||
preferredLoginName := loginName
|
||||
if request.RequestedOrgID != "" {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -675,6 +701,7 @@ func queryLoginPolicyToDomain(policy *query.LoginPolicy) *domain.LoginPolicy {
|
||||
MultiFactors: policy.MultiFactors,
|
||||
PasswordlessType: policy.PasswordlessType,
|
||||
HidePasswordReset: policy.HidePasswordReset,
|
||||
IgnoreUnknownUsernames: policy.IgnoreUnknownUsernames,
|
||||
PasswordCheckLifetime: policy.PasswordCheckLifetime,
|
||||
ExternalLoginCheckLifetime: policy.ExternalLoginCheckLifetime,
|
||||
MFAInitSkipLifetime: policy.MFAInitSkipLifetime,
|
||||
@ -703,7 +730,7 @@ func (repo *AuthRequestRepo) checkExternalUserLogin(ctx context.Context, request
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -749,11 +776,13 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
|
||||
}
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
request.LoginName = user.PreferredLoginName
|
||||
if user.PreferredLoginName != "" {
|
||||
request.LoginName = user.PreferredLoginName
|
||||
}
|
||||
userSession, err := userSessionByIDs(ctx, repo.UserSessionViewProvider, repo.UserEventProvider, request.AgentID, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -1129,10 +1158,16 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
|
||||
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
|
||||
user, err := userByID(ctx, userViewProvider, userEventProvider, userID)
|
||||
user, err = userByID(ctx, userViewProvider, userEventProvider, userID)
|
||||
if err != nil {
|
||||
if ignoreUnknownUsernames && errors.IsNotFound(err) {
|
||||
return &user_model.UserView{
|
||||
ID: userID,
|
||||
HumanView: &user_model.HumanView{},
|
||||
}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -421,7 +421,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
userViewProvider: &mockViewNoUser{},
|
||||
userEventProvider: &mockEventUser{},
|
||||
},
|
||||
args{&domain.AuthRequest{UserID: "UserID"}, false},
|
||||
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
|
||||
nil,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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{
|
||||
PasswordSet: true,
|
||||
}},
|
||||
|
@ -68,7 +68,9 @@ type InstanceSetup struct {
|
||||
AllowExternalIDP bool
|
||||
ForceMFA bool
|
||||
HidePasswordReset bool
|
||||
IgnoreUnknownUsername bool
|
||||
PasswordlessType domain.PasswordlessType
|
||||
DefaultRedirectURI string
|
||||
PasswordCheckLifetime time.Duration
|
||||
ExternalLoginCheckLifetime time.Duration
|
||||
MfaInitSkipLifetime time.Duration
|
||||
@ -205,7 +207,9 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str
|
||||
setup.LoginPolicy.AllowExternalIDP,
|
||||
setup.LoginPolicy.ForceMFA,
|
||||
setup.LoginPolicy.HidePasswordReset,
|
||||
setup.LoginPolicy.IgnoreUnknownUsername,
|
||||
setup.LoginPolicy.PasswordlessType,
|
||||
setup.LoginPolicy.DefaultRedirectURI,
|
||||
setup.LoginPolicy.PasswordCheckLifetime,
|
||||
setup.LoginPolicy.ExternalLoginCheckLifetime,
|
||||
setup.LoginPolicy.MfaInitSkipLifetime,
|
||||
|
@ -31,8 +31,10 @@ func writeModelToLoginPolicy(wm *LoginPolicyWriteModel) *domain.LoginPolicy {
|
||||
AllowRegister: wm.AllowRegister,
|
||||
AllowExternalIDP: wm.AllowExternalIDP,
|
||||
HidePasswordReset: wm.HidePasswordReset,
|
||||
IgnoreUnknownUsernames: wm.IgnoreUnknownUsernames,
|
||||
ForceMFA: wm.ForceMFA,
|
||||
PasswordlessType: wm.PasswordlessType,
|
||||
DefaultRedirectURI: wm.DefaultRedirectURI,
|
||||
PasswordCheckLifetime: wm.PasswordCheckLifetime,
|
||||
ExternalLoginCheckLifetime: wm.ExternalLoginCheckLifetime,
|
||||
MFAInitSkipLifetime: wm.MFAInitSkipLifetime,
|
||||
|
@ -17,7 +17,9 @@ func AddDefaultLoginPolicy(
|
||||
allowExternalIDP bool,
|
||||
forceMFA bool,
|
||||
hidePasswordReset bool,
|
||||
ignoreUnknownUsernames bool,
|
||||
passwordlessType domain.PasswordlessType,
|
||||
defaultRedirectURI string,
|
||||
passwordCheckLifetime time.Duration,
|
||||
externalLoginCheckLifetime time.Duration,
|
||||
mfaInitSkipLifetime time.Duration,
|
||||
@ -34,7 +36,9 @@ func AddDefaultLoginPolicy(
|
||||
allowExternalIDP,
|
||||
forceMFA,
|
||||
hidePasswordReset,
|
||||
ignoreUnknownUsernames,
|
||||
passwordlessType,
|
||||
defaultRedirectURI,
|
||||
passwordCheckLifetime,
|
||||
externalLoginCheckLifetime,
|
||||
mfaInitSkipLifetime,
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
@ -56,7 +57,9 @@ func (c *Commands) addDefaultLoginPolicy(ctx context.Context, instanceAgg *event
|
||||
policy.AllowExternalIDP,
|
||||
policy.ForceMFA,
|
||||
policy.HidePasswordReset,
|
||||
policy.IgnoreUnknownUsernames,
|
||||
policy.PasswordlessType,
|
||||
policy.DefaultRedirectURI,
|
||||
policy.PasswordCheckLifetime,
|
||||
policy.ExternalLoginCheckLifetime,
|
||||
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) {
|
||||
if ok := domain.ValidateDefaultRedirectURI(policy.DefaultRedirectURI); !ok {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-SFdqd", "Errors.IAM.LoginPolicy.RedirectURIInvalid")
|
||||
}
|
||||
err := c.defaultLoginPolicyWriteModelByID(ctx, existingPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -97,12 +103,15 @@ func (c *Commands) changeDefaultLoginPolicy(ctx context.Context, instanceAgg *ev
|
||||
policy.AllowExternalIDP,
|
||||
policy.ForceMFA,
|
||||
policy.HidePasswordReset,
|
||||
policy.IgnoreUnknownUsernames,
|
||||
policy.PasswordlessType,
|
||||
policy.DefaultRedirectURI,
|
||||
policy.PasswordCheckLifetime,
|
||||
policy.ExternalLoginCheckLifetime,
|
||||
policy.MFAInitSkipLifetime,
|
||||
policy.SecondFactorCheckLifetime,
|
||||
policy.MultiFactorCheckLifetime)
|
||||
policy.MultiFactorCheckLifetime,
|
||||
)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "INSTANCE-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
|
||||
}
|
||||
|
@ -65,8 +65,10 @@ func (wm *InstanceLoginPolicyWriteModel) NewChangedEvent(
|
||||
allowRegister,
|
||||
allowExternalIDP,
|
||||
forceMFA,
|
||||
hidePasswordReset bool,
|
||||
hidePasswordReset,
|
||||
ignoreUnknownUsernames bool,
|
||||
passwordlessType domain.PasswordlessType,
|
||||
defaultRedirectURI string,
|
||||
passwordCheckLifetime,
|
||||
externalLoginCheckLifetime,
|
||||
mfaInitSkipLifetime,
|
||||
@ -93,6 +95,12 @@ func (wm *InstanceLoginPolicyWriteModel) NewChangedEvent(
|
||||
if wm.HidePasswordReset != 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 {
|
||||
changes = append(changes, policy.ChangePasswordCheckLifetime(passwordCheckLifetime))
|
||||
}
|
||||
|
@ -50,7 +50,9 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*1,
|
||||
time.Hour*1,
|
||||
@ -90,7 +92,9 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"https://example.com/redirect",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -110,7 +114,9 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
HidePasswordReset: true,
|
||||
IgnoreUnknownUsernames: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
DefaultRedirectURI: "https://example.com/redirect",
|
||||
PasswordCheckLifetime: time.Hour * 1,
|
||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||
MFAInitSkipLifetime: time.Hour * 3,
|
||||
@ -130,7 +136,9 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) {
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
HidePasswordReset: true,
|
||||
IgnoreUnknownUsernames: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
DefaultRedirectURI: "https://example.com/redirect",
|
||||
PasswordCheckLifetime: time.Hour * 1,
|
||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||
MFAInitSkipLifetime: time.Hour * 3,
|
||||
@ -210,7 +218,9 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"https://example.com/redirect",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -229,7 +239,9 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
HidePasswordReset: true,
|
||||
IgnoreUnknownUsernames: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
DefaultRedirectURI: "https://example.com/redirect",
|
||||
PasswordCheckLifetime: time.Hour * 1,
|
||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||
MFAInitSkipLifetime: time.Hour * 3,
|
||||
@ -256,7 +268,9 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"https://example.com/redirect",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -275,7 +289,9 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*10,
|
||||
time.Hour*20,
|
||||
time.Hour*30,
|
||||
@ -294,7 +310,9 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
|
||||
AllowExternalIDP: false,
|
||||
ForceMFA: false,
|
||||
HidePasswordReset: false,
|
||||
IgnoreUnknownUsernames: false,
|
||||
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
||||
DefaultRedirectURI: "",
|
||||
PasswordCheckLifetime: time.Hour * 10,
|
||||
ExternalLoginCheckLifetime: time.Hour * 20,
|
||||
MFAInitSkipLifetime: time.Hour * 30,
|
||||
@ -314,7 +332,9 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
|
||||
AllowExternalIDP: false,
|
||||
ForceMFA: false,
|
||||
HidePasswordReset: false,
|
||||
IgnoreUnknownUsernames: false,
|
||||
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
||||
DefaultRedirectURI: "",
|
||||
PasswordCheckLifetime: time.Hour * 10,
|
||||
ExternalLoginCheckLifetime: time.Hour * 20,
|
||||
MFAInitSkipLifetime: time.Hour * 30,
|
||||
@ -408,7 +428,9 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -444,7 +466,9 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -500,7 +524,9 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -638,7 +664,9 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -674,7 +702,9 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -723,7 +753,9 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -777,7 +809,9 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -839,7 +873,9 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
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,
|
||||
redirectURI string,
|
||||
passwordLifetime, externalLoginLifetime, mfaInitSkipLifetime, secondFactorLifetime, multiFactorLifetime time.Duration) *instance.LoginPolicyChangedEvent {
|
||||
event, _ := instance.NewLoginPolicyChangedEvent(ctx,
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
@ -1403,7 +1440,9 @@ func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allow
|
||||
policy.ChangeForceMFA(forceMFA),
|
||||
policy.ChangeAllowUserNamePassword(allowUsernamePassword),
|
||||
policy.ChangeHidePasswordReset(hidePasswordReset),
|
||||
policy.ChangeIgnoreUnknownUsernames(ignoreUnknownUsernames),
|
||||
policy.ChangePasswordlessType(passwordlessType),
|
||||
policy.ChangeDefaultRedirectURI(redirectURI),
|
||||
policy.ChangePasswordCheckLifetime(passwordLifetime),
|
||||
policy.ChangeExternalLoginCheckLifetime(externalLoginLifetime),
|
||||
policy.ChangeMFAInitSkipLifetime(mfaInitSkipLifetime),
|
||||
|
@ -16,6 +16,9 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
|
||||
if resourceOwner == "" {
|
||||
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)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||
if err != nil {
|
||||
@ -36,7 +39,9 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
|
||||
policy.AllowExternalIDP,
|
||||
policy.ForceMFA,
|
||||
policy.HidePasswordReset,
|
||||
policy.IgnoreUnknownUsernames,
|
||||
policy.PasswordlessType,
|
||||
policy.DefaultRedirectURI,
|
||||
policy.PasswordCheckLifetime,
|
||||
policy.ExternalLoginCheckLifetime,
|
||||
policy.MFAInitSkipLifetime,
|
||||
@ -76,6 +81,9 @@ func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string,
|
||||
if resourceOwner == "" {
|
||||
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)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||
if err != nil {
|
||||
@ -94,7 +102,9 @@ func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string,
|
||||
policy.AllowExternalIDP,
|
||||
policy.ForceMFA,
|
||||
policy.HidePasswordReset,
|
||||
policy.IgnoreUnknownUsernames,
|
||||
policy.PasswordlessType,
|
||||
policy.DefaultRedirectURI,
|
||||
policy.PasswordCheckLifetime,
|
||||
policy.ExternalLoginCheckLifetime,
|
||||
policy.MFAInitSkipLifetime,
|
||||
|
@ -67,8 +67,10 @@ func (wm *OrgLoginPolicyWriteModel) NewChangedEvent(
|
||||
allowRegister,
|
||||
allowExternalIDP,
|
||||
forceMFA,
|
||||
hidePasswordReset bool,
|
||||
hidePasswordReset,
|
||||
ignoreUnknownUsernames bool,
|
||||
passwordlessType domain.PasswordlessType,
|
||||
defaultRedirectURI string,
|
||||
passwordCheckLifetime,
|
||||
externalLoginCheckLifetime,
|
||||
mfaInitSkipLifetime,
|
||||
@ -92,6 +94,9 @@ func (wm *OrgLoginPolicyWriteModel) NewChangedEvent(
|
||||
if wm.HidePasswordReset != hidePasswordReset {
|
||||
changes = append(changes, policy.ChangeHidePasswordReset(hidePasswordReset))
|
||||
}
|
||||
if wm.IgnoreUnknownUsernames != ignoreUnknownUsernames {
|
||||
changes = append(changes, policy.ChangeIgnoreUnknownUsernames(ignoreUnknownUsernames))
|
||||
}
|
||||
if wm.PasswordCheckLifetime != passwordCheckLifetime {
|
||||
changes = append(changes, policy.ChangePasswordCheckLifetime(passwordCheckLifetime))
|
||||
}
|
||||
@ -110,6 +115,9 @@ func (wm *OrgLoginPolicyWriteModel) NewChangedEvent(
|
||||
if passwordlessType.Valid() && wm.PasswordlessType != passwordlessType {
|
||||
changes = append(changes, policy.ChangePasswordlessType(passwordlessType))
|
||||
}
|
||||
if wm.DefaultRedirectURI != defaultRedirectURI {
|
||||
changes = append(changes, policy.ChangeDefaultRedirectURI(defaultRedirectURI))
|
||||
}
|
||||
if len(changes) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
@ -77,7 +77,9 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"https://example.com/redirect",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -96,7 +98,9 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
||||
AllowUsernamePassword: true,
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
IgnoreUnknownUsernames: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
DefaultRedirectURI: "https://example.com/redirect",
|
||||
PasswordCheckLifetime: time.Hour * 1,
|
||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||
MFAInitSkipLifetime: time.Hour * 3,
|
||||
@ -124,7 +128,9 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"https://example.com/redirect",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -145,7 +151,9 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
HidePasswordReset: true,
|
||||
IgnoreUnknownUsernames: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
DefaultRedirectURI: "https://example.com/redirect",
|
||||
PasswordCheckLifetime: time.Hour * 1,
|
||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||
MFAInitSkipLifetime: time.Hour * 3,
|
||||
@ -164,7 +172,9 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
HidePasswordReset: true,
|
||||
IgnoreUnknownUsernames: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
DefaultRedirectURI: "https://example.com/redirect",
|
||||
PasswordCheckLifetime: time.Hour * 1,
|
||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||
MFAInitSkipLifetime: time.Hour * 3,
|
||||
@ -222,11 +232,13 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.LoginPolicy{
|
||||
AllowRegister: true,
|
||||
AllowUsernamePassword: true,
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
AllowRegister: true,
|
||||
AllowUsernamePassword: true,
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
IgnoreUnknownUsernames: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
DefaultRedirectURI: "https://example.com/redirect",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
@ -245,11 +257,13 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.LoginPolicy{
|
||||
AllowRegister: true,
|
||||
AllowUsernamePassword: true,
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
AllowRegister: true,
|
||||
AllowUsernamePassword: true,
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
IgnoreUnknownUsernames: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
DefaultRedirectURI: "https://example.com/redirect",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
@ -270,7 +284,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"https://example.com/redirect",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -290,7 +306,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
HidePasswordReset: true,
|
||||
IgnoreUnknownUsernames: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
DefaultRedirectURI: "https://example.com/redirect",
|
||||
PasswordCheckLifetime: time.Hour * 1,
|
||||
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||
MFAInitSkipLifetime: time.Hour * 3,
|
||||
@ -316,7 +334,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"https://example.com/redirect",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -335,7 +355,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
&duration10,
|
||||
&duration20,
|
||||
&duration30,
|
||||
@ -355,7 +377,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
AllowUsernamePassword: false,
|
||||
AllowExternalIDP: false,
|
||||
ForceMFA: false,
|
||||
IgnoreUnknownUsernames: false,
|
||||
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
||||
DefaultRedirectURI: "",
|
||||
PasswordCheckLifetime: time.Hour * 10,
|
||||
ExternalLoginCheckLifetime: time.Hour * 20,
|
||||
MFAInitSkipLifetime: time.Hour * 30,
|
||||
@ -374,7 +398,9 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
AllowExternalIDP: false,
|
||||
ForceMFA: false,
|
||||
HidePasswordReset: false,
|
||||
IgnoreUnknownUsernames: false,
|
||||
PasswordlessType: domain.PasswordlessTypeNotAllowed,
|
||||
DefaultRedirectURI: "",
|
||||
PasswordCheckLifetime: time.Hour * 10,
|
||||
ExternalLoginCheckLifetime: time.Hour * 20,
|
||||
MFAInitSkipLifetime: time.Hour * 30,
|
||||
@ -465,7 +491,9 @@ func TestCommandSide_RemoveLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -603,7 +631,9 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -642,7 +672,9 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -701,7 +733,9 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -863,7 +897,9 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -902,7 +938,9 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -953,7 +991,9 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -1011,7 +1051,9 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -1077,7 +1119,9 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
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,
|
||||
redirectURI string,
|
||||
passwordLifetime, externalLoginLifetime, mfaInitSkipLifetime, secondFactorLifetime, multiFactorLifetime *time.Duration) *org.LoginPolicyChangedEvent {
|
||||
changes := []policy.LoginPolicyChanges{
|
||||
policy.ChangeAllowUserNamePassword(usernamePassword),
|
||||
@ -1703,7 +1748,9 @@ func newLoginPolicyChangedEvent(ctx context.Context, orgID string, usernamePassw
|
||||
policy.ChangeAllowExternalIDP(externalIDP),
|
||||
policy.ChangeForceMFA(mfa),
|
||||
policy.ChangeHidePasswordReset(passwordReset),
|
||||
policy.ChangeIgnoreUnknownUsernames(ignoreUnknownUsernames),
|
||||
policy.ChangePasswordlessType(passwordlessType),
|
||||
policy.ChangeDefaultRedirectURI(redirectURI),
|
||||
}
|
||||
if passwordLifetime != nil {
|
||||
changes = append(changes, policy.ChangePasswordCheckLifetime(*passwordLifetime))
|
||||
|
@ -16,7 +16,9 @@ type LoginPolicyWriteModel struct {
|
||||
AllowExternalIDP bool
|
||||
ForceMFA bool
|
||||
HidePasswordReset bool
|
||||
IgnoreUnknownUsernames bool
|
||||
PasswordlessType domain.PasswordlessType
|
||||
DefaultRedirectURI string
|
||||
PasswordCheckLifetime time.Duration
|
||||
ExternalLoginCheckLifetime time.Duration
|
||||
MFAInitSkipLifetime time.Duration
|
||||
@ -35,6 +37,8 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
|
||||
wm.ForceMFA = e.ForceMFA
|
||||
wm.PasswordlessType = e.PasswordlessType
|
||||
wm.HidePasswordReset = e.HidePasswordReset
|
||||
wm.IgnoreUnknownUsernames = e.IgnoreUnknownUsernames
|
||||
wm.DefaultRedirectURI = e.DefaultRedirectURI
|
||||
wm.PasswordCheckLifetime = e.PasswordCheckLifetime
|
||||
wm.ExternalLoginCheckLifetime = e.ExternalLoginCheckLifetime
|
||||
wm.MFAInitSkipLifetime = e.MFAInitSkipLifetime
|
||||
@ -57,9 +61,15 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
|
||||
if e.HidePasswordReset != nil {
|
||||
wm.HidePasswordReset = *e.HidePasswordReset
|
||||
}
|
||||
if e.IgnoreUnknownUsernames != nil {
|
||||
wm.IgnoreUnknownUsernames = *e.IgnoreUnknownUsernames
|
||||
}
|
||||
if e.PasswordlessType != nil {
|
||||
wm.PasswordlessType = *e.PasswordlessType
|
||||
}
|
||||
if e.DefaultRedirectURI != nil {
|
||||
wm.DefaultRedirectURI = *e.DefaultRedirectURI
|
||||
}
|
||||
if e.PasswordCheckLifetime != nil {
|
||||
wm.PasswordCheckLifetime = *e.PasswordCheckLifetime
|
||||
}
|
||||
|
@ -1156,7 +1156,9 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -1191,7 +1193,9 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -1227,7 +1231,9 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -1279,7 +1285,9 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -1365,7 +1373,9 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -1458,7 +1468,9 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
|
@ -1687,7 +1687,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -1750,7 +1752,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -1813,7 +1817,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -1893,7 +1899,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -2031,7 +2039,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -2137,7 +2147,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -2237,7 +2249,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
@ -2359,7 +2373,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
"",
|
||||
time.Hour*1,
|
||||
time.Hour*2,
|
||||
time.Hour*3,
|
||||
|
@ -1,6 +1,7 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
@ -19,6 +20,8 @@ type LoginPolicy struct {
|
||||
MultiFactors []MultiFactorType
|
||||
PasswordlessType PasswordlessType
|
||||
HidePasswordReset bool
|
||||
IgnoreUnknownUsernames bool
|
||||
DefaultRedirectURI string
|
||||
PasswordCheckLifetime time.Duration
|
||||
ExternalLoginCheckLifetime time.Duration
|
||||
MFAInitSkipLifetime time.Duration
|
||||
@ -26,6 +29,24 @@ type LoginPolicy struct {
|
||||
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 {
|
||||
models.ObjectRoot
|
||||
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 {
|
||||
url := login.LoginLink(origin)
|
||||
url := login.LoginLink(origin, user.ResourceOwner)
|
||||
var args = mapNotifyUserToArgs(user)
|
||||
args["TempUsername"] = username
|
||||
args["Domain"] = strings.Split(user.LastEmail, "@")[1]
|
||||
|
@ -26,7 +26,7 @@ func SendEmailVerificationCode(ctx context.Context, mailhtml string, translator
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
url := login.MailVerificationLink(origin, user.ID, codeString)
|
||||
url := login.MailVerificationLink(origin, user.ID, codeString, user.ResourceOwner)
|
||||
var args = mapNotifyUserToArgs(user)
|
||||
args["Code"] = codeString
|
||||
|
||||
|
@ -25,6 +25,7 @@ type UrlData struct {
|
||||
UserID string
|
||||
Code string
|
||||
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 {
|
||||
@ -32,7 +33,7 @@ func SendUserInitCode(ctx context.Context, mailhtml string, translator *i18n.Tra
|
||||
if err != nil {
|
||||
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)
|
||||
args["Code"] = codeString
|
||||
|
||||
|
@ -29,7 +29,7 @@ func SendPasswordCode(ctx context.Context, mailhtml string, translator *i18n.Tra
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
url := login.InitPasswordLink(origin, user.ID, codeString)
|
||||
url := login.InitPasswordLink(origin, user.ID, codeString, user.ResourceOwner)
|
||||
var args = mapNotifyUserToArgs(user)
|
||||
args["Code"] = codeString
|
||||
|
||||
|
@ -307,3 +307,25 @@ func prepareLabelPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LabelPolicy,
|
||||
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/zitadel/zitadel/internal/api/authz"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
@ -30,6 +29,8 @@ type LoginPolicy struct {
|
||||
PasswordlessType domain.PasswordlessType
|
||||
IsDefault bool
|
||||
HidePasswordReset bool
|
||||
IgnoreUnknownUsernames bool
|
||||
DefaultRedirectURI string
|
||||
PasswordCheckLifetime time.Duration
|
||||
ExternalLoginCheckLifetime time.Duration
|
||||
MFAInitSkipLifetime time.Duration
|
||||
@ -107,6 +108,14 @@ var (
|
||||
name: projection.LoginPolicyHidePWResetCol,
|
||||
table: loginPolicyTable,
|
||||
}
|
||||
LoginPolicyColumnIgnoreUnknownUsernames = Column{
|
||||
name: projection.IgnoreUnknownUsernames,
|
||||
table: loginPolicyTable,
|
||||
}
|
||||
LoginPolicyColumnDefaultRedirectURI = Column{
|
||||
name: projection.DefaultRedirectURI,
|
||||
table: loginPolicyTable,
|
||||
}
|
||||
LoginPolicyColumnPasswordCheckLifetime = Column{
|
||||
name: projection.PasswordCheckLifetimeCol,
|
||||
table: loginPolicyTable,
|
||||
@ -284,6 +293,8 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
|
||||
LoginPolicyColumnPasswordlessType.identifier(),
|
||||
LoginPolicyColumnIsDefault.identifier(),
|
||||
LoginPolicyColumnHidePasswordReset.identifier(),
|
||||
LoginPolicyColumnIgnoreUnknownUsernames.identifier(),
|
||||
LoginPolicyColumnDefaultRedirectURI.identifier(),
|
||||
LoginPolicyColumnPasswordCheckLifetime.identifier(),
|
||||
LoginPolicyColumnExternalLoginCheckLifetime.identifier(),
|
||||
LoginPolicyColumnMFAInitSkipLifetime.identifier(),
|
||||
@ -294,6 +305,7 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
|
||||
p := new(LoginPolicy)
|
||||
secondFactors := pq.Int32Array{}
|
||||
multiFactors := pq.Int32Array{}
|
||||
defaultRedirectURI := sql.NullString{}
|
||||
err := row.Scan(
|
||||
&p.OrgID,
|
||||
&p.CreationDate,
|
||||
@ -308,6 +320,8 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
|
||||
&p.PasswordlessType,
|
||||
&p.IsDefault,
|
||||
&p.HidePasswordReset,
|
||||
&p.IgnoreUnknownUsernames,
|
||||
&defaultRedirectURI,
|
||||
&p.PasswordCheckLifetime,
|
||||
&p.ExternalLoginCheckLifetime,
|
||||
&p.MFAInitSkipLifetime,
|
||||
@ -320,7 +334,7 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
|
||||
}
|
||||
return nil, errors.ThrowInternal(err, "QUERY-YcC53", "Errors.Internal")
|
||||
}
|
||||
|
||||
p.DefaultRedirectURI = defaultRedirectURI.String
|
||||
p.MultiFactors = make([]domain.MultiFactorType, len(multiFactors))
|
||||
for i, mfa := range multiFactors {
|
||||
p.MultiFactors[i] = domain.MultiFactorType(mfa)
|
||||
|
@ -44,6 +44,8 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
||||
` projections.login_policies.passwordless_type,`+
|
||||
` projections.login_policies.is_default,`+
|
||||
` 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.external_login_check_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.is_default,`+
|
||||
` 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.external_login_check_lifetime,`+
|
||||
` projections.login_policies.mfa_init_skip_lifetime,`+
|
||||
@ -100,6 +104,8 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
||||
"passwordless_type",
|
||||
"is_default",
|
||||
"hide_password_reset",
|
||||
"ignore_unknown_usernames",
|
||||
"default_redirect_uri",
|
||||
"password_check_lifetime",
|
||||
"external_login_check_lifetime",
|
||||
"mfa_init_skip_lifetime",
|
||||
@ -120,6 +126,8 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
||||
domain.PasswordlessTypeAllowed,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
"https://example.com/redirect",
|
||||
time.Hour * 2,
|
||||
time.Hour * 2,
|
||||
time.Hour * 2,
|
||||
@ -142,6 +150,8 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
IsDefault: true,
|
||||
HidePasswordReset: true,
|
||||
IgnoreUnknownUsernames: true,
|
||||
DefaultRedirectURI: "https://example.com/redirect",
|
||||
PasswordCheckLifetime: time.Hour * 2,
|
||||
ExternalLoginCheckLifetime: 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.is_default,`+
|
||||
` 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.external_login_check_lifetime,`+
|
||||
` projections.login_policies.mfa_init_skip_lifetime,`+
|
||||
|
@ -29,6 +29,8 @@ const (
|
||||
LoginPolicyMFAsCol = "multi_factors"
|
||||
LoginPolicyPasswordlessTypeCol = "passwordless_type"
|
||||
LoginPolicyHidePWResetCol = "hide_password_reset"
|
||||
IgnoreUnknownUsernames = "ignore_unknown_usernames"
|
||||
DefaultRedirectURI = "default_redirect_uri"
|
||||
PasswordCheckLifetimeCol = "password_check_lifetime"
|
||||
ExternalLoginCheckLifetimeCol = "external_login_check_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(LoginPolicyPasswordlessTypeCol, crdb.ColumnTypeEnum),
|
||||
crdb.NewColumn(LoginPolicyHidePWResetCol, crdb.ColumnTypeBool),
|
||||
crdb.NewColumn(IgnoreUnknownUsernames, crdb.ColumnTypeBool),
|
||||
crdb.NewColumn(DefaultRedirectURI, crdb.ColumnTypeText, crdb.Nullable()),
|
||||
crdb.NewColumn(PasswordCheckLifetimeCol, crdb.ColumnTypeInt64),
|
||||
crdb.NewColumn(ExternalLoginCheckLifetimeCol, 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(LoginPolicyIsDefaultCol, isDefault),
|
||||
handler.NewCol(LoginPolicyHidePWResetCol, policyEvent.HidePasswordReset),
|
||||
handler.NewCol(IgnoreUnknownUsernames, policyEvent.IgnoreUnknownUsernames),
|
||||
handler.NewCol(DefaultRedirectURI, policyEvent.DefaultRedirectURI),
|
||||
handler.NewCol(PasswordCheckLifetimeCol, policyEvent.PasswordCheckLifetime),
|
||||
handler.NewCol(ExternalLoginCheckLifetimeCol, policyEvent.ExternalLoginCheckLifetime),
|
||||
handler.NewCol(MFAInitSkipLifetimeCol, policyEvent.MFAInitSkipLifetime),
|
||||
@ -208,6 +214,12 @@ func (p *LoginPolicyProjection) reduceLoginPolicyChanged(event eventstore.Event)
|
||||
if policyEvent.HidePasswordReset != nil {
|
||||
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 {
|
||||
cols = append(cols, handler.NewCol(PasswordCheckLifetimeCol, *policyEvent.PasswordCheckLifetime))
|
||||
}
|
||||
|
@ -35,7 +35,9 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
"allowExternalIdp": false,
|
||||
"forceMFA": false,
|
||||
"hidePasswordReset": true,
|
||||
"ignoreUnknownUsernames": true,
|
||||
"passwordlessType": 1,
|
||||
"defaultRedirectURI": "https://example.com/redirect",
|
||||
"passwordCheckLifetime": 10000000,
|
||||
"externalLoginCheckLifetime": 10000000,
|
||||
"mfaInitSkipLifetime": 10000000,
|
||||
@ -53,7 +55,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
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{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -67,6 +69,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
domain.PasswordlessTypeAllowed,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
"https://example.com/redirect",
|
||||
time.Millisecond * 10,
|
||||
time.Millisecond * 10,
|
||||
time.Millisecond * 10,
|
||||
@ -91,7 +95,9 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
"allowExternalIdp": true,
|
||||
"forceMFA": true,
|
||||
"hidePasswordReset": true,
|
||||
"ignoreUnknownUsernames": true,
|
||||
"passwordlessType": 1,
|
||||
"defaultRedirectURI": "https://example.com/redirect",
|
||||
"passwordCheckLifetime": 10000000,
|
||||
"externalLoginCheckLifetime": 10000000,
|
||||
"mfaInitSkipLifetime": 10000000,
|
||||
@ -108,7 +114,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
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{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -118,6 +124,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
true,
|
||||
true,
|
||||
"https://example.com/redirect",
|
||||
time.Millisecond * 10,
|
||||
time.Millisecond * 10,
|
||||
time.Millisecond * 10,
|
||||
@ -298,7 +306,9 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
"allowExternalIdp": false,
|
||||
"forceMFA": false,
|
||||
"hidePasswordReset": true,
|
||||
"ignoreUnknownUsernames": true,
|
||||
"passwordlessType": 1,
|
||||
"defaultRedirectURI": "https://example.com/redirect",
|
||||
"passwordCheckLifetime": 10000000,
|
||||
"externalLoginCheckLifetime": 10000000,
|
||||
"mfaInitSkipLifetime": 10000000,
|
||||
@ -315,7 +325,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
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{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -329,6 +339,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
domain.PasswordlessTypeAllowed,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
"https://example.com/redirect",
|
||||
time.Millisecond * 10,
|
||||
time.Millisecond * 10,
|
||||
time.Millisecond * 10,
|
||||
@ -353,7 +365,9 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
"allowExternalIdp": true,
|
||||
"forceMFA": true,
|
||||
"hidePasswordReset": true,
|
||||
"passwordlessType": 1
|
||||
"ignoreUnknownUsernames": true,
|
||||
"passwordlessType": 1,
|
||||
"defaultRedirectURI": "https://example.com/redirect"
|
||||
}`),
|
||||
), instance.LoginPolicyChangedEventMapper),
|
||||
},
|
||||
@ -365,7 +379,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
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{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -375,6 +389,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) {
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
true,
|
||||
true,
|
||||
"https://example.com/redirect",
|
||||
"agg-id",
|
||||
},
|
||||
},
|
||||
|
@ -27,8 +27,10 @@ func NewLoginPolicyAddedEvent(
|
||||
allowRegister,
|
||||
allowExternalIDP,
|
||||
forceMFA,
|
||||
hidePasswordReset bool,
|
||||
hidePasswordReset,
|
||||
ignoreUnknownUsernames bool,
|
||||
passwordlessType domain.PasswordlessType,
|
||||
defaultRedirectURI string,
|
||||
passwordCheckLifetime,
|
||||
externalLoginCheckLifetime,
|
||||
mfaInitSkipLifetime,
|
||||
@ -46,7 +48,9 @@ func NewLoginPolicyAddedEvent(
|
||||
allowExternalIDP,
|
||||
forceMFA,
|
||||
hidePasswordReset,
|
||||
ignoreUnknownUsernames,
|
||||
passwordlessType,
|
||||
defaultRedirectURI,
|
||||
passwordCheckLifetime,
|
||||
externalLoginCheckLifetime,
|
||||
mfaInitSkipLifetime,
|
||||
|
@ -28,8 +28,10 @@ func NewLoginPolicyAddedEvent(
|
||||
allowRegister,
|
||||
allowExternalIDP,
|
||||
forceMFA,
|
||||
hidePasswordReset bool,
|
||||
hidePasswordReset,
|
||||
ignoreUnknownUsernames bool,
|
||||
passwordlessType domain.PasswordlessType,
|
||||
defaultRedirectURI string,
|
||||
passwordCheckLifetime,
|
||||
externalLoginCheckLifetime,
|
||||
mfaInitSkipLifetime,
|
||||
@ -47,7 +49,9 @@ func NewLoginPolicyAddedEvent(
|
||||
allowExternalIDP,
|
||||
forceMFA,
|
||||
hidePasswordReset,
|
||||
ignoreUnknownUsernames,
|
||||
passwordlessType,
|
||||
defaultRedirectURI,
|
||||
passwordCheckLifetime,
|
||||
externalLoginCheckLifetime,
|
||||
mfaInitSkipLifetime,
|
||||
|
@ -25,7 +25,9 @@ type LoginPolicyAddedEvent struct {
|
||||
AllowExternalIDP bool `json:"allowExternalIdp,omitempty"`
|
||||
ForceMFA bool `json:"forceMFA,omitempty"`
|
||||
HidePasswordReset bool `json:"hidePasswordReset,omitempty"`
|
||||
IgnoreUnknownUsernames bool `json:"ignoreUnknownUsernames,omitempty"`
|
||||
PasswordlessType domain.PasswordlessType `json:"passwordlessType,omitempty"`
|
||||
DefaultRedirectURI string `json:"defaultRedirectURI,omitempty"`
|
||||
PasswordCheckLifetime time.Duration `json:"passwordCheckLifetime,omitempty"`
|
||||
ExternalLoginCheckLifetime time.Duration `json:"externalLoginCheckLifetime,omitempty"`
|
||||
MFAInitSkipLifetime time.Duration `json:"mfaInitSkipLifetime,omitempty"`
|
||||
@ -47,8 +49,10 @@ func NewLoginPolicyAddedEvent(
|
||||
allowRegister,
|
||||
allowExternalIDP,
|
||||
forceMFA,
|
||||
hidePasswordReset bool,
|
||||
hidePasswordReset,
|
||||
ignoreUnknownUsernames bool,
|
||||
passwordlessType domain.PasswordlessType,
|
||||
defaultRedirectURI string,
|
||||
passwordCheckLifetime,
|
||||
externalLoginCheckLifetime,
|
||||
mfaInitSkipLifetime,
|
||||
@ -63,6 +67,8 @@ func NewLoginPolicyAddedEvent(
|
||||
ForceMFA: forceMFA,
|
||||
PasswordlessType: passwordlessType,
|
||||
HidePasswordReset: hidePasswordReset,
|
||||
IgnoreUnknownUsernames: ignoreUnknownUsernames,
|
||||
DefaultRedirectURI: defaultRedirectURI,
|
||||
PasswordCheckLifetime: passwordCheckLifetime,
|
||||
ExternalLoginCheckLifetime: externalLoginCheckLifetime,
|
||||
MFAInitSkipLifetime: mfaInitSkipLifetime,
|
||||
@ -92,7 +98,9 @@ type LoginPolicyChangedEvent struct {
|
||||
AllowExternalIDP *bool `json:"allowExternalIdp,omitempty"`
|
||||
ForceMFA *bool `json:"forceMFA,omitempty"`
|
||||
HidePasswordReset *bool `json:"hidePasswordReset,omitempty"`
|
||||
IgnoreUnknownUsernames *bool `json:"ignoreUnknownUsernames,omitempty"`
|
||||
PasswordlessType *domain.PasswordlessType `json:"passwordlessType,omitempty"`
|
||||
DefaultRedirectURI *string `json:"defaultRedirectURI,omitempty"`
|
||||
PasswordCheckLifetime *time.Duration `json:"passwordCheckLifetime,omitempty"`
|
||||
ExternalLoginCheckLifetime *time.Duration `json:"externalLoginCheckLifetime,omitempty"`
|
||||
MFAInitSkipLifetime *time.Duration `json:"mfaInitSkipLifetime,omitempty"`
|
||||
@ -167,26 +175,43 @@ func ChangePasswordCheckLifetime(passwordCheckLifetime time.Duration) func(*Logi
|
||||
e.PasswordCheckLifetime = &passwordCheckLifetime
|
||||
}
|
||||
}
|
||||
|
||||
func ChangeExternalLoginCheckLifetime(externalLoginCheckLifetime time.Duration) func(*LoginPolicyChangedEvent) {
|
||||
return func(e *LoginPolicyChangedEvent) {
|
||||
e.ExternalLoginCheckLifetime = &externalLoginCheckLifetime
|
||||
}
|
||||
}
|
||||
|
||||
func ChangeMFAInitSkipLifetime(mfaInitSkipLifetime time.Duration) func(*LoginPolicyChangedEvent) {
|
||||
return func(e *LoginPolicyChangedEvent) {
|
||||
e.MFAInitSkipLifetime = &mfaInitSkipLifetime
|
||||
}
|
||||
}
|
||||
|
||||
func ChangeSecondFactorCheckLifetime(secondFactorCheckLifetime time.Duration) func(*LoginPolicyChangedEvent) {
|
||||
return func(e *LoginPolicyChangedEvent) {
|
||||
e.SecondFactorCheckLifetime = &secondFactorCheckLifetime
|
||||
}
|
||||
}
|
||||
|
||||
func ChangeMultiFactorCheckLifetime(multiFactorCheckLifetime time.Duration) func(*LoginPolicyChangedEvent) {
|
||||
return func(e *LoginPolicyChangedEvent) {
|
||||
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) {
|
||||
e := &LoginPolicyChangedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
|
@ -176,6 +176,7 @@ Errors:
|
||||
LoginPolicy:
|
||||
NotFound: Login Policy konnte nicht gefunden werden
|
||||
Invalid: Login Policy ist ungültig
|
||||
RedirectURIInvalid: Default Redirect URI ist ungültig
|
||||
NotExisting: Login Policy existiert nicht auf dieser Organisation
|
||||
AlreadyExists: Login Policy existiert bereits
|
||||
IdpProviderAlreadyExisting: Idp Provider existiert bereits
|
||||
@ -285,6 +286,7 @@ Errors:
|
||||
NotChanged: Default Login Policy wurde nicht verändert
|
||||
NotExisting: Default Login Policy existiert nicht
|
||||
AlreadyExists: Default Login Policy existiert bereits
|
||||
RedirectURIInvalid: Default Redirect URI ist ungültig
|
||||
MFA:
|
||||
AlreadyExists: Multifaktor existiert bereits
|
||||
NotExisting: Multifaktor existiert nicht
|
||||
|
@ -176,7 +176,8 @@ Errors:
|
||||
LoginPolicy:
|
||||
NotFound: Login Policy not found
|
||||
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
|
||||
IdpProviderAlreadyExisting: Idp Provider already existing
|
||||
IdpProviderNotExisting: Idp Provider not existing
|
||||
@ -285,6 +286,7 @@ Errors:
|
||||
NotChanged: Default Login Policy has not been changed
|
||||
NotExisting: Default Login Policy not existig
|
||||
AlreadyExists: Default Login Policy already exists
|
||||
RedirectURIInvalid: Default Redirect URI is invalid
|
||||
MFA:
|
||||
AlreadyExists: Multifactor already exists
|
||||
NotExisting: Multifactor not existing
|
||||
|
@ -176,6 +176,7 @@ Errors:
|
||||
LoginPolicy:
|
||||
NotFound: Impostazioni di accesso non trovati
|
||||
Invalid: Impostazioni di accesso non sono validi
|
||||
RedirectURIInvalid: Default Redirect URI non valido
|
||||
NotExisting: Impostazioni di accesso non esistenti
|
||||
AlreadyExists: Impostazioni di accesso già esistenti
|
||||
IdpProviderAlreadyExisting: IDP già esistente
|
||||
@ -283,6 +284,7 @@ Errors:
|
||||
NotChanged: Le impostazioni di accesso predefinite non sono state cambiate
|
||||
NotExisting: Impostazioni di accesso predefinite non esistenti
|
||||
AlreadyExists: Impostazioni di accesso predefinite già esistenti
|
||||
RedirectURIInvalid: Default Redirect URI non valido
|
||||
MFA:
|
||||
AlreadyExists: Multifattore già esistente
|
||||
NotExisting: Multifattore non esistente
|
||||
|
@ -3478,7 +3478,7 @@ message AddCustomDomainPolicyRequest {
|
||||
required: ["org_id"]
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
string org_id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
@ -3677,11 +3677,21 @@ message UpdateLoginPolicyRequest {
|
||||
description: "defines if password reset link should be shown in the login screen"
|
||||
}
|
||||
];
|
||||
google.protobuf.Duration password_check_lifetime = 7;
|
||||
google.protobuf.Duration external_login_check_lifetime = 8;
|
||||
google.protobuf.Duration mfa_init_skip_lifetime = 9;
|
||||
google.protobuf.Duration second_factor_check_lifetime = 10;
|
||||
google.protobuf.Duration multi_factor_check_lifetime = 11;
|
||||
bool ignore_unknown_usernames = 7 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "defines if unknown username on login screen directly return an error or always display the password screen"
|
||||
}
|
||||
];
|
||||
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 {
|
||||
|
@ -4344,11 +4344,21 @@ message AddCustomLoginPolicyRequest {
|
||||
bool force_mfa = 4;
|
||||
zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}];
|
||||
bool hide_password_reset = 6;
|
||||
google.protobuf.Duration password_check_lifetime = 7;
|
||||
google.protobuf.Duration external_login_check_lifetime = 8;
|
||||
google.protobuf.Duration mfa_init_skip_lifetime = 9;
|
||||
google.protobuf.Duration second_factor_check_lifetime = 10;
|
||||
google.protobuf.Duration multi_factor_check_lifetime = 11;
|
||||
bool ignore_unknown_usernames = 7 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "defines if unknown username on login screen directly return an error or always display the password screen"
|
||||
}
|
||||
];
|
||||
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 {
|
||||
@ -4362,11 +4372,21 @@ message UpdateCustomLoginPolicyRequest {
|
||||
bool force_mfa = 4;
|
||||
zitadel.policy.v1.PasswordlessType passwordless_type = 5 [(validate.rules).enum = {defined_only: true}];
|
||||
bool hide_password_reset = 6;
|
||||
google.protobuf.Duration password_check_lifetime = 7;
|
||||
google.protobuf.Duration external_login_check_lifetime = 8;
|
||||
google.protobuf.Duration mfa_init_skip_lifetime = 9;
|
||||
google.protobuf.Duration second_factor_check_lifetime = 10;
|
||||
google.protobuf.Duration multi_factor_check_lifetime = 11;
|
||||
bool ignore_unknown_usernames = 7 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "defines if unknown username on login screen directly return an error or always display the password screen"
|
||||
}
|
||||
];
|
||||
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 {
|
||||
|
@ -149,11 +149,21 @@ message LoginPolicy {
|
||||
description: "defines if password reset link should be shown in the login screen"
|
||||
}
|
||||
];
|
||||
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;
|
||||
bool ignore_unknown_usernames = 9 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "defines if unknown username on login screen directly return an error or always display the password screen"
|
||||
}
|
||||
];
|
||||
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