fix(login): custom texts for pages called directly form mail link (#4415)

* fix(login): translate init password correctly

* refactor: no error return params

* fix(login): custom texts for pages called directly form mail link

* fix custom text on registration pages

Co-authored-by: adlerhurst <silvan.reusser@gmail.com>
This commit is contained in:
Livio Spring 2022-09-20 09:22:47 +02:00 committed by GitHub
parent fc4f4096e0
commit 05cb672cff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 100 additions and 111 deletions

View File

@ -44,9 +44,8 @@ func (l *Login) renderChangePassword(w http.ResponseWriter, r *http.Request, aut
baseData: l.getBaseData(r, authReq, "Change Password", errID, errMessage), baseData: l.getBaseData(r, authReq, "Change Password", errID, errMessage),
profileData: l.getProfileData(authReq), profileData: l.getProfileData(authReq),
} }
policy, description, _ := l.getPasswordComplexityPolicy(r, authReq, authReq.UserOrgID) policy := l.getPasswordComplexityPolicy(r, authReq.UserOrgID)
if policy != nil { if policy != nil {
data.PasswordPolicyDescription = description
data.MinLength = policy.MinLength data.MinLength = policy.MinLength
if policy.HasUppercase { if policy.HasUppercase {
data.HasUppercase = UpperCaseRegex data.HasUppercase = UpperCaseRegex

View File

@ -31,7 +31,6 @@ type initPasswordData struct {
profileData profileData
Code string Code string
UserID string UserID string
PasswordPolicyDescription string
MinLength uint64 MinLength uint64
HasUppercase string HasUppercase string
HasLowercase string HasLowercase string
@ -85,7 +84,7 @@ func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *dom
l.renderInitPassword(w, r, authReq, data.UserID, "", err) l.renderInitPassword(w, r, authReq, data.UserID, "", err)
return return
} }
l.renderInitPasswordDone(w, r, authReq) l.renderInitPasswordDone(w, r, authReq, userOrg)
} }
func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) { func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
@ -130,9 +129,8 @@ func (l *Login) renderInitPassword(w http.ResponseWriter, r *http.Request, authR
UserID: userID, UserID: userID,
Code: code, Code: code,
} }
policy, description, _ := l.getPasswordComplexityPolicyByUserID(r, authReq, userID) policy := l.getPasswordComplexityPolicyByUserID(r, userID)
if policy != nil { if policy != nil {
data.PasswordPolicyDescription = description
data.MinLength = policy.MinLength data.MinLength = policy.MinLength
if policy.HasUppercase { if policy.HasUppercase {
data.HasUppercase = UpperCaseRegex data.HasUppercase = UpperCaseRegex
@ -157,7 +155,11 @@ func (l *Login) renderInitPassword(w http.ResponseWriter, r *http.Request, authR
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplInitPassword], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplInitPassword], data, nil)
} }
func (l *Login) renderInitPasswordDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) { func (l *Login) renderInitPasswordDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, orgID string) {
data := l.getUserData(r, authReq, "Password Init Done", "", "") data := l.getUserData(r, authReq, "Password Init Done", "", "")
l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplInitPasswordDone], data, nil) translator := l.getTranslator(r.Context(), authReq)
if authReq == nil {
l.customTexts(r.Context(), translator, orgID)
}
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplInitPasswordDone], data, nil)
} }

View File

