From c9645690957f1ea72ce3f8214f6dae6213ee6731 Mon Sep 17 00:00:00 2001 From: Francis PEROT Date: Thu, 27 Feb 2020 14:45:35 +0100 Subject: [PATCH] [CLOUDTRUST-2320] Add endpoints for admin configuration --- Gopkg.lock | 38 ++++++--- Gopkg.toml | 4 +- api/management/api.go | 71 +++++++++++++++++ api/management/api_test.go | 62 +++++++++++++-- api/management/swagger-api_management.yaml | 60 ++++++++++++++ cmd/keycloakb/keycloak_bridge.go | 6 ++ internal/keycloakb/configdbinstrumenting.go | 18 +++++ .../keycloakb/configdbinstrumenting_test.go | 58 +++++++++++++- internal/keycloakb/configdbmodule.go | 25 +++++- internal/keycloakb/configdbmodule_test.go | 49 ++++++++++++ internal/messages/errormessages.go | 1 + pkg/management/authorization.go | 24 ++++++ pkg/management/authorization_test.go | 21 ++++- pkg/management/component.go | 65 +++++++++++++--- pkg/management/component_test.go | 78 +++++++++++++++++++ pkg/management/endpoint.go | 34 ++++++++ pkg/management/endpoint_test.go | 78 +++++++++++++++++++ 17 files changed, 659 insertions(+), 33 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 649bfdf40..2f0293685 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -18,7 +18,8 @@ version = "2020.02.11" [[projects]] - digest = "1:f00a04c3f9923917a7b65d7f2e785a653f88cd9fca0e164abac4f369a4533a62" + branch = "ct-2375" + digest = "1:4473c7f7e18551f287ad6334244f23102629d28a07d2cc9d732bc6fcaa78c4e8" name = "github.com/cloudtrust/common-service" packages = [ ".", @@ -38,16 +39,15 @@ "validation", ] pruneopts = "UT" - revision = "6f43146697b189b011f98619ca2674c29d769ea0" - version = "v2.0.1" + revision = "74a6646e4220c92027173b4be3bc8a818e313a64" [[projects]] - digest = "1:507116b0244b41be01dcbf553c56ab9045f2df92ff48bde722d155811ca0ab37" + branch = "ct-2375" + digest = "1:1b696d54587856a1607768ff288870210e6a1a27d886649faf80f92131fced44" name = "github.com/cloudtrust/keycloak-client" packages = ["."] pruneopts = "UT" - revision = "ce6d81a660e6e317d454ecfe2bb80057f8501531" - version = "v1.2.10" + revision = "dc1a849278cffb9d1749b20b36c74b659d38538d" [[projects]] digest = "1:910c5b111ca13127693df80a59be2c952ad97313626116c0c9895e7b588f2a29" @@ -140,7 +140,7 @@ version = "v1.3.4" [[projects]] - digest = "1:e02b687ade0c19c038ecce3ea326d756519adc4bba1203a9e76d620b12354892" + digest = "1:debcbc62c53851dab1fc881438a65449dd5ffa12f64f059fd6e32b938799513d" name = "github.com/google/flatbuffers" packages = ["go"] pruneopts = "UT" @@ -175,7 +175,7 @@ version = "v1.0.0" [[projects]] - digest = "1:9b73396bc7a21f88702fe3d314ca7860843c08f9c787bb47f008075f840df23d" + digest = "1:c266355b17e65dc0128f4a9df906567b28e135b774e0b395aab3d748af0871e2" name = "github.com/influxdata/influxdb" packages = [ "client/v2", @@ -313,12 +313,12 @@ version = "v1.0.5" [[projects]] - digest = "1:11118bd196646c6515fea3d6c43f66162833c6ae4939bfb229b9956d91c6cf17" + digest = "1:83d0e0f3f46dc86daf27e4d7c834c8c492db787c53c09d94183de7478db92142" name = "github.com/spf13/viper" packages = ["."] pruneopts = "UT" - revision = "b5bf975e5823809fb22c7644d008757f78a4259e" - version = "v1.4.0" + revision = "4525543ce4fe90f7970f5e2cdc300b8ffc8c0582" + version = "v1.6.2" [[projects]] digest = "1:5e8f46b412421d2d6cceea845d28ac46f3f5a5f60a6e86f0ee75e24fd43a02a9" @@ -328,6 +328,14 @@ revision = "3ebf1ddaeb260c4b1ae502a01c7844fa8c1fa0e9" version = "v1.5.1" +[[projects]] + digest = "1:f4b32291cad5efac2bfdba89ccde6aa04618b62ce06c1a571da2dc4f3f2677fb" + name = "github.com/subosito/gotenv" + packages = ["."] + pruneopts = "UT" + revision = "2ef7124db659d49edac6aa459693a15ae36c671a" + version = "v1.2.0" + [[projects]] digest = "1:54bf58828a4074c2304fcc52f018eb19fc02d5853ebd5121817330792c776734" name = "github.com/uber/jaeger-client-go" @@ -487,6 +495,14 @@ revision = "34f7caeaf69f4668a88f8294ec18665fd2756b84" version = "v2.0.4" +[[projects]] + digest = "1:ef4bd1cd2563d93d3f48352fc24d499a4a2b54c11932814173653ece7db13800" + name = "gopkg.in/ini.v1" + packages = ["."] + pruneopts = "UT" + revision = "32cf4f7e9c77f151e18b53067b55549b4d1d411d" + version = "v1.52.0" + [[projects]] digest = "1:3c4aaca5a82adc021322f239e0cf3f46852bb0b18ae050d0feab2e9e4c527462" name = "gopkg.in/square/go-jose.v2" diff --git a/Gopkg.toml b/Gopkg.toml index 4f65b74b1..6f9fbd5fc 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -27,11 +27,11 @@ [[constraint]] name = "github.com/cloudtrust/common-service" - version = "v2.0.1" + branch = "ct-2375" [[constraint]] name = "github.com/cloudtrust/keycloak-client" - version = "v1.2.10" + branch = "ct-2375" [[constraint]] name = "github.com/go-kit/kit" diff --git a/api/management/api.go b/api/management/api.go index 1dccb8c7c..15b0d47ad 100644 --- a/api/management/api.go +++ b/api/management/api.go @@ -137,6 +137,20 @@ var allowedBoConfKeys = map[string]bool{BOConfKeyCustomers: true, BOConfKeyTeams // BackOfficeConfiguration type type BackOfficeConfiguration map[string]map[string][]string +// RealmAdminConfiguration struct +type RealmAdminConfiguration struct { + Mode *string `json:"mode"` + AvailableChecks map[string]bool `json:"available-checks,omitempty"` + Accreditations []RealmAdminAccreditation `json:"accreditations,omitempty"` +} + +// RealmAdminAccreditation struct +type RealmAdminAccreditation struct { + Type *string `json:"type,omitempty"` + Validity *string `json:"validity,omitempty"` + Condition *string `json:"condition,omitempty"` +} + // FederatedIdentityRepresentation struct type FederatedIdentityRepresentation struct { UserID *string `json:"userID,omitempty"` @@ -392,6 +406,56 @@ func ConvertToKCFedID(fedID FederatedIdentityRepresentation) kc.FederatedIdentit return kcFedID } +// ConvertRealmAdminConfigurationFromDBStruct converts a RealmAdminConfiguration from DB struct to API struct +func ConvertRealmAdminConfigurationFromDBStruct(conf configuration.RealmAdminConfiguration) RealmAdminConfiguration { + return RealmAdminConfiguration{ + Mode: conf.Mode, + AvailableChecks: conf.AvailableChecks, + Accreditations: ConvertRealmAccreditationsFromDBStruct(conf.Accreditations), + } +} + +// ConvertToDBStruct converts a realm admin configuration into its database version +func (rac *RealmAdminConfiguration) ConvertToDBStruct() configuration.RealmAdminConfiguration { + return configuration.RealmAdminConfiguration{ + Mode: rac.Mode, + AvailableChecks: rac.AvailableChecks, + Accreditations: rac.ConvertRealmAccreditationsToDBStruct(), + } +} + +// ConvertRealmAccreditationsToDBStruct converts a slice of realm admin accreditation into its database version +func (rac *RealmAdminConfiguration) ConvertRealmAccreditationsToDBStruct() []configuration.RealmAdminAccreditation { + if len(rac.Accreditations) == 0 { + return nil + } + var res []configuration.RealmAdminAccreditation + for _, accred := range rac.Accreditations { + res = append(res, configuration.RealmAdminAccreditation{ + Type: accred.Type, + Validity: accred.Validity, + Condition: accred.Condition, + }) + } + return res +} + +// ConvertRealmAccreditationsFromDBStruct converts an array of accreditation from DB struct to API struct +func ConvertRealmAccreditationsFromDBStruct(accreds []configuration.RealmAdminAccreditation) []RealmAdminAccreditation { + if len(accreds) == 0 { + return nil + } + var res []RealmAdminAccreditation + for _, accred := range accreds { + res = append(res, RealmAdminAccreditation{ + Type: accred.Type, + Validity: accred.Validity, + Condition: accred.Condition, + }) + } + return res +} + // Validators // NewBackOfficeConfigurationFromJSON creates and validates a new BackOfficeConfiguration from a JSON value @@ -477,6 +541,13 @@ func (config RealmCustomConfiguration) Validate() error { Status() } +// Validate is a validator for RealmAdminConfiguration +func (config RealmAdminConfiguration) Validate() error { + return validation.NewParameterValidator(). + ValidateParameterRegExp("mode", config.Mode, RegExpName, true). + Status() +} + // Validate is a validator for RequiredAction func (requiredAction RequiredAction) Validate() error { if requiredAction != "" { diff --git a/api/management/api_test.go b/api/management/api_test.go index 20499d5e2..de467853f 100644 --- a/api/management/api_test.go +++ b/api/management/api_test.go @@ -287,6 +287,43 @@ func TestFederatedIdentityRepresentation(t *testing.T) { }) } +func TestConvertRealmAdminConfiguration(t *testing.T) { + t.Run("Empty struct", func(t *testing.T) { + var config = configuration.RealmAdminConfiguration{} + var res = ConvertRealmAdminConfigurationFromDBStruct(config) + assert.Nil(t, res.Mode) + assert.Nil(t, res.AvailableChecks) + assert.Nil(t, res.Accreditations) + assert.Equal(t, config, res.ConvertToDBStruct()) + }) + t.Run("Empty struct", func(t *testing.T) { + var mode = "mode" + var typeValue = "type" + var condition = "condition" + var validity = "2y" + var accred = configuration.RealmAdminAccreditation{ + Type: &typeValue, + Condition: &condition, + Validity: &validity, + } + var config = configuration.RealmAdminConfiguration{ + Mode: &mode, + AvailableChecks: map[string]bool{"true": true, "false": false}, + Accreditations: []configuration.RealmAdminAccreditation{accred}, + } + var res = ConvertRealmAdminConfigurationFromDBStruct(config) + assert.Equal(t, mode, *res.Mode) + assert.Len(t, res.AvailableChecks, 2) + assert.True(t, res.AvailableChecks["true"]) + assert.False(t, res.AvailableChecks["false"]) + assert.Len(t, res.Accreditations, 1) + assert.Equal(t, typeValue, *res.Accreditations[0].Type) + assert.Equal(t, condition, *res.Accreditations[0].Condition) + assert.Equal(t, validity, *res.Accreditations[0].Validity) + assert.Equal(t, config, res.ConvertToDBStruct()) + }) +} + func TestNewBackOfficeConfigurationFromJSON(t *testing.T) { t.Run("Invalid JSON", func(t *testing.T) { var _, err = NewBackOfficeConfigurationFromJSON(`{"shop":{"shelves":{"articles":{"books": [1, 2, 3], "chairs": [4, 5, 6]}}}}`) @@ -442,14 +479,29 @@ func TestValidateRealmCustomConfiguration(t *testing.T) { } } +func TestValidateRealmAdminConfiguration(t *testing.T) { + var realmAdminConf = RealmAdminConfiguration{} + + assert.NotNil(t, realmAdminConf.Validate()) + + var mode = "any-value" + realmAdminConf.Mode = &mode + assert.Nil(t, realmAdminConf.Validate()) +} + func TestValidateRequiredAction(t *testing.T) { - { + t.Run("Valid required action", func(t *testing.T) { action := createValidRequiredAction() assert.Nil(t, action.Validate()) - } - - action := RequiredAction("^") - assert.NotNil(t, action.Validate()) + }) + t.Run("Invalid required action", func(t *testing.T) { + action := RequiredAction("^") + assert.NotNil(t, action.Validate()) + }) + t.Run("Empty required action", func(t *testing.T) { + action := RequiredAction("") + assert.Nil(t, action.Validate()) + }) } func createValidUserRepresentation() UserRepresentation { diff --git a/api/management/swagger-api_management.yaml b/api/management/swagger-api_management.yaml index 21a3576f3..00e3e6f9a 100644 --- a/api/management/swagger-api_management.yaml +++ b/api/management/swagger-api_management.yaml @@ -968,6 +968,46 @@ paths: description: successful operation 400: description: invalid information provided (invalid client identifier or redirect URI not allowed for this client) + /realms/{realm}/admin-configuration: + get: + tags: + - Configuration + summary: Get the current admin configuration + parameters: + - name: realm + in: path + description: realm name (not id!) + required: true + schema: + type: string + responses: + 200: + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/AdminConfiguration' + put: + tags: + - Configuration + summary: Update the admin configuration for the given realm + parameters: + - name: realm + in: path + description: realm name (not id!) + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AdminConfiguration' + responses: + 200: + description: successful operation + 400: + description: invalid information provided /realms/{realm}/backoffice-configuration: get: tags: @@ -1245,6 +1285,26 @@ components: type: string redirect_successful_registration_url: type: string + AdminConfiguration: + type: object + properties: + mode: + type: string + available-checks: + type: object + additionalProperties: + type: boolean + accreditations: + type: array + items: + type: object + properties: + type: + type: string + validity: + type: string + condition: + type: string BackOfficeConfiguration: type: object additionalProperties: diff --git a/cmd/keycloakb/keycloak_bridge.go b/cmd/keycloakb/keycloak_bridge.go index 3aca519cb..12585a8ea 100644 --- a/cmd/keycloakb/keycloak_bridge.go +++ b/cmd/keycloakb/keycloak_bridge.go @@ -584,6 +584,8 @@ func main() { GetRealmCustomConfiguration: prepareEndpoint(management.MakeGetRealmCustomConfigurationEndpoint(keycloakComponent), "get_realm_custom_config_endpoint", influxMetrics, managementLogger, tracer, rateLimit["management"]), UpdateRealmCustomConfiguration: prepareEndpoint(management.MakeUpdateRealmCustomConfigurationEndpoint(keycloakComponent), "update_realm_custom_config_endpoint", influxMetrics, managementLogger, tracer, rateLimit["management"]), + GetRealmAdminConfiguration: prepareEndpoint(management.MakeGetRealmAdminConfigurationEndpoint(keycloakComponent), "get_realm_admin_config_endpoint", influxMetrics, managementLogger, tracer, rateLimit["management"]), + UpdateRealmAdminConfiguration: prepareEndpoint(management.MakeUpdateRealmAdminConfigurationEndpoint(keycloakComponent), "update_realm_admin_config_endpoint", influxMetrics, managementLogger, tracer, rateLimit["management"]), GetRealmBackOfficeConfiguration: prepareEndpoint(management.MakeGetRealmBackOfficeConfigurationEndpoint(keycloakComponent), "get_realm_back_office_config_endpoint", influxMetrics, managementLogger, tracer, rateLimit["management"]), UpdateRealmBackOfficeConfiguration: prepareEndpoint(management.MakeUpdateRealmBackOfficeConfigurationEndpoint(keycloakComponent), "update_realm_back_office_config_endpoint", influxMetrics, managementLogger, tracer, rateLimit["management"]), @@ -835,6 +837,8 @@ func main() { var getRealmCustomConfigurationHandler = configureManagementHandler(keycloakb.ComponentName, ComponentID, idGenerator, keycloakClient, audienceRequired, tracer, logger)(managementEndpoints.GetRealmCustomConfiguration) var updateRealmCustomConfigurationHandler = configureManagementHandler(keycloakb.ComponentName, ComponentID, idGenerator, keycloakClient, audienceRequired, tracer, logger)(managementEndpoints.UpdateRealmCustomConfiguration) + var getRealmAdminConfigurationHandler = configureManagementHandler(keycloakb.ComponentName, ComponentID, idGenerator, keycloakClient, audienceRequired, tracer, logger)(managementEndpoints.GetRealmAdminConfiguration) + var updateRealmAdminConfigurationHandler = configureManagementHandler(keycloakb.ComponentName, ComponentID, idGenerator, keycloakClient, audienceRequired, tracer, logger)(managementEndpoints.UpdateRealmAdminConfiguration) var getRealmBackOfficeConfigurationHandler = configureManagementHandler(keycloakb.ComponentName, ComponentID, idGenerator, keycloakClient, audienceRequired, tracer, logger)(managementEndpoints.GetRealmBackOfficeConfiguration) var updateRealmBackOfficeConfigurationHandler = configureManagementHandler(keycloakb.ComponentName, ComponentID, idGenerator, keycloakClient, audienceRequired, tracer, logger)(managementEndpoints.UpdateRealmBackOfficeConfiguration) @@ -904,6 +908,8 @@ func main() { // custom configuration per realm managementSubroute.Path("/realms/{realm}/configuration").Methods("GET").Handler(getRealmCustomConfigurationHandler) managementSubroute.Path("/realms/{realm}/configuration").Methods("PUT").Handler(updateRealmCustomConfigurationHandler) + managementSubroute.Path("/realms/{realm}/admin-configuration").Methods("GET").Handler(getRealmAdminConfigurationHandler) + managementSubroute.Path("/realms/{realm}/admin-configuration").Methods("PUT").Handler(updateRealmAdminConfigurationHandler) managementSubroute.Path("/realms/{realm}/backoffice-configuration/groups").Methods("GET").Handler(getRealmBackOfficeConfigurationHandler) managementSubroute.Path("/realms/{realm}/backoffice-configuration/groups").Methods("PUT").Handler(updateRealmBackOfficeConfigurationHandler) diff --git a/internal/keycloakb/configdbinstrumenting.go b/internal/keycloakb/configdbinstrumenting.go index faebdfc4e..0e28d9e75 100644 --- a/internal/keycloakb/configdbinstrumenting.go +++ b/internal/keycloakb/configdbinstrumenting.go @@ -22,6 +22,8 @@ type ConfigurationDBModule interface { NewTransaction(context context.Context) (database.Transaction, error) StoreOrUpdateConfiguration(context.Context, string, configuration.RealmConfiguration) error GetConfiguration(context.Context, string) (configuration.RealmConfiguration, error) + StoreOrUpdateAdminConfiguration(context.Context, string, configuration.RealmAdminConfiguration) error + GetAdminConfiguration(context.Context, string) (configuration.RealmAdminConfiguration, error) GetBackOfficeConfiguration(context.Context, string, []string) (dto.BackOfficeConfiguration, error) DeleteBackOfficeConfiguration(context.Context, string, string, string, *string, *string) error InsertBackOfficeConfiguration(context.Context, string, string, string, string, []string) error @@ -65,6 +67,22 @@ func (m *configDBModuleInstrumentingMW) GetConfiguration(ctx context.Context, re return m.next.GetConfiguration(ctx, realmName) } +// configDBModuleInstrumentingMW implements Module. +func (m *configDBModuleInstrumentingMW) StoreOrUpdateAdminConfiguration(ctx context.Context, realmName string, config configuration.RealmAdminConfiguration) error { + defer func(begin time.Time) { + m.h.With("correlation_id", ctx.Value(cs.CtContextCorrelationID).(string)).Observe(time.Since(begin).Seconds()) + }(time.Now()) + return m.next.StoreOrUpdateAdminConfiguration(ctx, realmName, config) +} + +// configDBModuleInstrumentingMW implements Module. +func (m *configDBModuleInstrumentingMW) GetAdminConfiguration(ctx context.Context, realmName string) (configuration.RealmAdminConfiguration, error) { + defer func(begin time.Time) { + m.h.With("correlation_id", ctx.Value(cs.CtContextCorrelationID).(string)).Observe(time.Since(begin).Seconds()) + }(time.Now()) + return m.next.GetAdminConfiguration(ctx, realmName) +} + // configDBModuleInstrumentingMW implements Module. func (m *configDBModuleInstrumentingMW) GetBackOfficeConfiguration(ctx context.Context, realmName string, groupNames []string) (dto.BackOfficeConfiguration, error) { defer func(begin time.Time) { diff --git a/internal/keycloakb/configdbinstrumenting_test.go b/internal/keycloakb/configdbinstrumenting_test.go index 8aee56856..760f2ce70 100644 --- a/internal/keycloakb/configdbinstrumenting_test.go +++ b/internal/keycloakb/configdbinstrumenting_test.go @@ -33,6 +33,7 @@ func TestComponentInstrumentingMW(t *testing.T) { var groupNames = []string{"group1", "group2", "group3"} var groupName = groupNames[0] var confType = "customers" + var adminConfig = configuration.RealmAdminConfiguration{} t.Run("Get configuration", func(t *testing.T) { mockComponent.EXPECT().GetConfiguration(ctx, realmID).Return(configuration.RealmConfiguration{}, nil).Times(1) @@ -62,6 +63,62 @@ func TestComponentInstrumentingMW(t *testing.T) { assert.Panics(t, f) }) + // Get configuration. + mockComponent.EXPECT().GetConfiguration(ctx, "realmID").Return(configuration.RealmConfiguration{}, nil).Times(1) + mockHistogram.EXPECT().With("correlation_id", corrID).Return(mockHistogram).Times(1) + mockHistogram.EXPECT().Observe(gomock.Any()).Return().Times(1) + m.GetConfiguration(ctx, "realmID") + + // Get configuration without correlation ID. + mockComponent.EXPECT().GetConfiguration(context.Background(), "realmID").Return(configuration.RealmConfiguration{}, nil).Times(1) + var f = func() { + m.GetConfiguration(context.Background(), "realmID") + } + assert.Panics(t, f) + + // Update configuration. + mockComponent.EXPECT().StoreOrUpdateConfiguration(ctx, "realmID", configuration.RealmConfiguration{}).Return(nil).Times(1) + mockHistogram.EXPECT().With("correlation_id", corrID).Return(mockHistogram).Times(1) + mockHistogram.EXPECT().Observe(gomock.Any()).Return().Times(1) + m.StoreOrUpdateConfiguration(ctx, "realmID", configuration.RealmConfiguration{}) + + // Update configuration without correlation ID. + mockComponent.EXPECT().StoreOrUpdateConfiguration(context.Background(), "realmID", configuration.RealmConfiguration{}).Return(nil).Times(1) + f = func() { + m.StoreOrUpdateConfiguration(context.Background(), "realmID", configuration.RealmConfiguration{}) + } + assert.Panics(t, f) + + t.Run("Get admin configuration with correlation ID", func(t *testing.T) { + mockComponent.EXPECT().GetAdminConfiguration(ctx, "realmID").Return(adminConfig, nil).Times(1) + mockHistogram.EXPECT().With("correlation_id", corrID).Return(mockHistogram).Times(1) + mockHistogram.EXPECT().Observe(gomock.Any()).Return().Times(1) + m.GetAdminConfiguration(ctx, "realmID") + }) + + t.Run("Get admin configuration without correlation ID", func(t *testing.T) { + mockComponent.EXPECT().GetAdminConfiguration(context.Background(), "realmID").Return(adminConfig, nil).Times(1) + var f = func() { + m.GetAdminConfiguration(context.Background(), "realmID") + } + assert.Panics(t, f) + }) + + t.Run("Update admin configuration with correlation ID", func(t *testing.T) { + mockComponent.EXPECT().StoreOrUpdateAdminConfiguration(ctx, "realmID", gomock.Any()).Return(nil).Times(1) + mockHistogram.EXPECT().With("correlation_id", corrID).Return(mockHistogram).Times(1) + mockHistogram.EXPECT().Observe(gomock.Any()).Return().Times(1) + m.StoreOrUpdateAdminConfiguration(ctx, "realmID", configuration.RealmAdminConfiguration{}) + }) + + t.Run("Update configuration without correlation ID", func(t *testing.T) { + mockComponent.EXPECT().StoreOrUpdateAdminConfiguration(context.Background(), "realmID", gomock.Any()).Return(nil).Times(1) + f = func() { + m.StoreOrUpdateAdminConfiguration(context.Background(), "realmID", configuration.RealmAdminConfiguration{}) + } + assert.Panics(t, f) + }) + t.Run("Get Back-office configuration", func(t *testing.T) { mockComponent.EXPECT().GetBackOfficeConfiguration(ctx, realmID, groupNames).Return(dto.BackOfficeConfiguration{}, nil) mockHistogram.EXPECT().With("correlation_id", corrID).Return(mockHistogram).Times(1) @@ -87,7 +144,6 @@ func TestComponentInstrumentingMW(t *testing.T) { m.DeleteBackOfficeConfiguration(context.Background(), realmID, groupName, confType, nil, nil) }) }) - t.Run("Insert Back-office configuration", func(t *testing.T) { mockComponent.EXPECT().InsertBackOfficeConfiguration(ctx, realmID, groupName, confType, realmID, groupNames).Return(nil) mockHistogram.EXPECT().With("correlation_id", corrID).Return(mockHistogram).Times(1) diff --git a/internal/keycloakb/configdbmodule.go b/internal/keycloakb/configdbmodule.go index 729ae692a..8ed1f4714 100644 --- a/internal/keycloakb/configdbmodule.go +++ b/internal/keycloakb/configdbmodule.go @@ -16,9 +16,12 @@ import ( ) const ( - updateConfigStmt = `INSERT INTO realm_configuration (realm_id, configuration) + updateConfigStmt = `INSERT INTO realm_configuration (realm_id, configuration) VALUES (?, ?) ON DUPLICATE KEY UPDATE configuration = ?;` + updateAdminConfigStmt = `INSERT INTO realm_configuration (realm_id, admin_configuration) + VALUES (?, ?) + ON DUPLICATE KEY UPDATE admin_configuration = ?;` selectBOConfigStmt = ` SELECT distinct target_realm_id, target_type, target_group_name FROM backoffice_configuration @@ -87,6 +90,26 @@ func (c *configurationDBModule) GetConfiguration(ctx context.Context, realmID st return config, err } +func (c *configurationDBModule) StoreOrUpdateAdminConfiguration(context context.Context, realmID string, config configuration.RealmAdminConfiguration) error { + var bytes, _ = json.Marshal(config) + var configJSON = string(bytes) + // update value in DB + var _, err = c.db.Exec(updateAdminConfigStmt, realmID, configJSON, configJSON) + return err +} + +func (c *configurationDBModule) GetAdminConfiguration(ctx context.Context, realmID string) (configuration.RealmAdminConfiguration, error) { + config, err := c.ConfigurationReaderDBModule.GetAdminConfiguration(ctx, realmID) + + if err == sql.ErrNoRows { + return config, errorhandler.Error{ + Status: 404, + Message: ComponentName + "." + msg.MsgErrNotConfigured + "." + msg.RealmAdminConfiguration + "." + realmID, + } + } + return config, err +} + func (c *configurationDBModule) GetBackOfficeConfiguration(ctx context.Context, realmID string, groupNames []string) (dto.BackOfficeConfiguration, error) { var sqlRequest = strings.Replace(selectBOConfigStmt, "???", "?"+strings.Repeat(",?", len(groupNames)-1), 1) var args = []interface{}{realmID} diff --git a/internal/keycloakb/configdbmodule_test.go b/internal/keycloakb/configdbmodule_test.go index 7d8909e7e..be2cfe751 100644 --- a/internal/keycloakb/configdbmodule_test.go +++ b/internal/keycloakb/configdbmodule_test.go @@ -75,6 +75,55 @@ func TestGetConfiguration(t *testing.T) { } } +func TestStoreOrGetAdminConfiguration(t *testing.T) { + var mockCtrl = gomock.NewController(t) + defer mockCtrl.Finish() + + var mockDB = mock.NewCloudtrustDB(mockCtrl) + var mockSQLRow = mock.NewSQLRow(mockCtrl) + var mockLogger = log.NewNopLogger() + + var configDBModule = NewConfigurationDBModule(mockDB, mockLogger) + var realmID = "myrealm" + var adminConfig configuration.RealmAdminConfiguration + var adminConfigStr = "{}" + var sqlError = errors.New("sql") + var ctx = context.TODO() + + t.Run("Store-SQL fails", func(t *testing.T) { + mockDB.EXPECT().Exec(gomock.Any(), gomock.Any()).Return(nil, sqlError) + assert.Equal(t, sqlError, configDBModule.StoreOrUpdateAdminConfiguration(ctx, realmID, adminConfig)) + }) + t.Run("Store-success", func(t *testing.T) { + mockDB.EXPECT().Exec(gomock.Any(), gomock.Any()).Return(nil, nil) + assert.Nil(t, configDBModule.StoreOrUpdateAdminConfiguration(ctx, realmID, adminConfig)) + }) + t.Run("Get-SQL query fails", func(t *testing.T) { + mockDB.EXPECT().QueryRow(gomock.Any(), realmID).Return(mockSQLRow) + mockSQLRow.EXPECT().Scan(gomock.Any()).Return(sqlError) + var _, err = configDBModule.GetAdminConfiguration(ctx, realmID) + assert.Equal(t, sqlError, err) + }) + + t.Run("Get-SQL query returns no row", func(t *testing.T) { + mockDB.EXPECT().QueryRow(gomock.Any(), realmID).Return(mockSQLRow) + mockSQLRow.EXPECT().Scan(gomock.Any()).Return(sql.ErrNoRows) + var _, err = configDBModule.GetAdminConfiguration(ctx, realmID) + assert.NotNil(t, err) + }) + + t.Run("Get-SQL query returns an admin configuration", func(t *testing.T) { + mockDB.EXPECT().QueryRow(gomock.Any(), realmID).Return(mockSQLRow) + mockSQLRow.EXPECT().Scan(gomock.Any()).DoAndReturn(func(conf *string) error { + *conf = adminConfigStr + return nil + }) + var conf, err = configDBModule.GetAdminConfiguration(ctx, realmID) + assert.Nil(t, err) + assert.Equal(t, adminConfig, conf) + }) +} + func TestBackOfficeConfiguration(t *testing.T) { var mockCtrl = gomock.NewController(t) defer mockCtrl.Finish() diff --git a/internal/messages/errormessages.go b/internal/messages/errormessages.go index 1830c14f6..01eab6903 100644 --- a/internal/messages/errormessages.go +++ b/internal/messages/errormessages.go @@ -18,6 +18,7 @@ const ( BodyContent = "bodyContent" RealmConfiguration = "realmConfiguration" + RealmAdminConfiguration = "realmAdminConfiguration" Authorization = "authorization" CurrentPassword = "currentPassword" NewPassword = "newPassword" diff --git a/pkg/management/authorization.go b/pkg/management/authorization.go index ac1d1cf34..31aecb1b5 100644 --- a/pkg/management/authorization.go +++ b/pkg/management/authorization.go @@ -58,6 +58,8 @@ var ( MGMTCreateClientRole = newAction("MGMT_CreateClientRole", security.ScopeRealm) MGMTGetRealmCustomConfiguration = newAction("MGMT_GetRealmCustomConfiguration", security.ScopeRealm) MGMTUpdateRealmCustomConfiguration = newAction("MGMT_UpdateRealmCustomConfiguration", security.ScopeRealm) + MGMTGetRealmAdminConfiguration = newAction("MGMT_GetRealmAdminConfiguration", security.ScopeRealm) + MGMTUpdateRealmAdminConfiguration = newAction("MGMT_UpdateRealmAdminConfiguration", security.ScopeRealm) MGMTGetRealmBackOfficeConfiguration = newAction("MGMT_GetRealmBackOfficeConfiguration", security.ScopeGroup) MGMTUpdateRealmBackOfficeConfiguration = newAction("MGMT_UpdateRealmBackOfficeConfiguration", security.ScopeGroup) MGMTGetUserRealmBackOfficeConfiguration = newAction("MGMT_GetUserRealmBackOfficeConfiguration", security.ScopeRealm) @@ -481,6 +483,28 @@ func (c *authorizationComponentMW) UpdateRealmCustomConfiguration(ctx context.Co return c.next.UpdateRealmCustomConfiguration(ctx, realmName, customConfig) } +func (c *authorizationComponentMW) GetRealmAdminConfiguration(ctx context.Context, realmName string) (api.RealmAdminConfiguration, error) { + var action = MGMTGetRealmAdminConfiguration.String() + var targetRealm = realmName + + if err := c.authManager.CheckAuthorizationOnTargetRealm(ctx, action, targetRealm); err != nil { + return api.RealmAdminConfiguration{}, err + } + + return c.next.GetRealmAdminConfiguration(ctx, realmName) +} + +func (c *authorizationComponentMW) UpdateRealmAdminConfiguration(ctx context.Context, realmName string, adminConfig api.RealmAdminConfiguration) error { + var action = MGMTUpdateRealmAdminConfiguration.String() + var targetRealm = realmName + + if err := c.authManager.CheckAuthorizationOnTargetRealm(ctx, action, targetRealm); err != nil { + return err + } + + return c.next.UpdateRealmAdminConfiguration(ctx, realmName, adminConfig) +} + func (c *authorizationComponentMW) GetRealmBackOfficeConfiguration(ctx context.Context, realmName string, groupName string) (api.BackOfficeConfiguration, error) { var action = MGMTGetRealmBackOfficeConfiguration.String() var targetRealm = realmName diff --git a/pkg/management/authorization_test.go b/pkg/management/authorization_test.go index a6abe4ed9..ff3e67fa4 100644 --- a/pkg/management/authorization_test.go +++ b/pkg/management/authorization_test.go @@ -82,7 +82,9 @@ func TestDeny(t *testing.T) { DefaultClientID: &clientID, DefaultRedirectURI: &clientURI, } - var config api.BackOfficeConfiguration + + var boConfig api.BackOfficeConfiguration + var adminConfig api.RealmAdminConfiguration var fedID = api.FederatedIdentityRepresentation{ UserID: &userID, @@ -213,10 +215,16 @@ func TestDeny(t *testing.T) { err = authorizationMW.UpdateRealmCustomConfiguration(ctx, realmName, customConfig) assert.Equal(t, security.ForbiddenError{}, err) + _, err = authorizationMW.GetRealmAdminConfiguration(ctx, realmName) + assert.Equal(t, security.ForbiddenError{}, err) + + err = authorizationMW.UpdateRealmAdminConfiguration(ctx, realmName, adminConfig) + assert.Equal(t, security.ForbiddenError{}, err) + _, err = authorizationMW.GetRealmBackOfficeConfiguration(ctx, realmName, groupID) assert.Equal(t, security.ForbiddenError{}, err) - err = authorizationMW.UpdateRealmBackOfficeConfiguration(ctx, realmName, groupID, config) + err = authorizationMW.UpdateRealmBackOfficeConfiguration(ctx, realmName, groupID, boConfig) assert.Equal(t, security.ForbiddenError{}, err) _, err = authorizationMW.GetUserRealmBackOfficeConfiguration(ctx, realmName) @@ -293,6 +301,7 @@ func TestAllowed(t *testing.T) { DefaultRedirectURI: &clientURI, } var config api.BackOfficeConfiguration + var adminConfig api.RealmAdminConfiguration var fedID = api.FederatedIdentityRepresentation{ UserID: &userID, @@ -473,6 +482,14 @@ func TestAllowed(t *testing.T) { err = authorizationMW.UpdateRealmCustomConfiguration(ctx, realmName, customConfig) assert.Nil(t, err) + mockManagementComponent.EXPECT().GetRealmAdminConfiguration(ctx, realmName).Return(adminConfig, nil).Times(1) + _, err = authorizationMW.GetRealmAdminConfiguration(ctx, realmName) + assert.Nil(t, err) + + mockManagementComponent.EXPECT().UpdateRealmAdminConfiguration(ctx, realmName, adminConfig).Return(nil).Times(1) + err = authorizationMW.UpdateRealmAdminConfiguration(ctx, realmName, adminConfig) + assert.Nil(t, err) + mockManagementComponent.EXPECT().GetRealmBackOfficeConfiguration(ctx, realmName, groupID).Return(config, nil).Times(1) _, err = authorizationMW.GetRealmBackOfficeConfiguration(ctx, realmName, groupID) assert.Nil(t, err) diff --git a/pkg/management/component.go b/pkg/management/component.go index d00223231..6eead04f9 100644 --- a/pkg/management/component.go +++ b/pkg/management/component.go @@ -104,6 +104,8 @@ type Component interface { GetRealmCustomConfiguration(ctx context.Context, realmName string) (api.RealmCustomConfiguration, error) UpdateRealmCustomConfiguration(ctx context.Context, realmID string, customConfig api.RealmCustomConfiguration) error + GetRealmAdminConfiguration(ctx context.Context, realmName string) (api.RealmAdminConfiguration, error) + UpdateRealmAdminConfiguration(ctx context.Context, realmID string, adminConfig api.RealmAdminConfiguration) error GetRealmBackOfficeConfiguration(ctx context.Context, realmID string, groupName string) (api.BackOfficeConfiguration, error) UpdateRealmBackOfficeConfiguration(ctx context.Context, realmID string, groupName string, config api.BackOfficeConfiguration) error GetUserRealmBackOfficeConfiguration(ctx context.Context, realmID string) (api.BackOfficeConfiguration, error) @@ -1289,6 +1291,58 @@ func (c *component) UpdateRealmCustomConfiguration(ctx context.Context, realmNam return err } +func (c *component) GetUserRealmBackOfficeConfiguration(ctx context.Context, realmName string) (api.BackOfficeConfiguration, error) { + var groups = ctx.Value(cs.CtContextGroups).([]string) + var dbResult, err = c.configDBModule.GetBackOfficeConfiguration(ctx, realmName, groups) + if err != nil { + c.logger.Warn(ctx, "err", err.Error()) + return nil, err + } + + return api.BackOfficeConfiguration(dbResult), nil +} + +// Retrieve the admin configuration from the database +func (c *component) GetRealmAdminConfiguration(ctx context.Context, realmName string) (api.RealmAdminConfiguration, error) { + var accessToken = ctx.Value(cs.CtContextAccessToken).(string) + + // get the realm config from Keycloak + realmConfig, err := c.keycloakClient.GetRealm(accessToken, realmName) + if err != nil { + c.logger.Warn(ctx, "err", err.Error()) + return api.RealmAdminConfiguration{}, err + } + + var config configuration.RealmAdminConfiguration + config, err = c.configDBModule.GetAdminConfiguration(ctx, *realmConfig.Id) + if err != nil { + c.logger.Warn(ctx, "err", err.Error()) + return api.RealmAdminConfiguration{}, err + } + + return api.ConvertRealmAdminConfigurationFromDBStruct(config), nil +} + +// Update the configuration in the database +func (c *component) UpdateRealmAdminConfiguration(ctx context.Context, realmName string, adminConfig api.RealmAdminConfiguration) error { + var accessToken = ctx.Value(cs.CtContextAccessToken).(string) + + // get the realm config from Keycloak + realmRepr, err := c.keycloakClient.GetRealm(accessToken, realmName) + if err != nil { + c.logger.Warn(ctx, "err", err.Error()) + return err + } + + err = c.configDBModule.StoreOrUpdateAdminConfiguration(ctx, *realmRepr.Id, adminConfig.ConvertToDBStruct()) + if err != nil { + c.logger.Warn(ctx, "err", err.Error()) + return err + } + + return nil +} + func (c *component) GetRealmBackOfficeConfiguration(ctx context.Context, realmID string, groupName string) (api.BackOfficeConfiguration, error) { var dbResult, err = c.configDBModule.GetBackOfficeConfiguration(ctx, realmID, []string{groupName}) if err != nil { @@ -1398,17 +1452,6 @@ func (c *component) findString(groups []string, searchGroup string) bool { return false } -func (c *component) GetUserRealmBackOfficeConfiguration(ctx context.Context, realmName string) (api.BackOfficeConfiguration, error) { - var groups = ctx.Value(cs.CtContextGroups).([]string) - var dbResult, err = c.configDBModule.GetBackOfficeConfiguration(ctx, realmName, groups) - if err != nil { - c.logger.Warn(ctx, "err", err.Error()) - return nil, err - } - - return api.BackOfficeConfiguration(dbResult), nil -} - func (c *component) CreateShadowUser(ctx context.Context, realmName string, userID string, provider string, fedID api.FederatedIdentityRepresentation) error { var accessToken = ctx.Value(cs.CtContextAccessToken).(string) diff --git a/pkg/management/component_test.go b/pkg/management/component_test.go index 7ef061e48..eaab3ea64 100644 --- a/pkg/management/component_test.go +++ b/pkg/management/component_test.go @@ -3445,6 +3445,84 @@ func TestUpdateRealmCustomConfiguration(t *testing.T) { } } +func TestGetRealmAdminConfiguration(t *testing.T) { + var mockCtrl = gomock.NewController(t) + defer mockCtrl.Finish() + + var mockKeycloakClient = mock.NewKeycloakClient(mockCtrl) + var mockEventDBModule = mock.NewEventDBModule(mockCtrl) + var mockConfigurationDBModule = mock.NewConfigurationDBModule(mockCtrl) + var logger = log.NewNopLogger() + + var allowedTrustIDGroups = []string{"grp1", "grp2"} + var realmName = "myrealm" + var realmID = "1234-5678" + var accessToken = "acce-ssto-ken" + var expectedError = errors.New("expectedError") + var dbAdminConfig configuration.RealmAdminConfiguration + var apiAdminConfig = api.ConvertRealmAdminConfigurationFromDBStruct(dbAdminConfig) + var ctx = context.WithValue(context.TODO(), cs.CtContextAccessToken, accessToken) + + var component = NewComponent(mockKeycloakClient, mockEventDBModule, mockConfigurationDBModule, allowedTrustIDGroups, logger) + + t.Run("Request to Keycloak client fails", func(t *testing.T) { + mockKeycloakClient.EXPECT().GetRealm(accessToken, realmName).Return(kc.RealmRepresentation{}, expectedError) + var _, err = component.GetRealmAdminConfiguration(ctx, realmName) + assert.Equal(t, expectedError, err) + }) + t.Run("Request to database fails", func(t *testing.T) { + mockKeycloakClient.EXPECT().GetRealm(accessToken, realmName).Return(kc.RealmRepresentation{Id: &realmID}, nil) + mockConfigurationDBModule.EXPECT().GetAdminConfiguration(ctx, gomock.Any()).Return(dbAdminConfig, expectedError) + var _, err = component.GetRealmAdminConfiguration(ctx, realmName) + assert.Equal(t, expectedError, err) + }) + t.Run("Success", func(t *testing.T) { + mockKeycloakClient.EXPECT().GetRealm(accessToken, realmName).Return(kc.RealmRepresentation{Id: &realmID}, nil) + mockConfigurationDBModule.EXPECT().GetAdminConfiguration(ctx, realmID).Return(dbAdminConfig, nil) + var res, err = component.GetRealmAdminConfiguration(ctx, realmName) + assert.Nil(t, err) + assert.Equal(t, apiAdminConfig, res) + }) +} + +func TestUpdateRealmAdminConfiguration(t *testing.T) { + var mockCtrl = gomock.NewController(t) + defer mockCtrl.Finish() + + var mockKeycloakClient = mock.NewKeycloakClient(mockCtrl) + var mockEventDBModule = mock.NewEventDBModule(mockCtrl) + var mockConfigurationDBModule = mock.NewConfigurationDBModule(mockCtrl) + var logger = log.NewNopLogger() + + var allowedTrustIDGroups = []string{"grp1", "grp2"} + var realmName = "myrealm" + var realmID = "1234-5678" + var accessToken = "acce-ssto-ken" + var expectedError = errors.New("expectedError") + var ctx = context.WithValue(context.TODO(), cs.CtContextAccessToken, accessToken) + var adminConfig api.RealmAdminConfiguration + + var component = NewComponent(mockKeycloakClient, mockEventDBModule, mockConfigurationDBModule, allowedTrustIDGroups, logger) + + t.Run("Request to Keycloak client fails", func(t *testing.T) { + mockKeycloakClient.EXPECT().GetRealm(accessToken, realmName).Return(kc.RealmRepresentation{}, expectedError) + var err = component.UpdateRealmAdminConfiguration(ctx, realmName, adminConfig) + assert.Equal(t, expectedError, err) + }) + t.Run("Request to database fails", func(t *testing.T) { + mockKeycloakClient.EXPECT().GetRealm(accessToken, realmName).Return(kc.RealmRepresentation{Id: &realmID}, nil) + mockConfigurationDBModule.EXPECT().StoreOrUpdateAdminConfiguration(ctx, realmID, gomock.Any()).Return(expectedError) + var err = component.UpdateRealmAdminConfiguration(ctx, realmName, adminConfig) + assert.Equal(t, expectedError, err) + }) + t.Run("Success", func(t *testing.T) { + mockKeycloakClient.EXPECT().GetRealm(accessToken, realmName).Return(kc.RealmRepresentation{Id: &realmID}, nil) + mockConfigurationDBModule.EXPECT().StoreOrUpdateAdminConfiguration(ctx, realmID, gomock.Any()).Return(nil) + var err = component.UpdateRealmAdminConfiguration(ctx, realmName, adminConfig) + assert.Nil(t, err) + }) +} + func createBackOfficeConfiguration(JSON string) dto.BackOfficeConfiguration { var conf dto.BackOfficeConfiguration json.Unmarshal([]byte(JSON), &conf) diff --git a/pkg/management/endpoint.go b/pkg/management/endpoint.go index 95e948243..b0ad21f69 100644 --- a/pkg/management/endpoint.go +++ b/pkg/management/endpoint.go @@ -57,6 +57,8 @@ type Endpoints struct { GetRealmCustomConfiguration endpoint.Endpoint UpdateRealmCustomConfiguration endpoint.Endpoint + GetRealmAdminConfiguration endpoint.Endpoint + UpdateRealmAdminConfiguration endpoint.Endpoint GetRealmBackOfficeConfiguration endpoint.Endpoint UpdateRealmBackOfficeConfiguration endpoint.Endpoint GetUserRealmBackOfficeConfiguration endpoint.Endpoint @@ -105,6 +107,8 @@ type ManagementComponent interface { GetRealmCustomConfiguration(ctx context.Context, realmID string) (api.RealmCustomConfiguration, error) UpdateRealmCustomConfiguration(ctx context.Context, realmID string, customConfig api.RealmCustomConfiguration) error + GetRealmAdminConfiguration(ctx context.Context, realmID string) (api.RealmAdminConfiguration, error) + UpdateRealmAdminConfiguration(ctx context.Context, realmID string, adminConfig api.RealmAdminConfiguration) error GetRealmBackOfficeConfiguration(ctx context.Context, realmName string, groupID string) (api.BackOfficeConfiguration, error) UpdateRealmBackOfficeConfiguration(ctx context.Context, realmName string, groupID string, boConf api.BackOfficeConfiguration) error GetUserRealmBackOfficeConfiguration(ctx context.Context, realmName string) (api.BackOfficeConfiguration, error) @@ -613,6 +617,36 @@ func MakeUpdateRealmCustomConfigurationEndpoint(managementComponent ManagementCo } } +// MakeGetRealmAdminConfigurationEndpoint creates an endpoint for GetRealmAdminConfiguration +func MakeGetRealmAdminConfigurationEndpoint(managementComponent ManagementComponent) cs.Endpoint { + return func(ctx context.Context, req interface{}) (interface{}, error) { + var m = req.(map[string]string) + return managementComponent.GetRealmAdminConfiguration(ctx, m["realm"]) + } +} + +// MakeUpdateRealmAdminConfigurationEndpoint creates an endpoint for UpdateRealmAdminConfiguration +func MakeUpdateRealmAdminConfigurationEndpoint(managementComponent ManagementComponent) cs.Endpoint { + return func(ctx context.Context, req interface{}) (interface{}, error) { + var m = req.(map[string]string) + var err error + + configJSON := m["body"] + + var adminConfig api.RealmAdminConfiguration + + if err = json.Unmarshal([]byte(configJSON), &adminConfig); err != nil { + return nil, errorhandler.CreateBadRequestError(msg.MsgErrInvalidParam + "." + msg.Body) + } + + if err = adminConfig.Validate(); err != nil { + return nil, err + } + + return nil, managementComponent.UpdateRealmAdminConfiguration(ctx, m["realm"], adminConfig) + } +} + // MakeGetRealmBackOfficeConfigurationEndpoint creates an endpoint for GetRealmBackOfficeConfiguration func MakeGetRealmBackOfficeConfigurationEndpoint(managementComponent ManagementComponent) cs.Endpoint { return func(ctx context.Context, req interface{}) (interface{}, error) { diff --git a/pkg/management/endpoint_test.go b/pkg/management/endpoint_test.go index e7118bb89..32321a6ac 100644 --- a/pkg/management/endpoint_test.go +++ b/pkg/management/endpoint_test.go @@ -1128,6 +1128,84 @@ func TestConfigurationEndpoints(t *testing.T) { }) } +func TestGetRealmAdminConfigurationEndpoint(t *testing.T) { + var mockCtrl = gomock.NewController(t) + defer mockCtrl.Finish() + + var mockManagementComponent = mock.NewManagementComponent(mockCtrl) + + var e = MakeGetRealmAdminConfigurationEndpoint(mockManagementComponent) + var ctx = context.Background() + + t.Run("No error", func(t *testing.T) { + var realmName = "master" + var adminConfig api.RealmAdminConfiguration + var req = make(map[string]string) + req["realm"] = realmName + + mockManagementComponent.EXPECT().GetRealmAdminConfiguration(ctx, realmName).Return(adminConfig, nil).Times(1) + var res, err = e(ctx, req) + assert.Nil(t, err) + assert.NotNil(t, res) + }) + t.Run("Request fails at component level", func(t *testing.T) { + var realmName = "master" + var adminConfig api.RealmAdminConfiguration + var expectedError = errors.New("component error") + var req = make(map[string]string) + req["realm"] = realmName + + mockManagementComponent.EXPECT().GetRealmAdminConfiguration(ctx, realmName).Return(adminConfig, expectedError).Times(1) + var _, err = e(ctx, req) + assert.Equal(t, expectedError, err) + }) +} + +func TestUpdateRealmAdminConfigurationEndpoint(t *testing.T) { + var mockCtrl = gomock.NewController(t) + defer mockCtrl.Finish() + + var mockManagementComponent = mock.NewManagementComponent(mockCtrl) + + var e = MakeUpdateRealmAdminConfigurationEndpoint(mockManagementComponent) + var ctx = context.Background() + + t.Run("No error", func(t *testing.T) { + var realmName = "master" + var configJSON = `{"mode":"modeValue"}` + var req = make(map[string]string) + req["realm"] = realmName + req["body"] = configJSON + + mockManagementComponent.EXPECT().UpdateRealmAdminConfiguration(ctx, realmName, gomock.Any()).Return(nil).Times(1) + var res, err = e(ctx, req) + assert.Nil(t, err) + assert.Nil(t, res) + }) + t.Run("Invalid body content", func(t *testing.T) { + var realmName = "master" + var configJSON = `{}` + var req = make(map[string]string) + req["realm"] = realmName + req["body"] = configJSON + + var _, err = e(ctx, req) + assert.NotNil(t, err) + }) + t.Run("JSON error", func(t *testing.T) { + var realmName = "master" + var configJSON = `{` + var req = make(map[string]string) + req["realm"] = realmName + req["body"] = configJSON + + mockManagementComponent.EXPECT().UpdateRealmAdminConfiguration(ctx, realmName, gomock.Any()).Return(nil).Times(0) + var res, err = e(ctx, req) + assert.NotNil(t, err) + assert.Nil(t, res) + }) +} + func TestCreateShadowUserEndpoint(t *testing.T) { var mockCtrl = gomock.NewController(t) defer mockCtrl.Finish()