Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
D
dex
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
dex
Commits
75a07f2b
Unverified
Commit
75a07f2b
authored
Nov 10, 2017
by
Eric Chiang
Committed by
GitHub
Nov 10, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1116 from srenatus/sr/local-users/say-email-in-login
password connectors: make prompt configurable
parents
04e276f2
b09a1345
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
81 additions
and
12 deletions
+81
-12
ldap-connector.md
Documentation/ldap-connector.md
+4
-0
connector.go
connector/connector.go
+3
-0
ldap.go
connector/ldap/ldap.go
+9
-0
ldap_test.go
connector/ldap/ldap_test.go
+25
-0
connectortest.go
connector/mock/connectortest.go
+2
-0
config-ldap.yaml
examples/config-ldap.yaml
+4
-2
handlers.go
server/handlers.go
+10
-2
server.go
server/server.go
+4
-0
server_test.go
server/server_test.go
+10
-0
templates.go
server/templates.go
+7
-5
password.html
web/templates/password.html
+3
-3
No files found.
Documentation/ldap-connector.md
View file @
75a07f2b
...
@@ -90,6 +90,10 @@ connectors:
...
@@ -90,6 +90,10 @@ connectors:
bindDN
:
uid=seviceaccount,cn=users,dc=example,dc=com
bindDN
:
uid=seviceaccount,cn=users,dc=example,dc=com
bindPW
:
password
bindPW
:
password
# The attribute to display in the provided password prompt. If unset, will
# display "Username"
usernamePrompt
:
SSO Username
# User search maps a username and password entered by a user to a LDAP entry.
# User search maps a username and password entered by a user to a LDAP entry.
userSearch
:
userSearch
:
# BaseDN to start the search from. It will translate to the query
# BaseDN to start the search from. It will translate to the query
...
...
connector/connector.go
View file @
75a07f2b
...
@@ -39,7 +39,10 @@ type Identity struct {
...
@@ -39,7 +39,10 @@ type Identity struct {
// PasswordConnector is an interface implemented by connectors which take a
// PasswordConnector is an interface implemented by connectors which take a
// username and password.
// username and password.
// Prompt() is used to inform the handler what to display in the password
// template. If this returns an empty string, it'll default to "Username".
type
PasswordConnector
interface
{
type
PasswordConnector
interface
{
Prompt
()
string
Login
(
ctx
context
.
Context
,
s
Scopes
,
username
,
password
string
)
(
identity
Identity
,
validPassword
bool
,
err
error
)
Login
(
ctx
context
.
Context
,
s
Scopes
,
username
,
password
string
)
(
identity
Identity
,
validPassword
bool
,
err
error
)
}
}
...
...
connector/ldap/ldap.go
View file @
75a07f2b
...
@@ -77,6 +77,11 @@ type Config struct {
...
@@ -77,6 +77,11 @@ type Config struct {
BindDN
string
`json:"bindDN"`
BindDN
string
`json:"bindDN"`
BindPW
string
`json:"bindPW"`
BindPW
string
`json:"bindPW"`
// UsernamePrompt allows users to override the username attribute (displayed
// in the username/password prompt). If unset, the handler will use
// "Username".
UsernamePrompt
string
`json:"usernamePrompt"`
// User entry search configuration.
// User entry search configuration.
UserSearch
struct
{
UserSearch
struct
{
// BsaeDN to start the search from. For example "cn=users,dc=example,dc=com"
// BsaeDN to start the search from. For example "cn=users,dc=example,dc=com"
...
@@ -545,3 +550,7 @@ func (c *ldapConnector) groups(ctx context.Context, user ldap.Entry) ([]string,
...
@@ -545,3 +550,7 @@ func (c *ldapConnector) groups(ctx context.Context, user ldap.Entry) ([]string,
}
}
return
groupNames
,
nil
return
groupNames
,
nil
}
}
func
(
c
*
ldapConnector
)
Prompt
()
string
{
return
c
.
UsernamePrompt
}
connector/ldap/ldap_test.go
View file @
75a07f2b
...
@@ -437,6 +437,31 @@ userpassword: foo
...
@@ -437,6 +437,31 @@ userpassword: foo
runTests
(
t
,
schema
,
connectLDAPS
,
c
,
tests
)
runTests
(
t
,
schema
,
connectLDAPS
,
c
,
tests
)
}
}
func
TestUsernamePrompt
(
t
*
testing
.
T
)
{
tests
:=
map
[
string
]
struct
{
config
Config
expected
string
}{
"with usernamePrompt unset it returns
\"\"
"
:
{
config
:
Config
{},
expected
:
""
,
},
"with usernamePrompt set it returns that"
:
{
config
:
Config
{
UsernamePrompt
:
"Email address"
},
expected
:
"Email address"
,
},
}
for
n
,
d
:=
range
tests
{
t
.
Run
(
n
,
func
(
t
*
testing
.
T
)
{
conn
:=
&
ldapConnector
{
Config
:
d
.
config
}
if
actual
:=
conn
.
Prompt
();
actual
!=
d
.
expected
{
t
.
Errorf
(
"expected %v, got %v"
,
d
.
expected
,
actual
)
}
})
}
}
// runTests runs a set of tests against an LDAP schema. It does this by
// runTests runs a set of tests against an LDAP schema. It does this by
// setting up an OpenLDAP server and injecting the provided scheme.
// setting up an OpenLDAP server and injecting the provided scheme.
//
//
...
...
connector/mock/connectortest.go
View file @
75a07f2b
...
@@ -110,3 +110,5 @@ func (p passwordConnector) Login(ctx context.Context, s connector.Scopes, userna
...
@@ -110,3 +110,5 @@ func (p passwordConnector) Login(ctx context.Context, s connector.Scopes, userna
}
}
return
identity
,
false
,
nil
return
identity
,
false
,
nil
}
}
func
(
p
passwordConnector
)
Prompt
()
string
{
return
""
}
examples/config-ldap.yaml
View file @
75a07f2b
...
@@ -15,11 +15,13 @@ connectors:
...
@@ -15,11 +15,13 @@ connectors:
# No TLS for this setup.
# No TLS for this setup.
insecureNoSSL
:
true
insecureNoSSL
:
true
# This would normally be a read-only user.
# This would normally be a read-only user.
bindDN
:
cn=admin,dc=example,dc=org
bindDN
:
cn=admin,dc=example,dc=org
bindPW
:
admin
bindPW
:
admin
usernamePrompt
:
Email Address
userSearch
:
userSearch
:
baseDN
:
ou=People,dc=example,dc=org
baseDN
:
ou=People,dc=example,dc=org
filter
:
"
(objectClass=person)"
filter
:
"
(objectClass=person)"
...
...
server/handlers.go
View file @
75a07f2b
...
@@ -250,7 +250,7 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
...
@@ -250,7 +250,7 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
}
}
http
.
Redirect
(
w
,
r
,
callbackURL
,
http
.
StatusFound
)
http
.
Redirect
(
w
,
r
,
callbackURL
,
http
.
StatusFound
)
case
connector
.
PasswordConnector
:
case
connector
.
PasswordConnector
:
if
err
:=
s
.
templates
.
password
(
w
,
r
.
URL
.
String
(),
""
,
false
);
err
!=
nil
{
if
err
:=
s
.
templates
.
password
(
w
,
r
.
URL
.
String
(),
""
,
usernamePrompt
(
conn
),
false
);
err
!=
nil
{
s
.
logger
.
Errorf
(
"Server template error: %v"
,
err
)
s
.
logger
.
Errorf
(
"Server template error: %v"
,
err
)
}
}
case
connector
.
SAMLConnector
:
case
connector
.
SAMLConnector
:
...
@@ -298,7 +298,7 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
...
@@ -298,7 +298,7 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
return
return
}
}
if
!
ok
{
if
!
ok
{
if
err
:=
s
.
templates
.
password
(
w
,
r
.
URL
.
String
(),
username
,
true
);
err
!=
nil
{
if
err
:=
s
.
templates
.
password
(
w
,
r
.
URL
.
String
(),
username
,
usernamePrompt
(
passwordConnector
),
true
);
err
!=
nil
{
s
.
logger
.
Errorf
(
"Server template error: %v"
,
err
)
s
.
logger
.
Errorf
(
"Server template error: %v"
,
err
)
}
}
return
return
...
@@ -1005,3 +1005,11 @@ func (s *Server) tokenErrHelper(w http.ResponseWriter, typ string, description s
...
@@ -1005,3 +1005,11 @@ func (s *Server) tokenErrHelper(w http.ResponseWriter, typ string, description s
s
.
logger
.
Errorf
(
"token error response: %v"
,
err
)
s
.
logger
.
Errorf
(
"token error response: %v"
,
err
)
}
}
}
}
// Check for username prompt override from connector. Defaults to "Username".
func
usernamePrompt
(
conn
connector
.
PasswordConnector
)
string
{
if
attr
:=
conn
.
Prompt
();
attr
!=
""
{
return
attr
}
return
"Username"
}
server/server.go
View file @
75a07f2b
...
@@ -344,6 +344,10 @@ func (db passwordDB) Refresh(ctx context.Context, s connector.Scopes, identity c
...
@@ -344,6 +344,10 @@ func (db passwordDB) Refresh(ctx context.Context, s connector.Scopes, identity c
return
identity
,
nil
return
identity
,
nil
}
}
func
(
db
passwordDB
)
Prompt
()
string
{
return
"Email Address"
}
// newKeyCacher returns a storage which caches keys so long as the next
// newKeyCacher returns a storage which caches keys so long as the next
func
newKeyCacher
(
s
storage
.
Storage
,
now
func
()
time
.
Time
)
storage
.
Storage
{
func
newKeyCacher
(
s
storage
.
Storage
,
now
func
()
time
.
Time
)
storage
.
Storage
{
if
now
==
nil
{
if
now
==
nil
{
...
...
server/server_test.go
View file @
75a07f2b
...
@@ -1017,6 +1017,16 @@ func TestPasswordDB(t *testing.T) {
...
@@ -1017,6 +1017,16 @@ func TestPasswordDB(t *testing.T) {
}
}
func
TestPasswordDBUsernamePrompt
(
t
*
testing
.
T
)
{
s
:=
memory
.
New
(
logger
)
conn
:=
newPasswordDB
(
s
)
expected
:=
"Email Address"
if
actual
:=
conn
.
Prompt
();
actual
!=
expected
{
t
.
Errorf
(
"expected %v, got %v"
,
expected
,
actual
)
}
}
type
storageWithKeysTrigger
struct
{
type
storageWithKeysTrigger
struct
{
storage
.
Storage
storage
.
Storage
f
func
()
f
func
()
...
...
server/templates.go
View file @
75a07f2b
...
@@ -139,6 +139,7 @@ func loadTemplates(c webConfig, templatesDir string) (*templates, error) {
...
@@ -139,6 +139,7 @@ func loadTemplates(c webConfig, templatesDir string) (*templates, error) {
"issuer"
:
func
()
string
{
return
c
.
issuer
},
"issuer"
:
func
()
string
{
return
c
.
issuer
},
"logo"
:
func
()
string
{
return
c
.
logoURL
},
"logo"
:
func
()
string
{
return
c
.
logoURL
},
"url"
:
func
(
s
string
)
string
{
return
join
(
c
.
issuerURL
,
s
)
},
"url"
:
func
(
s
string
)
string
{
return
join
(
c
.
issuerURL
,
s
)
},
"lower"
:
strings
.
ToLower
,
}
}
tmpls
,
err
:=
template
.
New
(
""
)
.
Funcs
(
funcs
)
.
ParseFiles
(
filenames
...
)
tmpls
,
err
:=
template
.
New
(
""
)
.
Funcs
(
funcs
)
.
ParseFiles
(
filenames
...
)
...
@@ -189,12 +190,13 @@ func (t *templates) login(w http.ResponseWriter, connectors []connectorInfo) err
...
@@ -189,12 +190,13 @@ func (t *templates) login(w http.ResponseWriter, connectors []connectorInfo) err
return
renderTemplate
(
w
,
t
.
loginTmpl
,
data
)
return
renderTemplate
(
w
,
t
.
loginTmpl
,
data
)
}
}
func
(
t
*
templates
)
password
(
w
http
.
ResponseWriter
,
postURL
,
lastUsername
string
,
lastWasInvalid
bool
)
error
{
func
(
t
*
templates
)
password
(
w
http
.
ResponseWriter
,
postURL
,
lastUsername
,
usernamePrompt
string
,
lastWasInvalid
bool
)
error
{
data
:=
struct
{
data
:=
struct
{
PostURL
string
PostURL
string
Username
string
Username
string
Invalid
bool
UsernamePrompt
string
}{
postURL
,
lastUsername
,
lastWasInvalid
}
Invalid
bool
}{
postURL
,
lastUsername
,
usernamePrompt
,
lastWasInvalid
}
return
renderTemplate
(
w
,
t
.
passwordTmpl
,
data
)
return
renderTemplate
(
w
,
t
.
passwordTmpl
,
data
)
}
}
...
...
web/templates/password.html
View file @
75a07f2b
...
@@ -5,9 +5,9 @@
...
@@ -5,9 +5,9 @@
<form
method=
"post"
action=
"{{ .PostURL }}"
>
<form
method=
"post"
action=
"{{ .PostURL }}"
>
<div
class=
"theme-form-row"
>
<div
class=
"theme-form-row"
>
<div
class=
"theme-form-label"
>
<div
class=
"theme-form-label"
>
<label
for=
"userid"
>
Username
</label>
<label
for=
"userid"
>
{{ .UsernamePrompt }}
</label>
</div>
</div>
<input
tabindex=
"1"
required
id=
"login"
name=
"login"
type=
"text"
class=
"theme-form-input"
placeholder=
"
username
"
{{
if
.
Username
}}
value=
"{{ .Username }}"
{{
else
}}
autofocus
{{
end
}}
/>
<input
tabindex=
"1"
required
id=
"login"
name=
"login"
type=
"text"
class=
"theme-form-input"
placeholder=
"
{{ .UsernamePrompt | lower }}
"
{{
if
.
Username
}}
value=
"{{ .Username }}"
{{
else
}}
autofocus
{{
end
}}
/>
</div>
</div>
<div
class=
"theme-form-row"
>
<div
class=
"theme-form-row"
>
<div
class=
"theme-form-label"
>
<div
class=
"theme-form-label"
>
...
@@ -18,7 +18,7 @@
...
@@ -18,7 +18,7 @@
{{ if .Invalid }}
{{ if .Invalid }}
<div
id=
"login-error"
class=
"dex-error-box"
>
<div
id=
"login-error"
class=
"dex-error-box"
>
Invalid
username
and password.
Invalid
{{ .UsernamePrompt }}
and password.
</div>
</div>
{{ end }}
{{ end }}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment