add config option to set default quota per role

This commit is contained in:
David Christofas
2023-02-22 16:22:45 +01:00
parent 30faae2ae4
commit 67549b4ebd
8 changed files with 99 additions and 18 deletions
+13
View File
@@ -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.
+1
View File
@@ -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),
),
)
}
+15 -14
View File
@@ -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 servers 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 servers 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
}
+11 -1
View File
@@ -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
}
}