@ -33,7 +33,6 @@ type initUserData struct {
Code string Code string
UserID string UserID string
PasswordSet bool PasswordSet bool
PasswordPolicyDescription string
MinLength uint64 MinLength uint64
HasUppercase string HasUppercase string
HasLowercase string HasLowercase string
@ -87,7 +86,7 @@ func (l *Login) checkUserInitCode(w http.ResponseWriter, r *http.Request, authRe
l.renderInitUser(w, r, authReq, data.UserID, "", data.PasswordSet, err) l.renderInitUser(w, r, authReq, data.UserID, "", data.PasswordSet, err)
return return
} }
l.renderInitUserDone(w, r, authReq) l.renderInitUserDone(w, r, authReq, userOrgID)
} }
func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID string, showPassword bool) { func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID string, showPassword bool) {
@ -119,9 +118,8 @@ func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *
Code: code, Code: code,
PasswordSet: passwordSet, PasswordSet: passwordSet,
} }
policy, description, _ := l.getPasswordComplexityPolicyByUserID(r, nil, userID) policy := l.getPasswordComplexityPolicyByUserID(r, userID)
if policy != nil { if policy != nil {
data.PasswordPolicyDescription = description
data.MinLength = policy.MinLength data.MinLength = policy.MinLength
if policy.HasUppercase { if policy.HasUppercase {
data.HasUppercase = UpperCaseRegex data.HasUppercase = UpperCaseRegex
@ -146,7 +144,11 @@ func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplInitUser], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplInitUser], data, nil)
} }
func (l *Login) renderInitUserDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) { func (l *Login) renderInitUserDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, orgID string) {
data := l.getUserData(r, authReq, "User Init Done", "", "") data := l.getUserData(r, authReq, "User Init Done", "", "")
l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplInitUserDone], data, nil) translator := l.getTranslator(r.Context(), authReq)
if authReq == nil {
l.customTexts(r.Context(), translator, orgID)
}
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplInitUserDone], data, nil)
} }

View File

@ -81,7 +81,7 @@ func (l *Login) checkMailCode(w http.ResponseWriter, r *http.Request, authReq *d
l.renderMailVerification(w, r, authReq, userID, err) l.renderMailVerification(w, r, authReq, userID, err)
return return
} }
l.renderMailVerified(w, r, authReq) l.renderMailVerified(w, r, authReq, userOrg)
} }
func (l *Login) renderMailVerification(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID string, err error) { func (l *Login) renderMailVerification(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID string, err error) {
@ -107,11 +107,14 @@ func (l *Login) renderMailVerification(w http.ResponseWriter, r *http.Request, a
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMailVerification], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMailVerification], data, nil)
} }
func (l *Login) renderMailVerified(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) { func (l *Login) renderMailVerified(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, orgID string) {
data := mailVerificationData{ data := mailVerificationData{
baseData: l.getBaseData(r, authReq, "Mail Verified", "", ""), baseData: l.getBaseData(r, authReq, "Mail Verified", "", ""),
profileData: l.getProfileData(authReq), profileData: l.getProfileData(authReq),
} }
translator := l.getTranslator(r.Context(), authReq) translator := l.getTranslator(r.Context(), authReq)
if authReq == nil {
l.customTexts(r.Context(), translator, orgID)
}
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMailVerified], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMailVerified], data, nil)
} }

View File

