diff --git a/services/userlog/pkg/config/config.go b/services/userlog/pkg/config/config.go index 782fb07917..0779c259a8 100644 --- a/services/userlog/pkg/config/config.go +++ b/services/userlog/pkg/config/config.go @@ -32,6 +32,8 @@ type Config struct { GlobalNotificationsSecret string `yaml:"global_notifications_secret" env:"USERLOG_GLOBAL_NOTIFICATIONS_SECRET" desc:"The secret to secure the global notifications endpoint. Only system admins and users knowing that secret can call the global notifications POST/DELETE endpoints."` + ServiceAccount ServiceAccount `yaml:"service_account"` + Context context.Context `yaml:"-"` } @@ -75,3 +77,9 @@ type HTTP struct { type TokenManager struct { JWTSecret string `yaml:"jwt_secret" env:"OCIS_JWT_SECRET;USERLOG_JWT_SECRET" desc:"The secret to mint and validate jwt tokens."` } + +// ServiceAccount is the configuration for the used service account +type ServiceAccount struct { + ServiceAccountID string `yaml:"service_account_id" env:"OCIS_SERVICE_ACCOUNT_ID;USERLOG_SERVICE_ACCOUNT_ID" desc:"The ID of the service account the service should use. See the 'auth-service' service description for more details."` + ServiceAccountSecret string `yaml:"service_account_secret" env:"OCIS_SERVICE_ACCOUNT_SECRET;USERLOG_SERVICE_ACCOUNT_SECRET" desc:"The service account secret."` +} diff --git a/services/userlog/pkg/config/defaults/defaultconfig.go b/services/userlog/pkg/config/defaults/defaultconfig.go index 1dfd6318ca..294923d1d7 100644 --- a/services/userlog/pkg/config/defaults/defaultconfig.go +++ b/services/userlog/pkg/config/defaults/defaultconfig.go @@ -52,6 +52,10 @@ func DefaultConfig() *config.Config { AllowCredentials: true, }, }, + ServiceAccount: config.ServiceAccount{ + ServiceAccountID: "service-user-id", + ServiceAccountSecret: "secret-string", + }, } } diff --git a/services/userlog/pkg/service/conversion.go b/services/userlog/pkg/service/conversion.go index df472094a8..c589753fc8 100644 --- a/services/userlog/pkg/service/conversion.go +++ b/services/userlog/pkg/service/conversion.go @@ -52,31 +52,32 @@ type OC10Notification struct { // Converter is responsible for converting eventhistory events to OC10Notifications type Converter struct { - locale string - gatewaySelector pool.Selectable[gateway.GatewayAPIClient] - machineAuthAPIKey string - serviceName string - translationPath string + locale string + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + serviceAccountID string + serviceAccountSecret string + serviceName string + translationPath string // cached within one request not to query other service too much - spaces map[string]*storageprovider.StorageSpace - users map[string]*user.User - resources map[string]*storageprovider.ResourceInfo - contexts map[string]context.Context + spaces map[string]*storageprovider.StorageSpace + users map[string]*user.User + resources map[string]*storageprovider.ResourceInfo + serviceAccountContext context.Context } // NewConverter returns a new Converter -func NewConverter(loc string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], machineAuthAPIKey string, name string, translationPath string) *Converter { +func NewConverter(loc string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], machineAuthAPIKey string, name string, translationPath string, serviceAccountID string, serviceAccountSecret string) *Converter { return &Converter{ - locale: loc, - gatewaySelector: gatewaySelector, - machineAuthAPIKey: machineAuthAPIKey, - serviceName: name, - translationPath: translationPath, - spaces: make(map[string]*storageprovider.StorageSpace), - users: make(map[string]*user.User), - resources: make(map[string]*storageprovider.ResourceInfo), - contexts: make(map[string]context.Context), + locale: loc, + gatewaySelector: gatewaySelector, + serviceAccountID: serviceAccountID, + serviceAccountSecret: serviceAccountSecret, + serviceName: name, + translationPath: translationPath, + spaces: make(map[string]*storageprovider.StorageSpace), + users: make(map[string]*user.User), + resources: make(map[string]*storageprovider.ResourceInfo), } } @@ -171,7 +172,7 @@ func (c *Converter) spaceMessage(eventid string, nt NotificationTemplate, execut return OC10Notification{}, err } - ctx, err := c.authenticate(usr) + ctx, err := c.authenticate() if err != nil { return OC10Notification{}, err } @@ -210,7 +211,7 @@ func (c *Converter) shareMessage(eventid string, nt NotificationTemplate, execut return OC10Notification{}, err } - ctx, err := c.authenticate(usr) + ctx, err := c.authenticate() if err != nil { return OC10Notification{}, err } @@ -327,13 +328,18 @@ func (c *Converter) deprovisionMessage(nt NotificationTemplate, deproDate string }, nil } -func (c *Converter) authenticate(usr *user.User) (context.Context, error) { - if ctx, ok := c.contexts[usr.GetId().GetOpaqueId()]; ok { - return ctx, nil +func (c *Converter) authenticate() (context.Context, error) { + if c.serviceAccountContext != nil { + return c.serviceAccountContext, nil } - ctx, err := authenticate(usr, c.gatewaySelector, c.machineAuthAPIKey) + + gatewayClient, err := c.gatewaySelector.Next() + if err != nil { + return nil, err + } + ctx, err := utils.GetServiceUserContext(c.serviceAccountID, gatewayClient, c.serviceAccountSecret) if err == nil { - c.contexts[usr.GetId().GetOpaqueId()] = ctx + c.serviceAccountContext = ctx } return ctx, err } diff --git a/services/userlog/pkg/service/service.go b/services/userlog/pkg/service/service.go index 7ebee77768..a9d18ce44c 100644 --- a/services/userlog/pkg/service/service.go +++ b/services/userlog/pkg/service/service.go @@ -13,7 +13,6 @@ import ( user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/utils" @@ -29,7 +28,6 @@ import ( micrometadata "go-micro.dev/v4/metadata" "go-micro.dev/v4/store" "go.opentelemetry.io/otel/trace" - "google.golang.org/grpc/metadata" ) // UserlogService is the service responsible for user activities @@ -137,12 +135,12 @@ func (ul *UserlogService) processEvent(event events.Event) { users = append(users, e.ExecutingUser.GetId().GetOpaqueId()) default: return - } + // space related // TODO: how to find spaceadmins? case events.SpaceDisabled: executant = e.Executant - users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ID.GetOpaqueId(), viewer) + users, err = ul.findSpaceMembers(ul.mustAuthenticate(), e.ID.GetOpaqueId(), viewer) case events.SpaceDeleted: executant = e.Executant for u := range e.FinalMembers { @@ -150,22 +148,22 @@ func (ul *UserlogService) processEvent(event events.Event) { } case events.SpaceShared: executant = e.Executant - users, err = ul.resolveID(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID) + users, err = ul.resolveID(ul.mustAuthenticate(), e.GranteeUserID, e.GranteeGroupID) case events.SpaceUnshared: executant = e.Executant - users, err = ul.resolveID(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID) + users, err = ul.resolveID(ul.mustAuthenticate(), e.GranteeUserID, e.GranteeGroupID) case events.SpaceMembershipExpired: - users, err = ul.resolveID(ul.impersonate(e.SpaceOwner), e.GranteeUserID, e.GranteeGroupID) + users, err = ul.resolveID(ul.mustAuthenticate(), e.GranteeUserID, e.GranteeGroupID) // share related case events.ShareCreated: executant = e.Executant - users, err = ul.resolveID(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID) + users, err = ul.resolveID(ul.mustAuthenticate(), e.GranteeUserID, e.GranteeGroupID) case events.ShareRemoved: executant = e.Executant - users, err = ul.resolveID(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID) + users, err = ul.resolveID(ul.mustAuthenticate(), e.GranteeUserID, e.GranteeGroupID) case events.ShareExpired: - users, err = ul.resolveID(ul.impersonate(e.ShareOwner), e.GranteeUserID, e.GranteeGroupID) + users, err = ul.resolveID(ul.mustAuthenticate(), e.GranteeUserID, e.GranteeGroupID) } if err != nil { @@ -520,26 +518,6 @@ func (ul *UserlogService) resolveGroup(ctx context.Context, groupID string) ([]s return userIDs, nil } -func (ul *UserlogService) impersonate(uid *user.UserId) context.Context { - if uid == nil { - ul.log.Error().Msg("cannot impersonate nil user") - return nil - } - - u, err := getUser(context.Background(), uid, ul.gatewaySelector) - if err != nil { - ul.log.Error().Err(err).Msg("cannot get user") - return nil - } - - ctx, err := authenticate(u, ul.gatewaySelector, ul.cfg.MachineAuthAPIKey) - if err != nil { - ul.log.Error().Err(err).Str("userid", u.GetId().GetOpaqueId()).Msg("failed to impersonate user") - return nil - } - return ctx -} - func (ul *UserlogService) getUserLocale(userid string) string { resp, err := ul.valueClient.GetValueByUniqueIdentifiers( micrometadata.Set(context.Background(), middleware.AccountID, userid), @@ -560,29 +538,25 @@ func (ul *UserlogService) getUserLocale(userid string) string { } func (ul *UserlogService) getConverter(locale string) *Converter { - return NewConverter(locale, ul.gatewaySelector, ul.cfg.MachineAuthAPIKey, ul.cfg.Service.Name, ul.cfg.TranslationPath) + return NewConverter(locale, ul.gatewaySelector, ul.cfg.MachineAuthAPIKey, ul.cfg.Service.Name, ul.cfg.TranslationPath, ul.cfg.ServiceAccount.ServiceAccountID, ul.cfg.ServiceAccount.ServiceAccountSecret) } -func authenticate(usr *user.User, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], machineAuthAPIKey string) (context.Context, error) { +func (ul *UserlogService) mustAuthenticate() context.Context { + ctx, err := authenticate(ul.cfg.ServiceAccount.ServiceAccountID, ul.gatewaySelector, ul.cfg.ServiceAccount.ServiceAccountSecret) + if err != nil { + ul.log.Error().Err(err).Str("accountid", ul.cfg.ServiceAccount.ServiceAccountID).Msg("failed to impersonate service account") + return nil + } + return ctx +} + +func authenticate(serviceAccountID string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], serviceAccountSecret string) (context.Context, error) { gatewayClient, err := gatewaySelector.Next() if err != nil { return nil, err } - ctx := revactx.ContextSetUser(context.Background(), usr) - authRes, err := gatewayClient.Authenticate(ctx, &gateway.AuthenticateRequest{ - Type: "machine", - ClientId: "userid:" + usr.GetId().GetOpaqueId(), - ClientSecret: machineAuthAPIKey, - }) - if err != nil { - return nil, err - } - if authRes.GetStatus().GetCode() != rpc.Code_CODE_OK { - return nil, fmt.Errorf("error impersonating user: %s", authRes.Status.Message) - } - - return metadata.AppendToOutgoingContext(ctx, revactx.TokenHeader, authRes.Token), nil + return utils.GetServiceUserContext(serviceAccountID, gatewayClient, serviceAccountSecret) } func getSpace(ctx context.Context, spaceID string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) (*storageprovider.StorageSpace, error) { @@ -665,6 +639,7 @@ func getResource(ctx context.Context, resourceid *storageprovider.ResourceId, ga func listStorageSpaceRequest(spaceID string) *storageprovider.ListStorageSpacesRequest { return &storageprovider.ListStorageSpacesRequest{ + Opaque: utils.AppendPlainToOpaque(nil, "unrestricted", "true"), Filters: []*storageprovider.ListStorageSpacesRequest_Filter{ { Type: storageprovider.ListStorageSpacesRequest_Filter_TYPE_ID,