diff --git a/services/frontend/pkg/config/config.go b/services/frontend/pkg/config/config.go index 0975ac68a..d4bb9c62c 100644 --- a/services/frontend/pkg/config/config.go +++ b/services/frontend/pkg/config/config.go @@ -179,10 +179,10 @@ type ServiceAccount struct { // PasswordPolicy configures reva password policy type PasswordPolicy struct { - MinCharacters int `yaml:"min_characters,omitempty" env:"FRONTEND_PASSWORD_POLICY_MIN_CHARACTERS" desc:"Define the minimum password length. Defaults to 0 if not set."` - MinLowerCaseCharacters int `yaml:"min_lowercase_characters" env:"FRONTEND_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS" desc:"Define the minimum number of uppercase letters. Defaults to 0 if not set."` - MinUpperCaseCharacters int `yaml:"min_uppercase_characters" env:"FRONTEND_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS" desc:"Define the minimum number of lowercase letters. Defaults to 0 if not set."` - MinDigits int `yaml:"min_digits" env:"FRONTEND_PASSWORD_POLICY_MIN_DIGITS" desc:"Define the minimum number of digits. Defaults to 0 if not set."` - MinSpecialCharacters int `yaml:"min_special_characters" env:"FRONTEND_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS" desc:"Define the minimum number of characters from the special characters list to be present. Defaults to 0 if not set."` - BannedPasswordsList string `yaml:"banned_passwords_list" env:"FRONTEND_PASSWORD_POLICY_BANNED_PASSWORDS_LIST" desc:"Path to the 'banned passwords list' file. See the documentation for more details."` + MinCharacters int `yaml:"min_characters,omitempty" env:"OCIS_PASSWORD_POLICY_MIN_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_CHARACTERS" desc:"Define the minimum password length. Defaults to 0 if not set."` + MinLowerCaseCharacters int `yaml:"min_lowercase_characters" env:"OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS" desc:"Define the minimum number of uppercase letters. Defaults to 0 if not set."` + MinUpperCaseCharacters int `yaml:"min_uppercase_characters" env:"OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS" desc:"Define the minimum number of lowercase letters. Defaults to 0 if not set."` + MinDigits int `yaml:"min_digits" env:"OCIS_PASSWORD_POLICY_MIN_DIGITS;FRONTEND_PASSWORD_POLICY_MIN_DIGITS" desc:"Define the minimum number of digits. Defaults to 0 if not set."` + MinSpecialCharacters int `yaml:"min_special_characters" env:"OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS;FRONTEND_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS" desc:"Define the minimum number of characters from the special characters list to be present. Defaults to 0 if not set."` + BannedPasswordsList string `yaml:"banned_passwords_list" env:"OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST;FRONTEND_PASSWORD_POLICY_BANNED_PASSWORDS_LIST" desc:"Path to the 'banned passwords list' file. See the documentation for more details."` } diff --git a/services/sharing/pkg/command/server.go b/services/sharing/pkg/command/server.go index 027c16983..8d18a3adc 100644 --- a/services/sharing/pkg/command/server.go +++ b/services/sharing/pkg/command/server.go @@ -57,7 +57,10 @@ func Server(cfg *config.Config) *cli.Command { gr.Add(func() error { pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid") - rCfg := revaconfig.SharingConfigFromStruct(cfg) + rCfg, err := revaconfig.SharingConfigFromStruct(cfg, logger) + if err != nil { + return err + } reg := registry.GetRegistry() runtime.RunWithOptions(rCfg, pidFile, diff --git a/services/sharing/pkg/config/config.go b/services/sharing/pkg/config/config.go index 8dc2ea8c5..f21d98474 100644 --- a/services/sharing/pkg/config/config.go +++ b/services/sharing/pkg/config/config.go @@ -26,9 +26,12 @@ type Config struct { PublicSharingDriver string `yaml:"public_sharing_driver" env:"SHARING_PUBLIC_DRIVER" desc:"Driver to be used to persist public shares. Supported values are 'jsoncs3', 'json' and 'cs3'."` PublicSharingDrivers PublicSharingDrivers `yaml:"public_sharing_drivers"` WriteableShareMustHavePassword bool `yaml:"public_sharing_writeableshare_must_have_password" env:"OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD;SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD" desc:"Set this to true if you want to enforce passwords on Uploader, Editor or Contributor shares. If not using the global OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD, you must define the FRONTEND_OCS_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD in the frontend service."` + PublicShareMustHavePassword bool `yaml:"public_sharing_share_must_have_password" env:"OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD;SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD" desc:"Set this to true if you want to enforce passwords on all public shares."` EnableExpiredSharesCleanup bool `yaml:"enable_expired_shares_cleanup"` Supervised bool `yaml:"-"` Context context.Context `yaml:"-"` + + PasswordPolicy PasswordPolicy `yaml:"password_policy"` } type Log struct { @@ -151,3 +154,13 @@ type Events struct { TLSRootCaCertPath string `yaml:"tls_root_ca_cert_path" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;SHARING_EVENTS_TLS_ROOT_CA_CERTIFICATE;SHARING_EVENTS_TLS_ROOT_CA_CERT" desc:"The root CA certificate used to validate the server's TLS certificate. If provided SHARING_EVENTS_TLS_INSECURE will be seen as false." deprecationVersion:"4.0.3" removalVersion:"5.0.0" deprecationInfo:"SHARING_EVENTS_TLS_ROOT_CA_CERT changing name for consistency" deprecationReplacement:"SHARING_EVENTS_TLS_ROOT_CA_CERTIFICATE"` EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;SHARING_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."` } + +// PasswordPolicy configures reva password policy +type PasswordPolicy struct { + MinCharacters int `yaml:"min_characters,omitempty" env:"OCIS_PASSWORD_POLICY_MIN_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_CHARACTERS" desc:"Define the minimum password length. Defaults to 0 if not set."` + MinLowerCaseCharacters int `yaml:"min_lowercase_characters" env:"OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS" desc:"Define the minimum number of uppercase letters. Defaults to 0 if not set."` + MinUpperCaseCharacters int `yaml:"min_uppercase_characters" env:"OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS" desc:"Define the minimum number of lowercase letters. Defaults to 0 if not set."` + MinDigits int `yaml:"min_digits" env:"OCIS_PASSWORD_POLICY_MIN_DIGITS;SHARING_PASSWORD_POLICY_MIN_DIGITS" desc:"Define the minimum number of digits. Defaults to 0 if not set."` + MinSpecialCharacters int `yaml:"min_special_characters" env:"OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS;SHARING_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS" desc:"Define the minimum number of characters from the special characters list to be present. Defaults to 0 if not set."` + BannedPasswordsList string `yaml:"banned_passwords_list" env:"OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST;SHARING_PASSWORD_POLICY_BANNED_PASSWORDS_LIST" desc:"Path to the 'banned passwords list' file. See the documentation for more details."` +} diff --git a/services/sharing/pkg/config/defaults/defaultconfig.go b/services/sharing/pkg/config/defaults/defaultconfig.go index 1a916b094..57026e0b9 100644 --- a/services/sharing/pkg/config/defaults/defaultconfig.go +++ b/services/sharing/pkg/config/defaults/defaultconfig.go @@ -75,7 +75,15 @@ func DefaultConfig() *config.Config { ClusterID: "ocis-cluster", EnableTLS: false, }, - EnableExpiredSharesCleanup: true, + EnableExpiredSharesCleanup: true, + PublicShareMustHavePassword: true, + PasswordPolicy: config.PasswordPolicy{ + MinCharacters: 8, + MinLowerCaseCharacters: 1, + MinUpperCaseCharacters: 1, + MinDigits: 1, + MinSpecialCharacters: 1, + }, } } diff --git a/services/sharing/pkg/revaconfig/config.go b/services/sharing/pkg/revaconfig/config.go index 4f3e236b1..ac3102fa0 100644 --- a/services/sharing/pkg/revaconfig/config.go +++ b/services/sharing/pkg/revaconfig/config.go @@ -1,11 +1,29 @@ package revaconfig import ( + "bufio" + "fmt" + "os" + "path/filepath" + + "github.com/owncloud/ocis/v2/ocis-pkg/log" + + "github.com/owncloud/ocis/v2/ocis-pkg/config/defaults" "github.com/owncloud/ocis/v2/services/sharing/pkg/config" ) // SharingConfigFromStruct will adapt an oCIS config struct into a reva mapstructure to start a reva service. -func SharingConfigFromStruct(cfg *config.Config) map[string]interface{} { +func SharingConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string]interface{}, error) { + var bannedPasswordsList map[string]struct{} + var err error + if cfg.PasswordPolicy.BannedPasswordsList != "" { + bannedPasswordsList, err = readMultilineFile(cfg.PasswordPolicy.BannedPasswordsList) + if err != nil { + err = fmt.Errorf("failed to load the banned passwords from a file %s: %w", cfg.PasswordPolicy.BannedPasswordsList, err) + logger.Err(err).Send() + return nil, err + } + } rcfg := map[string]interface{}{ "shared": map[string]interface{}{ "jwt_secret": cfg.TokenManager.JWTSecret, @@ -73,6 +91,17 @@ func SharingConfigFromStruct(cfg *config.Config) map[string]interface{} { }, }, "publicshareprovider": map[string]interface{}{ + "gateway_addr": cfg.Reva.Address, + "writeable_share_must_have_password": cfg.WriteableShareMustHavePassword, + "public_share_must_have_password": cfg.PublicShareMustHavePassword, + "password_policy": map[string]interface{}{ + "min_digits": cfg.PasswordPolicy.MinDigits, + "min_characters": cfg.PasswordPolicy.MinCharacters, + "min_lowercase_characters": cfg.PasswordPolicy.MinLowerCaseCharacters, + "min_uppercase_characters": cfg.PasswordPolicy.MinUpperCaseCharacters, + "min_special_characters": cfg.PasswordPolicy.MinSpecialCharacters, + "banned_passwords_list": bannedPasswordsList, + }, "driver": cfg.PublicSharingDriver, "drivers": map[string]interface{}{ "json": map[string]interface{}{ @@ -97,13 +126,12 @@ func SharingConfigFromStruct(cfg *config.Config) map[string]interface{} { "machine_auth_apikey": cfg.PublicSharingDrivers.CS3.SystemUserAPIKey, }, "jsoncs3": map[string]interface{}{ - "gateway_addr": cfg.Reva.Address, - "provider_addr": cfg.PublicSharingDrivers.JSONCS3.ProviderAddr, - "service_user_id": cfg.PublicSharingDrivers.JSONCS3.SystemUserID, - "service_user_idp": cfg.PublicSharingDrivers.JSONCS3.SystemUserIDP, - "machine_auth_apikey": cfg.PublicSharingDrivers.JSONCS3.SystemUserAPIKey, - "writeable_share_must_have_password": cfg.WriteableShareMustHavePassword, - "enable_expired_shares_cleanup": cfg.EnableExpiredSharesCleanup, + "gateway_addr": cfg.Reva.Address, + "provider_addr": cfg.PublicSharingDrivers.JSONCS3.ProviderAddr, + "service_user_id": cfg.PublicSharingDrivers.JSONCS3.SystemUserID, + "service_user_idp": cfg.PublicSharingDrivers.JSONCS3.SystemUserIDP, + "machine_auth_apikey": cfg.PublicSharingDrivers.JSONCS3.SystemUserAPIKey, + "enable_expired_shares_cleanup": cfg.EnableExpiredSharesCleanup, }, }, }, @@ -126,5 +154,33 @@ func SharingConfigFromStruct(cfg *config.Config) map[string]interface{} { }, }, } - return rcfg + return rcfg, nil +} + +func readMultilineFile(path string) (map[string]struct{}, error) { + if !fileExists(path) { + path = filepath.Join(defaults.BaseConfigPath(), path) + } + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + scanner := bufio.NewScanner(file) + data := make(map[string]struct{}) + for scanner.Scan() { + line := scanner.Text() + if line != "" { + data[line] = struct{}{} + } + } + return data, err +} + +func fileExists(path string) bool { + info, err := os.Stat(path) + if err != nil { + return false + } + return !info.IsDir() }