Repository: owncloud/ocis
Pull request #8918 (2024-04-22 15:15:32)

[backport] [full-ci] Fix admin role
# Description

Backport of #8897 


Patch:
diff --git a/changelog/unreleased/fix-admin-role-assignment.md b/changelog/unreleased/fix-admin-role-assignment.md
new file mode 100644
index 00000000000..30e20f041c8
--- /dev/null
+++ b/changelog/unreleased/fix-admin-role-assignment.md
@@ -0,0 +1,7 @@
+Bugfix: Update the admin user role assignment to enforce the config
+
+The admin user role assigment was not updated after the first assignment. We now read the assigned role during init and update the admin user ID accordingly if the role is not assigned.
+This is especially needed when the OCIS_ADMIN_USER_ID is set after the autoprovisioning of the admin user when it originates from an external Identity Provider.
+
+https://github.com/owncloud/ocis/pull/8918
+https://github.com/owncloud/ocis/pull/8897
diff --git a/services/settings/pkg/store/metadata/store.go b/services/settings/pkg/store/metadata/store.go
index 68946a3a80d..3985e84a1d4 100644
--- a/services/settings/pkg/store/metadata/store.go
+++ b/services/settings/pkg/store/metadata/store.go
@@ -7,6 +7,7 @@ import (
 	"log"
 	"sync"
 
+	"github.com/cs3org/reva/v2/pkg/errtypes"
 	"github.com/cs3org/reva/v2/pkg/storage/utils/metadata"
 	"github.com/gofrs/uuid"
 	olog "github.com/owncloud/ocis/v2/ocis-pkg/log"
@@ -139,10 +140,20 @@ func (s *Store) initMetadataClient(mdc MetadataClient) error {
 		if err != nil {
 			return err
 		}
-		if len(assIDs) > 0 {
+
+		adminUserID := accountUUID == s.cfg.AdminUserID
+		if len(assIDs) > 0 && !adminUserID {
 			// There is already a role assignment for this ID, skip to the next
 			continue
 		}
+		// for the adminUserID we need to check if the user has the admin role every time
+		if adminUserID {
+			err = s.userMustHaveAdminRole(accountUUID, assIDs, mdc)
+			if err != nil {
+				return err
+			}
+			continue
+		}
 
 		ass := &settingsmsg.UserRoleAssignment{
 			Id:          uuid.Must(uuid.NewV4()).String(),
@@ -164,6 +175,64 @@ func (s *Store) initMetadataClient(mdc MetadataClient) error {
 	return nil
 }
 
+func (s *Store) userMustHaveAdminRole(accountUUID string, assIDs []string, mdc MetadataClient) error {
+	ctx := context.TODO()
+	var hasAdminRole bool
+
+	// load the assignments from the store and check if the admin role is already assigned
+	for _, assID := range assIDs {
+		b, err := mdc.SimpleDownload(ctx, assignmentPath(accountUUID, assID))
+		switch err.(type) {
+		case nil:
+			// continue
+		case errtypes.NotFound:
+			continue
+		default:
+			return err
+		}
+
+		a := &settingsmsg.UserRoleAssignment{}
+		err = json.Unmarshal(b, a)
+		if err != nil {
+			return err
+		}
+
+		if a.RoleId == defaults.BundleUUIDRoleAdmin {
+			hasAdminRole = true
+		}
+	}
+
+	// delete old role assignment and set admin role
+	if !hasAdminRole {
+		err := mdc.Delete(ctx, accountPath(accountUUID))
+		switch err.(type) {
+		case nil:
+			// continue
+		case errtypes.NotFound:
+			// already gone, continue
+		default:
+			return err
+		}
+
+		err = mdc.MakeDirIfNotExist(ctx, accountPath(accountUUID))
+		if err != nil {
+			return err
+		}
+
+		ass := &settingsmsg.UserRoleAssignment{
+			Id:          uuid.Must(uuid.NewV4()).String(),
+			AccountUuid: accountUUID,
+			RoleId:      defaults.BundleUUIDRoleAdmin,
+		}
+		b, err := json.Marshal(ass)
+		if err != nil {
+			return err
+		}
+		return mdc.SimpleUpload(ctx, assignmentPath(accountUUID, ass.Id), b)
+	}
+	return nil
+}
+
 func init() {
 	settings.Registry[managerName] = New
 }


Test patch:
diff --git a/services/settings/pkg/store/metadata/store_test.go b/services/settings/pkg/store/metadata/store_test.go
index 0859d38b524..e204618f27a 100644
--- a/services/settings/pkg/store/metadata/store_test.go
+++ b/services/settings/pkg/store/metadata/store_test.go
@@ -2,10 +2,15 @@ package store
 
 import (
 	"context"
+	"encoding/json"
 	"strings"
+	"testing"
 
 	"github.com/cs3org/reva/v2/pkg/errtypes"
+	. "github.com/onsi/gomega"
+	settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
 	"github.com/owncloud/ocis/v2/services/settings/pkg/config/defaults"
+	rdefaults "github.com/owncloud/ocis/v2/services/settings/pkg/store/defaults"
 )
 
 const (
@@ -112,3 +117,74 @@ func (m *MockedMetadataClient) IDExists(id string) bool {
 func (m *MockedMetadataClient) IDHasContent(id string, content []byte) bool {
 	return string(m.data[id]) == string(content)
 }
+
+// TestAdminUserIDInit test the happy path during initialization
+func TestAdminUserIDInit(t *testing.T) {
+	RegisterTestingT(t)
+	s := &Store{
+		cfg: defaults.DefaultConfig(),
+	}
+	s.cfg.Bundles = rdefaults.GenerateBundlesDefaultRoles()
+	s.cfg.AdminUserID = "admin"
+
+	// the first assignment is always happening during the initialisation of the metadata client
+	err := NewMDC(s)
+	Expect(err).To(BeNil())
+
+	assID, err := s.mdc.ReadDir(context.TODO(), accountPath(s.cfg.AdminUserID))
+	Expect(len(assID)).To(Equal(1))
+	ass, err := s.mdc.SimpleDownload(context.TODO(), assignmentPath(s.cfg.AdminUserID, assID[0]))
+	Expect(ass).ToNot(BeNil())
+
+	assignment := &settingsmsg.UserRoleAssignment{}
+	err = json.Unmarshal(ass, assignment)
+	Expect(err).To(BeNil())
+	Expect(assignment.RoleId).To(Equal(rdefaults.BundleUUIDRoleAdmin))
+}
+
+// TestAdminUserIDUpdate test the update on following initialisations
+func TestAdminUserIDUpdate(t *testing.T) {
+	RegisterTestingT(t)
+	s := &Store{
+		cfg: defaults.DefaultConfig(),
+	}
+	s.cfg.Bundles = rdefaults.GenerateBundlesDefaultRoles()
+	s.cfg.AdminUserID = "admin"
+
+	// the first assignment is always happening during the initialisation of the metadata client
+	err := NewMDC(s)
+	Expect(err).To(BeNil())
+
+	// read assignment
+	assID, err := s.mdc.ReadDir(context.TODO(), accountPath(s.cfg.AdminUserID))
+	Expect(len(assID)).To(Equal(1))
+
+	// set assignment to user role
+	userRoleAssignment := &settingsmsg.UserRoleAssignment{
+		AccountUuid: s.cfg.AdminUserID,
+		RoleId:      rdefaults.BundleUUIDRoleUser,
+	}
+	b, err := json.Marshal(userRoleAssignment)
+	err = s.mdc.Delete(context.TODO(), assignmentPath(s.cfg.AdminUserID, assID[0]))
+	Expect(err).To(BeNil())
+	err = s.mdc.SimpleUpload(context.TODO(), assignmentPath(s.cfg.AdminUserID, assID[0]), b)
+	Expect(err).To(BeNil())
+
+	// this happens on every Read / Write on the store
+	// the actual init is only done if the metadata client has not been initialized before
+	// this normally needs a restart of the service
+	err = s.initMetadataClient(s.mdc)
+	Expect(err).To(BeNil())
+
+	// read assignment id, changes every time the assignment is written
+	assID, err = s.mdc.ReadDir(context.TODO(), accountPath(s.cfg.AdminUserID))
+	Expect(len(assID)).To(Equal(1))
+
+	// check if the assignment is the admin role again
+	ass, err := s.mdc.SimpleDownload(context.TODO(), assignmentPath(s.cfg.AdminUserID, assID[0]))
+	Expect(ass).ToNot(BeNil())
+	assignment := &settingsmsg.UserRoleAssignment{}
+	err = json.Unmarshal(ass, assignment)
+	Expect(err).To(BeNil())
+	Expect(assignment.RoleId).To(Equal(rdefaults.BundleUUIDRoleAdmin))
+}