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

@ -29,14 +29,13 @@ type initPasswordFormData struct {
type initPasswordData struct { type initPasswordData struct {
baseData baseData
profileData profileData
Code string Code string
UserID string UserID string
PasswordPolicyDescription string MinLength uint64
MinLength uint64 HasUppercase string
HasUppercase string HasLowercase string
HasLowercase string HasNumber string
HasNumber string HasSymbol string
HasSymbol string
} }
func InitPasswordLink(origin, userID, code, orgID string) string { func InitPasswordLink(origin, userID, code, orgID string) 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

@ -30,15 +30,14 @@ type initUserFormData struct {
type initUserData struct { type initUserData struct {
baseData baseData
profileData profileData
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 HasNumber string
HasNumber string HasSymbol string
HasSymbol string
} }
func InitUserLink(origin, userID, code, orgID string, passwordSet bool) string { func InitUserLink(origin, userID, code, orgID string, passwordSet bool) 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,15 +30,14 @@ 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 HasNumber string
HasNumber string HasSymbol string
HasSymbol string ShowUsername bool
ShowUsername bool ShowUsernameSuffix bool
ShowUsernameSuffix bool OrgRegister bool
OrgRegister bool
} }
func (l *Login) handleRegister(w http.ResponseWriter, r *http.Request) { func (l *Login) handleRegister(w http.ResponseWriter, r *http.Request) {
@ -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,12 +611,11 @@ 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 HasNumber string
HasNumber string HasSymbol string
HasSymbol string
} }
type userSelectionData struct { type userSelectionData struct {

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 }}