Repository: owncloud/ocis (go)

Issue:
hardcoded admin id
## Describe the bug

oCIS still uses a hardcoded UUID for the admin user (`ddc2004c-0977-11eb-9d3f-a793888cd0f8`) to be able to assign the admin role to that user. This needs to be reworked so that the admin user has a different uuid in every ocis instance (but still get the correct role assigne as part of the bootstrapping)


cc @micbar @C0rby 

Pull request:
Get rid of hardcoded admin user uuid
The UUID is generated by `ocs init` now and stored in the config file. To avoid that every ocis install uses the same UUID

## Related Issue
<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->
- Fixes #3524


Patch:
diff --git a/.drone.star b/.drone.star
index 5781862821e..13366709172 100644
--- a/.drone.star
+++ b/.drone.star
@@ -1660,7 +1660,7 @@ def ocisServer(storage, accounts_hash_difficulty = 4, volumes = [], depends_on =
             "name": "wait-for-ocis-server",
             "image": OC_CI_ALPINE,
             "commands": [
-                "curl -k -u admin:admin --fail --retry-connrefused --retry 10 --retry-all-errors 'https://ocis-server:9200/graph/v1.0/users/ddc2004c-0977-11eb-9d3f-a793888cd0f8'",
+                "curl -k -u admin:admin --fail --retry-connrefused --retry 10 --retry-all-errors 'https://ocis-server:9200/graph/v1.0/users/admin'",
             ],
             "depends_on": depends_on,
         }
diff --git a/.vscode/launch.json b/.vscode/launch.json
index ede7b3f5c49..4d181ce3a0b 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -15,14 +15,16 @@
                 "OCIS_LOG_LEVEL": "debug",
                 "OCIS_LOG_PRETTY": "true",
                 "OCIS_LOG_COLOR": "true",
-                // enable basic auth for dev setup so that we can use curl for testing
-                "PROXY_ENABLE_BASIC_AUTH": "true",
+                // user id of "admin", for user creation and admin role assignement
+                "OCIS_ADMIN_USER_ID": "admin-user-id",
                 // set insecure options because we don't have valid certificates in dev environments
                 "OCIS_INSECURE": "true",
                 // set some hardcoded secrets
                 "OCIS_JWT_SECRET": "some-ocis-jwt-secret",
-                "STORAGE_TRANSFER_SECRET": "some-ocis-transfer-secret",
                 "OCIS_MACHINE_AUTH_API_KEY": "some-ocis-machine-auth-api-key",
+                "STORAGE_TRANSFER_SECRET": "some-ocis-transfer-secret",
+                // enable basic auth for dev setup so that we can use curl for testing
+                "PROXY_ENABLE_BASIC_AUTH": "true",
                 // idm ldap
                 "IDM_SVC_PASSWORD": "some-ldap-idm-password",
                 "GRAPH_LDAP_BIND_PASSWORD": "some-ldap-idm-password",
diff --git a/changelog/unreleased/change-ocis-init.md b/changelog/unreleased/change-ocis-init.md
index a4a81eb3854..9c029b1f623 100644
--- a/changelog/unreleased/change-ocis-init.md
+++ b/changelog/unreleased/change-ocis-init.md
@@ -1,10 +1,12 @@
 Change: Introduce `ocis init` and remove all default secrets
 
-We've removed all default secrets. This means you can't start oCIS any longer
-without setting these via environment variable or configuration file.
+We've removed all default secrets and the hardcoded UUID of the user `admin`.
+This means you can't start oCIS any longer without setting these via
+environment variable or configuration file.
 
 In order to make this easy for you, we introduced a new command: `ocis init`.
 You can run this command before starting oCIS with `ocis server` and it will
 bootstrap you a configuration file for a secure oCIS instance.
 
 https://github.com/owncloud/ocis/pull/3551
