mirror of
https://github.com/photoprism/photoprism.git
synced 2026-01-23 02:24:24 +00:00
OIDC: Allow to use nickname as username #782
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
parent
ad581aff4b
commit
fbb0284efa
12 changed files with 222 additions and 160 deletions
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-03-22 14:48+0000\n"
|
||||
"POT-Creation-Date: 2024-07-05 07:50+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
|
@ -17,383 +17,387 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: messages.go:100
|
||||
#: messages.go:101
|
||||
msgid "Something went wrong, try again"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:101
|
||||
#: messages.go:102
|
||||
msgid "Unable to do that"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:102
|
||||
msgid "Changes could not be saved"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:103
|
||||
msgid "Could not be deleted"
|
||||
msgid "Too many requests"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:104
|
||||
msgid "Changes could not be saved"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:105
|
||||
msgid "Could not be deleted"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:106
|
||||
#, c-format
|
||||
msgid "%s already exists"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:105
|
||||
#: messages.go:107
|
||||
msgid "Not found"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:106
|
||||
#: messages.go:108
|
||||
msgid "File not found"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:107
|
||||
#: messages.go:109
|
||||
msgid "File too large"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:108
|
||||
#: messages.go:110
|
||||
msgid "Unsupported"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:109
|
||||
#: messages.go:111
|
||||
msgid "Unsupported type"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:110
|
||||
#: messages.go:112
|
||||
msgid "Unsupported format"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:111
|
||||
#: messages.go:113
|
||||
msgid "Originals folder is empty"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:112
|
||||
#: messages.go:114
|
||||
msgid "Selection not found"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:113
|
||||
#: messages.go:115
|
||||
msgid "Entity not found"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:114
|
||||
#: messages.go:116
|
||||
msgid "Account not found"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:115
|
||||
#: messages.go:117
|
||||
msgid "User not found"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:116
|
||||
#: messages.go:118
|
||||
msgid "Label not found"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:117
|
||||
#: messages.go:119
|
||||
msgid "Album not found"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:118
|
||||
#: messages.go:120
|
||||
msgid "Subject not found"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:119
|
||||
#: messages.go:121
|
||||
msgid "Person not found"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:120
|
||||
#: messages.go:122
|
||||
msgid "Face not found"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:121
|
||||
#: messages.go:123
|
||||
msgid "Not available in public mode"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:122
|
||||
#: messages.go:124
|
||||
msgid "Not available in read-only mode"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:123
|
||||
#: messages.go:125
|
||||
msgid "Please log in to your account"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:124
|
||||
#: messages.go:126
|
||||
msgid "Permission denied"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:125
|
||||
#: messages.go:127
|
||||
msgid "Upload might be offensive"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:126
|
||||
#: messages.go:128
|
||||
msgid "Upload failed"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:127
|
||||
#: messages.go:129
|
||||
msgid "No items selected"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:128
|
||||
#: messages.go:130
|
||||
msgid "Failed creating file, please check permissions"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:129
|
||||
#: messages.go:131
|
||||
msgid "Failed creating folder, please check permissions"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:130
|
||||
#: messages.go:132
|
||||
msgid "Could not connect, please try again"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:131
|
||||
#: messages.go:133
|
||||
msgid "Enter verification code"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:132
|
||||
#: messages.go:134
|
||||
msgid "Invalid verification code, please try again"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:133
|
||||
#: messages.go:135
|
||||
msgid "Invalid password, please try again"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:134
|
||||
#: messages.go:136
|
||||
msgid "Feature disabled"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:135
|
||||
#: messages.go:137
|
||||
msgid "No labels selected"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:136
|
||||
#: messages.go:138
|
||||
msgid "No albums selected"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:137
|
||||
#: messages.go:139
|
||||
msgid "No files available for download"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:138
|
||||
#: messages.go:140
|
||||
msgid "Failed to create zip file"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:139
|
||||
#: messages.go:141
|
||||
msgid "Invalid credentials"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:140
|
||||
#: messages.go:142
|
||||
msgid "Invalid link"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:141
|
||||
#: messages.go:143
|
||||
msgid "Invalid name"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:142
|
||||
#: messages.go:144
|
||||
msgid "Busy, please try again later"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:143
|
||||
#: messages.go:145
|
||||
#, c-format
|
||||
msgid "The wakeup interval is %s, but must be 1h or less"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:144
|
||||
#: messages.go:146
|
||||
msgid "Your account could not be connected"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:147
|
||||
#: messages.go:149
|
||||
msgid "Changes successfully saved"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:148
|
||||
#: messages.go:150
|
||||
msgid "Album created"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:149
|
||||
#: messages.go:151
|
||||
msgid "Album saved"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:150
|
||||
#: messages.go:152
|
||||
#, c-format
|
||||
msgid "Album %s deleted"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:151
|
||||
#: messages.go:153
|
||||
msgid "Album contents cloned"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:152
|
||||
#: messages.go:154
|
||||
msgid "File removed from stack"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:153
|
||||
msgid "File deleted"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:154
|
||||
#, c-format
|
||||
msgid "Selection added to %s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:155
|
||||
#, c-format
|
||||
msgid "One entry added to %s"
|
||||
msgid "File deleted"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:156
|
||||
#, c-format
|
||||
msgid "%d entries added to %s"
|
||||
msgid "Selection added to %s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:157
|
||||
#, c-format
|
||||
msgid "One entry removed from %s"
|
||||
msgid "One entry added to %s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:158
|
||||
#, c-format
|
||||
msgid "%d entries removed from %s"
|
||||
msgid "%d entries added to %s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:159
|
||||
msgid "Account created"
|
||||
#, c-format
|
||||
msgid "One entry removed from %s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:160
|
||||
msgid "Account saved"
|
||||
#, c-format
|
||||
msgid "%d entries removed from %s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:161
|
||||
msgid "Account deleted"
|
||||
msgid "Account created"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:162
|
||||
msgid "Settings saved"
|
||||
msgid "Account saved"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:163
|
||||
msgid "Password changed"
|
||||
msgid "Account deleted"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:164
|
||||
#, c-format
|
||||
msgid "Import completed in %d s"
|
||||
msgid "Settings saved"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:165
|
||||
msgid "Import canceled"
|
||||
msgid "Password changed"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:166
|
||||
#, c-format
|
||||
msgid "Indexing completed in %d s"
|
||||
msgid "Import completed in %d s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:167
|
||||
msgid "Indexing originals..."
|
||||
msgid "Import canceled"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:168
|
||||
#, c-format
|
||||
msgid "Indexing files in %s"
|
||||
msgid "Indexing completed in %d s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:169
|
||||
msgid "Indexing canceled"
|
||||
msgid "Indexing originals..."
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:170
|
||||
#, c-format
|
||||
msgid "Removed %d files and %d photos"
|
||||
msgid "Indexing files in %s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:171
|
||||
#, c-format
|
||||
msgid "Moving files from %s"
|
||||
msgid "Indexing canceled"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:172
|
||||
#, c-format
|
||||
msgid "Copying files from %s"
|
||||
msgid "Removed %d files and %d photos"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:173
|
||||
msgid "Labels deleted"
|
||||
#, c-format
|
||||
msgid "Moving files from %s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:174
|
||||
msgid "Label saved"
|
||||
#, c-format
|
||||
msgid "Copying files from %s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:175
|
||||
msgid "Subject saved"
|
||||
msgid "Labels deleted"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:176
|
||||
msgid "Subject deleted"
|
||||
msgid "Label saved"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:177
|
||||
msgid "Person saved"
|
||||
msgid "Subject saved"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:178
|
||||
msgid "Person deleted"
|
||||
msgid "Subject deleted"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:179
|
||||
msgid "File uploaded"
|
||||
msgid "Person saved"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:180
|
||||
msgid "Person deleted"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:181
|
||||
msgid "File uploaded"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:182
|
||||
#, c-format
|
||||
msgid "%d files uploaded in %d s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:181
|
||||
#: messages.go:183
|
||||
msgid "Processing upload..."
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:182
|
||||
#: messages.go:184
|
||||
msgid "Upload has been processed"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:183
|
||||
#: messages.go:185
|
||||
msgid "Selection approved"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:184
|
||||
#: messages.go:186
|
||||
msgid "Selection archived"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:185
|
||||
#: messages.go:187
|
||||
msgid "Selection restored"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:186
|
||||
#: messages.go:188
|
||||
msgid "Selection marked as private"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:187
|
||||
msgid "Albums deleted"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:188
|
||||
#, c-format
|
||||
msgid "Zip created in %d s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:189
|
||||
msgid "Permanently deleted"
|
||||
msgid "Albums deleted"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:190
|
||||
#, c-format
|
||||
msgid "%s has been restored"
|
||||
msgid "Zip created in %d s"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:191
|
||||
msgid "Successfully verified"
|
||||
msgid "Permanently deleted"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:192
|
||||
#, c-format
|
||||
msgid "%s has been restored"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:193
|
||||
msgid "Successfully verified"
|
||||
msgstr ""
|
||||
|
||||
#: messages.go:194
|
||||
msgid "Successfully activated"
|
||||
msgstr ""
|
||||
|
|
|
|||
1
assets/static/brands/google-color.svg
Normal file
1
assets/static/brands/google-color.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/><path d="M1 1h22v22H1z" fill="none"/></svg>
|
||||
|
After Width: | Height: | Size: 742 B |
|
|
@ -37,11 +37,11 @@ func OIDCLogin(router *gin.RouterGroup) {
|
|||
// Abort in public mode and if OIDC is disabled.
|
||||
if get.Config().Public() {
|
||||
event.AuditErr([]string{clientIp, "oidc", action, authn.ErrDisabledInPublicMode.Error()})
|
||||
Abort(c, http.StatusForbidden, i18n.ErrForbidden)
|
||||
c.Redirect(http.StatusTemporaryRedirect, conf.LoginUri())
|
||||
return
|
||||
} else if !conf.OIDCEnabled() {
|
||||
event.AuditErr([]string{clientIp, "oidc", action, authn.ErrAuthenticationDisabled.Error()})
|
||||
Abort(c, http.StatusMethodNotAllowed, i18n.ErrUnsupported)
|
||||
c.Redirect(http.StatusTemporaryRedirect, conf.LoginUri())
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ func OIDCLogin(router *gin.RouterGroup) {
|
|||
|
||||
// Abort if failure rate limit is exceeded.
|
||||
if r.Reject() || limiter.Auth.Reject(clientIp) {
|
||||
limiter.AbortJSON(c)
|
||||
c.HTML(http.StatusTooManyRequests, "auth.gohtml", CreateSessionError(http.StatusTooManyRequests, i18n.Error(i18n.ErrTooManyRequests)))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -59,8 +59,8 @@ func OIDCLogin(router *gin.RouterGroup) {
|
|||
provider := get.OIDC()
|
||||
|
||||
if provider == nil {
|
||||
event.AuditErr([]string{clientIp, "oidc", action, authn.ErrInvalidProvider.Error()})
|
||||
Abort(c, http.StatusInternalServerError, i18n.ErrConnectionFailed)
|
||||
event.AuditErr([]string{clientIp, "oidc", action, authn.ErrInvalidProviderConfiguration.Error()})
|
||||
c.HTML(http.StatusInternalServerError, "auth.gohtml", CreateSessionError(http.StatusInternalServerError, i18n.Error(i18n.ErrConnectionFailed)))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ func OIDCRedirect(router *gin.RouterGroup) {
|
|||
|
||||
// Abort if failure rate limit is exceeded.
|
||||
if r.Reject() || limiter.Auth.Reject(clientIp) {
|
||||
c.HTML(http.StatusTooManyRequests, "auth.gohtml", CreateSessionError(http.StatusTooManyRequests, i18n.Error(i18n.ErrForbidden)))
|
||||
c.HTML(http.StatusTooManyRequests, "auth.gohtml", CreateSessionError(http.StatusTooManyRequests, i18n.Error(i18n.ErrTooManyRequests)))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -112,9 +112,14 @@ func OIDCRedirect(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
// Find existing user record and update it, if necessary.
|
||||
if oidcUser := entity.OidcUser(userInfo, conf.OIDCUsername()); oidcUser.UserName == "" || authn.ProviderOIDC.NotEqual(oidcUser.AuthProvider) {
|
||||
event.AuditErr([]string{clientIp, "oidc", action, authn.ErrInvalidUsername.Error()})
|
||||
event.LoginError(clientIp, "oidc", oidcUser.UserName, userAgent, authn.ErrInvalidUsername.Error())
|
||||
if oidcUser := entity.OidcUser(userInfo, conf.OIDCUsername()); authn.ProviderOIDC.NotEqual(oidcUser.AuthProvider) {
|
||||
event.AuditErr([]string{clientIp, "oidc", action, authn.ErrAuthProviderIsNotOIDC.Error()})
|
||||
event.LoginError(clientIp, "oidc", oidcUser.UserName, userAgent, authn.ErrAuthProviderIsNotOIDC.Error())
|
||||
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
|
||||
return
|
||||
} else if oidcUser.UserName == "" {
|
||||
event.AuditErr([]string{clientIp, "oidc", action, authn.ErrUsernameRequired.Error()})
|
||||
event.LoginError(clientIp, "oidc", oidcUser.UserName, userAgent, authn.ErrUsernameRequired.Error())
|
||||
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
|
||||
return
|
||||
} else if user = entity.FindUser(oidcUser); user != nil {
|
||||
|
|
|
|||
|
|
@ -95,13 +95,16 @@ func (c *Config) OIDCRegister() bool {
|
|||
return c.options.OIDCRegister
|
||||
}
|
||||
|
||||
// OIDCUsername returns the claim to use as username when signing up via OIDC.
|
||||
// OIDCUsername returns the preferred username claim for new users signing up via OIDC.
|
||||
func (c *Config) OIDCUsername() string {
|
||||
if c.options.OIDCUsername == authn.ClaimEmail {
|
||||
switch c.options.OIDCUsername {
|
||||
case authn.ClaimEmail:
|
||||
return authn.ClaimEmail
|
||||
case authn.ClaimNickname:
|
||||
return authn.ClaimNickname
|
||||
default:
|
||||
return authn.ClaimPreferredUsername
|
||||
}
|
||||
|
||||
return authn.ClaimUsername
|
||||
}
|
||||
|
||||
// OIDCDomain returns the email domain name for restricted single sign-on via OIDC.
|
||||
|
|
|
|||
|
|
@ -113,15 +113,19 @@ func TestConfig_OIDCRedirect(t *testing.T) {
|
|||
func TestConfig_OIDCUsername(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
assert.Equal(t, authn.ClaimUsername, c.OIDCUsername())
|
||||
assert.Equal(t, authn.ClaimPreferredUsername, c.OIDCUsername())
|
||||
|
||||
c.options.OIDCUsername = "email"
|
||||
|
||||
assert.Equal(t, authn.ClaimEmail, c.OIDCUsername())
|
||||
|
||||
c.options.OIDCUsername = "nickname"
|
||||
|
||||
assert.Equal(t, authn.ClaimNickname, c.OIDCUsername())
|
||||
|
||||
c.options.OIDCUsername = ""
|
||||
|
||||
assert.Equal(t, authn.ClaimUsername, c.OIDCUsername())
|
||||
assert.Equal(t, authn.ClaimPreferredUsername, c.OIDCUsername())
|
||||
}
|
||||
|
||||
func TestConfig_OIDCDomain(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: cli.StringFlag{
|
||||
Name: "oidc-uri",
|
||||
Usage: "identity provider `URI` for single sign-on via OpenID Connect, e.g. \"https://accounts.google.com/\"",
|
||||
Usage: "identity provider issuer `URI` for single sign-on via OpenID Connect, e.g. \"https://accounts.google.com\"",
|
||||
Value: "",
|
||||
EnvVar: EnvVar("OIDC_URI"),
|
||||
}}, {
|
||||
|
|
@ -93,13 +93,13 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: cli.StringFlag{
|
||||
Name: "oidc-username",
|
||||
Usage: "username `CLAIM` for OpenID Connect users (preferred_username, email)",
|
||||
Value: authn.ClaimUsername,
|
||||
Usage: "preferred username `CLAIM` for new OpenID Connect users (preferred_username, email, nickname)",
|
||||
Value: authn.ClaimPreferredUsername,
|
||||
EnvVar: EnvVar("OIDC_USERNAME"),
|
||||
}}, {
|
||||
Flag: cli.BoolFlag{
|
||||
Name: "oidc-webdav",
|
||||
Usage: "enable WebDAV for new OpenID Connect users if their role allows it",
|
||||
Usage: "allow new OpenID Connect users to use WebDAV when they have a role that allows it",
|
||||
EnvVar: EnvVar("OIDC_WEBDAV"),
|
||||
}}, {
|
||||
Flag: cli.BoolFlag{
|
||||
|
|
|
|||
|
|
@ -110,9 +110,35 @@ func OidcUser(userInfo oidc.UserInfo, usernameClaim string) User {
|
|||
|
||||
switch usernameClaim {
|
||||
case authn.ClaimEmail:
|
||||
userName = clean.Username(userInfo.GetEmail())
|
||||
if name := clean.Email(userInfo.GetEmail()); userInfo.IsEmailVerified() && len(name) > 4 {
|
||||
userName = name
|
||||
} else if name = clean.Handle(userInfo.GetPreferredUsername()); len(name) > 0 {
|
||||
userName = name
|
||||
} else if name = clean.Handle(userInfo.GetName()); len(name) > 0 {
|
||||
userName = name
|
||||
} else if name = clean.Handle(userInfo.GetNickname()); len(name) > 0 {
|
||||
userName = name
|
||||
}
|
||||
case authn.ClaimNickname:
|
||||
if name := clean.Handle(userInfo.GetNickname()); len(name) > 0 {
|
||||
userName = name
|
||||
} else if name = clean.Handle(userInfo.GetPreferredUsername()); len(name) > 0 {
|
||||
userName = name
|
||||
} else if name = clean.Handle(userInfo.GetName()); len(name) > 0 {
|
||||
userName = name
|
||||
} else if name = clean.Email(userInfo.GetEmail()); userInfo.IsEmailVerified() && len(name) > 4 {
|
||||
userName = name
|
||||
}
|
||||
default:
|
||||
userName = clean.Username(userInfo.GetPreferredUsername())
|
||||
if name := clean.Handle(userInfo.GetPreferredUsername()); len(name) > 0 {
|
||||
userName = name
|
||||
} else if name = clean.Email(userInfo.GetEmail()); userInfo.IsEmailVerified() && len(name) > 4 {
|
||||
userName = name
|
||||
} else if name = clean.Handle(userInfo.GetName()); len(name) > 0 {
|
||||
userName = name
|
||||
} else if name = clean.Handle(userInfo.GetNickname()); len(name) > 0 {
|
||||
userName = name
|
||||
}
|
||||
}
|
||||
|
||||
userEmail = clean.Email(userInfo.GetEmail())
|
||||
|
|
|
|||
|
|
@ -22,6 +22,36 @@ func TestNewUser(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOidcUser(t *testing.T) {
|
||||
t.Run("ClaimPreferredUsername", func(t *testing.T) {
|
||||
info := oidc.NewUserInfo()
|
||||
info.SetName("Jane")
|
||||
info.SetFamilyName("Doe")
|
||||
info.SetEmail("jane@doe.com", true)
|
||||
info.SetSubject("abcd123")
|
||||
info.SetPreferredUsername("Jane Doe")
|
||||
m := OidcUser(info, authn.ClaimPreferredUsername)
|
||||
|
||||
assert.Equal(t, "oidc", m.AuthProvider)
|
||||
assert.Equal(t, "abcd123", m.AuthID)
|
||||
assert.Equal(t, "jane@doe.com", m.UserEmail)
|
||||
assert.Equal(t, "jane.doe", m.UserName)
|
||||
assert.Equal(t, "Jane", m.DisplayName)
|
||||
})
|
||||
t.Run("ClaimNickname", func(t *testing.T) {
|
||||
info := oidc.NewUserInfo()
|
||||
info.SetName("Jane")
|
||||
info.SetFamilyName("Doe")
|
||||
info.SetNickname("Jens Mander")
|
||||
info.SetEmail("jane@doe.com", true)
|
||||
info.SetSubject("abcd123")
|
||||
m := OidcUser(info, authn.ClaimNickname)
|
||||
|
||||
assert.Equal(t, "oidc", m.AuthProvider)
|
||||
assert.Equal(t, "abcd123", m.AuthID)
|
||||
assert.Equal(t, "jane@doe.com", m.UserEmail)
|
||||
assert.Equal(t, "jens.mander", m.UserName)
|
||||
assert.Equal(t, "Jane", m.DisplayName)
|
||||
})
|
||||
t.Run("ClaimEmail", func(t *testing.T) {
|
||||
info := oidc.NewUserInfo()
|
||||
info.SetName("Jane")
|
||||
|
|
@ -36,27 +66,12 @@ func TestOidcUser(t *testing.T) {
|
|||
assert.Equal(t, "jane@doe.com", m.UserName)
|
||||
assert.Equal(t, "Jane", m.DisplayName)
|
||||
})
|
||||
t.Run("ClaimUsername", func(t *testing.T) {
|
||||
t.Run("EmptyAuthId", func(t *testing.T) {
|
||||
info := oidc.NewUserInfo()
|
||||
info.SetName("Jane")
|
||||
info.SetFamilyName("Doe")
|
||||
info.SetEmail("jane@doe.com", true)
|
||||
info.SetSubject("abcd123")
|
||||
info.SetPreferredUsername("Jane Doe")
|
||||
m := OidcUser(info, authn.ClaimUsername)
|
||||
|
||||
assert.Equal(t, "oidc", m.AuthProvider)
|
||||
assert.Equal(t, "abcd123", m.AuthID)
|
||||
assert.Equal(t, "jane@doe.com", m.UserEmail)
|
||||
assert.Equal(t, "jane doe", m.UserName)
|
||||
assert.Equal(t, "Jane", m.DisplayName)
|
||||
})
|
||||
t.Run("AuthIdEmpty", func(t *testing.T) {
|
||||
info := oidc.NewUserInfo()
|
||||
info.SetName("Jane")
|
||||
info.SetFamilyName("Doe")
|
||||
info.SetEmail("jane@doe.com", true)
|
||||
m := OidcUser(info, authn.ClaimUsername)
|
||||
m := OidcUser(info, authn.ClaimPreferredUsername)
|
||||
|
||||
assert.Empty(t, m)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -31,16 +31,17 @@ var (
|
|||
|
||||
// OIDC and OAuth2-related error messages:
|
||||
var (
|
||||
ErrInvalidProvider = errors.New("invalid provider")
|
||||
ErrInvalidGrantType = errors.New("invalid grant type")
|
||||
ErrInvalidClientID = errors.New("invalid client id")
|
||||
ErrInvalidAuthID = errors.New("invalid auth id")
|
||||
ErrAuthCodeRequired = errors.New("auth code required")
|
||||
ErrClientIDRequired = errors.New("client id required")
|
||||
ErrInvalidClientSecret = errors.New("invalid client secret")
|
||||
ErrClientSecretRequired = errors.New("client secret required")
|
||||
ErrVerifiedEmailRequired = errors.New("verified email required")
|
||||
ErrRegistrationDisabled = errors.New("registration disabled")
|
||||
ErrInvalidProviderConfiguration = errors.New("invalid provider configuration")
|
||||
ErrInvalidGrantType = errors.New("invalid grant type")
|
||||
ErrInvalidClientID = errors.New("invalid client id")
|
||||
ErrInvalidAuthID = errors.New("invalid auth id")
|
||||
ErrAuthProviderIsNotOIDC = errors.New("auth provider is not oidc")
|
||||
ErrAuthCodeRequired = errors.New("auth code required")
|
||||
ErrClientIDRequired = errors.New("client id required")
|
||||
ErrInvalidClientSecret = errors.New("invalid client secret")
|
||||
ErrClientSecretRequired = errors.New("client secret required")
|
||||
ErrVerifiedEmailRequired = errors.New("verified email required")
|
||||
ErrRegistrationDisabled = errors.New("registration disabled")
|
||||
)
|
||||
|
||||
// User-related error messages:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package authn
|
||||
|
||||
const (
|
||||
ClaimEmail = "email"
|
||||
ClaimUsername = "preferred_username"
|
||||
OidcScopes = "openid email profile"
|
||||
ClaimPreferredUsername = "preferred_username"
|
||||
ClaimEmail = "email"
|
||||
ClaimNickname = "nickname"
|
||||
OidcScopes = "openid email profile"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package i18n
|
|||
const (
|
||||
ErrUnexpected Message = iota + 1
|
||||
ErrBadRequest
|
||||
ErrTooManyRequests
|
||||
ErrSaveFailed
|
||||
ErrDeleteFailed
|
||||
ErrAlreadyExists
|
||||
|
|
@ -99,6 +100,7 @@ var Messages = MessageMap{
|
|||
// Error messages:
|
||||
ErrUnexpected: gettext("Something went wrong, try again"),
|
||||
ErrBadRequest: gettext("Unable to do that"),
|
||||
ErrTooManyRequests: gettext("Too many requests"),
|
||||
ErrSaveFailed: gettext("Changes could not be saved"),
|
||||
ErrDeleteFailed: gettext("Could not be deleted"),
|
||||
ErrAlreadyExists: gettext("%s already exists"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue