CORS: add support for more parameters

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2022-08-02 18:44:34 +02:00
parent 455bb550ee
commit fdc10aa6c7
No known key found for this signature in database
GPG key ID: 2F1FB59433D5A8CB
10 changed files with 89 additions and 52 deletions

View file

@ -194,6 +194,9 @@ The configuration file contains the following sections:
- `exposed_headers`, list of strings.
- `allow_credentials` boolean.
- `max_age`, integer.
- `options_passthrough`, boolean.
- `options_success_status`, integer.
- `allow_private_network`, boolean.
- `cache` struct containing cache configuration for the authenticated users.
- `enabled`, boolean, set to true to enable user caching. Default: true.
- `expiration_time`, integer. Expiration time, in minutes, for the cached users. 0 means unlimited. Default: 0.
@ -326,6 +329,9 @@ The configuration file contains the following sections:
- `exposed_headers`, list of strings.
- `allow_credentials` boolean.
- `max_age`, integer.
- `options_passthrough`, boolean.
- `options_success_status`, integer.
- `allow_private_network`, boolean.
- `setup` struct containing configurations for the initial setup screen
- `installation_code`, string. If set, this installation code will be required when creating the first admin account. Please note that even if set using an environment variable this field is read at SFTPGo startup and not at runtime. This is not a license key or similar, the purpose here is to prevent anyone who can access to the initial setup screen from creating an admin user. Default: blank.
- `installation_code_hint`, string. Description for the installation code input field. Default: `Installation code`.

4
go.mod
View file

@ -48,7 +48,7 @@ require (
github.com/pquerna/otp v1.3.0
github.com/prometheus/client_golang v1.12.2
github.com/robfig/cron/v3 v3.0.1
github.com/rs/cors v1.8.2
github.com/rs/cors v1.8.3-0.20220619195839-da52b0701de5
github.com/rs/xid v1.4.0
github.com/rs/zerolog v1.27.0
github.com/sftpgo/sdk v0.1.2-0.20220727164210-06723ba7ce9a
@ -155,7 +155,7 @@ require (
golang.org/x/tools v0.1.12 // indirect
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f // indirect
google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78 // indirect
google.golang.org/grpc v1.48.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.66.6 // indirect

8
go.sum
View file

@ -696,8 +696,8 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/cors v1.8.3-0.20220619195839-da52b0701de5 h1:7PcjxKTsfGXpTMiTNNa1VllbsYSZJN5nhvVEWQMdX8Y=
github.com/rs/cors v1.8.3-0.20220619195839-da52b0701de5/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
@ -1225,8 +1225,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-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-20220801145646-83ce21fca29f h1:XVHpVMvPs4MtH3h6cThzKs2snNexcfd35vQx2T3IuIY=
google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78 h1:QntLWYqZeuBtJkth3m/6DLznnI0AHJr+AgJXvVh/izw=
google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
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.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=

View file

@ -747,6 +747,11 @@ func (j *eventCronJob) Run() {
ticker := time.NewTicker(updateInterval)
done := make(chan bool)
defer func() {
done <- true
ticker.Stop()
}()
go func(taskName string) {
eventManagerLog(logger.LevelDebug, "update task %q timestamp worker started", taskName)
for {
@ -762,9 +767,6 @@ func (j *eventCronJob) Run() {
}(task.Name)
executeRuleAsyncActions(rule, EventParams{}, nil)
done <- true
ticker.Stop()
} else {
executeRuleAsyncActions(rule, EventParams{}, nil)
}

View file

@ -285,13 +285,16 @@ func Init() {
CACertificates: []string{},
CARevocationLists: []string{},
Cors: webdavd.CorsConfig{
Enabled: false,
AllowedOrigins: []string{},
AllowedMethods: []string{},
AllowedHeaders: []string{},
ExposedHeaders: []string{},
AllowCredentials: false,
MaxAge: 0,
Enabled: false,
AllowedOrigins: []string{},
AllowedMethods: []string{},
AllowedHeaders: []string{},
ExposedHeaders: []string{},
AllowCredentials: false,
MaxAge: 0,
OptionsPassthrough: false,
OptionsSuccessStatus: 0,
AllowPrivateNetwork: false,
},
Cache: webdavd.Cache{
Users: webdavd.UsersCacheConfig{
@ -373,13 +376,16 @@ func Init() {
TokenValidation: 0,
MaxUploadFileSize: 1048576000,
Cors: httpd.CorsConfig{
Enabled: false,
AllowedOrigins: []string{},
AllowedMethods: []string{},
AllowedHeaders: []string{},
ExposedHeaders: []string{},
AllowCredentials: false,
MaxAge: 0,
Enabled: false,
AllowedOrigins: []string{},
AllowedMethods: []string{},
AllowedHeaders: []string{},
ExposedHeaders: []string{},
AllowCredentials: false,
MaxAge: 0,
OptionsPassthrough: false,
OptionsSuccessStatus: 0,
AllowPrivateNetwork: false,
},
Setup: httpd.SetupConfig{
InstallationCode: "",
@ -1899,6 +1905,9 @@ func setViperDefaults() {
viper.SetDefault("webdavd.cors.allowed_headers", globalConf.WebDAVD.Cors.AllowedHeaders)
viper.SetDefault("webdavd.cors.exposed_headers", globalConf.WebDAVD.Cors.ExposedHeaders)
viper.SetDefault("webdavd.cors.allow_credentials", globalConf.WebDAVD.Cors.AllowCredentials)
viper.SetDefault("webdavd.cors.options_passthrough", globalConf.WebDAVD.Cors.OptionsPassthrough)
viper.SetDefault("webdavd.cors.options_success_status", globalConf.WebDAVD.Cors.OptionsSuccessStatus)
viper.SetDefault("webdavd.cors.allow_private_network", globalConf.WebDAVD.Cors.AllowPrivateNetwork)
viper.SetDefault("webdavd.cors.max_age", globalConf.WebDAVD.Cors.MaxAge)
viper.SetDefault("webdavd.cache.users.expiration_time", globalConf.WebDAVD.Cache.Users.ExpirationTime)
viper.SetDefault("webdavd.cache.users.max_size", globalConf.WebDAVD.Cache.Users.MaxSize)
@ -1961,6 +1970,9 @@ func setViperDefaults() {
viper.SetDefault("httpd.cors.exposed_headers", globalConf.HTTPDConfig.Cors.ExposedHeaders)
viper.SetDefault("httpd.cors.allow_credentials", globalConf.HTTPDConfig.Cors.AllowCredentials)
viper.SetDefault("httpd.cors.max_age", globalConf.HTTPDConfig.Cors.MaxAge)
viper.SetDefault("httpd.cors.options_passthrough", globalConf.HTTPDConfig.Cors.OptionsPassthrough)
viper.SetDefault("httpd.cors.options_success_status", globalConf.HTTPDConfig.Cors.OptionsSuccessStatus)
viper.SetDefault("httpd.cors.allow_private_network", globalConf.HTTPDConfig.Cors.AllowPrivateNetwork)
viper.SetDefault("httpd.setup.installation_code", globalConf.HTTPDConfig.Setup.InstallationCode)
viper.SetDefault("httpd.setup.installation_code_hint", globalConf.HTTPDConfig.Setup.InstallationCodeHint)
viper.SetDefault("httpd.hide_support_link", globalConf.HTTPDConfig.HideSupportLink)

View file

@ -639,13 +639,16 @@ type SetupConfig struct {
// CorsConfig defines the CORS configuration
type CorsConfig struct {
AllowedOrigins []string `json:"allowed_origins" mapstructure:"allowed_origins"`
AllowedMethods []string `json:"allowed_methods" mapstructure:"allowed_methods"`
AllowedHeaders []string `json:"allowed_headers" mapstructure:"allowed_headers"`
ExposedHeaders []string `json:"exposed_headers" mapstructure:"exposed_headers"`
AllowCredentials bool `json:"allow_credentials" mapstructure:"allow_credentials"`
Enabled bool `json:"enabled" mapstructure:"enabled"`
MaxAge int `json:"max_age" mapstructure:"max_age"`
AllowedOrigins []string `json:"allowed_origins" mapstructure:"allowed_origins"`
AllowedMethods []string `json:"allowed_methods" mapstructure:"allowed_methods"`
AllowedHeaders []string `json:"allowed_headers" mapstructure:"allowed_headers"`
ExposedHeaders []string `json:"exposed_headers" mapstructure:"exposed_headers"`
AllowCredentials bool `json:"allow_credentials" mapstructure:"allow_credentials"`
Enabled bool `json:"enabled" mapstructure:"enabled"`
MaxAge int `json:"max_age" mapstructure:"max_age"`
OptionsPassthrough bool `json:"options_passthrough" mapstructure:"options_passthrough"`
OptionsSuccessStatus int `json:"options_success_status" mapstructure:"options_success_status"`
AllowPrivateNetwork bool `json:"allow_private_network" mapstructure:"allow_private_network"`
}
// Conf httpd daemon configuration

View file

@ -1138,12 +1138,15 @@ func (s *httpdServer) initializeRouter() {
}
if s.cors.Enabled {
c := cors.New(cors.Options{
AllowedOrigins: util.RemoveDuplicates(s.cors.AllowedOrigins, true),
AllowedMethods: util.RemoveDuplicates(s.cors.AllowedMethods, true),
AllowedHeaders: util.RemoveDuplicates(s.cors.AllowedHeaders, true),
ExposedHeaders: util.RemoveDuplicates(s.cors.ExposedHeaders, true),
MaxAge: s.cors.MaxAge,
AllowCredentials: s.cors.AllowCredentials,
AllowedOrigins: util.RemoveDuplicates(s.cors.AllowedOrigins, true),
AllowedMethods: util.RemoveDuplicates(s.cors.AllowedMethods, true),
AllowedHeaders: util.RemoveDuplicates(s.cors.AllowedHeaders, true),
ExposedHeaders: util.RemoveDuplicates(s.cors.ExposedHeaders, true),
MaxAge: s.cors.MaxAge,
AllowCredentials: s.cors.AllowCredentials,
OptionsPassthrough: s.cors.OptionsPassthrough,
OptionsSuccessStatus: s.cors.OptionsSuccessStatus,
AllowPrivateNetwork: s.cors.AllowPrivateNetwork,
})
s.router.Use(c.Handler)
}

View file

@ -57,13 +57,15 @@ func (s *webDavServer) listenAndServe(compressor *middleware.Compressor) error {
}
if s.config.Cors.Enabled {
c := cors.New(cors.Options{
AllowedOrigins: util.RemoveDuplicates(s.config.Cors.AllowedOrigins, true),
AllowedMethods: util.RemoveDuplicates(s.config.Cors.AllowedMethods, true),
AllowedHeaders: util.RemoveDuplicates(s.config.Cors.AllowedHeaders, true),
ExposedHeaders: util.RemoveDuplicates(s.config.Cors.ExposedHeaders, true),
MaxAge: s.config.Cors.MaxAge,
AllowCredentials: s.config.Cors.AllowCredentials,
OptionsPassthrough: true,
AllowedOrigins: util.RemoveDuplicates(s.config.Cors.AllowedOrigins, true),
AllowedMethods: util.RemoveDuplicates(s.config.Cors.AllowedMethods, true),
AllowedHeaders: util.RemoveDuplicates(s.config.Cors.AllowedHeaders, true),
ExposedHeaders: util.RemoveDuplicates(s.config.Cors.ExposedHeaders, true),
MaxAge: s.config.Cors.MaxAge,
AllowCredentials: s.config.Cors.AllowCredentials,
OptionsPassthrough: s.config.Cors.OptionsPassthrough,
OptionsSuccessStatus: s.config.Cors.OptionsSuccessStatus,
AllowPrivateNetwork: s.config.Cors.AllowPrivateNetwork,
})
handler = c.Handler(handler)
}

View file

@ -52,13 +52,16 @@ type ServiceStatus struct {
// CorsConfig defines the CORS configuration
type CorsConfig struct {
AllowedOrigins []string `json:"allowed_origins" mapstructure:"allowed_origins"`
AllowedMethods []string `json:"allowed_methods" mapstructure:"allowed_methods"`
AllowedHeaders []string `json:"allowed_headers" mapstructure:"allowed_headers"`
ExposedHeaders []string `json:"exposed_headers" mapstructure:"exposed_headers"`
AllowCredentials bool `json:"allow_credentials" mapstructure:"allow_credentials"`
Enabled bool `json:"enabled" mapstructure:"enabled"`
MaxAge int `json:"max_age" mapstructure:"max_age"`
AllowedOrigins []string `json:"allowed_origins" mapstructure:"allowed_origins"`
AllowedMethods []string `json:"allowed_methods" mapstructure:"allowed_methods"`
AllowedHeaders []string `json:"allowed_headers" mapstructure:"allowed_headers"`
ExposedHeaders []string `json:"exposed_headers" mapstructure:"exposed_headers"`
AllowCredentials bool `json:"allow_credentials" mapstructure:"allow_credentials"`
Enabled bool `json:"enabled" mapstructure:"enabled"`
MaxAge int `json:"max_age" mapstructure:"max_age"`
OptionsPassthrough bool `json:"options_passthrough" mapstructure:"options_passthrough"`
OptionsSuccessStatus int `json:"options_success_status" mapstructure:"options_success_status"`
AllowPrivateNetwork bool `json:"allow_private_network" mapstructure:"allow_private_network"`
}
// UsersCacheConfig defines the cache configuration for users

View file

@ -165,7 +165,10 @@
"allowed_headers": [],
"exposed_headers": [],
"allow_credentials": false,
"max_age": 0
"max_age": 0,
"options_passthrough": false,
"options_success_status": 0,
"allow_private_network": false
},
"cache": {
"users": {
@ -330,7 +333,10 @@
"allowed_headers": [],
"exposed_headers": [],
"allow_credentials": false,
"max_age": 0
"max_age": 0,
"options_passthrough": false,
"options_success_status": 0,
"allow_private_network": false
},
"setup": {
"installation_code": "",