use service accounts for userlog
Signed-off-by: jkoberg <jkoberg@owncloud.com>
This commit is contained in:
@@ -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."`
|
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:"-"`
|
Context context.Context `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,3 +77,9 @@ type HTTP struct {
|
|||||||
type TokenManager 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."`
|
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."`
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ func DefaultConfig() *config.Config {
|
|||||||
AllowCredentials: true,
|
AllowCredentials: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ServiceAccount: config.ServiceAccount{
|
||||||
|
ServiceAccountID: "service-user-id",
|
||||||
|
ServiceAccountSecret: "secret-string",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,31 +52,32 @@ type OC10Notification struct {
|
|||||||
|
|
||||||
// Converter is responsible for converting eventhistory events to OC10Notifications
|
// Converter is responsible for converting eventhistory events to OC10Notifications
|
||||||
type Converter struct {
|
type Converter struct {
|
||||||
locale string
|
locale string
|
||||||
gatewaySelector pool.Selectable[gateway.GatewayAPIClient]
|
gatewaySelector pool.Selectable[gateway.GatewayAPIClient]
|
||||||
machineAuthAPIKey string
|
serviceAccountID string
|
||||||
serviceName string
|
serviceAccountSecret string
|
||||||
translationPath string
|
serviceName string
|
||||||
|
translationPath string
|
||||||
|
|
||||||
// cached within one request not to query other service too much
|
// cached within one request not to query other service too much
|
||||||
spaces map[string]*storageprovider.StorageSpace
|
spaces map[string]*storageprovider.StorageSpace
|
||||||
users map[string]*user.User
|
users map[string]*user.User
|
||||||
resources map[string]*storageprovider.ResourceInfo
|
resources map[string]*storageprovider.ResourceInfo
|
||||||
contexts map[string]context.Context
|
serviceAccountContext context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConverter returns a new Converter
|
// 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{
|
return &Converter{
|
||||||
locale: loc,
|
locale: loc,
|
||||||
gatewaySelector: gatewaySelector,
|
gatewaySelector: gatewaySelector,
|
||||||
machineAuthAPIKey: machineAuthAPIKey,
|
serviceAccountID: serviceAccountID,
|
||||||
serviceName: name,
|
serviceAccountSecret: serviceAccountSecret,
|
||||||
translationPath: translationPath,
|
serviceName: name,
|
||||||
spaces: make(map[string]*storageprovider.StorageSpace),
|
translationPath: translationPath,
|
||||||
users: make(map[string]*user.User),
|
spaces: make(map[string]*storageprovider.StorageSpace),
|
||||||
resources: make(map[string]*storageprovider.ResourceInfo),
|
users: make(map[string]*user.User),
|
||||||
contexts: make(map[string]context.Context),
|
resources: make(map[string]*storageprovider.ResourceInfo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +172,7 @@ func (c *Converter) spaceMessage(eventid string, nt NotificationTemplate, execut
|
|||||||
return OC10Notification{}, err
|
return OC10Notification{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, err := c.authenticate(usr)
|
ctx, err := c.authenticate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return OC10Notification{}, err
|
return OC10Notification{}, err
|
||||||
}
|
}
|
||||||
@@ -210,7 +211,7 @@ func (c *Converter) shareMessage(eventid string, nt NotificationTemplate, execut
|
|||||||
return OC10Notification{}, err
|
return OC10Notification{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, err := c.authenticate(usr)
|
ctx, err := c.authenticate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return OC10Notification{}, err
|
return OC10Notification{}, err
|
||||||
}
|
}
|
||||||
@@ -327,13 +328,18 @@ func (c *Converter) deprovisionMessage(nt NotificationTemplate, deproDate string
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Converter) authenticate(usr *user.User) (context.Context, error) {
|
func (c *Converter) authenticate() (context.Context, error) {
|
||||||
if ctx, ok := c.contexts[usr.GetId().GetOpaqueId()]; ok {
|
if c.serviceAccountContext != nil {
|
||||||
return ctx, 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 {
|
if err == nil {
|
||||||
c.contexts[usr.GetId().GetOpaqueId()] = ctx
|
c.serviceAccountContext = ctx
|
||||||
}
|
}
|
||||||
return ctx, err
|
return ctx, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||||
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/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/events"
|
||||||
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
|
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
|
||||||
"github.com/cs3org/reva/v2/pkg/utils"
|
"github.com/cs3org/reva/v2/pkg/utils"
|
||||||
@@ -29,7 +28,6 @@ import (
|
|||||||
micrometadata "go-micro.dev/v4/metadata"
|
micrometadata "go-micro.dev/v4/metadata"
|
||||||
"go-micro.dev/v4/store"
|
"go-micro.dev/v4/store"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
"google.golang.org/grpc/metadata"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserlogService is the service responsible for user activities
|
// 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())
|
users = append(users, e.ExecutingUser.GetId().GetOpaqueId())
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// space related // TODO: how to find spaceadmins?
|
// space related // TODO: how to find spaceadmins?
|
||||||
case events.SpaceDisabled:
|
case events.SpaceDisabled:
|
||||||
executant = e.Executant
|
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:
|
case events.SpaceDeleted:
|
||||||
executant = e.Executant
|
executant = e.Executant
|
||||||
for u := range e.FinalMembers {
|
for u := range e.FinalMembers {
|
||||||
@@ -150,22 +148,22 @@ func (ul *UserlogService) processEvent(event events.Event) {
|
|||||||
}
|
}
|
||||||
case events.SpaceShared:
|
case events.SpaceShared:
|
||||||
executant = e.Executant
|
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:
|
case events.SpaceUnshared:
|
||||||
executant = e.Executant
|
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:
|
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
|
// share related
|
||||||
case events.ShareCreated:
|
case events.ShareCreated:
|
||||||
executant = e.Executant
|
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:
|
case events.ShareRemoved:
|
||||||
executant = e.Executant
|
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:
|
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 {
|
if err != nil {
|
||||||
@@ -520,26 +518,6 @@ func (ul *UserlogService) resolveGroup(ctx context.Context, groupID string) ([]s
|
|||||||
return userIDs, nil
|
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 {
|
func (ul *UserlogService) getUserLocale(userid string) string {
|
||||||
resp, err := ul.valueClient.GetValueByUniqueIdentifiers(
|
resp, err := ul.valueClient.GetValueByUniqueIdentifiers(
|
||||||
micrometadata.Set(context.Background(), middleware.AccountID, userid),
|
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 {
|
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()
|
gatewayClient, err := gatewaySelector.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := revactx.ContextSetUser(context.Background(), usr)
|
return utils.GetServiceUserContext(serviceAccountID, gatewayClient, serviceAccountSecret)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSpace(ctx context.Context, spaceID string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) (*storageprovider.StorageSpace, error) {
|
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 {
|
func listStorageSpaceRequest(spaceID string) *storageprovider.ListStorageSpacesRequest {
|
||||||
return &storageprovider.ListStorageSpacesRequest{
|
return &storageprovider.ListStorageSpacesRequest{
|
||||||
|
Opaque: utils.AppendPlainToOpaque(nil, "unrestricted", "true"),
|
||||||
Filters: []*storageprovider.ListStorageSpacesRequest_Filter{
|
Filters: []*storageprovider.ListStorageSpacesRequest_Filter{
|
||||||
{
|
{
|
||||||
Type: storageprovider.ListStorageSpacesRequest_Filter_TYPE_ID,
|
Type: storageprovider.ListStorageSpacesRequest_Filter_TYPE_ID,
|
||||||
|
|||||||
Reference in New Issue
Block a user