mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-25 00:50:31 +00:00
backport OIDC related changes from main
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
b34bc2b818
commit
2da19ef233
19 changed files with 364 additions and 111 deletions
|
@ -99,6 +99,7 @@ var (
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
EnableWebAdmin: true,
|
EnableWebAdmin: true,
|
||||||
EnableWebClient: true,
|
EnableWebClient: true,
|
||||||
|
EnabledLoginMethods: 0,
|
||||||
EnableHTTPS: false,
|
EnableHTTPS: false,
|
||||||
CertificateFile: "",
|
CertificateFile: "",
|
||||||
CertificateKeyFile: "",
|
CertificateKeyFile: "",
|
||||||
|
@ -121,6 +122,7 @@ var (
|
||||||
ImplicitRoles: false,
|
ImplicitRoles: false,
|
||||||
Scopes: []string{"openid", "profile", "email"},
|
Scopes: []string{"openid", "profile", "email"},
|
||||||
CustomFields: []string{},
|
CustomFields: []string{},
|
||||||
|
Debug: false,
|
||||||
},
|
},
|
||||||
Security: httpd.SecurityConf{
|
Security: httpd.SecurityConf{
|
||||||
Enabled: false,
|
Enabled: false,
|
||||||
|
@ -1448,6 +1450,12 @@ func getHTTPDOIDCFromEnv(idx int) (httpd.OIDC, bool) {
|
||||||
isSet = true
|
isSet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__OIDC__DEBUG", idx))
|
||||||
|
if ok {
|
||||||
|
result.Debug = debug
|
||||||
|
isSet = true
|
||||||
|
}
|
||||||
|
|
||||||
return result, isSet
|
return result, isSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1634,7 +1642,7 @@ func getHTTPDBindingProxyConfigsFromEnv(idx int, binding *httpd.Binding) bool {
|
||||||
return isSet
|
return isSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHTTPDBindingFromEnv(idx int) {
|
func getHTTPDBindingFromEnv(idx int) { //nolint:gocyclo
|
||||||
binding := getDefaultHTTPBinding(idx)
|
binding := getDefaultHTTPBinding(idx)
|
||||||
isSet := false
|
isSet := false
|
||||||
|
|
||||||
|
@ -1674,6 +1682,12 @@ func getHTTPDBindingFromEnv(idx int) {
|
||||||
isSet = true
|
isSet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enabledLoginMethods, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__ENABLED_LOGIN_METHODS", idx))
|
||||||
|
if ok {
|
||||||
|
binding.EnabledLoginMethods = int(enabledLoginMethods)
|
||||||
|
isSet = true
|
||||||
|
}
|
||||||
|
|
||||||
renderOpenAPI, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__RENDER_OPENAPI", idx))
|
renderOpenAPI, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__RENDER_OPENAPI", idx))
|
||||||
if ok {
|
if ok {
|
||||||
binding.RenderOpenAPI = renderOpenAPI
|
binding.RenderOpenAPI = renderOpenAPI
|
||||||
|
|
|
@ -1055,6 +1055,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
||||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__PORT", "9000")
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__PORT", "9000")
|
||||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_ADMIN", "0")
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_ADMIN", "0")
|
||||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_CLIENT", "0")
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_CLIENT", "0")
|
||||||
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLED_LOGIN_METHODS", "3")
|
||||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__RENDER_OPENAPI", "0")
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__RENDER_OPENAPI", "0")
|
||||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_HTTPS", "1 ")
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_HTTPS", "1 ")
|
||||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__MIN_TLS_VERSION", "13")
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__MIN_TLS_VERSION", "13")
|
||||||
|
@ -1077,6 +1078,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
||||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__SCOPES", "openid")
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__SCOPES", "openid")
|
||||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__IMPLICIT_ROLES", "1")
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__IMPLICIT_ROLES", "1")
|
||||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__CUSTOM_FIELDS", "field1,field2")
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__CUSTOM_FIELDS", "field1,field2")
|
||||||
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__DEBUG", "1")
|
||||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ENABLED", "true")
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ENABLED", "true")
|
||||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS", "*.example.com,*.example.net")
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS", "*.example.com,*.example.net")
|
||||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS_ARE_REGEX", "1")
|
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS_ARE_REGEX", "1")
|
||||||
|
@ -1122,6 +1124,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
||||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__MIN_TLS_VERSION")
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__MIN_TLS_VERSION")
|
||||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_ADMIN")
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_ADMIN")
|
||||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_CLIENT")
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_CLIENT")
|
||||||
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLED_LOGIN_METHODS")
|
||||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__RENDER_OPENAPI")
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__RENDER_OPENAPI")
|
||||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__CLIENT_AUTH_TYPE")
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__CLIENT_AUTH_TYPE")
|
||||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__TLS_CIPHER_SUITES")
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__TLS_CIPHER_SUITES")
|
||||||
|
@ -1142,6 +1145,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
||||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__SCOPES")
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__SCOPES")
|
||||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__IMPLICIT_ROLES")
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__IMPLICIT_ROLES")
|
||||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__CUSTOM_FIELDS")
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__CUSTOM_FIELDS")
|
||||||
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__DEBUG")
|
||||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ENABLED")
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ENABLED")
|
||||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS")
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS")
|
||||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS_ARE_REGEX")
|
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS_ARE_REGEX")
|
||||||
|
@ -1182,6 +1186,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
||||||
require.Equal(t, 12, bindings[0].MinTLSVersion)
|
require.Equal(t, 12, bindings[0].MinTLSVersion)
|
||||||
require.True(t, bindings[0].EnableWebAdmin)
|
require.True(t, bindings[0].EnableWebAdmin)
|
||||||
require.True(t, bindings[0].EnableWebClient)
|
require.True(t, bindings[0].EnableWebClient)
|
||||||
|
require.Equal(t, 0, bindings[0].EnabledLoginMethods)
|
||||||
require.True(t, bindings[0].RenderOpenAPI)
|
require.True(t, bindings[0].RenderOpenAPI)
|
||||||
require.Len(t, bindings[0].TLSCipherSuites, 1)
|
require.Len(t, bindings[0].TLSCipherSuites, 1)
|
||||||
require.Empty(t, bindings[0].OIDC.ConfigURL)
|
require.Empty(t, bindings[0].OIDC.ConfigURL)
|
||||||
|
@ -1190,17 +1195,20 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
||||||
require.False(t, bindings[0].Security.Enabled)
|
require.False(t, bindings[0].Security.Enabled)
|
||||||
require.Equal(t, 0, bindings[0].ClientIPHeaderDepth)
|
require.Equal(t, 0, bindings[0].ClientIPHeaderDepth)
|
||||||
require.Len(t, bindings[0].OIDC.Scopes, 3)
|
require.Len(t, bindings[0].OIDC.Scopes, 3)
|
||||||
|
require.False(t, bindings[0].OIDC.Debug)
|
||||||
require.Equal(t, 8000, bindings[1].Port)
|
require.Equal(t, 8000, bindings[1].Port)
|
||||||
require.Equal(t, "127.0.0.1", bindings[1].Address)
|
require.Equal(t, "127.0.0.1", bindings[1].Address)
|
||||||
require.False(t, bindings[1].EnableHTTPS)
|
require.False(t, bindings[1].EnableHTTPS)
|
||||||
require.Equal(t, 12, bindings[0].MinTLSVersion)
|
require.Equal(t, 12, bindings[0].MinTLSVersion)
|
||||||
require.True(t, bindings[1].EnableWebAdmin)
|
require.True(t, bindings[1].EnableWebAdmin)
|
||||||
require.True(t, bindings[1].EnableWebClient)
|
require.True(t, bindings[1].EnableWebClient)
|
||||||
|
require.Equal(t, 0, bindings[1].EnabledLoginMethods)
|
||||||
require.True(t, bindings[1].RenderOpenAPI)
|
require.True(t, bindings[1].RenderOpenAPI)
|
||||||
require.Nil(t, bindings[1].TLSCipherSuites)
|
require.Nil(t, bindings[1].TLSCipherSuites)
|
||||||
require.Equal(t, 1, bindings[1].HideLoginURL)
|
require.Equal(t, 1, bindings[1].HideLoginURL)
|
||||||
require.Empty(t, bindings[1].OIDC.ClientID)
|
require.Empty(t, bindings[1].OIDC.ClientID)
|
||||||
require.Len(t, bindings[1].OIDC.Scopes, 3)
|
require.Len(t, bindings[1].OIDC.Scopes, 3)
|
||||||
|
require.False(t, bindings[1].OIDC.Debug)
|
||||||
require.False(t, bindings[1].Security.Enabled)
|
require.False(t, bindings[1].Security.Enabled)
|
||||||
require.Equal(t, "Web Admin", bindings[1].Branding.WebAdmin.Name)
|
require.Equal(t, "Web Admin", bindings[1].Branding.WebAdmin.Name)
|
||||||
require.Equal(t, "WebClient", bindings[1].Branding.WebClient.ShortName)
|
require.Equal(t, "WebClient", bindings[1].Branding.WebClient.ShortName)
|
||||||
|
@ -1211,6 +1219,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
||||||
require.Equal(t, 13, bindings[2].MinTLSVersion)
|
require.Equal(t, 13, bindings[2].MinTLSVersion)
|
||||||
require.False(t, bindings[2].EnableWebAdmin)
|
require.False(t, bindings[2].EnableWebAdmin)
|
||||||
require.False(t, bindings[2].EnableWebClient)
|
require.False(t, bindings[2].EnableWebClient)
|
||||||
|
require.Equal(t, 3, bindings[2].EnabledLoginMethods)
|
||||||
require.False(t, bindings[2].RenderOpenAPI)
|
require.False(t, bindings[2].RenderOpenAPI)
|
||||||
require.Equal(t, 1, bindings[2].ClientAuthType)
|
require.Equal(t, 1, bindings[2].ClientAuthType)
|
||||||
require.Len(t, bindings[2].TLSCipherSuites, 2)
|
require.Len(t, bindings[2].TLSCipherSuites, 2)
|
||||||
|
@ -1237,6 +1246,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
||||||
require.Len(t, bindings[2].OIDC.CustomFields, 2)
|
require.Len(t, bindings[2].OIDC.CustomFields, 2)
|
||||||
require.Equal(t, "field1", bindings[2].OIDC.CustomFields[0])
|
require.Equal(t, "field1", bindings[2].OIDC.CustomFields[0])
|
||||||
require.Equal(t, "field2", bindings[2].OIDC.CustomFields[1])
|
require.Equal(t, "field2", bindings[2].OIDC.CustomFields[1])
|
||||||
|
require.True(t, bindings[2].OIDC.Debug)
|
||||||
require.True(t, bindings[2].Security.Enabled)
|
require.True(t, bindings[2].Security.Enabled)
|
||||||
require.Len(t, bindings[2].Security.AllowedHosts, 2)
|
require.Len(t, bindings[2].Security.AllowedHosts, 2)
|
||||||
require.Equal(t, "*.example.com", bindings[2].Security.AllowedHosts[0])
|
require.Equal(t, "*.example.com", bindings[2].Security.AllowedHosts[0])
|
||||||
|
|
|
@ -1044,9 +1044,7 @@ func (p *BoltProvider) updateFolder(folder *vfs.BaseVirtualFolder) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *BoltProvider) deleteFolderMappings(tx *bolt.Tx, folder vfs.BaseVirtualFolder, usersBucket,
|
func (p *BoltProvider) deleteFolderMappings(folder vfs.BaseVirtualFolder, usersBucket, groupsBucket *bolt.Bucket) error {
|
||||||
groupsBucket *bolt.Bucket,
|
|
||||||
) error {
|
|
||||||
for _, username := range folder.Users {
|
for _, username := range folder.Users {
|
||||||
var u []byte
|
var u []byte
|
||||||
if u = usersBucket.Get([]byte(username)); u == nil {
|
if u = usersBucket.Get([]byte(username)); u == nil {
|
||||||
|
@ -1126,7 +1124,7 @@ func (p *BoltProvider) deleteFolder(folder vfs.BaseVirtualFolder) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = p.deleteFolderMappings(tx, folder, usersBucket, groupsBucket); err != nil {
|
if err = p.deleteFolderMappings(folder, usersBucket, groupsBucket); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -260,6 +260,8 @@ The configuration file contains the following sections:
|
||||||
- `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_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`.
|
- `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`.
|
||||||
- `enable_https`, boolean. Set to `true` and provide both a certificate and a key file to enable HTTPS connection for this binding. Default `false`.
|
- `enable_https`, boolean. Set to `true` and provide both a certificate and a key file to enable HTTPS connection for this binding. Default `false`.
|
||||||
|
- `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_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.
|
- `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.
|
||||||
- `min_tls_version`, integer. Defines the minimum version of TLS to be enabled. `12` means TLS 1.2 (and therefore TLS 1.2 and TLS 1.3 will be enabled),`13` means TLS 1.3. Default: `12`.
|
- `min_tls_version`, integer. Defines the minimum version of TLS to be enabled. `12` means TLS 1.2 (and therefore TLS 1.2 and TLS 1.3 will be enabled),`13` means TLS 1.3. Default: `12`.
|
||||||
|
@ -283,6 +285,7 @@ The configuration file contains the following sections:
|
||||||
- `role_field`, string. Defines the optional ID token claims field to map to a SFTPGo role. If the defined ID token claims field is set to `admin` the authenticated user is mapped to an SFTPGo admin. You don't need to specify this field if you want to use OpenID only for the Web Client UI. Default: blank.
|
- `role_field`, string. Defines the optional ID token claims field to map to a SFTPGo role. If the defined ID token claims field is set to `admin` the authenticated user is mapped to an SFTPGo admin. You don't need to specify this field if you want to use OpenID only for the Web Client UI. Default: blank.
|
||||||
- `implicit_roles`, boolean. If set, the `role_field` is ignored and the SFTPGo role is assumed based on the login link used. Default: `false`.
|
- `implicit_roles`, boolean. If set, the `role_field` is ignored and the SFTPGo role is assumed based on the login link used. Default: `false`.
|
||||||
- `custom_fields`, list of strings. Custom token claims fields to pass to the pre-login hook. Default: empty.
|
- `custom_fields`, list of strings. Custom token claims fields to pass to the pre-login hook. Default: empty.
|
||||||
|
- `debug`, boolean. If set, the received id tokens will be logged at debug level. Default: `false`.
|
||||||
- `security`, struct. Defines security headers to add to HTTP responses and allows to restrict allowed hosts. The following parameters are supported:
|
- `security`, struct. Defines security headers to add to HTTP responses and allows to restrict allowed hosts. The following parameters are supported:
|
||||||
- `enabled`, boolean. Set to `true` to enable security configurations. Default: `false`.
|
- `enabled`, boolean. Set to `true` to enable security configurations. Default: `false`.
|
||||||
- `allowed_hosts`, list of strings. Fully qualified domain names that are allowed. An empty list allows any and all host names. Default: empty.
|
- `allowed_hosts`, list of strings. Fully qualified domain names that are allowed. An empty list allows any and all host names. Default: empty.
|
||||||
|
|
|
@ -42,8 +42,12 @@ Add the following configuration parameters to the SFTPGo configuration file (or
|
||||||
"client_secret": "jRsmE0SWnuZjP7djBqNq0mrf8QN77j2c",
|
"client_secret": "jRsmE0SWnuZjP7djBqNq0mrf8QN77j2c",
|
||||||
"config_url": "http://192.168.1.12:8086/auth/realms/sftpgo",
|
"config_url": "http://192.168.1.12:8086/auth/realms/sftpgo",
|
||||||
"redirect_base_url": "http://192.168.1.50:8080",
|
"redirect_base_url": "http://192.168.1.50:8080",
|
||||||
|
"scopes": [
|
||||||
|
"openid",
|
||||||
|
"profile",
|
||||||
|
"email"
|
||||||
|
],
|
||||||
"username_field": "preferred_username",
|
"username_field": "preferred_username",
|
||||||
"scopes": [ "openid", "profile", "email" ],
|
|
||||||
"role_field": "sftpgo_role",
|
"role_field": "sftpgo_role",
|
||||||
"implicit_roles": false,
|
"implicit_roles": false,
|
||||||
"custom_fields": []
|
"custom_fields": []
|
||||||
|
|
|
@ -889,7 +889,7 @@ func TestLoginExternalAuth(t *testing.T) {
|
||||||
err = config.LoadConfig(configDir, "")
|
err = config.LoadConfig(configDir, "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
providerConf := config.GetProviderConf()
|
providerConf := config.GetProviderConf()
|
||||||
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm)
|
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u), os.ModePerm)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
providerConf.ExternalAuthHook = extAuthPath
|
providerConf.ExternalAuthHook = extAuthPath
|
||||||
providerConf.ExternalAuthScope = 0
|
providerConf.ExternalAuthScope = 0
|
||||||
|
@ -913,7 +913,7 @@ func TestLoginExternalAuth(t *testing.T) {
|
||||||
Type: sdk.GroupTypePrimary,
|
Type: sdk.GroupTypePrimary,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm)
|
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u), os.ModePerm)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
_, err = getFTPClient(u, true, nil)
|
_, err = getFTPClient(u, true, nil)
|
||||||
if !assert.Error(t, err) {
|
if !assert.Error(t, err) {
|
||||||
|
@ -924,7 +924,7 @@ func TestLoginExternalAuth(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Groups = nil
|
u.Groups = nil
|
||||||
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm)
|
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u), os.ModePerm)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
u.Username = defaultUsername + "1"
|
u.Username = defaultUsername + "1"
|
||||||
client, err = getFTPClient(u, true, nil)
|
client, err = getFTPClient(u, true, nil)
|
||||||
|
@ -3022,7 +3022,7 @@ func TestExternalAuthWithClientCert(t *testing.T) {
|
||||||
err = config.LoadConfig(configDir, "")
|
err = config.LoadConfig(configDir, "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
providerConf := config.GetProviderConf()
|
providerConf := config.GetProviderConf()
|
||||||
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm)
|
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u), os.ModePerm)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
providerConf.ExternalAuthHook = extAuthPath
|
providerConf.ExternalAuthHook = extAuthPath
|
||||||
providerConf.ExternalAuthScope = 8
|
providerConf.ExternalAuthScope = 8
|
||||||
|
@ -3436,24 +3436,13 @@ func getTestSFTPUser() dataprovider.User {
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExtAuthScriptContent(user dataprovider.User, nonJSONResponse bool, username string) []byte {
|
func getExtAuthScriptContent(user dataprovider.User) []byte {
|
||||||
extAuthContent := []byte("#!/bin/sh\n\n")
|
extAuthContent := []byte("#!/bin/sh\n\n")
|
||||||
extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("if test \"$SFTPGO_AUTHD_USERNAME\" = \"%v\"; then\n", user.Username))...)
|
extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("if test \"$SFTPGO_AUTHD_USERNAME\" = \"%v\"; then\n", user.Username))...)
|
||||||
if len(username) > 0 {
|
|
||||||
user.Username = username
|
|
||||||
}
|
|
||||||
u, _ := json.Marshal(user)
|
u, _ := json.Marshal(user)
|
||||||
if nonJSONResponse {
|
|
||||||
extAuthContent = append(extAuthContent, []byte("echo 'text response'\n")...)
|
|
||||||
} else {
|
|
||||||
extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("echo '%v'\n", string(u)))...)
|
extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("echo '%v'\n", string(u)))...)
|
||||||
}
|
|
||||||
extAuthContent = append(extAuthContent, []byte("else\n")...)
|
extAuthContent = append(extAuthContent, []byte("else\n")...)
|
||||||
if nonJSONResponse {
|
|
||||||
extAuthContent = append(extAuthContent, []byte("echo 'text response'\n")...)
|
|
||||||
} else {
|
|
||||||
extAuthContent = append(extAuthContent, []byte("echo '{\"username\":\"\"}'\n")...)
|
extAuthContent = append(extAuthContent, []byte("echo '{\"username\":\"\"}'\n")...)
|
||||||
}
|
|
||||||
extAuthContent = append(extAuthContent, []byte("fi\n")...)
|
extAuthContent = append(extAuthContent, []byte("fi\n")...)
|
||||||
return extAuthContent
|
return extAuthContent
|
||||||
}
|
}
|
||||||
|
|
21
go.mod
21
go.mod
|
@ -3,7 +3,7 @@ module github.com/drakkan/sftpgo/v2
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/storage v1.23.0
|
cloud.google.com/go/storage v1.24.0
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1
|
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1
|
||||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
|
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
|
||||||
|
@ -17,7 +17,7 @@ require (
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.1
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.1
|
||||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.13
|
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.13
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.16.9
|
github.com/aws/aws-sdk-go-v2/service/sts v1.16.9
|
||||||
github.com/cockroachdb/cockroach-go/v2 v2.2.14
|
github.com/cockroachdb/cockroach-go/v2 v2.2.15
|
||||||
github.com/coreos/go-oidc/v3 v3.2.0
|
github.com/coreos/go-oidc/v3 v3.2.0
|
||||||
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001
|
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001
|
||||||
github.com/fclairamb/ftpserverlib v0.18.1-0.20220515214847-f96d31ec626e
|
github.com/fclairamb/ftpserverlib v0.18.1-0.20220515214847-f96d31ec626e
|
||||||
|
@ -35,7 +35,7 @@ require (
|
||||||
github.com/hashicorp/go-plugin v1.4.4
|
github.com/hashicorp/go-plugin v1.4.4
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.1
|
github.com/hashicorp/go-retryablehttp v0.7.1
|
||||||
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
|
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
|
||||||
github.com/klauspost/compress v1.15.8
|
github.com/klauspost/compress v1.15.9
|
||||||
github.com/lestrrat-go/jwx v1.2.25
|
github.com/lestrrat-go/jwx v1.2.25
|
||||||
github.com/lib/pq v1.10.6
|
github.com/lib/pq v1.10.6
|
||||||
github.com/lithammer/shortuuid/v3 v3.0.7
|
github.com/lithammer/shortuuid/v3 v3.0.7
|
||||||
|
@ -53,7 +53,7 @@ require (
|
||||||
github.com/rs/zerolog v1.27.0
|
github.com/rs/zerolog v1.27.0
|
||||||
github.com/sftpgo/sdk v0.1.1
|
github.com/sftpgo/sdk v0.1.1
|
||||||
github.com/shirou/gopsutil/v3 v3.22.6
|
github.com/shirou/gopsutil/v3 v3.22.6
|
||||||
github.com/spf13/afero v1.9.0
|
github.com/spf13/afero v1.9.2
|
||||||
github.com/spf13/cobra v1.5.0
|
github.com/spf13/cobra v1.5.0
|
||||||
github.com/spf13/viper v1.12.0
|
github.com/spf13/viper v1.12.0
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.0
|
||||||
|
@ -67,10 +67,10 @@ require (
|
||||||
gocloud.dev v0.25.0
|
gocloud.dev v0.25.0
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
|
||||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e
|
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e
|
||||||
golang.org/x/oauth2 v0.0.0-20220630143837-2104d58473e0
|
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
|
||||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858
|
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
|
||||||
google.golang.org/api v0.87.0
|
google.golang.org/api v0.88.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -109,10 +109,9 @@ require (
|
||||||
github.com/google/go-cmp v0.5.8 // indirect
|
github.com/google/go-cmp v0.5.8 // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.4.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.4.0 // indirect
|
||||||
github.com/googleapis/go-type-adapters v1.0.0 // indirect
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
|
github.com/hashicorp/yamux v0.1.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
|
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
|
||||||
|
@ -155,7 +154,7 @@ require (
|
||||||
golang.org/x/tools v0.1.11 // indirect
|
golang.org/x/tools v0.1.11 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
|
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20220715211116-798f69b842b9 // indirect
|
google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252 // indirect
|
||||||
google.golang.org/grpc v1.48.0 // indirect
|
google.golang.org/grpc v1.48.0 // indirect
|
||||||
google.golang.org/protobuf v1.28.0 // indirect
|
google.golang.org/protobuf v1.28.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.66.6 // indirect
|
gopkg.in/ini.v1 v1.66.6 // indirect
|
||||||
|
|
40
go.sum
40
go.sum
|
@ -75,8 +75,9 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
||||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||||
cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA=
|
cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA=
|
||||||
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
|
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
|
||||||
cloud.google.com/go/storage v1.23.0 h1:wWRIaDURQA8xxHguFCshYepGlrWIrbBnAmc7wfg07qY=
|
|
||||||
cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
|
cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
|
||||||
|
cloud.google.com/go/storage v1.24.0 h1:a4N0gIkx83uoVFGz8B2eAV3OhN90QoWF5OZWLKl39ig=
|
||||||
|
cloud.google.com/go/storage v1.24.0/go.mod h1:3xrJEFMXBsQLgxwThyjuD3aYlroL0TMRec1ypGUQ0KE=
|
||||||
cloud.google.com/go/trace v1.0.0/go.mod h1:4iErSByzxkyHWzzlAj63/Gmjz0NH1ASqhJguHpGcr6A=
|
cloud.google.com/go/trace v1.0.0/go.mod h1:4iErSByzxkyHWzzlAj63/Gmjz0NH1ASqhJguHpGcr6A=
|
||||||
cloud.google.com/go/trace v1.2.0/go.mod h1:Wc8y/uYyOhPy12KEnXG9XGrvfMz5F5SrYecQlbW1rwM=
|
cloud.google.com/go/trace v1.2.0/go.mod h1:Wc8y/uYyOhPy12KEnXG9XGrvfMz5F5SrYecQlbW1rwM=
|
||||||
contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
|
contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
|
||||||
|
@ -233,8 +234,8 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
|
||||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
github.com/cockroachdb/cockroach-go/v2 v2.2.14 h1:wUJwq9OgsvICHwFgVc5n9ooF+AAyDhKgi+be5uEEYm8=
|
github.com/cockroachdb/cockroach-go/v2 v2.2.15 h1:6TeTC1JLSlHJWJCswWZ7mQyT16kY5mQSs53C2coQISI=
|
||||||
github.com/cockroachdb/cockroach-go/v2 v2.2.14/go.mod h1:xZ2VHjUEb/cySv0scXBx7YsBnHtLHkR1+w/w73b5i3M=
|
github.com/cockroachdb/cockroach-go/v2 v2.2.15/go.mod h1:xZ2VHjUEb/cySv0scXBx7YsBnHtLHkR1+w/w73b5i3M=
|
||||||
github.com/coreos/go-oidc/v3 v3.2.0 h1:2eR2MGR7thBXSQ2YbODlF0fcmgtliLCfr9iX6RW11fc=
|
github.com/coreos/go-oidc/v3 v3.2.0 h1:2eR2MGR7thBXSQ2YbODlF0fcmgtliLCfr9iX6RW11fc=
|
||||||
github.com/coreos/go-oidc/v3 v3.2.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
|
github.com/coreos/go-oidc/v3 v3.2.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
@ -444,7 +445,6 @@ github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/Oth
|
||||||
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
|
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
|
||||||
github.com/googleapis/gax-go/v2 v2.4.0 h1:dS9eYAjhrE2RjmzYw2XAPvcXfmcQLtFEQWn0CR82awk=
|
github.com/googleapis/gax-go/v2 v2.4.0 h1:dS9eYAjhrE2RjmzYw2XAPvcXfmcQLtFEQWn0CR82awk=
|
||||||
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
|
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
|
||||||
github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA=
|
|
||||||
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
|
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
|
||||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
@ -467,8 +467,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I=
|
github.com/hashicorp/yamux v0.1.0 h1:DzDIF6Sd7GD2sX0kDFpHAsJMY4L+OfTvtuaQsOYXxzk=
|
||||||
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
github.com/hashicorp/yamux v0.1.0/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
|
@ -540,8 +540,8 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/klauspost/compress v1.15.8 h1:JahtItbkWjf2jzm/T+qgMxkP9EMHsqEUA6vCMGmXvhA=
|
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
|
||||||
github.com/klauspost/compress v1.15.8/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
|
github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
|
||||||
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
|
@ -720,8 +720,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spf13/afero v1.9.0 h1:sFSLUHgxdnN32Qy38hK3QkYBFXZj9DKjVjCUCtD7juY=
|
github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
|
||||||
github.com/spf13/afero v1.9.0/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
||||||
|
@ -870,8 +870,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-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-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-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
|
||||||
golang.org/x/oauth2 v0.0.0-20220630143837-2104d58473e0 h1:VnGaRqoLmqZH/3TMLJwYCEWkR4j1nuIU1U9TvbqsDUw=
|
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c h1:q3gFqPqH7NVofKo3c3yETAP//pPI+G5mvB7qqj1Y5kY=
|
||||||
golang.org/x/oauth2 v0.0.0-20220630143837-2104d58473e0/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
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-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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -972,8 +972,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-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-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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
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-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 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
@ -992,8 +992,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-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-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-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-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
|
||||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
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-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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
@ -1116,8 +1116,8 @@ google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3
|
||||||
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
|
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
|
||||||
google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=
|
google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=
|
||||||
google.golang.org/api v0.86.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
|
google.golang.org/api v0.86.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
|
||||||
google.golang.org/api v0.87.0 h1:pUQVF/F+X7Tl1lo4LJoJf5BOpjtmINU80p9XpYTU2p4=
|
google.golang.org/api v0.88.0 h1:MPwxQRqpyskYhr2iNyfsQ8R06eeyhe7UEuR30p136ZQ=
|
||||||
google.golang.org/api v0.87.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
|
google.golang.org/api v0.88.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
@ -1224,8 +1224,8 @@ google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljW
|
||||||
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||||
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||||
google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||||
google.golang.org/genproto v0.0.0-20220715211116-798f69b842b9 h1:1aEQRgZ4Gks2SRAkLzIPpIszRazwVfjSFe1cKc+e0Jg=
|
google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252 h1:G5AjFxR+ibe9Taamo0TdW+iylfBYK10DSkHYdx7PZ9w=
|
||||||
google.golang.org/genproto v0.0.0-20220715211116-798f69b842b9/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=
|
google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
|
|
@ -19,6 +19,7 @@ package httpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -410,6 +411,17 @@ type Binding struct {
|
||||||
// Enable the built-in client interface.
|
// Enable the built-in client interface.
|
||||||
// You have to define TemplatesPath and StaticFilesPath for this to work
|
// You have to define TemplatesPath and StaticFilesPath for this to work
|
||||||
EnableWebClient bool `json:"enable_web_client" mapstructure:"enable_web_client"`
|
EnableWebClient bool `json:"enable_web_client" mapstructure:"enable_web_client"`
|
||||||
|
// 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.
|
||||||
|
EnabledLoginMethods int `json:"enabled_login_methods" mapstructure:"enabled_login_methods"`
|
||||||
// you also need to provide a certificate for enabling HTTPS
|
// you also need to provide a certificate for enabling HTTPS
|
||||||
EnableHTTPS bool `json:"enable_https" mapstructure:"enable_https"`
|
EnableHTTPS bool `json:"enable_https" mapstructure:"enable_https"`
|
||||||
// Certificate and matching private key for this specific binding, if empty the global
|
// Certificate and matching private key for this specific binding, if empty the global
|
||||||
|
@ -520,6 +532,66 @@ func (b *Binding) IsValid() bool {
|
||||||
return false
|
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&4 == 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binding) isWebClientLoginFormDisabled() bool {
|
||||||
|
if b.EnableWebClient {
|
||||||
|
if b.EnabledLoginMethods == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Binding) showAdminLoginURL() bool {
|
func (b *Binding) showAdminLoginURL() bool {
|
||||||
if !b.EnableWebAdmin {
|
if !b.EnableWebAdmin {
|
||||||
return false
|
return false
|
||||||
|
@ -783,6 +855,10 @@ func (c *Conf) Initialize(configDir string, isShared int) error {
|
||||||
exitChannel <- err
|
exitChannel <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err := b.checkLoginMethods(); err != nil {
|
||||||
|
exitChannel <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
server := newHttpdServer(b, staticFilesPath, c.SigningPassphrase, c.Cors, openAPIPath)
|
server := newHttpdServer(b, staticFilesPath, c.SigningPassphrase, c.Cors, openAPIPath)
|
||||||
|
|
||||||
exitChannel <- server.listenAndServe()
|
exitChannel <- server.listenAndServe()
|
||||||
|
|
|
@ -532,6 +532,39 @@ func TestInitialization(t *testing.T) {
|
||||||
if assert.Error(t, err) {
|
if assert.Error(t, err) {
|
||||||
assert.Contains(t, err.Error(), "oidc")
|
assert.Contains(t, err.Error(), "oidc")
|
||||||
}
|
}
|
||||||
|
httpdConf.Bindings[0].OIDC = httpd.OIDC{}
|
||||||
|
httpdConf.Bindings[0].EnableWebClient = true
|
||||||
|
httpdConf.Bindings[0].EnableWebAdmin = true
|
||||||
|
httpdConf.Bindings[0].EnabledLoginMethods = 1
|
||||||
|
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 = 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")
|
||||||
|
}
|
||||||
|
httpdConf.Bindings[0].EnabledLoginMethods = 3
|
||||||
|
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].EnableWebAdmin = false
|
||||||
|
err = httpdConf.Initialize(configDir, isShared)
|
||||||
|
if assert.Error(t, err) {
|
||||||
|
assert.Contains(t, err.Error(), "no login method available for WebClient UI")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicUserHandling(t *testing.T) {
|
func TestBasicUserHandling(t *testing.T) {
|
||||||
|
|
|
@ -90,6 +90,9 @@ type OIDC struct {
|
||||||
Scopes []string `json:"scopes" mapstructure:"scopes"`
|
Scopes []string `json:"scopes" mapstructure:"scopes"`
|
||||||
// Custom token claims fields to pass to the pre-login hook
|
// Custom token claims fields to pass to the pre-login hook
|
||||||
CustomFields []string `json:"custom_fields" mapstructure:"custom_fields"`
|
CustomFields []string `json:"custom_fields" mapstructure:"custom_fields"`
|
||||||
|
// Debug enables the OIDC debug mode. In debug mode, the received id_token will be logged
|
||||||
|
// at the debug level
|
||||||
|
Debug bool `json:"debug" mapstructure:"debug"`
|
||||||
provider *oidc.Provider
|
provider *oidc.Provider
|
||||||
verifier OIDCTokenVerifier
|
verifier OIDCTokenVerifier
|
||||||
providerLogoutURL string
|
providerLogoutURL string
|
||||||
|
@ -477,6 +480,16 @@ func (s *httpdServer) oidcLoginRedirect(w http.ResponseWriter, r *http.Request,
|
||||||
oidc.Nonce(pendingAuth.Nonce)), http.StatusFound)
|
oidc.Nonce(pendingAuth.Nonce)), http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *httpdServer) debugTokenClaims(claims map[string]any, rawIDToken string) {
|
||||||
|
if s.binding.OIDC.Debug {
|
||||||
|
if claims == nil {
|
||||||
|
logger.Debug(logSender, "", "raw id token %q", rawIDToken)
|
||||||
|
} else {
|
||||||
|
logger.Debug(logSender, "", "raw id token %q, parsed claims %+v", rawIDToken, claims)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *httpdServer) handleOIDCRedirect(w http.ResponseWriter, r *http.Request) {
|
func (s *httpdServer) handleOIDCRedirect(w http.ResponseWriter, r *http.Request) {
|
||||||
state := r.URL.Query().Get("state")
|
state := r.URL.Query().Get("state")
|
||||||
authReq, err := oidcMgr.getPendingAuth(state)
|
authReq, err := oidcMgr.getPendingAuth(state)
|
||||||
|
@ -516,6 +529,7 @@ func (s *httpdServer) handleOIDCRedirect(w http.ResponseWriter, r *http.Request)
|
||||||
doRedirect()
|
doRedirect()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
s.debugTokenClaims(nil, rawIDToken)
|
||||||
idToken, err := s.binding.OIDC.verifier.Verify(ctx, rawIDToken)
|
idToken, err := s.binding.OIDC.verifier.Verify(ctx, rawIDToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug(logSender, "", "failed to verify oidc token: %v", err)
|
logger.Debug(logSender, "", "failed to verify oidc token: %v", err)
|
||||||
|
@ -541,6 +555,7 @@ func (s *httpdServer) handleOIDCRedirect(w http.ResponseWriter, r *http.Request)
|
||||||
doLogout(rawIDToken)
|
doLogout(rawIDToken)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
s.debugTokenClaims(claims, rawIDToken)
|
||||||
token := oidcToken{
|
token := oidcToken{
|
||||||
AccessToken: oauth2Token.AccessToken,
|
AccessToken: oauth2Token.AccessToken,
|
||||||
TokenType: oauth2Token.TokenType,
|
TokenType: oauth2Token.TokenType,
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
package httpd
|
package httpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -30,6 +32,7 @@ import (
|
||||||
|
|
||||||
"github.com/coreos/go-oidc/v3/oidc"
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
"github.com/go-chi/jwtauth/v5"
|
"github.com/go-chi/jwtauth/v5"
|
||||||
|
"github.com/lestrrat-go/jwx/jwa"
|
||||||
"github.com/rs/xid"
|
"github.com/rs/xid"
|
||||||
"github.com/sftpgo/sdk"
|
"github.com/sftpgo/sdk"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -1160,6 +1163,97 @@ func TestOIDCIsAdmin(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOIDCWithLoginFormsDisabled(t *testing.T) {
|
||||||
|
oidcMgr, ok := oidcMgr.(*memoryOIDCManager)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
server := getTestOIDCServer()
|
||||||
|
server.binding.OIDC.ImplicitRoles = true
|
||||||
|
server.binding.EnabledLoginMethods = 3
|
||||||
|
server.binding.EnableWebAdmin = true
|
||||||
|
server.binding.EnableWebClient = true
|
||||||
|
err := server.binding.OIDC.initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
server.initializeRouter()
|
||||||
|
// login with an admin user
|
||||||
|
authReq := newOIDCPendingAuth(tokenAudienceWebAdmin)
|
||||||
|
oidcMgr.addPendingAuth(authReq)
|
||||||
|
token := &oauth2.Token{
|
||||||
|
AccessToken: "1234",
|
||||||
|
Expiry: time.Now().Add(5 * time.Minute),
|
||||||
|
}
|
||||||
|
token = token.WithExtra(map[string]any{
|
||||||
|
"id_token": "id_token_val",
|
||||||
|
})
|
||||||
|
server.binding.OIDC.oauth2Config = &mockOAuth2Config{
|
||||||
|
tokenSource: &mockTokenSource{},
|
||||||
|
authCodeURL: webOIDCRedirectPath,
|
||||||
|
token: token,
|
||||||
|
}
|
||||||
|
idToken := &oidc.IDToken{
|
||||||
|
Nonce: authReq.Nonce,
|
||||||
|
Expiry: time.Now().Add(5 * time.Minute),
|
||||||
|
}
|
||||||
|
setIDTokenClaims(idToken, []byte(`{"preferred_username":"admin","sid":"sid456"}`))
|
||||||
|
server.binding.OIDC.verifier = &mockOIDCVerifier{
|
||||||
|
err: nil,
|
||||||
|
token: idToken,
|
||||||
|
}
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
r, err := http.NewRequest(http.MethodGet, webOIDCRedirectPath+"?state="+authReq.State, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
server.router.ServeHTTP(rr, r)
|
||||||
|
assert.Equal(t, http.StatusFound, rr.Code)
|
||||||
|
assert.Equal(t, webUsersPath, rr.Header().Get("Location"))
|
||||||
|
var tokenCookie string
|
||||||
|
for k := range oidcMgr.tokens {
|
||||||
|
tokenCookie = k
|
||||||
|
}
|
||||||
|
// we should be able to create admins without setting a password
|
||||||
|
if csrfTokenAuth == nil {
|
||||||
|
csrfTokenAuth = jwtauth.New(jwa.HS256.String(), util.GenerateRandomBytes(32), nil)
|
||||||
|
}
|
||||||
|
adminUsername := "testAdmin"
|
||||||
|
form := make(url.Values)
|
||||||
|
form.Set(csrfFormToken, createCSRFToken(""))
|
||||||
|
form.Set("username", adminUsername)
|
||||||
|
form.Set("password", "")
|
||||||
|
form.Set("status", "1")
|
||||||
|
form.Set("permissions", "*")
|
||||||
|
rr = httptest.NewRecorder()
|
||||||
|
r, err = http.NewRequest(http.MethodPost, webAdminPath, bytes.NewBuffer([]byte(form.Encode())))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
r.Header.Set("Cookie", fmt.Sprintf("%v=%v", oidcCookieKey, tokenCookie))
|
||||||
|
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
server.router.ServeHTTP(rr, r)
|
||||||
|
assert.Equal(t, http.StatusSeeOther, rr.Code)
|
||||||
|
_, err = dataprovider.AdminExists(adminUsername)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = dataprovider.DeleteAdmin(adminUsername, "", "")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// login and password related routes are disabled
|
||||||
|
rr = httptest.NewRecorder()
|
||||||
|
r, err = http.NewRequest(http.MethodPost, webAdminLoginPath, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
server.router.ServeHTTP(rr, r)
|
||||||
|
assert.Equal(t, http.StatusMethodNotAllowed, rr.Code)
|
||||||
|
rr = httptest.NewRecorder()
|
||||||
|
r, err = http.NewRequest(http.MethodPost, webAdminTwoFactorPath, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
server.router.ServeHTTP(rr, r)
|
||||||
|
assert.Equal(t, http.StatusNotFound, rr.Code)
|
||||||
|
rr = httptest.NewRecorder()
|
||||||
|
r, err = http.NewRequest(http.MethodPost, webClientLoginPath, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
server.router.ServeHTTP(rr, r)
|
||||||
|
assert.Equal(t, http.StatusMethodNotAllowed, rr.Code)
|
||||||
|
rr = httptest.NewRecorder()
|
||||||
|
r, err = http.NewRequest(http.MethodPost, webClientForgotPwdPath, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
server.router.ServeHTTP(rr, r)
|
||||||
|
assert.Equal(t, http.StatusNotFound, rr.Code)
|
||||||
|
}
|
||||||
|
|
||||||
func TestDbOIDCManager(t *testing.T) {
|
func TestDbOIDCManager(t *testing.T) {
|
||||||
if !isSharedProviderSupported() {
|
if !isSharedProviderSupported() {
|
||||||
t.Skip("this test it is not available with this provider")
|
t.Skip("this test it is not available with this provider")
|
||||||
|
@ -1284,6 +1378,7 @@ func getTestOIDCServer() *httpdServer {
|
||||||
ImplicitRoles: false,
|
ImplicitRoles: false,
|
||||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||||
CustomFields: nil,
|
CustomFields: nil,
|
||||||
|
Debug: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
enableWebAdmin: true,
|
enableWebAdmin: true,
|
||||||
|
|
|
@ -160,15 +160,16 @@ func (s *httpdServer) renderClientLoginPage(w http.ResponseWriter, error, ip str
|
||||||
CSRFToken: createCSRFToken(ip),
|
CSRFToken: createCSRFToken(ip),
|
||||||
StaticURL: webStaticFilesPath,
|
StaticURL: webStaticFilesPath,
|
||||||
Branding: s.binding.Branding.WebClient,
|
Branding: s.binding.Branding.WebClient,
|
||||||
|
FormDisabled: s.binding.isWebClientLoginFormDisabled(),
|
||||||
}
|
}
|
||||||
if s.binding.showAdminLoginURL() {
|
if s.binding.showAdminLoginURL() {
|
||||||
data.AltLoginURL = webAdminLoginPath
|
data.AltLoginURL = webAdminLoginPath
|
||||||
data.AltLoginName = s.binding.Branding.WebAdmin.ShortName
|
data.AltLoginName = s.binding.Branding.WebAdmin.ShortName
|
||||||
}
|
}
|
||||||
if smtp.IsEnabled() {
|
if smtp.IsEnabled() && !data.FormDisabled {
|
||||||
data.ForgotPwdURL = webClientForgotPwdPath
|
data.ForgotPwdURL = webClientForgotPwdPath
|
||||||
}
|
}
|
||||||
if s.binding.OIDC.isEnabled() {
|
if s.binding.OIDC.isEnabled() && !s.binding.isWebClientOIDCLoginDisabled() {
|
||||||
data.OpenIDLoginURL = webClientOIDCLoginPath
|
data.OpenIDLoginURL = webClientOIDCLoginPath
|
||||||
}
|
}
|
||||||
renderClientTemplate(w, templateClientLogin, data)
|
renderClientTemplate(w, templateClientLogin, data)
|
||||||
|
@ -537,15 +538,16 @@ func (s *httpdServer) renderAdminLoginPage(w http.ResponseWriter, error, ip stri
|
||||||
CSRFToken: createCSRFToken(ip),
|
CSRFToken: createCSRFToken(ip),
|
||||||
StaticURL: webStaticFilesPath,
|
StaticURL: webStaticFilesPath,
|
||||||
Branding: s.binding.Branding.WebAdmin,
|
Branding: s.binding.Branding.WebAdmin,
|
||||||
|
FormDisabled: s.binding.isWebAdminLoginFormDisabled(),
|
||||||
}
|
}
|
||||||
if s.binding.showClientLoginURL() {
|
if s.binding.showClientLoginURL() {
|
||||||
data.AltLoginURL = webClientLoginPath
|
data.AltLoginURL = webClientLoginPath
|
||||||
data.AltLoginName = s.binding.Branding.WebClient.ShortName
|
data.AltLoginName = s.binding.Branding.WebClient.ShortName
|
||||||
}
|
}
|
||||||
if smtp.IsEnabled() {
|
if smtp.IsEnabled() && !data.FormDisabled {
|
||||||
data.ForgotPwdURL = webAdminForgotPwdPath
|
data.ForgotPwdURL = webAdminForgotPwdPath
|
||||||
}
|
}
|
||||||
if s.binding.OIDC.hasRoles() {
|
if s.binding.OIDC.hasRoles() && !s.binding.isWebAdminOIDCLoginDisabled() {
|
||||||
data.OpenIDLoginURL = webAdminOIDCLoginPath
|
data.OpenIDLoginURL = webAdminOIDCLoginPath
|
||||||
}
|
}
|
||||||
renderAdminTemplate(w, templateLogin, data)
|
renderAdminTemplate(w, templateLogin, data)
|
||||||
|
@ -1398,9 +1400,10 @@ func (s *httpdServer) setupWebClientRoutes() {
|
||||||
http.Redirect(w, r, webClientLoginPath, http.StatusFound)
|
http.Redirect(w, r, webClientLoginPath, http.StatusFound)
|
||||||
})
|
})
|
||||||
s.router.Get(webClientLoginPath, s.handleClientWebLogin)
|
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)
|
s.router.Get(webClientOIDCLoginPath, s.handleWebClientOIDCLogin)
|
||||||
}
|
}
|
||||||
|
if !s.binding.isWebClientLoginFormDisabled() {
|
||||||
s.router.Post(webClientLoginPath, s.handleWebClientLoginPost)
|
s.router.Post(webClientLoginPath, s.handleWebClientLoginPost)
|
||||||
s.router.Get(webClientForgotPwdPath, s.handleWebClientForgotPwd)
|
s.router.Get(webClientForgotPwdPath, s.handleWebClientForgotPwd)
|
||||||
s.router.Post(webClientForgotPwdPath, s.handleWebClientForgotPwdPost)
|
s.router.Post(webClientForgotPwdPath, s.handleWebClientForgotPwdPost)
|
||||||
|
@ -1418,6 +1421,7 @@ func (s *httpdServer) setupWebClientRoutes() {
|
||||||
s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
|
s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
|
||||||
s.jwtAuthenticatorPartial(tokenAudienceWebClientPartial)).
|
s.jwtAuthenticatorPartial(tokenAudienceWebClientPartial)).
|
||||||
Post(webClientTwoFactorRecoveryPath, s.handleWebClientTwoFactorRecoveryPost)
|
Post(webClientTwoFactorRecoveryPath, s.handleWebClientTwoFactorRecoveryPost)
|
||||||
|
}
|
||||||
// share API exposed to external users
|
// share API exposed to external users
|
||||||
s.router.Get(webClientPubSharesPath+"/{id}", s.downloadFromShare)
|
s.router.Get(webClientPubSharesPath+"/{id}", s.downloadFromShare)
|
||||||
s.router.Get(webClientPubSharesPath+"/{id}/browse", s.handleShareGetFiles)
|
s.router.Get(webClientPubSharesPath+"/{id}/browse", s.handleShareGetFiles)
|
||||||
|
@ -1497,16 +1501,13 @@ func (s *httpdServer) setupWebAdminRoutes() {
|
||||||
s.redirectToWebPath(w, r, webAdminLoginPath)
|
s.redirectToWebPath(w, r, webAdminLoginPath)
|
||||||
})
|
})
|
||||||
s.router.Get(webAdminLoginPath, s.handleWebAdminLogin)
|
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(webAdminOIDCLoginPath, s.handleWebAdminOIDCLogin)
|
||||||
}
|
}
|
||||||
s.router.Post(webAdminLoginPath, s.handleWebAdminLoginPost)
|
|
||||||
s.router.Get(webAdminSetupPath, s.handleWebAdminSetupGet)
|
s.router.Get(webAdminSetupPath, s.handleWebAdminSetupGet)
|
||||||
s.router.Post(webAdminSetupPath, s.handleWebAdminSetupPost)
|
s.router.Post(webAdminSetupPath, s.handleWebAdminSetupPost)
|
||||||
s.router.Get(webAdminForgotPwdPath, s.handleWebAdminForgotPwd)
|
if !s.binding.isWebAdminLoginFormDisabled() {
|
||||||
s.router.Post(webAdminForgotPwdPath, s.handleWebAdminForgotPwdPost)
|
s.router.Post(webAdminLoginPath, s.handleWebAdminLoginPost)
|
||||||
s.router.Get(webAdminResetPwdPath, s.handleWebAdminPasswordReset)
|
|
||||||
s.router.Post(webAdminResetPwdPath, s.handleWebAdminPasswordResetPost)
|
|
||||||
s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
|
s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
|
||||||
s.jwtAuthenticatorPartial(tokenAudienceWebAdminPartial)).
|
s.jwtAuthenticatorPartial(tokenAudienceWebAdminPartial)).
|
||||||
Get(webAdminTwoFactorPath, s.handleWebAdminTwoFactor)
|
Get(webAdminTwoFactorPath, s.handleWebAdminTwoFactor)
|
||||||
|
@ -1519,6 +1520,11 @@ func (s *httpdServer) setupWebAdminRoutes() {
|
||||||
s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
|
s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
|
||||||
s.jwtAuthenticatorPartial(tokenAudienceWebAdminPartial)).
|
s.jwtAuthenticatorPartial(tokenAudienceWebAdminPartial)).
|
||||||
Post(webAdminTwoFactorRecoveryPath, s.handleWebAdminTwoFactorRecoveryPost)
|
Post(webAdminTwoFactorRecoveryPath, s.handleWebAdminTwoFactorRecoveryPost)
|
||||||
|
s.router.Get(webAdminForgotPwdPath, s.handleWebAdminForgotPwd)
|
||||||
|
s.router.Post(webAdminForgotPwdPath, s.handleWebAdminForgotPwdPost)
|
||||||
|
s.router.Get(webAdminResetPwdPath, s.handleWebAdminPasswordReset)
|
||||||
|
s.router.Post(webAdminResetPwdPath, s.handleWebAdminPasswordResetPost)
|
||||||
|
}
|
||||||
|
|
||||||
s.router.Group(func(router chi.Router) {
|
s.router.Group(func(router chi.Router) {
|
||||||
if s.binding.OIDC.isEnabled() {
|
if s.binding.OIDC.isEnabled() {
|
||||||
|
|
|
@ -49,6 +49,7 @@ type loginPage struct {
|
||||||
ForgotPwdURL string
|
ForgotPwdURL string
|
||||||
OpenIDLoginURL string
|
OpenIDLoginURL string
|
||||||
Branding UIBranding
|
Branding UIBranding
|
||||||
|
FormDisabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type twoFactorPage struct {
|
type twoFactorPage struct {
|
||||||
|
|
|
@ -1319,7 +1319,7 @@ func getAdminFromPostFields(r *http.Request) (dataprovider.Admin, error) {
|
||||||
}
|
}
|
||||||
status, err := strconv.Atoi(r.Form.Get("status"))
|
status, err := strconv.Atoi(r.Form.Get("status"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return admin, err
|
return admin, fmt.Errorf("invalid status: %w", err)
|
||||||
}
|
}
|
||||||
admin.Username = r.Form.Get("username")
|
admin.Username = r.Form.Get("username")
|
||||||
admin.Password = r.Form.Get("password")
|
admin.Password = r.Form.Get("password")
|
||||||
|
@ -1857,6 +1857,9 @@ func (s *httpdServer) handleWebAddAdminPost(w http.ResponseWriter, r *http.Reque
|
||||||
s.renderAddUpdateAdminPage(w, r, &admin, err.Error(), true)
|
s.renderAddUpdateAdminPage(w, r, &admin, err.Error(), true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if admin.Password == "" && s.binding.isWebAdminLoginFormDisabled() {
|
||||||
|
admin.Password = util.GenerateUniqueID()
|
||||||
|
}
|
||||||
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
|
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
|
||||||
if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
|
if err := verifyCSRFToken(r.Form.Get(csrfFormToken), ipAddr); err != nil {
|
||||||
s.renderForbiddenPage(w, r, err.Error())
|
s.renderForbiddenPage(w, r, err.Error())
|
||||||
|
|
|
@ -227,6 +227,7 @@ func CommandLog(command, path, target, user, fileMode, connectionID, protocol st
|
||||||
logger.Info().
|
logger.Info().
|
||||||
Timestamp().
|
Timestamp().
|
||||||
Str("sender", command).
|
Str("sender", command).
|
||||||
|
Str("local_addr", localAddr).
|
||||||
Str("remote_addr", remoteAddr).
|
Str("remote_addr", remoteAddr).
|
||||||
Str("username", user).
|
Str("username", user).
|
||||||
Str("file_path", path).
|
Str("file_path", path).
|
||||||
|
@ -235,7 +236,7 @@ func CommandLog(command, path, target, user, fileMode, connectionID, protocol st
|
||||||
Int("uid", uid).
|
Int("uid", uid).
|
||||||
Int("gid", gid).
|
Int("gid", gid).
|
||||||
Str("access_time", atime).
|
Str("access_time", atime).
|
||||||
Str("modification_time", atime).
|
Str("modification_time", mtime).
|
||||||
Int64("size", size).
|
Int64("size", size).
|
||||||
Str("ssh_command", sshCommand).
|
Str("ssh_command", sshCommand).
|
||||||
Str("connection_id", connectionID).
|
Str("connection_id", connectionID).
|
||||||
|
|
|
@ -246,6 +246,7 @@
|
||||||
"address": "",
|
"address": "",
|
||||||
"enable_web_admin": true,
|
"enable_web_admin": true,
|
||||||
"enable_web_client": true,
|
"enable_web_client": true,
|
||||||
|
"enabled_login_methods": 0,
|
||||||
"enable_https": false,
|
"enable_https": false,
|
||||||
"certificate_file": "",
|
"certificate_file": "",
|
||||||
"certificate_key_file": "",
|
"certificate_key_file": "",
|
||||||
|
@ -271,7 +272,8 @@
|
||||||
"username_field": "",
|
"username_field": "",
|
||||||
"role_field": "",
|
"role_field": "",
|
||||||
"implicit_roles": false,
|
"implicit_roles": false,
|
||||||
"custom_fields": []
|
"custom_fields": [],
|
||||||
|
"debug": false
|
||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
|
|
|
@ -28,6 +28,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
{{end}}
|
{{end}}
|
||||||
<form id="login_form" action="{{.CurrentURL}}" method="POST" autocomplete="off"
|
<form id="login_form" action="{{.CurrentURL}}" method="POST" autocomplete="off"
|
||||||
class="user-custom">
|
class="user-custom">
|
||||||
|
{{if not .FormDisabled}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control form-control-user-custom"
|
<input type="text" class="form-control form-control-user-custom"
|
||||||
id="inputUsername" name="username" placeholder="Username" required>
|
id="inputUsername" name="username" placeholder="Username" required>
|
||||||
|
@ -45,6 +46,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
<button type="submit" class="btn btn-primary btn-user-custom btn-block">
|
<button type="submit" class="btn btn-primary btn-user-custom btn-block">
|
||||||
Login
|
Login
|
||||||
</button>
|
</button>
|
||||||
|
{{end}}
|
||||||
{{if .OpenIDLoginURL}}
|
{{if .OpenIDLoginURL}}
|
||||||
<hr>
|
<hr>
|
||||||
<a href="{{.OpenIDLoginURL}}" class="btn btn-secondary btn-user-custom btn-block">
|
<a href="{{.OpenIDLoginURL}}" class="btn btn-secondary btn-user-custom btn-block">
|
||||||
|
|
|
@ -25,6 +25,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
{{end}}
|
{{end}}
|
||||||
<form id="login_form" action="{{.CurrentURL}}" method="POST" autocomplete="off"
|
<form id="login_form" action="{{.CurrentURL}}" method="POST" autocomplete="off"
|
||||||
class="user-custom">
|
class="user-custom">
|
||||||
|
{{if not .FormDisabled}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control form-control-user-custom"
|
<input type="text" class="form-control form-control-user-custom"
|
||||||
id="inputUsername" name="username" placeholder="Username" required>
|
id="inputUsername" name="username" placeholder="Username" required>
|
||||||
|
@ -42,6 +43,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
<button type="submit" class="btn btn-primary btn-user-custom btn-block">
|
<button type="submit" class="btn btn-primary btn-user-custom btn-block">
|
||||||
Login
|
Login
|
||||||
</button>
|
</button>
|
||||||
|
{{end}}
|
||||||
{{if .OpenIDLoginURL}}
|
{{if .OpenIDLoginURL}}
|
||||||
<hr>
|
<hr>
|
||||||
<a href="{{.OpenIDLoginURL}}" class="btn btn-secondary btn-user-custom btn-block">
|
<a href="{{.OpenIDLoginURL}}" class="btn btn-secondary btn-user-custom btn-block">
|
||||||
|
|
Loading…
Reference in a new issue