graph: add appRoleAssignments and minimal application resource (#5318)

* bump libregraph-go lib

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* add appRoleAssignment stubs

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* add get application stub

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* fetch appRoles for application from settings service

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* initial list appRoleAssignments implementation

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* initial create appRoleAssignment implementation, extract assignmentToAppRoleAssignment, configurable app id and displayname

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* initial delete appRoleAssignment implementation, changed error handling and logging

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* initial expand appRoleAssignment on users

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* test user expand appRoleAssignment

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* test appRoleAssignment

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* fix education test by actually using the mocked roleManager

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* test getapplication

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* list assignments

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* use common not exists error handling

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* default to just 'ownCloud Infinite Scale' as application name

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* fix store_test

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* roll application uuid on init

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* fix tests

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* extract method

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Apply suggestions from code review

Co-authored-by: Michael Barz <mbarz@owncloud.com>

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Michael Barz <mbarz@owncloud.com>
This commit is contained in:
Jörn Friedrich Dreyer
2023-01-12 16:09:34 +01:00
committed by GitHub
co-authored by Michael Barz
parent 1b6c269de5
commit 078698fdf4
29 changed files with 1085 additions and 115 deletions
+1 -1
View File
@@ -79,7 +79,7 @@ func (g Service) CheckPermission(ctx context.Context, req *permissions.CheckPerm
permission, err := g.manager.ReadPermissionByName(req.Permission, roleIDs)
if err != nil {
if !errors.Is(err, settings.ErrPermissionNotFound) {
if !errors.Is(err, settings.ErrNotFound) {
return &permissions.CheckPermissionResponse{
Status: status.NewInternal(ctx, err.Error()),
}, nil
@@ -12,7 +12,12 @@ var (
Registry = map[string]RegisterFunc{}
// ErrPermissionNotFound defines a new error for when a permission was not found
//
// Deprecated use the more generic ErrNotFound
ErrPermissionNotFound = errors.New("permission not found")
// ErrNotFound is the error to use when a resource was not found.
ErrNotFound = errors.New("not found")
)
// RegisterFunc stores store constructors
@@ -1,6 +1,8 @@
package errortypes
// BundleNotFound is the error to use when a bundle is not found.
//
// Deprecated: use the genreric services/settings/pkg/settings.NotFound error
type BundleNotFound string
func (e BundleNotFound) Error() string { return "error: bundle not found: " + string(e) }
@@ -2,6 +2,7 @@
package store
import (
"errors"
"fmt"
"os"
"path/filepath"
@@ -9,7 +10,7 @@ import (
"github.com/gofrs/uuid"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
"github.com/owncloud/ocis/v2/services/settings/pkg/store/errortypes"
"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
)
var m = &sync.RWMutex{}
@@ -111,7 +112,7 @@ func (s Store) WriteBundle(record *settingsmsg.Bundle) (*settingsmsg.Bundle, err
func (s Store) AddSettingToBundle(bundleID string, setting *settingsmsg.Setting) (*settingsmsg.Setting, error) {
bundle, err := s.ReadBundle(bundleID)
if err != nil {
if _, notFound := err.(errortypes.BundleNotFound); !notFound {
if !errors.Is(err, settings.ErrNotFound) {
return nil, err
}
bundle = new(settingsmsg.Bundle)
+4 -3
View File
@@ -1,10 +1,11 @@
package store
import (
"fmt"
"io"
"os"
"github.com/owncloud/ocis/v2/services/settings/pkg/store/errortypes"
"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
@@ -13,7 +14,7 @@ import (
func (s Store) parseRecordFromFile(record proto.Message, filePath string) error {
_, err := os.Stat(filePath)
if err != nil {
return errortypes.BundleNotFound(err.Error())
return fmt.Errorf("%q: %w", filePath, settings.ErrNotFound)
}
file, err := os.Open(filePath)
@@ -28,7 +29,7 @@ func (s Store) parseRecordFromFile(record proto.Message, filePath string) error
}
if len(b) == 0 {
return errortypes.BundleNotFound(filePath)
return fmt.Errorf("%q: %w", filePath, settings.ErrNotFound)
}
if err := protojson.Unmarshal(b, record); err != nil {
@@ -55,7 +55,7 @@ func (s Store) ReadPermissionByName(name string, roleIDs []string) (*settingsmsg
}
}
}
return nil, settings.ErrPermissionNotFound
return nil, settings.ErrNotFound
}
// extractPermissionsByResource collects all permissions from the provided role that match the requested resource
@@ -6,8 +6,10 @@ import (
"encoding/json"
"fmt"
"github.com/cs3org/reva/v2/pkg/errtypes"
"github.com/gofrs/uuid"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
)
// ListRoleAssignments loads and returns all role assignments matching the given assignment identifier.
@@ -15,14 +17,24 @@ func (s *Store) ListRoleAssignments(accountUUID string) ([]*settingsmsg.UserRole
s.Init()
ctx := context.TODO()
assIDs, err := s.mdc.ReadDir(ctx, accountPath(accountUUID))
if err != nil {
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))
if err != nil {
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
continue
default:
return nil, err
}
@@ -42,10 +54,17 @@ func (s *Store) WriteRoleAssignment(accountUUID, roleID string) (*settingsmsg.Us
s.Init()
ctx := context.TODO()
// as per https://github.com/owncloud/product/issues/103 "Each user can have exactly one role"
_ = s.mdc.Delete(ctx, accountPath(accountUUID))
// TODO: How to differentiate between 'not found' and other errors?
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))
err = s.mdc.MakeDirIfNotExist(ctx, accountPath(accountUUID))
if err != nil {
return nil, err
}
@@ -67,7 +86,12 @@ func (s *Store) RemoveRoleAssignment(assignmentID string) error {
s.Init()
ctx := context.TODO()
accounts, err := s.mdc.ReadDir(ctx, accountsFolderLocation)
if err != nil {
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return fmt.Errorf("assignmentID '%s' %w", assignmentID, settings.ErrNotFound)
default:
return err
}
@@ -81,11 +105,13 @@ func (s *Store) RemoveRoleAssignment(assignmentID string) error {
for _, assID := range assIDs {
if assID == assignmentID {
return s.mdc.Delete(ctx, assignmentPath(accID, assID))
// 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' not found", assignmentID)
return fmt.Errorf("assignmentID '%s' %w", assignmentID, settings.ErrNotFound)
}
func accountPath(accountUUID string) string {
@@ -7,8 +7,10 @@ import (
"errors"
"fmt"
"github.com/cs3org/reva/v2/pkg/errtypes"
"github.com/gofrs/uuid"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
)
// ListBundles returns all bundles in the dataPath folder that match the given type.
@@ -18,7 +20,12 @@ func (s *Store) ListBundles(bundleType settingsmsg.Bundle_Type, bundleIDs []stri
if len(bundleIDs) == 0 {
bIDs, err := s.mdc.ReadDir(ctx, bundleFolderLocation)
if err != nil {
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return make([]*settingsmsg.Bundle, 0), nil
default:
return nil, err
}
@@ -27,7 +34,12 @@ func (s *Store) ListBundles(bundleType settingsmsg.Bundle_Type, bundleIDs []stri
var bundles []*settingsmsg.Bundle
for _, id := range bundleIDs {
b, err := s.mdc.SimpleDownload(ctx, bundlePath(id))
if err != nil {
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
continue
default:
return nil, err
}
@@ -50,7 +62,12 @@ func (s *Store) ReadBundle(bundleID string) (*settingsmsg.Bundle, error) {
s.Init()
ctx := context.TODO()
b, err := s.mdc.SimpleDownload(ctx, bundlePath(bundleID))
if err != nil {
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return nil, fmt.Errorf("bundleID '%s' %w", bundleID, settings.ErrNotFound)
default:
return nil, err
}
@@ -64,7 +81,12 @@ func (s *Store) ReadSetting(settingID string) (*settingsmsg.Setting, error) {
ctx := context.TODO()
ids, err := s.mdc.ReadDir(ctx, bundleFolderLocation)
if err != nil {
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return nil, fmt.Errorf("settingID '%s' %w", settingID, settings.ErrNotFound)
default:
return nil, err
}
@@ -72,6 +94,9 @@ func (s *Store) ReadSetting(settingID string) (*settingsmsg.Setting, error) {
for _, id := range ids {
b, err := s.ReadBundle(id)
if err != nil {
if errors.Is(err, settings.ErrNotFound) {
continue
}
return nil, err
}
@@ -82,7 +107,7 @@ func (s *Store) ReadSetting(settingID string) (*settingsmsg.Setting, error) {
}
}
return nil, fmt.Errorf("setting '%s' not found", settingID)
return nil, fmt.Errorf("settingID '%s' %w", settingID, settings.ErrNotFound)
}
// WriteBundle sends the givens record to the metadataclient. returns `record` for legacy reasons
@@ -102,7 +127,9 @@ func (s *Store) AddSettingToBundle(bundleID string, setting *settingsmsg.Setting
s.Init()
b, err := s.ReadBundle(bundleID)
if err != nil {
// TODO: How to differentiate 'not found'?
if !errors.Is(err, settings.ErrNotFound) {
return nil, err
}
b = new(settingsmsg.Bundle)
b.Id = bundleID
b.Type = settingsmsg.Bundle_TYPE_DEFAULT
@@ -55,7 +55,7 @@ func (s *Store) ReadPermissionByName(name string, roleIDs []string) (*settingsms
}
}
}
return nil, settings.ErrPermissionNotFound
return nil, settings.ErrNotFound
}
// extractPermissionsByResource collects all permissions from the provided role that match the requested resource
@@ -4,6 +4,7 @@ import (
"context"
"strings"
"github.com/cs3org/reva/v2/pkg/errtypes"
"github.com/owncloud/ocis/v2/services/settings/pkg/config/defaults"
)
@@ -53,9 +54,12 @@ func NewMDC(s *Store) error {
return s.initMetadataClient(mdc)
}
// SimpleDownload returns nil if not found
// SimpleDownload returns errtypes.NotFound if not found
func (m *MockedMetadataClient) SimpleDownload(_ context.Context, id string) ([]byte, error) {
return m.data[id], nil
if data, ok := m.data[id]; ok {
return data, nil
}
return nil, errtypes.NotFound("not found")
}
// SimpleUpload can't error
+20 -3
View File
@@ -7,8 +7,10 @@ import (
"errors"
"fmt"
"github.com/cs3org/reva/v2/pkg/errtypes"
"github.com/gofrs/uuid"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
)
// ListValues reads all values that match the given bundleId and accountUUID.
@@ -20,7 +22,12 @@ func (s *Store) ListValues(bundleID, accountUUID string) ([]*settingsmsg.Value,
ctx := context.TODO()
vIDs, err := s.mdc.ReadDir(ctx, valuesFolderLocation)
if err != nil {
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return make([]*settingsmsg.Value, 0), nil
default:
return nil, err
}
@@ -28,7 +35,12 @@ func (s *Store) ListValues(bundleID, accountUUID string) ([]*settingsmsg.Value,
var values []*settingsmsg.Value
for _, vid := range vIDs {
b, err := s.mdc.SimpleDownload(ctx, valuePath(vid))
if err != nil {
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
continue
default:
return nil, err
}
@@ -61,7 +73,12 @@ func (s *Store) ReadValue(valueID string) (*settingsmsg.Value, error) {
ctx := context.TODO()
b, err := s.mdc.SimpleDownload(ctx, valuePath(valueID))
if err != nil {
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return nil, fmt.Errorf("valueID '%s' %w", valueID, settings.ErrNotFound)
default:
return nil, err
}
val := &settingsmsg.Value{}