Skip to content

Commit

Permalink
Merge 48453ef into d974483
Browse files Browse the repository at this point in the history
  • Loading branch information
fperot74 authored Feb 25, 2020
2 parents d974483 + 48453ef commit 344af33
Show file tree
Hide file tree
Showing 18 changed files with 715 additions and 186 deletions.
13 changes: 13 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions api/management/api.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package management_api

import (
"encoding/json"
"strconv"

errorhandler "github.com/cloudtrust/common-service/errors"

"github.com/cloudtrust/common-service/configuration"
"github.com/cloudtrust/common-service/validation"
internal "github.com/cloudtrust/keycloak-bridge/internal/messages"
Expand Down Expand Up @@ -123,6 +126,15 @@ type RealmCustomConfiguration struct {
RedirectSuccessfulRegistrationURL *string `json:"redirect_successful_registration_url"`
}

// BackOffice configuration keys
const (
BOConfKeyCustomers = "customers"
BOConfKeyTeams = "teams"
)

// BackOfficeConfiguration type
type BackOfficeConfiguration map[string]map[string][]string

// FederatedIdentityRepresentation struct
type FederatedIdentityRepresentation struct {
UserID *string `json:"userID,omitempty"`
Expand Down Expand Up @@ -444,6 +456,25 @@ func (config RealmCustomConfiguration) Validate() error {
Status()
}

var allowedBoConfKeys = map[string]bool{BOConfKeyCustomers: true, BOConfKeyTeams: true}

// NewBackOfficeConfigurationFromJSON creates a new BackOfficeConfiguration from a JSON value
func NewBackOfficeConfigurationFromJSON(confJSON string) (BackOfficeConfiguration, error) {
var boConf BackOfficeConfiguration
var err = json.Unmarshal([]byte(confJSON), &boConf)
if err != nil {
return BackOfficeConfiguration{}, errorhandler.CreateBadRequestError(errorhandler.MsgErrInvalidQueryParam + ".body")
}

var validator = validation.NewParameterValidator()
for keyBoConf, valueBoConf := range boConf {
validator = validator.ValidateParameterIn("body.userType", &keyBoConf, allowedBoConfKeys, true).
ValidateParameterNotNil("body.allowedGroups", &valueBoConf)
}

return boConf, validator.Status()
}

// Validate is a validator for RequiredAction
func (requiredAction RequiredAction) Validate() error {
if requiredAction != "" {
Expand Down
81 changes: 81 additions & 0 deletions api/management/swagger-api_management.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,77 @@ paths:
description: successful operation
400:
description: invalid information provided (invalid client identifier or redirect URI not allowed for this client)
/realms/{realm}/backoffice-configuration:
get:
tags:
- Configuration
summary: Get the current backoffice configuration. Uses groups of the current user.
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/BackOfficeConfiguration'
/realms/{realm}/backoffice-configuration/groups:
get:
tags:
- Configuration
summary: Get the current backoffice configuration. Uses groups of the current user.
parameters:
- name: realm
in: path
description: realm name (not id!)
required: true
schema:
type: string
- name: groupName
in: query
description: group name
required: true
schema:
type: string
responses:
200:
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/BackOfficeConfiguration'
put:
tags:
- Configuration
summary: Update the backoffice configuration for the given realm
parameters:
- name: realm
in: path
description: realm name (not id!)
required: true
schema:
type: string
- name: groupName
in: query
description: group name
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/BackOfficeConfiguration'
responses:
200:
description: successful operation
400:
description: invalid information provided
/realms/{realm}/users/{userID}/federated-identity/{provider}:
post:
tags:
Expand Down Expand Up @@ -1174,6 +1245,16 @@ components:
type: string
redirect_successful_registration_url:
type: string
BackOfficeConfiguration:
type: object
additionalProperties:
type: object
items:
type: object
additionalProperties:
type: array
items:
type: string
FederatedIdentity:
type: object
properties:
Expand Down
16 changes: 14 additions & 2 deletions cmd/keycloakb/keycloak_bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,10 @@ 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"]),

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"]),
GetUserRealmBackOfficeConfiguration: prepareEndpoint(management.MakeGetUserRealmBackOfficeConfigurationEndpoint(keycloakComponent), "get_user_realm_back_office_config_endpoint", influxMetrics, managementLogger, tracer, rateLimit["management"]),

CreateShadowUser: prepareEndpoint(management.MakeCreateShadowUserEndpoint(keycloakComponent), "create_shadow_user_endpoint", influxMetrics, managementLogger, tracer, rateLimit["management"]),
}
}
Expand All @@ -598,7 +602,7 @@ func main() {
eventsDBModule := configureEventsDbModule(baseEventsDBModule, influxMetrics, accountLogger, tracer)

// module for retrieving the custom configuration
var configDBModule account.ConfigurationDBModule
var configDBModule keycloakb.ConfigurationDBModule
{
configDBModule = keycloakb.NewConfigurationDBModule(configurationRoDBConn, accountLogger)
configDBModule = keycloakb.MakeConfigurationDBModuleInstrumentingMW(influxMetrics.NewHistogram("configDB_module"))(configDBModule)
Expand Down Expand Up @@ -832,6 +836,10 @@ 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 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)
var getUserRealmBackOfficeConfigurationHandler = configureManagementHandler(keycloakb.ComponentName, ComponentID, idGenerator, keycloakClient, audienceRequired, tracer, logger)(managementEndpoints.GetUserRealmBackOfficeConfiguration)

var createShadowUserHandler = configureManagementHandler(keycloakb.ComponentName, ComponentID, idGenerator, keycloakClient, audienceRequired, tracer, logger)(managementEndpoints.CreateShadowUser)

// KYC handlers
Expand Down Expand Up @@ -893,10 +901,14 @@ func main() {
managementSubroute.Path("/realms/{realm}/groups/{groupID}/authorizations").Methods("GET").Handler(getAuthorizationsHandler)
managementSubroute.Path("/realms/{realm}/groups/{groupID}/authorizations").Methods("PUT").Handler(updateAuthorizationsHandler)

// custom configuration par realm
// 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}/backoffice-configuration/groups").Methods("GET").Handler(getRealmBackOfficeConfigurationHandler)
managementSubroute.Path("/realms/{realm}/backoffice-configuration/groups").Methods("PUT").Handler(updateRealmBackOfficeConfigurationHandler)
managementSubroute.Path("/realms/{realm}/backoffice-configuration").Methods("GET").Handler(getUserRealmBackOfficeConfigurationHandler)

// brokering - shadow users
managementSubroute.Path("/realms/{realm}/users/{userID}/federated-identity/{provider}").Methods("POST").Handler(createShadowUserHandler)

Expand Down
31 changes: 28 additions & 3 deletions internal/keycloakb/configdbinstrumenting.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ type configDBModuleInstrumentingMW struct {
// ConfigurationDBModule is the interface of the configuration module.
type ConfigurationDBModule interface {
NewTransaction(context context.Context) (database.Transaction, error)
StoreOrUpdate(context.Context, string, configuration.RealmConfiguration) error
StoreOrUpdateConfiguration(context.Context, string, configuration.RealmConfiguration) error
GetConfiguration(context.Context, string) (configuration.RealmConfiguration, error)
GetBackOfficeConfiguration(context.Context, string, []string) (BackOfficeConfiguration, error)
DeleteBackOfficeConfiguration(context.Context, string, string, string, *string, *string) error
InsertBackOfficeConfiguration(context.Context, string, string, string, string, []string) error
GetAuthorizations(context context.Context, realmID string, groupName string) ([]configuration.Authorization, error)
CreateAuthorization(context context.Context, authz configuration.Authorization) error
DeleteAuthorizations(context context.Context, realmID string, groupName string) error
Expand All @@ -46,11 +49,11 @@ func (m *configDBModuleInstrumentingMW) NewTransaction(ctx context.Context) (dat
}

// configDBModuleInstrumentingMW implements Module.
func (m *configDBModuleInstrumentingMW) StoreOrUpdate(ctx context.Context, realmName string, config configuration.RealmConfiguration) error {
func (m *configDBModuleInstrumentingMW) StoreOrUpdateConfiguration(ctx context.Context, realmName string, config configuration.RealmConfiguration) 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.StoreOrUpdate(ctx, realmName, config)
return m.next.StoreOrUpdateConfiguration(ctx, realmName, config)
}

// configDBModuleInstrumentingMW implements Module.
Expand All @@ -61,6 +64,28 @@ func (m *configDBModuleInstrumentingMW) GetConfiguration(ctx context.Context, re
return m.next.GetConfiguration(ctx, realmName)
}

// configDBModuleInstrumentingMW implements Module.
func (m *configDBModuleInstrumentingMW) GetBackOfficeConfiguration(ctx context.Context, realmName string, groupNames []string) (BackOfficeConfiguration, 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.GetBackOfficeConfiguration(ctx, realmName, groupNames)
}

func (m *configDBModuleInstrumentingMW) DeleteBackOfficeConfiguration(ctx context.Context, realmID string, groupName string, confType string, targetRealmID *string, targetGroupName *string) 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.DeleteBackOfficeConfiguration(ctx, realmID, groupName, confType, targetRealmID, targetGroupName)
}

func (m *configDBModuleInstrumentingMW) InsertBackOfficeConfiguration(ctx context.Context, realmID, groupName, confType, targetRealmID string, targetGroupNames []string) 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.InsertBackOfficeConfiguration(ctx, realmID, groupName, confType, targetRealmID, targetGroupNames)
}

// configDBModuleInstrumentingMW implements Module.
func (m *configDBModuleInstrumentingMW) GetAuthorizations(ctx context.Context, realmID string, groupID string) ([]configuration.Authorization, error) {
defer func(begin time.Time) {
Expand Down
8 changes: 4 additions & 4 deletions internal/keycloakb/configdbinstrumenting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ func TestComponentInstrumentingMW(t *testing.T) {
assert.Panics(t, f)

// Update configuration.
mockComponent.EXPECT().StoreOrUpdate(ctx, "realmID", configuration.RealmConfiguration{}).Return(nil).Times(1)
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.StoreOrUpdate(ctx, "realmID", configuration.RealmConfiguration{})
m.StoreOrUpdateConfiguration(ctx, "realmID", configuration.RealmConfiguration{})

// Update configuration without correlation ID.
mockComponent.EXPECT().StoreOrUpdate(context.Background(), "realmID", configuration.RealmConfiguration{}).Return(nil).Times(1)
mockComponent.EXPECT().StoreOrUpdateConfiguration(context.Background(), "realmID", configuration.RealmConfiguration{}).Return(nil).Times(1)
f = func() {
m.StoreOrUpdate(context.Background(), "realmID", configuration.RealmConfiguration{})
m.StoreOrUpdateConfiguration(context.Background(), "realmID", configuration.RealmConfiguration{})
}
assert.Panics(t, f)
}
73 changes: 72 additions & 1 deletion internal/keycloakb/configdbmodule.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"encoding/json"
"strings"

"github.com/cloudtrust/common-service/configuration"
"github.com/cloudtrust/common-service/database"
Expand All @@ -17,13 +18,33 @@ const (
updateConfigStmt = `INSERT INTO realm_configuration (realm_id, configuration)
VALUES (?, ?)
ON DUPLICATE KEY UPDATE configuration = ?;`
selectBOConfigStmt = `
SELECT distinct type, target_realm_id, target_group_name
FROM backoffice_configuration
WHERE realm_id=? AND group_name IN (?)
`
insertBOConfigStmt = `
INSERT INTO backoffice_configuration (realm_id, group_name, type, target_realm_id, target_group_name)
VALUES (?,?,?,?,?)
`
deleteBOConfigStmt = `
DELETE FROM backoffice_configuration
WHERE realm_id=?
AND group_name=?
AND type=?
AND (? IS NULL OR target_realm_id=?)
AND (? IS NULL OR target_group_name=?)
`
selectAuthzStmt = `SELECT realm_id, group_name, action, target_realm_id, target_group_name FROM authorizations WHERE realm_id = ? AND group_name = ?;`
createAuthzStmt = `INSERT INTO authorizations (realm_id, group_name, action, target_realm_id, target_group_name)
VALUES (?, ?, ?, ?, ?);`
deleteAuthzStmt = `DELETE FROM authorizations WHERE realm_id = ? AND group_name = ?;`
deleteAllAuthzWithGroupStmt = `DELETE FROM authorizations WHERE (realm_id = ? AND group_name = ?) OR (target_realm_id = ? AND target_group_name = ?);`
)

// BackOfficeConfiguration definition
type BackOfficeConfiguration map[string]map[string][]string

// Scanner used to get data from SQL cursors
type Scanner interface {
Scan(...interface{}) error
Expand All @@ -44,7 +65,7 @@ func NewConfigurationDBModule(db sqltypes.CloudtrustDB, logger log.Logger) Confi
}
}

func (c *configurationDBModule) StoreOrUpdate(context context.Context, realmID string, config configuration.RealmConfiguration) error {
func (c *configurationDBModule) StoreOrUpdateConfiguration(context context.Context, realmID string, config configuration.RealmConfiguration) error {
// transform customConfig object into JSON string
configJSON, err := json.Marshal(config)
if err != nil {
Expand All @@ -68,6 +89,56 @@ func (c *configurationDBModule) GetConfiguration(ctx context.Context, realmID st
return config, err
}

func (c *configurationDBModule) GetBackOfficeConfiguration(ctx context.Context, realmID string, groupNames []string) (BackOfficeConfiguration, error) {
var res = make(BackOfficeConfiguration)
var rows, err = c.db.Query(selectBOConfigStmt, realmID, groupNames[0])
if err != nil {
c.logger.Warn(ctx, "msg", "Can't get back-office configuration", "error", err.Error(), "realmID", realmID, "groups", strings.Join(groupNames, ","))
return nil, err
}

for rows.Next() {
var confType, targetRealmID, targetGroupName string
err = rows.Scan(&confType, &targetRealmID, &targetGroupName)
if err != nil {
c.logger.Warn(ctx, "msg", "Can't get row from back-office configuration", "error", err.Error(), "realmID", realmID, "groups", strings.Join(groupNames, ","))
return nil, err
}
if _, ok := res[confType]; !ok {
res[confType] = make(map[string][]string)
}
if realmSubConf, ok := res[confType][targetRealmID]; !ok {
res[confType][targetRealmID] = []string{targetGroupName}
} else {
res[confType][targetRealmID] = append(realmSubConf, targetGroupName)
}
}

return res, nil
}

func (c *configurationDBModule) DeleteBackOfficeConfiguration(ctx context.Context, realmID, groupName, confType string, targetRealmID *string, targetGroupName *string) error {
var _, err = c.db.Exec(deleteBOConfigStmt, realmID, groupName, confType, targetRealmID, targetRealmID, targetGroupName, targetGroupName)
if err != nil {
c.logger.Warn(ctx, "msg", "Can't delete back-office configuration", "error", err.Error(), "realmName", realmID, "group", groupName,
"confType", confType, "targetRealmName", targetRealmID, "group", targetGroupName)
return err
}
return nil
}

func (c *configurationDBModule) InsertBackOfficeConfiguration(ctx context.Context, realmID, groupName, confType, targetRealmID string, targetGroupNames []string) error {
for _, targetGroupName := range targetGroupNames {
var _, err = c.db.Exec(insertBOConfigStmt, realmID, groupName, confType, targetRealmID, targetGroupName)
if err != nil {
c.logger.Warn(ctx, "msg", "Can't insert into back-office configuration", "error", err.Error(), "realmID", realmID, "groupName", groupName,
"confType", confType, "targetRealmName", targetRealmID, "group", targetGroupName)
return err
}
}
return nil
}

func (c *configurationDBModule) GetAuthorizations(ctx context.Context, realmID string, groupName string) ([]configuration.Authorization, error) {
// Get Authorizations from DB
rows, err := c.db.Query(selectAuthzStmt, realmID, groupName)
Expand Down
Loading

0 comments on commit 344af33

Please sign in to comment.