@ -2,10 +2,9 @@ package login
import ( import (
"net/http" "net/http"
"regexp"
"strconv"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/logging"
iam_model "github.com/zitadel/zitadel/internal/iam/model" iam_model "github.com/zitadel/zitadel/internal/iam/model"
) )
@ -16,59 +15,19 @@ const (
SymbolRegex = `[^A-Za-z0-9]` SymbolRegex = `[^A-Za-z0-9]`
) )
var ( func (l *Login) getPasswordComplexityPolicy(r *http.Request, orgID string) *iam_model.PasswordComplexityPolicyView {
hasStringLowerCase = regexp.MustCompile(LowerCaseRegex).MatchString
hasStringUpperCase = regexp.MustCompile(UpperCaseRegex).MatchString
hasNumber = regexp.MustCompile(NumberRegex).MatchString
hasSymbol = regexp.MustCompile(SymbolRegex).MatchString
)
func (l *Login) getPasswordComplexityPolicy(r *http.Request, authReq *domain.AuthRequest, orgID string) (*iam_model.PasswordComplexityPolicyView, string, error) {
policy, err := l.authRepo.GetMyPasswordComplexityPolicy(setContext(r.Context(), orgID)) policy, err := l.authRepo.GetMyPasswordComplexityPolicy(setContext(r.Context(), orgID))
if err != nil { logging.WithFields("orgID", orgID).OnError(err).Error("could not load password complexity policy")
return nil, err.Error(), err return policy
}
description, err := l.generatePolicyDescription(r, authReq, policy)
return policy, description, nil
} }
func (l *Login) getPasswordComplexityPolicyByUserID(r *http.Request, authReq *domain.AuthRequest, userID string) (*iam_model.PasswordComplexityPolicyView, string, error) { func (l *Login) getPasswordComplexityPolicyByUserID(r *http.Request, userID string) *iam_model.PasswordComplexityPolicyView {
user, err := l.query.GetUserByID(r.Context(), false, userID) user, err := l.query.GetUserByID(r.Context(), false, userID)
if err != nil { if err != nil {
return nil, "", nil logging.WithFields("userID", userID).OnError(err).Error("could not load user for password complexity policy")
return nil
} }
policy, err := l.authRepo.GetMyPasswordComplexityPolicy(setContext(r.Context(), user.ResourceOwner)) policy, err := l.authRepo.GetMyPasswordComplexityPolicy(setContext(r.Context(), user.ResourceOwner))
if err != nil { logging.WithFields("orgID", user.ResourceOwner, "userID", userID).OnError(err).Error("could not load password complexity policy")
return nil, err.Error(), err return policy
}
description, err := l.generatePolicyDescription(r, authReq, policy)
return policy, description, nil
}
func (l *Login) generatePolicyDescription(r *http.Request, authReq *domain.AuthRequest, policy *iam_model.PasswordComplexityPolicyView) (string, error) {
description := "<ul class=\"lgn-no-dots lgn-policy\" id=\"passwordcomplexity\">"
translator := l.getTranslator(r.Context(), authReq)
minLength := l.renderer.LocalizeFromRequest(translator, r, "Password.MinLength", nil)
description += "<li id=\"minlength\" class=\"invalid\"><i class=\"lgn-icon-times-solid lgn-warn\"></i><span>" + minLength + " " + strconv.Itoa(int(policy.MinLength)) + "</span></li>"
if policy.HasUppercase {
uppercase := l.renderer.LocalizeFromRequest(translator, r, "Password.HasUppercase", nil)
description += "<li id=\"uppercase\" class=\"invalid\"><i class=\"lgn-icon-times-solid lgn-warn\"></i><span>" + uppercase + "</span></li>"
}
if policy.HasLowercase {
lowercase := l.renderer.LocalizeFromRequest(translator, r, "Password.HasLowercase", nil)
description += "<li id=\"lowercase\" class=\"invalid\"><i class=\"lgn-icon-times-solid lgn-warn\"></i><span>" + lowercase + "</span></li>"
}
if policy.HasNumber {
hasnumber := l.renderer.LocalizeFromRequest(translator, r, "Password.HasNumber", nil)
description += "<li id=\"number\" class=\"invalid\"><i class=\"lgn-icon-times-solid lgn-warn\"></i><span>" + hasnumber + "</span></li>"
}
if policy.HasSymbol {
hassymbol := l.renderer.LocalizeFromRequest(translator, r, "Password.HasSymbol", nil)
description += "<li id=\"symbol\" class=\"invalid\"><i class=\"lgn-icon-times-solid lgn-warn\"></i><span>" + hassymbol + "</span></li>"
}
confirmation := l.renderer.LocalizeFromRequest(translator, r, "Password.Confirmation", nil)
description += "<li id=\"confirmation\" class=\"invalid\"><i class=\"lgn-icon-times-solid lgn-warn\"></i><span>" + confirmation + "</span></li>"
description += "</ul>"
return description, nil
} }

View File

@ -183,10 +183,10 @@ func (l *Login) checkPasswordlessRegistration(w http.ResponseWriter, r *http.Req
l.renderPasswordlessRegistration(w, r, authReq, formData.UserID, formData.OrgID, formData.CodeID, formData.Code, formData.RequestPlatformType, err) l.renderPasswordlessRegistration(w, r, authReq, formData.UserID, formData.OrgID, formData.CodeID, formData.Code, formData.RequestPlatformType, err)
return return
} }
l.renderPasswordlessRegistrationDone(w, r, authReq, nil) l.renderPasswordlessRegistrationDone(w, r, authReq, formData.OrgID, nil)
} }
func (l *Login) renderPasswordlessRegistrationDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) { func (l *Login) renderPasswordlessRegistrationDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, orgID string, err error) {
var errID, errMessage string var errID, errMessage string
if err != nil { if err != nil {
errID, errMessage = l.getErrorMessage(r, err) errID, errMessage = l.getErrorMessage(r, err)
@ -195,5 +195,9 @@ func (l *Login) renderPasswordlessRegistrationDone(w http.ResponseWriter, r *htt
userData: l.getUserData(r, authReq, "Passwordless Registration Done", errID, errMessage), userData: l.getUserData(r, authReq, "Passwordless Registration Done", errID, errMessage),
HideNextButton: authReq == nil, HideNextButton: authReq == nil,
} }
l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplPasswordlessRegistrationDone], data, nil) translator := l.getTranslator(r.Context(), authReq)
if authReq == nil {
l.customTexts(r.Context(), translator, orgID)
}
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplPasswordlessRegistrationDone], data, nil)
} }

