mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-11 09:33:17 -04:00
add config option to set default quota per role
This commit is contained in:
12
changelog/unreleased/role-quota.md
Normal file
12
changelog/unreleased/role-quota.md
Normal file
@@ -0,0 +1,12 @@
|
||||
Enhancement: Added option to configure default quota per role
|
||||
|
||||
Admins can assign default quotas to users with certain roles by adding the following config to the `proxy.yaml`.
|
||||
E.g.:
|
||||
```
|
||||
role_quotas:
|
||||
d7beeea8-8ff4-406b-8fb6-ab2dd81e6b11: 2300000
|
||||
```
|
||||
|
||||
It maps a role ID to the quota in bytes.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/5616
|
||||
3
go.mod
3
go.mod
@@ -11,7 +11,7 @@ require (
|
||||
github.com/blevesearch/bleve/v2 v2.3.5
|
||||
github.com/coreos/go-oidc/v3 v3.4.0
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20221012090518-ef2996678965
|
||||
github.com/cs3org/reva/v2 v2.12.1-0.20230214085134-ec27f5f8feb3
|
||||
github.com/cs3org/reva/v2 v2.12.1-0.20230221142843-06fcf4e500bf
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/ggwhite/go-masker v1.0.9
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
@@ -251,6 +251,7 @@ require (
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/prometheus/statsd_exporter v0.22.8 // indirect
|
||||
github.com/rivo/uniseg v0.4.2 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
||||
github.com/rs/cors v1.8.2 // indirect
|
||||
github.com/rs/xid v1.4.0 // indirect
|
||||
github.com/russellhaering/goxmldsig v1.1.1 // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@@ -343,8 +343,8 @@ github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3p
|
||||
github.com/crewjam/saml v0.4.6/go.mod h1:ZBOXnNPFzB3CgOkRm7Nd6IVdkG+l/wF+0ZXLqD96t1A=
|
||||
github.com/crewjam/saml v0.4.9 h1:X2jDv4dv3IvfT9t+RhADavzNFAcq3fVxzTCIH3G605U=
|
||||
github.com/crewjam/saml v0.4.9/go.mod h1:9Zh6dWPtB3MSzTRt8fIFH60Z351QQ+s7hCU3J/tTlA4=
|
||||
github.com/cs3org/reva/v2 v2.12.1-0.20230214085134-ec27f5f8feb3 h1:KaFl1ZfjjKSlDsq/zvhBV9f+mXYqnLdK5IhAaZBoXDo=
|
||||
github.com/cs3org/reva/v2 v2.12.1-0.20230214085134-ec27f5f8feb3/go.mod h1:u73Df9JAZsDj43GIjQIb3DO1PLJuPutZXkRqQH0oGXA=
|
||||
github.com/cs3org/reva/v2 v2.12.1-0.20230221142843-06fcf4e500bf h1:OZwvSm5O4t/jxoVVgJcwbM2BHYv10rGq0KqqVAwTERY=
|
||||
github.com/cs3org/reva/v2 v2.12.1-0.20230221142843-06fcf4e500bf/go.mod h1:dbaNP2U3nGQA5BHLc5w/hqviq7b0F4eygNwC38jeaiU=
|
||||
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI=
|
||||
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY=
|
||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||
|
||||
@@ -13,6 +13,19 @@ The following request authentication schemes are implemented:
|
||||
- Signed URL
|
||||
- Public Share Token
|
||||
|
||||
## Automatic quota assignments
|
||||
|
||||
It is possible to automatically assign a specific quota amount to new users depending on their role.
|
||||
To do this you need to add the following config snippet to the proxy.yaml config file.
|
||||
|
||||
```yaml
|
||||
role_quotas:
|
||||
<role ID1>: <quota1>
|
||||
<role ID2>: <quota2>
|
||||
```
|
||||
|
||||
There you need to configure the mapping between the roles by their ID and the quota in Bytes.
|
||||
|
||||
## Recommendations for Production Deployments
|
||||
|
||||
In a production deployment, you want to have basic authentication (`PROXY_ENABLE_BASIC_AUTH`) disabled which is the default state. You also want to setup a firewall to only allow requests to the proxy service or the reverse proxy if you have one. Requests to the other services should be blocked by the firewall.
|
||||
|
||||
@@ -249,6 +249,7 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config)
|
||||
middleware.Logger(logger),
|
||||
middleware.TokenManagerConfig(*cfg.TokenManager),
|
||||
middleware.RevaGatewayClient(revaClient),
|
||||
middleware.RoleQuotas(cfg.RoleQuotas),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -21,20 +21,21 @@ type Config struct {
|
||||
Reva *shared.Reva `yaml:"reva"`
|
||||
GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"`
|
||||
|
||||
Policies []Policy `yaml:"policies"`
|
||||
OIDC OIDC `yaml:"oidc"`
|
||||
TokenManager *TokenManager `mask:"struct" yaml:"token_manager"`
|
||||
PolicySelector *PolicySelector `yaml:"policy_selector"`
|
||||
PreSignedURL PreSignedURL `yaml:"pre_signed_url"`
|
||||
AccountBackend string `yaml:"account_backend" env:"PROXY_ACCOUNT_BACKEND_TYPE" desc:"Account backend the PROXY service should use. Currently only 'cs3' is possible here."`
|
||||
UserOIDCClaim string `yaml:"user_oidc_claim" env:"PROXY_USER_OIDC_CLAIM" desc:"The name of an OpenID Connect claim that is used for resolving users with the account backend. The value of the claim must hold a per user unique, stable and non re-assignable identifier. The availability of claims depends on your Identity Provider. There are common claims available for most Identity providers like 'email' or 'preferred_user' but you can also add your own claim."`
|
||||
UserCS3Claim string `yaml:"user_cs3_claim" env:"PROXY_USER_CS3_CLAIM" desc:"The name of a CS3 user attribute (claim) that should be mapped to the 'user_oidc_claim'. Supported values are 'username', 'mail' and 'userid'."`
|
||||
MachineAuthAPIKey string `mask:"password" yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;PROXY_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."`
|
||||
AutoprovisionAccounts bool `yaml:"auto_provision_accounts" env:"PROXY_AUTOPROVISION_ACCOUNTS" desc:"Set this to 'true' to automatically provision users that do not yet exist in the users service on-demand upon first sign-in. To use this a write-enabled libregraph user backend needs to be setup an running."`
|
||||
EnableBasicAuth bool `yaml:"enable_basic_auth" env:"PROXY_ENABLE_BASIC_AUTH" desc:"Set this to true to enable 'basic authentication' (username/password)."`
|
||||
InsecureBackends bool `yaml:"insecure_backends" env:"PROXY_INSECURE_BACKENDS" desc:"Disable TLS certificate validation for all HTTP backend connections."`
|
||||
BackendHTTPSCACert string `yaml:"backend_https_cacert" env:"PROXY_HTTPS_CACERT" desc:"Path/File for the root CA certificate used to validate the server’s TLS certificate for https enabled backend services."`
|
||||
AuthMiddleware AuthMiddleware `yaml:"auth_middleware"`
|
||||
RoleQuotas map[string]uint64 `yaml:"role_quotas"`
|
||||
Policies []Policy `yaml:"policies"`
|
||||
OIDC OIDC `yaml:"oidc"`
|
||||
TokenManager *TokenManager `mask:"struct" yaml:"token_manager"`
|
||||
PolicySelector *PolicySelector `yaml:"policy_selector"`
|
||||
PreSignedURL PreSignedURL `yaml:"pre_signed_url"`
|
||||
AccountBackend string `yaml:"account_backend" env:"PROXY_ACCOUNT_BACKEND_TYPE" desc:"Account backend the PROXY service should use. Currently only 'cs3' is possible here."`
|
||||
UserOIDCClaim string `yaml:"user_oidc_claim" env:"PROXY_USER_OIDC_CLAIM" desc:"The name of an OpenID Connect claim that is used for resolving users with the account backend. The value of the claim must hold a per user unique, stable and non re-assignable identifier. The availability of claims depends on your Identity Provider. There are common claims available for most Identity providers like 'email' or 'preferred_user' but you can also add your own claim."`
|
||||
UserCS3Claim string `yaml:"user_cs3_claim" env:"PROXY_USER_CS3_CLAIM" desc:"The name of a CS3 user attribute (claim) that should be mapped to the 'user_oidc_claim'. Supported values are 'username', 'mail' and 'userid'."`
|
||||
MachineAuthAPIKey string `mask:"password" yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;PROXY_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."`
|
||||
AutoprovisionAccounts bool `yaml:"auto_provision_accounts" env:"PROXY_AUTOPROVISION_ACCOUNTS" desc:"Set this to 'true' to automatically provision users that do not yet exist in the users service on-demand upon first sign-in. To use this a write-enabled libregraph user backend needs to be setup an running."`
|
||||
EnableBasicAuth bool `yaml:"enable_basic_auth" env:"PROXY_ENABLE_BASIC_AUTH" desc:"Set this to true to enable 'basic authentication' (username/password)."`
|
||||
InsecureBackends bool `yaml:"insecure_backends" env:"PROXY_INSECURE_BACKENDS" desc:"Disable TLS certificate validation for all HTTP backend connections."`
|
||||
BackendHTTPSCACert string `yaml:"backend_https_cacert" env:"PROXY_HTTPS_CACERT" desc:"Path/File for the root CA certificate used to validate the server’s TLS certificate for https enabled backend services."`
|
||||
AuthMiddleware AuthMiddleware `yaml:"auth_middleware"`
|
||||
|
||||
Context context.Context `yaml:"-" json:"-"`
|
||||
}
|
||||
|
||||
@@ -2,13 +2,17 @@ package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
@@ -22,6 +26,7 @@ func CreateHome(optionSetters ...Option) func(next http.Handler) http.Handler {
|
||||
next: next,
|
||||
logger: logger,
|
||||
revaGatewayClient: options.RevaGatewayClient,
|
||||
roleQuotas: options.RoleQuotas,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +35,7 @@ type createHome struct {
|
||||
next http.Handler
|
||||
logger log.Logger
|
||||
revaGatewayClient gateway.GatewayAPIClient
|
||||
roleQuotas map[string]uint64
|
||||
}
|
||||
|
||||
func (m createHome) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
@@ -45,6 +51,19 @@ func (m createHome) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
ctx := metadata.AppendToOutgoingContext(req.Context(), revactx.TokenHeader, token)
|
||||
|
||||
createHomeReq := &provider.CreateHomeRequest{}
|
||||
u, ok := revactx.ContextGetUser(ctx)
|
||||
if ok {
|
||||
roleIDs, err := m.getUserRoles(u)
|
||||
if err != nil {
|
||||
m.logger.Error().Err(err).Str("userid", u.Id.OpaqueId).Msg("failed to get roles for user")
|
||||
errorcode.GeneralException.Render(w, req, http.StatusInternalServerError, "Unauthorized")
|
||||
return
|
||||
}
|
||||
if limit, hasLimit := m.checkRoleQuotaLimit(roleIDs); hasLimit {
|
||||
createHomeReq.Opaque = utils.AppendPlainToOpaque(nil, "quota", strconv.FormatUint(limit, 10))
|
||||
}
|
||||
}
|
||||
|
||||
createHomeRes, err := m.revaGatewayClient.CreateHome(ctx, createHomeReq)
|
||||
|
||||
if err != nil {
|
||||
@@ -62,3 +81,27 @@ func (m createHome) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
func (m createHome) shouldServe(req *http.Request) bool {
|
||||
return req.Header.Get("x-access-token") != ""
|
||||
}
|
||||
|
||||
func (m createHome) getUserRoles(user *userv1beta1.User) ([]string, error) {
|
||||
var roleIDs []string
|
||||
if err := utils.ReadJSONFromOpaque(user.Opaque, "roles", &roleIDs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tmp := make(map[string]struct{})
|
||||
for _, id := range roleIDs {
|
||||
tmp[id] = struct{}{}
|
||||
}
|
||||
|
||||
dedup := make([]string, 0, len(tmp))
|
||||
for k := range tmp {
|
||||
dedup = append(dedup, k)
|
||||
}
|
||||
return dedup, nil
|
||||
}
|
||||
|
||||
func (m createHome) checkRoleQuotaLimit(roleIDs []string) (uint64, bool) {
|
||||
id := roleIDs[0] // At the moment a user can only have one role.
|
||||
quota, ok := m.roleQuotas[id]
|
||||
return quota, ok
|
||||
}
|
||||
|
||||
@@ -60,6 +60,9 @@ type Options struct {
|
||||
AccessTokenVerifyMethod string
|
||||
// JWKS sets the options for fetching the JWKS from the IDP
|
||||
JWKS config.JWKS
|
||||
// RoleQuotas hold userid:quota mappings. These will be used when provisioning new users.
|
||||
// The users will get as much quota as is set for their role.
|
||||
RoleQuotas map[string]uint64
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
@@ -206,9 +209,16 @@ func AccessTokenVerifyMethod(method string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// JWKS sets the options for fetching the JWKS from the IDP
|
||||
// JWKSOptions sets the options for fetching the JWKS from the IDP
|
||||
func JWKSOptions(jo config.JWKS) Option {
|
||||
return func(o *Options) {
|
||||
o.JWKS = jo
|
||||
}
|
||||
}
|
||||
|
||||
// RoleQuotas sets the role quota mapping setting
|
||||
func RoleQuotas(roleQuotas map[string]uint64) Option {
|
||||
return func(o *Options) {
|
||||
o.RoleQuotas = roleQuotas
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user