web UI: allow to enable OIDC login and/or login forms

any combination is now supported

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2022-07-22 20:55:33 +02:00
parent e6bfbcd489
commit 1470018054
No known key found for this signature in database
GPG key ID: 2F1FB59433D5A8CB
6 changed files with 62 additions and 20 deletions

View file

@ -255,7 +255,7 @@ The configuration file contains the following sections:
- `address`, string. Leave blank to listen on all available network interfaces. On *NIX you can specify an absolute path to listen on a Unix-domain socket Default: blank.
- `enable_web_admin`, boolean. Set to `false` to disable the built-in web admin for this binding. You also need to define `templates_path` and `static_files_path` to use the built-in web admin interface. Default `true`.
- `enable_web_client`, boolean. Set to `false` to disable the built-in web client for this binding. You also need to define `templates_path` and `static_files_path` to use the built-in web client interface. Default `true`.
- `enabled_login_methods`, integer. Defines the login methods available for the WebAdmin and WebClient UIs. `0` means any configured method: username/password login form and OIDC, if enabled. `1` means OIDC for the WebAdmin UI. The username/password login form will not be available for the WebAdmin UI. `2` means OIDC for the WebClient UI. The username/password login form will not be available for the WebClient UI. You can combine the values. For example `3` means that you can only login using OIDC on both WebClient and WebAdmin UI. Default: `0`.
- `enabled_login_methods`, integer. Defines the login methods available for the WebAdmin and WebClient UIs. `0` means any configured method: username/password login form and OIDC, if enabled. `1` means OIDC for the WebAdmin UI. `2` means OIDC for the WebClient UI. `4` means login form for the WebAdmin UI. `8` means login form for the WebClient UI. You can combine the values. For example `3` means that you can only login using OIDC on both WebClient and WebAdmin UI. Default: `0`.
- `enable_https`, boolean. Set to `true` and provide both a certificate and a key file to enable HTTPS connection for this binding. Default `false`.
- `certificate_file`, string. Binding specific TLS certificate. This can be an absolute path or a path relative to the config dir.
- `certificate_key_file`, string. Binding specific private key matching the above certificate. This can be an absolute path or a path relative to the config dir. If not set the global ones will be used, if any.

6
go.mod
View file

@ -67,9 +67,9 @@ require (
gocloud.dev v0.25.0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
golang.org/x/net v0.0.0-20220708220712-1185a9018129
golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92
golang.org/x/sys v0.0.0-20220721230656-c6bc011c0c49
golang.org/x/time v0.0.0-20220609170525-579cf78fd858
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
google.golang.org/api v0.88.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
)

12
go.sum
View file

@ -869,8 +869,8 @@ golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92 h1:oVlhw3Oe+1reYsE2Nqu19PDJfLzwdU3QUUrG86rLK68=
golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c h1:q3gFqPqH7NVofKo3c3yETAP//pPI+G5mvB7qqj1Y5kY=
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -970,8 +970,8 @@ golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220721230656-c6bc011c0c49 h1:TMjZDarEwf621XDryfitp/8awEhiZNiwgphKlTMGRIg=
golang.org/x/sys v0.0.0-20220721230656-c6bc011c0c49/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -990,8 +990,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=

View file