View File

@ -30,7 +30,6 @@ type registerFormData struct {
type registerData struct { type registerData struct {
baseData baseData
registerFormData registerFormData
PasswordPolicyDescription string
MinLength uint64 MinLength uint64
HasUppercase string HasUppercase string
HasLowercase string HasLowercase string
@ -124,9 +123,8 @@ func (l *Login) renderRegister(w http.ResponseWriter, r *http.Request, authReque
registerFormData: *formData, registerFormData: *formData,
} }
pwPolicy, description, _ := l.getPasswordComplexityPolicy(r, authRequest, resourceOwner) pwPolicy := l.getPasswordComplexityPolicy(r, resourceOwner)
if pwPolicy != nil { if pwPolicy != nil {
data.PasswordPolicyDescription = description
data.MinLength = pwPolicy.MinLength data.MinLength = pwPolicy.MinLength
if pwPolicy.HasUppercase { if pwPolicy.HasUppercase {
data.HasUppercase = UpperCaseRegex data.HasUppercase = UpperCaseRegex
@ -171,6 +169,9 @@ func (l *Login) renderRegister(w http.ResponseWriter, r *http.Request, authReque
return formData.Gender == g return formData.Gender == g
}, },
} }
if authRequest == nil {
l.customTexts(r.Context(), translator, resourceOwner)
}
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplRegister], data, funcs) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplRegister], data, funcs)
} }

View File

@ -90,9 +90,8 @@ func (l *Login) renderRegisterOrg(w http.ResponseWriter, r *http.Request, authRe
baseData: l.getBaseData(r, authRequest, "Register", errID, errMessage), baseData: l.getBaseData(r, authRequest, "Register", errID, errMessage),
registerOrgFormData: *formData, registerOrgFormData: *formData,
} }
pwPolicy, description, _ := l.getPasswordComplexityPolicy(r, authRequest, "0") pwPolicy := l.getPasswordComplexityPolicy(r, "0")
if pwPolicy != nil { if pwPolicy != nil {
data.PasswordPolicyDescription = description
data.MinLength = pwPolicy.MinLength data.MinLength = pwPolicy.MinLength
if pwPolicy.HasUppercase { if pwPolicy.HasUppercase {
data.HasUppercase = UpperCaseRegex data.HasUppercase = UpperCaseRegex
@ -114,6 +113,9 @@ func (l *Login) renderRegisterOrg(w http.ResponseWriter, r *http.Request, authRe
} }
translator := l.getTranslator(r.Context(), authRequest) translator := l.getTranslator(r.Context(), authRequest)
if authRequest == nil {
l.customTexts(r.Context(), translator, "")
}
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplRegisterOrg], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplRegisterOrg], data, nil)
} }

View File