+https://github.com/owncloud/ocis/issues/3524
diff --git a/extensions/idm/ldif/base.ldif.tmpl b/extensions/idm/ldif/base.ldif.tmpl
index e29221c62f8..6e1b9c2059b 100644
--- a/extensions/idm/ldif/base.ldif.tmpl
+++ b/extensions/idm/ldif/base.ldif.tmpl
@@ -29,7 +29,7 @@ cn: admin
 displayName: Admin
 description: An admin for this oCIS instance.
 mail: admin@example.org
-ownCloudUUID: ddc2004c-0977-11eb-9d3f-a793888cd0f8
+ownCloudUUID: {{ .ID }}
 {{ else -}}
 dn: uid={{ .Name }},ou=sysusers,o=libregraph-idm
 objectClass: account
diff --git a/extensions/idm/pkg/command/server.go b/extensions/idm/pkg/command/server.go
index 90f399dfa93..6286cd1f128 100644
--- a/extensions/idm/pkg/command/server.go
+++ b/extensions/idm/pkg/command/server.go
@@ -90,12 +90,14 @@ func bootstrap(logger log.Logger, cfg *config.Config, srvcfg server.Config) erro
 	type svcUser struct {
 		Name     string
 		Password string
+		ID       string
 	}
 
 	serviceUsers := []svcUser{
 		{
 			Name:     "admin",
 			Password: cfg.ServiceUserPasswords.OcisAdmin,
+			ID:       cfg.Commons.AdminUserID,
 		},
 		{
 			Name:     "libregraph",
diff --git a/extensions/idm/pkg/config/parser/parse.go b/extensions/idm/pkg/config/parser/parse.go
index 7d04c55ad4d..9fe4a856657 100644
--- a/extensions/idm/pkg/config/parser/parse.go
+++ b/extensions/idm/pkg/config/parser/parse.go
@@ -33,6 +33,10 @@ func ParseConfig(cfg *config.Config) error {
 }
 
 func Validate(cfg *config.Config) error {
+	if cfg.AdminUserID == "" {
+		return shared.MissingAdminUserID(cfg.Service.Name)
+	}
+
 	if cfg.ServiceUserPasswords.Idm == "" {
 		return shared.MissingServiceUserPassword(cfg.Service.Name, "IDM")
 	}
@@ -44,6 +48,7 @@ func Validate(cfg *config.Config) error {
 	if cfg.ServiceUserPasswords.Idp == "" {
 		return shared.MissingServiceUserPassword(cfg.Service.Name, "IDP")
 	}
+
 	if cfg.ServiceUserPasswords.Reva == "" {
 		return shared.MissingServiceUserPassword(cfg.Service.Name, "REVA")
 	}
diff --git a/extensions/proxy/pkg/config/config.go b/extensions/proxy/pkg/config/config.go
index f6264f413d6..032e81efa9b 100644
--- a/extensions/proxy/pkg/config/config.go
+++ b/extensions/proxy/pkg/config/config.go
@@ -53,7 +53,7 @@ type Route struct {
 	Backend string `yaml:"backend"`
 	// Service name to look up in the registry
 	Service     string `yaml:"service"`
-	ApacheVHost bool   `yaml:"apache-vhost"`
+	ApacheVHost bool   `yaml:"apache_vhost"`
 }
 
 // RouteType defines the type of a route
diff --git a/extensions/settings/pkg/config/parser/parse.go b/extensions/settings/pkg/config/parser/parse.go
index b59d8ee9fd1..5fa45bac680 100644
--- a/extensions/settings/pkg/config/parser/parse.go
+++ b/extensions/settings/pkg/config/parser/parse.go
@@ -41,5 +41,9 @@ func Validate(cfg *config.Config) error {
 		return shared.MissingMachineAuthApiKeyError(cfg.Service.Name)
 	}
 
+	if cfg.AdminUserID == "" {
+		return shared.MissingAdminUserID(cfg.Service.Name)
+	}
+
 	return nil
 }
diff --git a/extensions/settings/pkg/service/v0/service.go b/extensions/settings/pkg/service/v0/service.go
index 213151e9d74..ee3ced9b4e5 100644
--- a/extensions/settings/pkg/service/v0/service.go
+++ b/extensions/settings/pkg/service/v0/service.go
@@ -126,7 +126,7 @@ func (g Service) RegisterDefaultRoles() {
 		}
 	}
 
-	for _, req := range defaultRoleAssignments() {
+	for _, req := range g.defaultRoleAssignments() {
 		if _, err := g.manager.WriteRoleAssignment(req.AccountUuid, req.RoleId); err != nil {
 			g.logger.Error().Err(err).Msg("failed to register role assignment")
 		}
diff --git a/extensions/settings/pkg/service/v0/settings.go b/extensions/settings/pkg/service/v0/settings.go
index 404845e51c7..04b481c782f 100644
--- a/extensions/settings/pkg/service/v0/settings.go
+++ b/extensions/settings/pkg/service/v0/settings.go
@@ -532,20 +532,14 @@ func generatePermissionRequests() []*settingssvc.AddSettingToBundleRequest {
 	}
 }
 
-func defaultRoleAssignments() []*settingsmsg.UserRoleAssignment {
+func (g Service) defaultRoleAssignments() []*settingsmsg.UserRoleAssignment {
 	return []*settingsmsg.UserRoleAssignment{
 		// default admin users
 		{
-			AccountUuid: "058bff95-6708-4fe5-91e4-9ea3d377588b",
+			AccountUuid: "058bff95-6708-4fe5-91e4-9ea3d377588b", // demo user "moss"
 			RoleId:      BundleUUIDRoleAdmin,
 		}, {
-			AccountUuid: "ddc2004c-0977-11eb-9d3f-a793888cd0f8",
-			RoleId:      BundleUUIDRoleAdmin,
-		}, {
-			AccountUuid: "820ba2a1-3f54-4538-80a4-2d73007e30bf",
-			RoleId:      BundleUUIDRoleAdmin,
-		}, {
-			AccountUuid: "bc596f3c-c955-4328-80a0-60d018b4ad57",
+			AccountUuid: g.config.Commons.AdminUserID,
 			RoleId:      BundleUUIDRoleAdmin,
 		},
 		// default users with role "user"
diff --git a/extensions/settings/pkg/store/defaults/defaults.go b/extensions/settings/pkg/store/defaults/defaults.go
index 27f3d43f9f1..2b3e94435fb 100644
--- a/extensions/settings/pkg/store/defaults/defaults.go
+++ b/extensions/settings/pkg/store/defaults/defaults.go
@@ -1,6 +1,7 @@
 package defaults
 
 import (
+	"github.com/owncloud/ocis/extensions/settings/pkg/config"
 	settingsmsg "github.com/owncloud/ocis/protogen/gen/ocis/messages/settings/v0"
 )
 
@@ -496,20 +497,14 @@ var languageSetting = settingsmsg.Setting_SingleChoiceValue{
 }
 
 // DefaultRoleAssignments returns (as one might guess) the default role assignments
-func DefaultRoleAssignments() []*settingsmsg.UserRoleAssignment {
+func DefaultRoleAssignments(cfg *config.Config) []*settingsmsg.UserRoleAssignment {
 	return []*settingsmsg.UserRoleAssignment{
 		// default admin users
 		{
-			AccountUuid: "058bff95-6708-4fe5-91e4-9ea3d377588b",
+			AccountUuid: "058bff95-6708-4fe5-91e4-9ea3d377588b", // demo user "moss"
 			RoleId:      BundleUUIDRoleAdmin,
 		}, {
-			AccountUuid: "ddc2004c-0977-11eb-9d3f-a793888cd0f8",
-			RoleId:      BundleUUIDRoleAdmin,
-		}, {
-			AccountUuid: "820ba2a1-3f54-4538-80a4-2d73007e30bf",
-			RoleId:      BundleUUIDRoleAdmin,
-		}, {
-			AccountUuid: "bc596f3c-c955-4328-80a0-60d018b4ad57",
+			AccountUuid: cfg.Commons.AdminUserID,
 			RoleId:      BundleUUIDRoleAdmin,
 		},
 		// default users with role "user"
diff --git a/extensions/settings/pkg/store/metadata/assignments.go b/extensions/settings/pkg/store/metadata/assignments.go
index 88ce7e1c18b..63c7b31121e 100644
--- a/extensions/settings/pkg/store/metadata/assignments.go
+++ b/extensions/settings/pkg/store/metadata/assignments.go
@@ -14,7 +14,7 @@ import (
 // ListRoleAssignments loads and returns all role assignments matching the given assignment identifier.
 func (s *Store) ListRoleAssignments(accountUUID string) ([]*settingsmsg.UserRoleAssignment, error) {
 	if s.mdc == nil {
-		return defaultRoleAssignments(accountUUID), nil
+		return s.defaultRoleAssignments(accountUUID), nil
 	}
 	s.Init()
 	ctx := context.TODO()
@@ -92,9 +92,9 @@ func (s *Store) RemoveRoleAssignment(assignmentID string) error {
 	return fmt.Errorf("assignmentID '%s' not found", assignmentID)
 }
 
-func defaultRoleAssignments(accID string) []*settingsmsg.UserRoleAssignment {
+func (s *Store) defaultRoleAssignments(accID string) []*settingsmsg.UserRoleAssignment {
 	var assmnts []*settingsmsg.UserRoleAssignment
-	for _, r := range defaults.DefaultRoleAssignments() {
+	for _, r := range defaults.DefaultRoleAssignments(s.cfg) {
 		if r.AccountUuid == accID {
 			assmnts = append(assmnts, r)
 		}
diff --git a/extensions/settings/pkg/store/metadata/store.go b/extensions/settings/pkg/store/metadata/store.go
index c98615559a7..e9d8739dd35 100644
--- a/extensions/settings/pkg/store/metadata/store.go
+++ b/extensions/settings/pkg/store/metadata/store.go
@@ -123,7 +123,7 @@ func (s *Store) initMetadataClient(mdc MetadataClient) error {
 		}
 	}
 
-	for _, p := range defaults.DefaultRoleAssignments() {
+	for _, p := range defaults.DefaultRoleAssignments(s.cfg) {
 		accountUUID := p.AccountUuid
 		roleID := p.RoleId
 		err = mdc.MakeDirIfNotExist(ctx, accountPath(accountUUID))
diff --git a/ocis-pkg/config/config.go b/ocis-pkg/config/config.go
index 9b2a7b6e376..2d5474e4b82 100644
--- a/ocis-pkg/config/config.go
+++ b/ocis-pkg/config/config.go
@@ -70,20 +70,21 @@ type Config struct {
 	MachineAuthAPIKey string               `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY"`
 	TransferSecret    string               `yaml:"transfer_secret" env:"STORAGE_TRANSFER_SECRET"`
 	SystemUserID      string               `yaml:"system_user_id" env:"OCIS_SYSTEM_USER_ID"`
+	AdminUserID       string               `yaml:"admin_user_id" env:"OCIS_ADMIN_USER_ID"`
 	Runtime           Runtime              `yaml:"runtime"`
 
 	Accounts          *accounts.Config      `yaml:"accounts"`
-	AppProvider       *appProvider.Config   `yaml:"app-provider"`
-	AppRegistry       *appRegistry.Config   `yaml:"app-registry"`
+	AppProvider       *appProvider.Config   `yaml:"app_provider"`
+	AppRegistry       *appRegistry.Config   `yaml:"app_registry"`
 	Audit             *audit.Config         `yaml:"audit"`
-	AuthBasic         *authbasic.Config     `yaml:"auth-basic"`
-	AuthBearer        *authbearer.Config    `yaml:"auth-bearer"`
-	AuthMachine       *authmachine.Config   `yaml:"auth-machine"`
+	AuthBasic         *authbasic.Config     `yaml:"auth_basic"`
+	AuthBearer        *authbearer.Config    `yaml:"auth_bearer"`
+	AuthMachine       *authmachine.Config   `yaml:"auth_machine"`
 	Frontend          *frontend.Config      `yaml:"frontend"`
 	Gateway           *gateway.Config       `yaml:"gateway"`
 	GLAuth            *glauth.Config        `yaml:"glauth"`
 	Graph             *graph.Config         `yaml:"graph"`
-	GraphExplorer     *graphExplorer.Config `yaml:"graph-explorer"`
+	GraphExplorer     *graphExplorer.Config `yaml:"graph_explorer"`
 	Group             *group.Config         `yaml:"group"`
 	IDM               *idm.Config           `yaml:"idm"`
 	IDP               *idp.Config           `yaml:"idp"`
@@ -94,10 +95,10 @@ type Config struct {
 	Proxy             *proxy.Config         `yaml:"proxy"`
 	Settings          *settings.Config      `yaml:"settings"`
 	Sharing           *sharing.Config       `yaml:"sharing"`
-	StorageSystem     *storagesystem.Config `yaml:"storage-system"`
-	StoragePublicLink *storagepublic.Config `yaml:"storage-public"`
-	StorageShares     *storageshares.Config `yaml:"storage-shares"`
-	StorageUsers      *storageusers.Config  `yaml:"storage-users"`
+	StorageSystem     *storagesystem.Config `yaml:"storage_system"`
+	StoragePublicLink *storagepublic.Config `yaml:"storage_public"`
+	StorageShares     *storageshares.Config `yaml:"storage_shares"`
+	StorageUsers      *storageusers.Config  `yaml:"storage_users"`
 	Store             *store.Config         `yaml:"store"`
 	Thumbnails        *thumbnails.Config    `yaml:"thumbnails"`
 	User              *user.Config          `yaml:"user"`
diff --git a/ocis-pkg/config/parser/parse.go b/ocis-pkg/config/parser/parse.go
index 9df81ab3890..b5381afc563 100644
--- a/ocis-pkg/config/parser/parse.go
+++ b/ocis-pkg/config/parser/parse.go
@@ -98,6 +98,11 @@ func EnsureCommons(cfg *config.Config) {
 	if cfg.SystemUserID != "" {
 		cfg.Commons.SystemUserID = cfg.SystemUserID
 	}
+
+	// copy admin user id to the commons part if set
+	if cfg.AdminUserID != "" {
+		cfg.Commons.AdminUserID = cfg.AdminUserID
+	}
 }
 
 func Validate(cfg *config.Config) error {
@@ -117,5 +122,9 @@ func Validate(cfg *config.Config) error {
 		return shared.MissingSystemUserID("ocis")
 	}
 
+	if cfg.AdminUserID == "" {
+		return shared.MissingAdminUserID("ocis")
+	}
+
 	return nil
 }
diff --git a/ocis-pkg/shared/errors.go b/ocis-pkg/shared/errors.go
index c0815c35cc4..394407bba40 100644
--- a/ocis-pkg/shared/errors.go
+++ b/ocis-pkg/shared/errors.go
@@ -53,3 +53,11 @@ func MissingSystemUserID(service string) error {
 		"the config/corresponding environment variable).",
 		service, defaults.BaseConfigPath())
 }
+
+func MissingAdminUserID(service string) error {
+	return fmt.Errorf("The admin user ID has not been configured for %s. "+
+		"Make sure your %s config contains the proper values "+
+		"(e.g. by running ocis init or setting it manually in "+
+		"the config/corresponding environment variable).",
+		service, defaults.BaseConfigPath())
+}
diff --git a/ocis-pkg/shared/shared_types.go b/ocis-pkg/shared/shared_types.go
index a92f0f4c9af..33aa70b9e3e 100644
--- a/ocis-pkg/shared/shared_types.go
+++ b/ocis-pkg/shared/shared_types.go
@@ -45,4 +45,5 @@ type Commons struct {
 	MachineAuthAPIKey string        `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY"`
 	TransferSecret    string        `yaml:"transfer_secret,omitempty" env:"REVA_TRANSFER_SECRET"`
 	SystemUserID      string        `yaml:"system_user_id" env:"OCIS_SYSTEM_USER_ID"`
+	AdminUserID       string        `yaml:"admin_user_id" env:"OCIS_ADMIN_USER_ID"`
 }
diff --git a/ocis/pkg/init/init.go b/ocis/pkg/init/init.go
index de71b69daaa..b88acc580dc 100644
--- a/ocis/pkg/init/init.go
+++ b/ocis/pkg/init/init.go
@@ -100,17 +100,18 @@ type OcisConfig struct {
 	MachineAuthApiKey string       `yaml:"machine_auth_api_key"`
 	TransferSecret    string       `yaml:"transfer_secret"`
 	SystemUserID      string       `yaml:"system_user_id"`
+	AdminUserID       string       `yaml:"admin_user_id"`
 	Graph             GraphExtension
 	Idp               LdapBasedExtension
 	Idm               IdmExtension
 	Proxy             InsecureProxyExtension
 	Frontend          FrontendExtension
-	AuthBasic         AuthbasicExtension  `yaml:"auth-basic"`
-	AuthBearer        AuthbearerExtension `yaml:"auth-bearer"`
+	AuthBasic         AuthbasicExtension  `yaml:"auth_basic"`
+	AuthBearer        AuthbearerExtension `yaml:"auth_bearer"`
 	User              UserAndGroupExtension
 	Group             UserAndGroupExtension
-	StorageMetadata   DataProviderInsecureSettings `yaml:"storage-system"`
-	StorageUsers      DataProviderInsecureSettings `yaml:"storage-users"`
+	StorageMetadata   DataProviderInsecureSettings `yaml:"storage_system"`
+	StorageUsers      DataProviderInsecureSettings `yaml:"storage_users"`
 	Ocdav             InsecureExtension
 	Thumbnails        ThumbNailExtension
 }
@@ -162,6 +163,7 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin
 	}
 
 	systemUserID := uuid.Must(uuid.NewV4()).String()
+	adminUserID := uuid.Must(uuid.NewV4()).String()
 
 	idmServicePassword, err := generators.GenerateRandomPassword(passwordLength)
 	if err != nil {
@@ -203,6 +205,7 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin
 		MachineAuthApiKey: machineAuthApiKey,
 		TransferSecret:    revaTransferSecret,
 		SystemUserID:      systemUserID,
+		AdminUserID:       adminUserID,
 		Idm: IdmExtension{
 			ServiceUserPasswords: ServiceUserPasswordsSettings{
 				AdminPassword: ocisAdminServicePassword,


Test patch:
diff --git a/extensions/settings/pkg/store/metadata/assignments_test.go b/extensions/settings/pkg/store/metadata/assignments_test.go
index 2111366e31f..62b0b43682c 100644
--- a/extensions/settings/pkg/store/metadata/assignments_test.go
+++ b/extensions/settings/pkg/store/metadata/assignments_test.go
@@ -5,8 +5,10 @@ import (
 	"sync"
 	"testing"
 
+	"github.com/gofrs/uuid"
 	"github.com/owncloud/ocis/extensions/settings/pkg/config/defaults"
 	olog "github.com/owncloud/ocis/ocis-pkg/log"
+	"github.com/owncloud/ocis/ocis-pkg/shared"
 	settingsmsg "github.com/owncloud/ocis/protogen/gen/ocis/messages/settings/v0"
 	"github.com/stretchr/testify/require"
 )
@@ -18,7 +20,6 @@ var (
 	s = &Store{
 		Logger: logger,
 		l:      &sync.Mutex{},
-		cfg:    defaults.DefaultConfig(),
 	}
 
 	logger = olog.NewLogger(
@@ -89,6 +90,11 @@ var (
 )
 
 func init() {
+	s.cfg = defaults.DefaultConfig()
+	s.cfg.Commons = &shared.Commons{
+		AdminUserID: uuid.Must(uuid.NewV4()).String(),
+	}
+
 	_ = NewMDC(s)
 	setupRoles()
 }