// Package store implements the go-micro store interface package store import ( "context" "encoding/json" "fmt" "github.com/google/uuid" settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0" "github.com/qsfera/server/services/settings/pkg/settings" "github.com/qsfera/server/services/settings/pkg/store/defaults" "github.com/opencloud-eu/reva/v2/pkg/errtypes" ) // ListRoleAssignments loads and returns all role assignments matching the given assignment identifier. func (s *Store) ListRoleAssignments(accountUUID string) ([]*settingsmsg.UserRoleAssignment, error) { // shortcut for service accounts for _, serviceAccountID := range s.cfg.ServiceAccountIDs { if accountUUID == serviceAccountID { return []*settingsmsg.UserRoleAssignment{ { Id: uuid.NewString(), // should we hardcode this id too? AccountUuid: accountUUID, RoleId: defaults.BundleUUIDServiceAccount, }, }, nil } } s.Init() ctx := context.TODO() assIDs, err := s.mdc.ReadDir(ctx, accountPath(accountUUID)) switch err.(type) { case nil: // continue case errtypes.NotFound: return make([]*settingsmsg.UserRoleAssignment, 0), nil default: return nil, err } ass := make([]*settingsmsg.UserRoleAssignment, 0, len(assIDs)) for _, assID := range assIDs { b, err := s.mdc.SimpleDownload(ctx, assignmentPath(accountUUID, assID)) switch err.(type) { case nil: // continue case errtypes.NotFound: continue default: return nil, err } a := &settingsmsg.UserRoleAssignment{} err = json.Unmarshal(b, a) if err != nil { return nil, err } ass = append(ass, a) } return ass, nil } // ListRoleAssignmentsByRole returns all role assignmentes matching the give roleID func (s *Store) ListRoleAssignmentsByRole(roleID string) ([]*settingsmsg.UserRoleAssignment, error) { s.Init() ctx := context.TODO() accountIDs, err := s.mdc.ReadDir(ctx, accountsFolderLocation) switch err.(type) { case nil: // continue case errtypes.NotFound: return make([]*settingsmsg.UserRoleAssignment, 0), nil default: return nil, err } assignments := make([]*settingsmsg.UserRoleAssignment, 0, len(accountIDs)) // This is very inefficient, with the current layout we need to iterated through all // account folders and read each assignment file in there to check if that contains // the give role ID. for _, account := range accountIDs { assignmentIDs, err := s.mdc.ReadDir(ctx, accountPath(account)) switch err.(type) { case nil: // continue case errtypes.NotFound: return make([]*settingsmsg.UserRoleAssignment, 0), nil default: return nil, err } for _, assignmentID := range assignmentIDs { b, err := s.mdc.SimpleDownload(ctx, assignmentPath(account, assignmentID)) switch err.(type) { case nil: // continue case errtypes.NotFound: continue default: return nil, err } a := &settingsmsg.UserRoleAssignment{} err = json.Unmarshal(b, a) if err != nil { return nil, err } if a.GetRoleId() == roleID { assignments = append(assignments, a) } } } return assignments, nil } // WriteRoleAssignment appends the given role assignment to the existing assignments of the respective account. func (s *Store) WriteRoleAssignment(accountUUID, roleID string) (*settingsmsg.UserRoleAssignment, error) { s.Init() ctx := context.TODO() // as per https://github.com/owncloud/product/issues/103 "Each user can have exactly one role" err := s.mdc.Delete(ctx, accountPath(accountUUID)) switch err.(type) { case nil: // continue case errtypes.NotFound: // already gone, continue default: return nil, err } err = s.mdc.MakeDirIfNotExist(ctx, accountPath(accountUUID)) if err != nil { return nil, err } ass := &settingsmsg.UserRoleAssignment{ Id: uuid.NewString(), AccountUuid: accountUUID, RoleId: roleID, } b, err := json.Marshal(ass) if err != nil { return nil, err } return ass, s.mdc.SimpleUpload(ctx, assignmentPath(accountUUID, ass.Id), b) } // RemoveRoleAssignment deletes the given role assignment from the existing assignments of the respective account. func (s *Store) RemoveRoleAssignment(assignmentID string) error { s.Init() ctx := context.TODO() accounts, err := s.mdc.ReadDir(ctx, accountsFolderLocation) switch err.(type) { case nil: // continue case errtypes.NotFound: return fmt.Errorf("assignmentID '%s' %w", assignmentID, settings.ErrNotFound) default: return err } // TODO: use indexer to avoid spamming Metadata service for _, accID := range accounts { assIDs, err := s.mdc.ReadDir(ctx, accountPath(accID)) if err != nil { // TODO: error? continue } for _, assID := range assIDs { if assID == assignmentID { // as per https://github.com/owncloud/product/issues/103 "Each user can have exactly one role" // we also have to delete the cached dir listing return s.mdc.Delete(ctx, accountPath(accID)) } } } return fmt.Errorf("assignmentID '%s' %w", assignmentID, settings.ErrNotFound) } func accountPath(accountUUID string) string { return fmt.Sprintf("%s/%s", accountsFolderLocation, accountUUID) } func assignmentPath(accountUUID string, assignmentID string) string { return fmt.Sprintf("%s/%s/%s", accountsFolderLocation, accountUUID, assignmentID) }