feat: Add a global flag to enable multi-tenant support

When enabled the graph service refuses to start with the "ldap" backend.
Additional enforcements will follow in subsequent commits.
This commit is contained in:
Ralf Haferkamp
2025-09-24 15:46:54 +02:00
committed by Ralf Haferkamp
parent bbf30b5802
commit 7e86d85d62
5 changed files with 96 additions and 14 deletions
+1
View File
@@ -318,6 +318,7 @@ config = {
"USE_PREPARED_LDAP_USERS": True,
},
"extraServerEnvironment": {
"OC_MULTI_TENANT_ENABLED": True,
"OC_LDAP_USER_SCHEMA_TENANT_ID": "departmentNumber",
"OC_LDAP_URI": "ldaps://ldap-server:1636",
"OC_LDAP_INSECURE": True,
+15 -14
View File
@@ -69,20 +69,21 @@ type Cache struct {
// Commons holds configuration that are common to all extensions. Each extension can then decide whether
// to overwrite its values.
type Commons struct {
Log *Log `yaml:"log"`
Tracing *Tracing `yaml:"tracing"`
Cache *Cache `yaml:"cache"`
GRPCClientTLS *GRPCClientTLS `yaml:"grpc_client_tls"`
GRPCServiceTLS *GRPCServiceTLS `yaml:"grpc_service_tls"`
HTTPServiceTLS HTTPServiceTLS `yaml:"http_service_tls"`
OpenCloudURL string `yaml:"opencloud_url" env:"OC_URL" desc:"URL, where OpenCloud is reachable for users." introductionVersion:"1.0.0"`
TokenManager *TokenManager `mask:"struct" yaml:"token_manager"`
Reva *Reva `yaml:"reva"`
MachineAuthAPIKey string `mask:"password" yaml:"machine_auth_api_key" env:"OC_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary for the access to resources from other services." introductionVersion:"1.0.0"`
TransferSecret string `mask:"password" yaml:"transfer_secret,omitempty" env:"REVA_TRANSFER_SECRET" desc:"The secret used for signing the requests towards the data gateway for up- and downloads." introductionVersion:"1.0.0"`
SystemUserID string `yaml:"system_user_id" env:"OC_SYSTEM_USER_ID" desc:"ID of the OpenCloud storage-system system user. Admins need to set the ID for the storage-system system user in this config option which is then used to reference the user. Any reasonable long string is possible, preferably this would be an UUIDv4 format." introductionVersion:"1.0.0"`
SystemUserAPIKey string `mask:"password" yaml:"system_user_api_key" env:"SYSTEM_USER_API_KEY" desc:"API key for all system users." introductionVersion:"1.0.0"`
AdminUserID string `yaml:"admin_user_id" env:"OC_ADMIN_USER_ID" desc:"ID of a user, that should receive admin privileges. Consider that the UUID can be encoded in some LDAP deployment configurations like in .ldif files. These need to be decoded beforehand." introductionVersion:"1.0.0"`
Log *Log `yaml:"log"`
Tracing *Tracing `yaml:"tracing"`
Cache *Cache `yaml:"cache"`
GRPCClientTLS *GRPCClientTLS `yaml:"grpc_client_tls"`
GRPCServiceTLS *GRPCServiceTLS `yaml:"grpc_service_tls"`
HTTPServiceTLS HTTPServiceTLS `yaml:"http_service_tls"`
OpenCloudURL string `yaml:"opencloud_url" env:"OC_URL" desc:"URL, where OpenCloud is reachable for users." introductionVersion:"1.0.0"`
TokenManager *TokenManager `mask:"struct" yaml:"token_manager"`
Reva *Reva `yaml:"reva"`
MachineAuthAPIKey string `mask:"password" yaml:"machine_auth_api_key" env:"OC_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary for the access to resources from other services." introductionVersion:"1.0.0"`
TransferSecret string `mask:"password" yaml:"transfer_secret,omitempty" env:"REVA_TRANSFER_SECRET" desc:"The secret used for signing the requests towards the data gateway for up- and downloads." introductionVersion:"1.0.0"`
SystemUserID string `yaml:"system_user_id" env:"OC_SYSTEM_USER_ID" desc:"ID of the OpenCloud storage-system system user. Admins need to set the ID for the storage-system system user in this config option which is then used to reference the user. Any reasonable long string is possible, preferably this would be an UUIDv4 format." introductionVersion:"1.0.0"`
SystemUserAPIKey string `mask:"password" yaml:"system_user_api_key" env:"SYSTEM_USER_API_KEY" desc:"API key for all system users." introductionVersion:"1.0.0"`
AdminUserID string `yaml:"admin_user_id" env:"OC_ADMIN_USER_ID" desc:"ID of a user, that should receive admin privileges. Consider that the UUID can be encoded in some LDAP deployment configurations like in .ldif files. These need to be decoded beforehand." introductionVersion:"1.0.0"`
MultiTenantEnabled bool `yaml:"multi_tenant_enabled" env:"OC_MULTI_TENANT_ENABLED" desc:"Set this to true to enable multi-tenant support." introductionVersion:"%%NEXT%%"`
// NOTE: you will not fing GRPCMaxReceivedMessageSize size being used in the code. The envvar is actually extracted in revas `pool` package: https://github.com/cs3org/reva/blob/edge/pkg/rgrpc/todo/pool/connection.go
// It is mentioned here again so it is documented
@@ -42,6 +42,10 @@ func Validate(cfg *config.Config) error {
return shared.MissingJWTTokenError(cfg.Service.Name)
}
// ensure that the "cs3" identity backend is used in multi-tenant setups
if cfg.Commons.MultiTenantEnabled && cfg.Identity.Backend != "cs3" {
return fmt.Errorf("Multi-tenant support is enabled. The identity backend must be set to 'cs3' for the 'graph' service.")
}
if cfg.Identity.Backend == "ldap" {
if err := validateLDAPSettings(cfg); err != nil {
return err
@@ -0,0 +1,63 @@
package parser_test
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/opencloud-eu/opencloud/pkg/shared"
"github.com/opencloud-eu/opencloud/services/graph/pkg/config"
"github.com/opencloud-eu/opencloud/services/graph/pkg/config/defaults"
"github.com/opencloud-eu/opencloud/services/graph/pkg/config/parser"
)
var _ = Describe("Validate", func() {
var cfg *config.Config
BeforeEach(func() {
cfg = defaults.DefaultConfig()
cfg.Application.ID = "graph-app-id"
cfg.ServiceAccount.ServiceAccountID = "graph-service-account"
cfg.ServiceAccount.ServiceAccountSecret = "graph-service-password"
cfg.Commons = &shared.Commons{
TokenManager: &shared.TokenManager{
JWTSecret: "jwt-secret",
},
}
defaults.EnsureDefaults(cfg)
})
When("multi-tenant support is disabled", func() {
It("should accept a setup with the 'cs3' identity backend", func() {
cfg.Identity.Backend = "cs3"
err := parser.Validate(cfg)
Expect(err).ToNot(HaveOccurred())
})
It("should accept a setup with the 'ldap' identity backend", func() {
cfg.Identity.Backend = "ldap"
// we need to set a password to pass validation
cfg.Identity.LDAP.BindPassword = "bind-password"
err := parser.Validate(cfg)
Expect(err).ToNot(HaveOccurred())
})
})
When("multi-tenant support is disabled", func() {
BeforeEach(func() {
cfg.Commons.MultiTenantEnabled = true
})
It("should accept a setup with the 'cs3' identity backend", func() {
cfg.Identity.Backend = "cs3"
err := parser.Validate(cfg)
Expect(err).ToNot(HaveOccurred())
})
It("should reject a setup with the 'ldap' identity backend", func() {
cfg.Identity.Backend = "ldap"
cfg.Identity.LDAP.BindPassword = "bind-password"
err := parser.Validate(cfg)
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(ContainSubstring("The identity backend must be set to 'cs3' for the 'graph' service.")))
})
})
})
@@ -0,0 +1,13 @@
package parser_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestParser(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Parser Suite")
}