@ -611,7 +611,6 @@ type profileData struct {
type passwordData struct { type passwordData struct {
baseData baseData
profileData profileData
PasswordPolicyDescription string
MinLength uint64 MinLength uint64
HasUppercase string HasUppercase string
HasLowercase string HasLowercase string

View File

@ -26,7 +26,7 @@
data-has-lowercase="{{ .HasLowercase }}" data-has-number="{{ .HasNumber }}" data-has-lowercase="{{ .HasLowercase }}" data-has-number="{{ .HasNumber }}"
data-has-symbol="{{ .HasSymbol }}" class="lgn-input" type="password" id="change-new-password" data-has-symbol="{{ .HasSymbol }}" class="lgn-input" type="password" id="change-new-password"
name="change-new-password" autocomplete="new-password" required> name="change-new-password" autocomplete="new-password" required>
{{ .PasswordPolicyDescription }} {{ template "password-complexity-policy-description" . }}
</div> </div>
<div class="field"> <div class="field">

View File

@ -27,7 +27,7 @@
data-has-lowercase="{{ .HasLowercase }}" data-has-number="{{ .HasNumber }}" data-has-lowercase="{{ .HasLowercase }}" data-has-number="{{ .HasNumber }}"
data-has-symbol="{{ .HasSymbol }}" class="lgn-input" type="password" id="password" name="password" data-has-symbol="{{ .HasSymbol }}" class="lgn-input" type="password" id="password" name="password"
autocomplete="new-password" autofocus required> autocomplete="new-password" autofocus required>
{{ .PasswordPolicyDescription }} {{ template "password-complexity-policy-description" . }}
</div> </div>
<div class="field"> <div class="field">
<label class="lgn-label" for="passwordconfirm">{{t "InitPassword.NewPasswordConfirmLabel"}}</label> <label class="lgn-label" for="passwordconfirm">{{t "InitPassword.NewPasswordConfirmLabel"}}</label>

View File

@ -31,7 +31,7 @@
data-has-lowercase="{{ .HasLowercase }}" data-has-number="{{ .HasNumber }}" data-has-lowercase="{{ .HasLowercase }}" data-has-number="{{ .HasNumber }}"
data-has-symbol="{{ .HasSymbol }}" class="lgn-input" type="password" id="password" name="password" data-has-symbol="{{ .HasSymbol }}" class="lgn-input" type="password" id="password" name="password"
autocomplete="new-password" autofocus required> autocomplete="new-password" autofocus required>
{{ .PasswordPolicyDescription }} {{ template "password-complexity-policy-description" . }}
</div> </div>
<div class="field"> <div class="field">
<label class="lgn-label" for="passwordconfirm">{{t "InitUser.NewPasswordConfirmLabel"}}</label> <label class="lgn-label" for="passwordconfirm">{{t "InitUser.NewPasswordConfirmLabel"}}</label>

View File

@ -0,0 +1,18 @@
{{define "password-complexity-policy-description"}}
<ul class="lgn-no-dots lgn-policy" id="passwordcomplexity">
<li id="minlength" class="invalid"><i class="lgn-icon-times-solid lgn-warn"></i><span>{{t "Password.MinLength"}} {{.MinLength}}</span></li>
{{if .HasUppercase }}
<li id="uppercase" class="invalid"><i class="lgn-icon-times-solid lgn-warn"></i><span>{{t "Password.HasUppercase"}}</span></li>
{{end}}
{{if .HasLowercase}}
<li id="lowercase" class="invalid"><i class="lgn-icon-times-solid lgn-warn"></i><span>{{t "Password.HasLowercase"}}</span></li>
{{end}}
{{if .HasNumber}}
<li id="number" class="invalid"><i class="lgn-icon-times-solid lgn-warn"></i><span>{{t "Password.HasNumber"}}</span></li>
{{end}}
{{if .HasSymbol}}
<li id="symbol" class="invalid"><i class="lgn-icon-times-solid lgn-warn"></i><span>{{t "Password.HasSymbol"}}</span></li>
{{end}}
<li id="confirmation" class="invalid"><i class="lgn-icon-times-solid lgn-warn"></i><span>{{t "Password.Confirmation"}}</span></li>
</ul>
{{end}}

View File

@ -97,7 +97,7 @@
</div> </div>
<div class="lgn-field"> <div class="lgn-field">
{{ .PasswordPolicyDescription }} {{ template "password-complexity-policy-description" . }}
</div> </div>
{{ if or .TOSLink .PrivacyLink }} {{ if or .TOSLink .PrivacyLink }}

View File

@ -64,7 +64,7 @@
</div> </div>
</div> </div>
<div class="lgn-field"> <div class="lgn-field">
{{ .PasswordPolicyDescription }} {{ template "password-complexity-policy-description" . }}
</div> </div>
{{ if or .TOSLink .PrivacyLink }} {{ if or .TOSLink .PrivacyLink }}