@ -413,8 +413,10 @@ type Binding struct {
// Defines the login methods available for the WebAdmin and WebClient UIs:
//
// - 0 means any configured method: username/password login form and OIDC, if enabled
// - 1 means OIDC for the WebAdmin UI. The username/password login form will not be available
// - 2 means OIDC for the WebClient UI. The username/password login form will not be available
// - 1 means OIDC for the WebAdmin UI
// - 2 means OIDC for the WebClient UI
// - 4 means login form for the WebAdmin UI
// - 8 means login form for the WebClient UI
//
// You can combine the values. For example 3 means that you can only login using OIDC on
// both WebClient and WebAdmin UI.
@ -529,12 +531,32 @@ func (b *Binding) IsValid() bool {
return false
}
func (b *Binding) isWebAdminOIDCLoginDisabled() bool {
if b.EnableWebAdmin {
if b.EnabledLoginMethods == 0 {
return false
}
return b.EnabledLoginMethods&1 == 0
}
return false
}
func (b *Binding) isWebClientOIDCLoginDisabled() bool {
if b.EnableWebClient {
if b.EnabledLoginMethods == 0 {
return false
}
return b.EnabledLoginMethods&2 == 0
}
return false
}
func (b *Binding) isWebAdminLoginFormDisabled() bool {
if b.EnableWebAdmin {
if b.EnabledLoginMethods == 0 {
return false
}
return b.EnabledLoginMethods&1 != 0
return b.EnabledLoginMethods&4 == 0
}
return false
}
@ -544,18 +566,28 @@ func (b *Binding) isWebClientLoginFormDisabled() bool {
if b.EnabledLoginMethods == 0 {
return false
}
return b.EnabledLoginMethods&2 != 0
return b.EnabledLoginMethods&8 == 0
}
return false
}
func (b *Binding) checkLoginMethods() error {
if b.isWebAdminLoginFormDisabled() && b.isWebAdminOIDCLoginDisabled() {
return errors.New("no login method available for WebAdmin UI")
}
if !b.isWebAdminOIDCLoginDisabled() {
if b.isWebAdminLoginFormDisabled() && !b.OIDC.hasRoles() {
return errors.New("no login method available for WebAdmin UI")
}
}
if b.isWebClientLoginFormDisabled() && b.isWebClientOIDCLoginDisabled() {
return errors.New("no login method available for WebClient UI")
}
if !b.isWebClientOIDCLoginDisabled() {
if b.isWebClientLoginFormDisabled() && !b.OIDC.isEnabled() {
return errors.New("no login method available for WebClient UI")
}
}
return nil
}

View file

@ -533,6 +533,16 @@ func TestInitialization(t *testing.T) {
}
httpdConf.Bindings[0].EnabledLoginMethods = 2
err = httpdConf.Initialize(configDir, isShared)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "no login method available for WebAdmin UI")
}
httpdConf.Bindings[0].EnabledLoginMethods = 6
err = httpdConf.Initialize(configDir, isShared)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "no login method available for WebClient UI")
}
httpdConf.Bindings[0].EnabledLoginMethods = 4
err = httpdConf.Initialize(configDir, isShared)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "no login method available for WebClient UI")
}

View file

@ -174,7 +174,7 @@ func (s *httpdServer) renderClientLoginPage(w http.ResponseWriter, error, ip str
if smtp.IsEnabled() && !data.FormDisabled {
data.ForgotPwdURL = webClientForgotPwdPath
}
if s.binding.OIDC.isEnabled() {
if s.binding.OIDC.isEnabled() && !s.binding.isWebClientOIDCLoginDisabled() {
data.OpenIDLoginURL = webClientOIDCLoginPath
}
renderClientTemplate(w, templateClientLogin, data)
@ -552,7 +552,7 @@ func (s *httpdServer) renderAdminLoginPage(w http.ResponseWriter, error, ip stri
if smtp.IsEnabled() && !data.FormDisabled {
data.ForgotPwdURL = webAdminForgotPwdPath
}
if s.binding.OIDC.hasRoles() {
if s.binding.OIDC.hasRoles() && !s.binding.isWebAdminOIDCLoginDisabled() {
data.OpenIDLoginURL = webAdminOIDCLoginPath
}
renderAdminTemplate(w, templateLogin, data)
@ -1396,7 +1396,7 @@ func (s *httpdServer) setupWebClientRoutes() {
http.Redirect(w, r, webClientLoginPath, http.StatusFound)
})
s.router.Get(webClientLoginPath, s.handleClientWebLogin)
if s.binding.OIDC.isEnabled() {
if s.binding.OIDC.isEnabled() && !s.binding.isWebClientOIDCLoginDisabled() {
s.router.Get(webClientOIDCLoginPath, s.handleWebClientOIDCLogin)
}
if !s.binding.isWebClientLoginFormDisabled() {
@ -1497,7 +1497,7 @@ func (s *httpdServer) setupWebAdminRoutes() {
s.redirectToWebPath(w, r, webAdminLoginPath)
})
s.router.Get(webAdminLoginPath, s.handleWebAdminLogin)
if s.binding.OIDC.hasRoles() {
if s.binding.OIDC.hasRoles() && !s.binding.isWebAdminOIDCLoginDisabled() {
s.router.Get(webAdminOIDCLoginPath, s.handleWebAdminOIDCLogin)
}
s.router.Get(webAdminSetupPath, s.handleWebAdminSetupGet)