mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-05 19:59:37 -06:00
Extract role assignments from claims
Add a UserRoleAssigner implementation that extract role names from the users' claims and creates role assignments in the settings service based on a configured mapping of claim values to ocis role names. Closes: #5669
This commit is contained in:
committed by
Ralf Haferkamp
parent
d57d52b33d
commit
a448c75c75
97
services/proxy/pkg/autoprovision/creator.go
Normal file
97
services/proxy/pkg/autoprovision/creator.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package autoprovision
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/auth/scope"
|
||||
"github.com/cs3org/reva/v2/pkg/token"
|
||||
settingsService "github.com/owncloud/ocis/v2/services/settings/pkg/service/v0"
|
||||
)
|
||||
|
||||
// Creator provides an interface to get a user or reva token with admin privileges
|
||||
type Creator interface {
|
||||
// GetAutoProvisionAdmin returns a user with the Admin role assigned
|
||||
GetAutoProvisionAdmin() (*cs3.User, error)
|
||||
// GetAutoProvisionAdminToken returns a reva token with admin privileges
|
||||
GetAutoProvisionAdminToken(ctx context.Context) (string, error)
|
||||
}
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
tokenManager token.Manager
|
||||
}
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// WithTokenManager sets the reva token manager
|
||||
func WithTokenManager(t token.Manager) Option {
|
||||
return func(o *Options) {
|
||||
o.tokenManager = t
|
||||
}
|
||||
}
|
||||
|
||||
type creator struct {
|
||||
Options
|
||||
}
|
||||
|
||||
// NewCreator returns a new Creator instance
|
||||
func NewCreator(opts ...Option) creator {
|
||||
opt := Options{}
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return creator{
|
||||
Options: opt,
|
||||
}
|
||||
}
|
||||
|
||||
// This returns an hardcoded internal User, that is privileged to create new User via
|
||||
// the Graph API. This user is needed for autoprovisioning of users from incoming OIDC
|
||||
// claims.
|
||||
func (c creator) GetAutoProvisionAdmin() (*cs3.User, error) {
|
||||
roleIDsJSON, err := json.Marshal([]string{settingsService.BundleUUIDRoleAdmin})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
autoProvisionUserCreator := &cs3.User{
|
||||
DisplayName: "Autoprovision User",
|
||||
Username: "autoprovisioner",
|
||||
Id: &cs3.UserId{
|
||||
Idp: "internal",
|
||||
OpaqueId: "autoprov-user-id00-0000-000000000000",
|
||||
},
|
||||
Opaque: &types.Opaque{
|
||||
Map: map[string]*types.OpaqueEntry{
|
||||
"roles": {
|
||||
Decoder: "json",
|
||||
Value: roleIDsJSON,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return autoProvisionUserCreator, nil
|
||||
}
|
||||
|
||||
func (c creator) GetAutoProvisionAdminToken(ctx context.Context) (string, error) {
|
||||
userCreator, err := c.GetAutoProvisionAdmin()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
s, err := scope.AddOwnerScope(nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
token, err := c.tokenManager.MintToken(ctx, userCreator, s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/version"
|
||||
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
storesvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/store/v0"
|
||||
"github.com/owncloud/ocis/v2/services/proxy/pkg/autoprovision"
|
||||
"github.com/owncloud/ocis/v2/services/proxy/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/services/proxy/pkg/config/parser"
|
||||
"github.com/owncloud/ocis/v2/services/proxy/pkg/logging"
|
||||
@@ -137,32 +138,50 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config) alice.Chain {
|
||||
rolesClient := settingssvc.NewRoleService("com.owncloud.api.settings", grpc.DefaultClient())
|
||||
revaClient, err := pool.GetGatewayServiceClient(cfg.Reva.Address, cfg.Reva.GetRevaOptions()...)
|
||||
if err != nil {
|
||||
logger.Fatal().Err(err).Msg("Failed to get gateway client")
|
||||
}
|
||||
tokenManager, err := jwt.New(map[string]interface{}{
|
||||
"secret": cfg.TokenManager.JWTSecret,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatal().Err(err).
|
||||
Msg("Failed to create token manager")
|
||||
}
|
||||
autoProvsionCreator := autoprovision.NewCreator(autoprovision.WithTokenManager(tokenManager))
|
||||
var userProvider backend.UserBackend
|
||||
switch cfg.AccountBackend {
|
||||
case "cs3":
|
||||
tokenManager, err := jwt.New(map[string]interface{}{
|
||||
"secret": cfg.TokenManager.JWTSecret,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error().Err(err).
|
||||
Msg("Failed to create token manager")
|
||||
}
|
||||
|
||||
userProvider = backend.NewCS3UserBackend(
|
||||
backend.WithLogger(logger),
|
||||
backend.WithRevaAuthenticator(revaClient),
|
||||
backend.WithMachineAuthAPIKey(cfg.MachineAuthAPIKey),
|
||||
backend.WithOIDCissuer(cfg.OIDC.Issuer),
|
||||
backend.WithTokenManager(tokenManager),
|
||||
backend.WithAutoProvisonCreator(autoProvsionCreator),
|
||||
)
|
||||
default:
|
||||
logger.Fatal().Msgf("Invalid accounts backend type '%s'", cfg.AccountBackend)
|
||||
}
|
||||
|
||||
roleAssigner := userroles.NewDefaultRoleAssigner(
|
||||
userroles.WithRoleService(rolesClient),
|
||||
userroles.WithLogger(logger),
|
||||
)
|
||||
var roleAssigner userroles.UserRoleAssigner
|
||||
switch cfg.RoleAssignment.Driver {
|
||||
case "default":
|
||||
roleAssigner = userroles.NewDefaultRoleAssigner(
|
||||
userroles.WithRoleService(rolesClient),
|
||||
userroles.WithLogger(logger),
|
||||
)
|
||||
case "oidc":
|
||||
roleAssigner = userroles.NewOIDCRoleAssigner(
|
||||
userroles.WithRoleService(rolesClient),
|
||||
userroles.WithLogger(logger),
|
||||
userroles.WithRolesClaim(cfg.RoleAssignment.OIDCRoleMapper.RoleClaim),
|
||||
userroles.WithRoleMapping(cfg.RoleAssignment.OIDCRoleMapper.RoleMapping),
|
||||
userroles.WithAutoProvisonCreator(autoProvsionCreator),
|
||||
)
|
||||
default:
|
||||
logger.Fatal().Msgf("Invalid role assignment driver '%s'", cfg.RoleAssignment.Driver)
|
||||
}
|
||||
|
||||
storeClient := storesvc.NewStoreService("com.owncloud.api.store", grpc.DefaultClient())
|
||||
if err != nil {
|
||||
@@ -243,7 +262,6 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config)
|
||||
middleware.Logger(logger),
|
||||
middleware.UserProvider(userProvider),
|
||||
middleware.UserRoleAssigner(roleAssigner),
|
||||
middleware.TokenManagerConfig(*cfg.TokenManager),
|
||||
middleware.UserOIDCClaim(cfg.UserOIDCClaim),
|
||||
middleware.UserCS3Claim(cfg.UserCS3Claim),
|
||||
middleware.AutoprovisionAccounts(cfg.AutoprovisionAccounts),
|
||||
@@ -256,7 +274,6 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config)
|
||||
// finally, trigger home creation when a user logs in
|
||||
middleware.CreateHome(
|
||||
middleware.Logger(logger),
|
||||
middleware.TokenManagerConfig(*cfg.TokenManager),
|
||||
middleware.RevaGatewayClient(revaClient),
|
||||
middleware.RoleQuotas(cfg.RoleQuotas),
|
||||
),
|
||||
|
||||
@@ -25,6 +25,7 @@ type Config struct {
|
||||
Policies []Policy `yaml:"policies"`
|
||||
OIDC OIDC `yaml:"oidc"`
|
||||
TokenManager *TokenManager `mask:"struct" yaml:"token_manager"`
|
||||
RoleAssignment RoleAssignment `yaml:"role_assignment"`
|
||||
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."`
|
||||
@@ -121,6 +122,18 @@ type UserinfoCache struct {
|
||||
TTL int `yaml:"ttl" env:"PROXY_OIDC_USERINFO_CACHE_TTL" desc:"Max TTL in seconds for the OIDC user info cache."`
|
||||
}
|
||||
|
||||
// RoleAssignment contains the configuration for how to assign roles to users during login
|
||||
type RoleAssignment struct {
|
||||
Driver string `yaml:"driver" env:"PROXY_ROLE_ASSIGNMENT_DRIVER" desc:"The mechanism that should be used to assign roles to user upon login. Supported values: 'default' or 'oidc'. 'default' will assign the role 'user' to users which don't have a role assigned at the time they login. 'oidc' will assign the role based on the value of a claim (configured via PROXY_ROLE_ASSIGNMENT_OIDC_CLAIM) from the users OIDC claims."`
|
||||
OIDCRoleMapper OIDCRoleMapper `yaml:"oidc_role_mapper"`
|
||||
}
|
||||
|
||||
// OIDCRoleMapper contains the configuration for the "oidc" role assignment driber
|
||||
type OIDCRoleMapper struct {
|
||||
RoleClaim string `yaml:"role_claim" env:"PROXY_ROLE_ASSIGNMENT_OIDC_CLAIM" desc:"The OIDC claim used to create the users role assignment."`
|
||||
RoleMapping map[string]string `yaml:"role_mapping" desc:"A mapping of ocis role names to PROXY_ROLE_ASSIGNMENT_OIDC_CLAIM claim values. This setting can only be configured in the configuration file and not via environment variables."`
|
||||
}
|
||||
|
||||
// PolicySelector is the toplevel-configuration for different selectors
|
||||
type PolicySelector struct {
|
||||
Static *StaticSelectorConf `yaml:"static"`
|
||||
|
||||
@@ -52,7 +52,20 @@ func DefaultConfig() *config.Config {
|
||||
},
|
||||
},
|
||||
PolicySelector: nil,
|
||||
Reva: shared.DefaultRevaConfig(),
|
||||
RoleAssignment: config.RoleAssignment{
|
||||
Driver: "default",
|
||||
// this default is only relevant when Driver is set to "oidc"
|
||||
OIDCRoleMapper: config.OIDCRoleMapper{
|
||||
RoleClaim: "roles",
|
||||
RoleMapping: map[string]string{
|
||||
"admin": "ocisAdmin",
|
||||
"spaceadmin": "ocisSpaceAdmin",
|
||||
"user": "ocisUser",
|
||||
"guest": "ocisGuest",
|
||||
},
|
||||
},
|
||||
},
|
||||
Reva: shared.DefaultRevaConfig(),
|
||||
PreSignedURL: config.PreSignedURL{
|
||||
AllowedHTTPMethods: []string{"GET"},
|
||||
Enabled: true,
|
||||
|
||||
@@ -10,16 +10,13 @@ import (
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/auth/scope"
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/token"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/oidc"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/registry"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
settingsService "github.com/owncloud/ocis/v2/services/settings/pkg/service/v0"
|
||||
"github.com/owncloud/ocis/v2/services/proxy/pkg/autoprovision"
|
||||
"go-micro.dev/v4/selector"
|
||||
)
|
||||
|
||||
@@ -33,11 +30,11 @@ type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
logger log.Logger
|
||||
tokenManager token.Manager
|
||||
authProvider RevaAuthenticator
|
||||
machineAuthAPIKey string
|
||||
oidcISS string
|
||||
logger log.Logger
|
||||
authProvider RevaAuthenticator
|
||||
machineAuthAPIKey string
|
||||
oidcISS string
|
||||
autoProvsionCreator autoprovision.Creator
|
||||
}
|
||||
|
||||
func WithLogger(l log.Logger) Option {
|
||||
@@ -46,12 +43,6 @@ func WithLogger(l log.Logger) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithTokenManager(t token.Manager) Option {
|
||||
return func(o *Options) {
|
||||
o.tokenManager = t
|
||||
}
|
||||
}
|
||||
|
||||
func WithRevaAuthenticator(ra RevaAuthenticator) Option {
|
||||
return func(o *Options) {
|
||||
o.authProvider = ra
|
||||
@@ -70,6 +61,12 @@ func WithOIDCissuer(oidcISS string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithAutoProvisonCreator(c autoprovision.Creator) Option {
|
||||
return func(o *Options) {
|
||||
o.autoProvsionCreator = c
|
||||
}
|
||||
}
|
||||
|
||||
// NewCS3UserBackend creates a user-provider which fetches users from a CS3 UserBackend
|
||||
func NewCS3UserBackend(opts ...Option) UserBackend {
|
||||
opt := Options{}
|
||||
@@ -133,7 +130,7 @@ func (c *cs3backend) Authenticate(ctx context.Context, username string, password
|
||||
// function will just return the existing user.
|
||||
func (c *cs3backend) CreateUserFromClaims(ctx context.Context, claims map[string]interface{}) (*cs3.User, error) {
|
||||
newctx := context.Background()
|
||||
token, err := c.generateAutoProvisionAdminToken(newctx)
|
||||
token, err := c.autoProvsionCreator.GetAutoProvisionAdminToken(newctx)
|
||||
if err != nil {
|
||||
c.logger.Error().Err(err).Msg("Error generating token for autoprovisioning user.")
|
||||
return nil, err
|
||||
@@ -266,51 +263,3 @@ func (c cs3backend) cs3UserFromLibregraph(ctx context.Context, lu *libregraph.Us
|
||||
cs3user.Mail = lu.GetMail()
|
||||
return cs3user
|
||||
}
|
||||
|
||||
// This returns an hardcoded internal User, that is privileged to create new User via
|
||||
// the Graph API. This user is needed for autoprovisioning of users from incoming OIDC
|
||||
// claims.
|
||||
func getAutoProvisionUserCreator() (*cs3.User, error) {
|
||||
roleIDsJSON, err := json.Marshal([]string{settingsService.BundleUUIDRoleAdmin})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
autoProvisionUserCreator := &cs3.User{
|
||||
DisplayName: "Autoprovision User",
|
||||
Username: "autoprovisioner",
|
||||
Id: &cs3.UserId{
|
||||
Idp: "internal",
|
||||
OpaqueId: "autoprov-user-id00-0000-000000000000",
|
||||
},
|
||||
Opaque: &types.Opaque{
|
||||
Map: map[string]*types.OpaqueEntry{
|
||||
"roles": &types.OpaqueEntry{
|
||||
Decoder: "json",
|
||||
Value: roleIDsJSON,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return autoProvisionUserCreator, nil
|
||||
}
|
||||
|
||||
func (c cs3backend) generateAutoProvisionAdminToken(ctx context.Context) (string, error) {
|
||||
userCreator, err := getAutoProvisionUserCreator()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
s, err := scope.AddOwnerScope(nil)
|
||||
if err != nil {
|
||||
c.logger.Error().Err(err).Msg("could not get owner scope")
|
||||
return "", err
|
||||
}
|
||||
|
||||
token, err := c.tokenManager.MintToken(ctx, userCreator, s)
|
||||
if err != nil {
|
||||
c.logger.Error().Err(err).Msg("could not mint token")
|
||||
return "", err
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
162
services/proxy/pkg/userroles/oidcroles.go
Normal file
162
services/proxy/pkg/userroles/oidcroles.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package userroles
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/middleware"
|
||||
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
"go-micro.dev/v4/metadata"
|
||||
)
|
||||
|
||||
type oidcRoleAssigner struct {
|
||||
Options
|
||||
}
|
||||
|
||||
// NewOIDCRoleAssigner returns an implemenation of the UserRoleAssigner interface
|
||||
func NewOIDCRoleAssigner(opts ...Option) UserRoleAssigner {
|
||||
opt := Options{}
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return oidcRoleAssigner{
|
||||
Options: opt,
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateUserRoleAssignment assigns the role "User" to the supplied user. Unless the user
|
||||
// already has a different role assigned.
|
||||
func (ra oidcRoleAssigner) UpdateUserRoleAssignment(ctx context.Context, user *cs3.User, claims map[string]interface{}) (*cs3.User, error) {
|
||||
|
||||
// To list roles and update assignment we need some elevated access to the settings service
|
||||
// prepare a new request context for that until we have service accounts
|
||||
newctx, err := ra.prepareAdminContext()
|
||||
if err != nil {
|
||||
ra.logger.Error().Err(err).Msg("Error creating admin context")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claimValueToRoleID, err := ra.oidcClaimvaluesToRoleIDs(newctx)
|
||||
if err != nil {
|
||||
ra.logger.Error().Err(err).Msg("Error mapping claims to roles ids")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roleIDsFromClaim := make([]string, 0, 1)
|
||||
ra.logger.Error().Interface("rolesclaim", claims[ra.rolesClaim]).Msg("Got ClaimRoles")
|
||||
claimRoles, ok := claims[ra.rolesClaim].([]interface{})
|
||||
if !ok {
|
||||
ra.logger.Error().Err(err).Msg("No roles in user claims.")
|
||||
return nil, err
|
||||
}
|
||||
for _, cri := range claimRoles {
|
||||
cr, ok := cri.(string)
|
||||
if !ok {
|
||||
err := errors.New("invalid role in claims")
|
||||
ra.logger.Error().Err(err).Interface("claim value", cri).Msg("Is not a valid string.")
|
||||
return nil, err
|
||||
}
|
||||
id, ok := claimValueToRoleID[cr]
|
||||
if !ok {
|
||||
ra.logger.Error().Str("role", cr).Msg("Skipping unmaped role from claims.")
|
||||
continue
|
||||
}
|
||||
roleIDsFromClaim = append(roleIDsFromClaim, id)
|
||||
}
|
||||
ra.logger.Error().Interface("roleIDs", roleIDsFromClaim).Msg("Mapped roles from claim")
|
||||
|
||||
switch len(roleIDsFromClaim) {
|
||||
default:
|
||||
err := errors.New("too many roles found in claims")
|
||||
ra.logger.Error().Err(err).Msg("Only one role per user is allowed.")
|
||||
return nil, err
|
||||
case 0:
|
||||
err := errors.New("no role in claim, maps to a ocis role")
|
||||
ra.logger.Error().Err(err).Msg("")
|
||||
return nil, err
|
||||
case 1:
|
||||
// exactly one mapping. This is right
|
||||
}
|
||||
|
||||
assignedRoles, err := loadRolesIDs(newctx, user.GetId().GetOpaqueId(), ra.roleService)
|
||||
if err != nil {
|
||||
ra.logger.Error().Err(err).Msgf("Could not load roles")
|
||||
return nil, err
|
||||
}
|
||||
if len(assignedRoles) > 1 {
|
||||
err := errors.New("too many roles assigned")
|
||||
ra.logger.Error().Err(err).Msg("The user has too many roles assigned")
|
||||
return nil, err
|
||||
}
|
||||
ra.logger.Error().Interface("assignedRoleIds", assignedRoles).Msg("Currently assigned roles")
|
||||
if len(assignedRoles) == 0 || (assignedRoles[0] != roleIDsFromClaim[0]) {
|
||||
if _, err = ra.roleService.AssignRoleToUser(newctx, &settingssvc.AssignRoleToUserRequest{
|
||||
AccountUuid: user.GetId().GetOpaqueId(),
|
||||
RoleId: roleIDsFromClaim[0],
|
||||
}); err != nil {
|
||||
ra.logger.Error().Err(err).Msg("Role assignment failed")
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
user.Opaque = utils.AppendJSONToOpaque(user.Opaque, "roles", roleIDsFromClaim)
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// ApplyUserRole it looks up the user's role in the settings service and adds it
|
||||
// user's opaque data
|
||||
func (ra oidcRoleAssigner) ApplyUserRole(ctx context.Context, user *cs3.User) (*cs3.User, error) {
|
||||
roleIDs, err := loadRolesIDs(ctx, user.Id.OpaqueId, ra.roleService)
|
||||
if err != nil {
|
||||
ra.logger.Error().Err(err).Msgf("Could not load roles")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user.Opaque = utils.AppendJSONToOpaque(user.Opaque, "roles", roleIDs)
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (ra oidcRoleAssigner) prepareAdminContext() (context.Context, error) {
|
||||
newctx := context.Background()
|
||||
autoProvisionUser, err := ra.autoProvsionCreator.GetAutoProvisionAdmin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token, err := ra.autoProvsionCreator.GetAutoProvisionAdminToken(newctx)
|
||||
if err != nil {
|
||||
ra.logger.Error().Err(err).Msg("Error generating token for provisioning role assignments.")
|
||||
return nil, err
|
||||
}
|
||||
newctx = revactx.ContextSetToken(newctx, token)
|
||||
newctx = metadata.Set(newctx, middleware.AccountID, autoProvisionUser.Id.OpaqueId)
|
||||
newctx = metadata.Set(newctx, middleware.RoleIDs, string(autoProvisionUser.Opaque.Map["roles"].Value))
|
||||
return newctx, nil
|
||||
}
|
||||
|
||||
func (ra oidcRoleAssigner) oidcClaimvaluesToRoleIDs(ctx context.Context) (map[string]string, error) {
|
||||
roleClaimToID := map[string]string{}
|
||||
// Get all roles to find the role IDs.
|
||||
// TODO: we need to cache this. Roles IDs change rarely and this is a pretty expensiveV call
|
||||
req := &settingssvc.ListBundlesRequest{}
|
||||
res, err := ra.roleService.ListRoles(ctx, req)
|
||||
if err != nil {
|
||||
ra.logger.Error().Err(err).Msg("Failed to list all roles")
|
||||
return roleClaimToID, err
|
||||
}
|
||||
|
||||
for _, role := range res.Bundles {
|
||||
ra.logger.Error().Str("role", role.Name).Str("id", role.Id).Msg("Got Role")
|
||||
roleClaim, ok := ra.roleMapping[role.Name]
|
||||
if !ok {
|
||||
err := errors.New("Incomplete role mapping")
|
||||
ra.logger.Error().Err(err).Str("role", role.Name).Msg("Role not mapped to a claim value")
|
||||
return roleClaimToID, err
|
||||
}
|
||||
roleClaimToID[roleClaim] = role.Id
|
||||
}
|
||||
return roleClaimToID, nil
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
"github.com/owncloud/ocis/v2/services/proxy/pkg/autoprovision"
|
||||
)
|
||||
|
||||
//go:generate mockery --name=UserRoleAssigner
|
||||
@@ -21,26 +22,53 @@ type UserRoleAssigner interface {
|
||||
ApplyUserRole(ctx context.Context, user *cs3.User) (*cs3.User, error)
|
||||
}
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
roleService settingssvc.RoleService
|
||||
rolesClaim string
|
||||
logger log.Logger
|
||||
roleService settingssvc.RoleService
|
||||
rolesClaim string
|
||||
roleMapping map[string]string
|
||||
autoProvsionCreator autoprovision.Creator
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// WithLogger configure the logger
|
||||
func WithLogger(l log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.logger = l
|
||||
}
|
||||
}
|
||||
|
||||
// WithRoleService sets the roleservice instance to use
|
||||
func WithRoleService(rs settingssvc.RoleService) Option {
|
||||
return func(o *Options) {
|
||||
o.roleService = rs
|
||||
}
|
||||
}
|
||||
|
||||
// WithRolesClaim sets the OIDC claim for looking up role names
|
||||
func WithRolesClaim(claim string) Option {
|
||||
return func(o *Options) {
|
||||
o.rolesClaim = claim
|
||||
}
|
||||
}
|
||||
|
||||
// WithRoleMapping configures the map of ocis role names to claims values
|
||||
func WithRoleMapping(roleMap map[string]string) Option {
|
||||
return func(o *Options) {
|
||||
o.roleMapping = roleMap
|
||||
}
|
||||
}
|
||||
|
||||
// WithAutoProvisonCreator configures the autoprovision creator to use
|
||||
func WithAutoProvisonCreator(c autoprovision.Creator) Option {
|
||||
return func(o *Options) {
|
||||
o.autoProvsionCreator = c
|
||||
}
|
||||
}
|
||||
|
||||
// loadRolesIDs returns the role-ids assigned to an user
|
||||
func loadRolesIDs(ctx context.Context, opaqueUserID string, rs settingssvc.RoleService) ([]string, error) {
|
||||
req := &settingssvc.ListRoleAssignmentsRequest{AccountUuid: opaqueUserID}
|
||||
|
||||
Reference in New Issue
Block a user