Skip to content

Commit

Permalink
[CLOUDTRUST-1552] Basic migration report
Browse files Browse the repository at this point in the history
  • Loading branch information
harture committed Aug 16, 2019
1 parent 1d8b54d commit edb0b58
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 31 deletions.
19 changes: 16 additions & 3 deletions cmd/keycloakb/keycloak_bridge.go
Expand Up @@ -355,11 +355,12 @@ func main() {
{
var statisticsLogger = log.With(logger, "svc", "statistics")

statisticsComponent := statistics.NewComponent(eventsRODBModule)
statisticsComponent := statistics.NewComponent(eventsRODBModule, keycloakClient, statisticsLogger)
statisticsComponent = statistics.MakeAuthorizationManagementComponentMW(log.With(statisticsLogger, "mw", "endpoint"), authorizationManager)(statisticsComponent)

statisticsEndpoints = statistics.Endpoints{
GetStatistics: prepareEndpoint(statistics.MakeGetStatisticsEndpoint(statisticsComponent), "get_statistics", influxMetrics, statisticsLogger, tracer, rateLimit["event"]),
GetStatistics: prepareEndpoint(statistics.MakeGetStatisticsEndpoint(statisticsComponent), "get_statistics", influxMetrics, statisticsLogger, tracer, rateLimit["event"]),
GetMigrationReport: prepareEndpoint(statistics.MakeGetMigrationReportEndpoint(statisticsComponent), "get_migration_report", influxMetrics, statisticsLogger, tracer, rateLimit["event"]),
}
}

Expand Down Expand Up @@ -514,8 +515,10 @@ func main() {
route.Path("/rights").Methods("GET").Handler(rightsHandler)

// Statistics
var getStatisticsHandler = configureEventsHandler(ComponentName, ComponentID, idGenerator, keycloakClient, audienceRequired, tracer, logger)(statisticsEndpoints.GetStatistics)
var getStatisticsHandler = configureStatisiticsHandler(ComponentName, ComponentID, idGenerator, keycloakClient, audienceRequired, tracer, logger)(statisticsEndpoints.GetStatistics)
var getMigrationReportHandler = configureStatisiticsHandler(ComponentName, ComponentID, idGenerator, keycloakClient, audienceRequired, tracer, logger)(statisticsEndpoints.GetMigrationReport)
route.Path("/statistics/realms/{realm}").Methods("GET").Handler(getStatisticsHandler)
route.Path("/statistics/realms/{realm}/migration").Methods("GET").Handler(getMigrationReportHandler)

// Events
var getEventsHandler = configureEventsHandler(ComponentName, ComponentID, idGenerator, keycloakClient, audienceRequired, tracer, logger)(eventsEndpoints.GetEvents)
Expand Down Expand Up @@ -783,6 +786,16 @@ func configureEventsHandler(ComponentName string, ComponentID string, idGenerato
}
}

func configureStatisiticsHandler(ComponentName string, ComponentID string, idGenerator idgenerator.IDGenerator, keycloakClient *keycloak.Client, audienceRequired string, tracer tracing.OpentracingClient, logger log.Logger) func(endpoint endpoint.Endpoint) http.Handler {
return func(endpoint endpoint.Endpoint) http.Handler {
var handler http.Handler
handler = statistics.MakeStatisticsHandler(endpoint, logger)
handler = middleware.MakeHTTPCorrelationIDMW(idGenerator, tracer, logger, ComponentName, ComponentID)(handler)
handler = middleware.MakeHTTPOIDCTokenValidationMW(keycloakClient, audienceRequired, logger)(handler)
return handler
}
}

func configureManagementHandler(ComponentName string, ComponentID string, idGenerator idgenerator.IDGenerator, keycloakClient *keycloak.Client, audienceRequired string, tracer tracing.OpentracingClient, logger log.Logger) func(endpoint endpoint.Endpoint) http.Handler {
return func(endpoint endpoint.Endpoint) http.Handler {
var handler http.Handler
Expand Down
5 changes: 5 additions & 0 deletions configs/authorization.json
Expand Up @@ -106,6 +106,11 @@
"*": {}
}
},
"ST_GetMigrationReport": {
"master": {
"*": {}
}
},
"GetClients":{
"*": {}
},
Expand Down
3 changes: 3 additions & 0 deletions configs/keycloak_bridge.yml
Expand Up @@ -54,6 +54,7 @@ db-audit-rw-max-idle-conns: 2
db-audit-rw-conn-max-lifetime: 10
db-audit-rw-migration: false
db-audit-rw-migration-version: 0.1
db-audit-rw-connection-check: false


# DB Audit RO
Expand All @@ -68,6 +69,7 @@ db-audit-ro-max-idle-conns: 2
db-audit-ro-conn-max-lifetime: 10
db-audit-ro-migration: false
db-audit-ro-migration-version: 0.1
db-audit-ro-connection-check: false

# DB Configuration
db-config-host-port: 127.0.0.1:3306
Expand All @@ -81,6 +83,7 @@ db-config-max-idle-conns: 2
db-config-conn-max-lifetime: 10
db-config-migration: false
db-config-migration-version: 0.1
db-config-connection-check: false

# audit events
events-db: false
Expand Down
22 changes: 16 additions & 6 deletions pkg/statistics/authorization.go
Expand Up @@ -3,14 +3,15 @@ package statistics
import (
"context"

"github.com/cloudtrust/common-service/log"
"github.com/cloudtrust/common-service/security"
api "github.com/cloudtrust/keycloak-bridge/api/statistics"
"github.com/cloudtrust/common-service/log"
)

// Actions used for authorization module
const (
STGetStatistics = "ST_GetStatistics"
STGetStatistics = "ST_GetStatistics"
STGetMigrationReport = "ST_GetMigrationReport"
)

// Tracking middleware at component level.
Expand All @@ -31,13 +32,22 @@ func MakeAuthorizationManagementComponentMW(logger log.Logger, authorizationMana
}
}

func (c *authorizationComponentMW) GetStatistics(ctx context.Context, m map[string]string) (api.StatisticsRepresentation, error) {
func (c *authorizationComponentMW) GetStatistics(ctx context.Context, realm string) (api.StatisticsRepresentation, error) {
var action = STGetStatistics
var targetRealm = m["realm"] // Get the realm provided as parameter in path

if err := c.authManager.CheckAuthorizationOnTargetRealm(ctx, action, targetRealm); err != nil {
if err := c.authManager.CheckAuthorizationOnTargetRealm(ctx, action, realm); err != nil {
return api.StatisticsRepresentation{}, err
}

return c.next.GetStatistics(ctx, m)
return c.next.GetStatistics(ctx, realm)
}

func (c *authorizationComponentMW) GetMigrationReport(ctx context.Context, realm string) (map[string]bool, error) {
var action = STGetMigrationReport

if err := c.authManager.CheckAuthorizationOnTargetRealm(ctx, action, realm); err != nil {
return map[string]bool{}, err
}

return c.next.GetMigrationReport(ctx, realm)
}
10 changes: 5 additions & 5 deletions pkg/statistics/authorization_test.go
Expand Up @@ -5,18 +5,18 @@ import (
"testing"

cs "github.com/cloudtrust/common-service"
"github.com/cloudtrust/common-service/log"
"github.com/cloudtrust/common-service/security"
api "github.com/cloudtrust/keycloak-bridge/api/statistics"
"github.com/cloudtrust/keycloak-bridge/pkg/statistics/mock"
"github.com/cloudtrust/common-service/log"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)

const (
WithoutAuthorization = `{}`
WithAuthorization = `{
"master": {
"master": {
"toe": {
"ST_GetStatistics": {"*": {"*": {} }}
}
Expand Down Expand Up @@ -58,15 +58,15 @@ func testAuthorization(t *testing.T, jsonAuthz string, tester func(Component, *m

func TestGetStatisticsAllow(t *testing.T) {
testAuthorization(t, WithAuthorization, func(auth Component, mockComponent *mock.Component, ctx context.Context, mp map[string]string) {
mockComponent.EXPECT().GetStatistics(ctx, mp).Return(api.StatisticsRepresentation{}, nil).Times(1)
_, err := auth.GetStatistics(ctx, mp)
mockComponent.EXPECT().GetStatistics(ctx, mp["realm"]).Return(api.StatisticsRepresentation{}, nil).Times(1)
_, err := auth.GetStatistics(ctx, mp["realm"])
assert.Nil(t, err)
})
}

func TestGetStatisticsDeny(t *testing.T) {
testAuthorization(t, WithoutAuthorization, func(auth Component, mockComponent *mock.Component, ctx context.Context, mp map[string]string) {
_, err := auth.GetStatistics(ctx, mp)
_, err := auth.GetStatistics(ctx, mp["realm"])
assert.Equal(t, security.ForbiddenError{}, err)
})
}
61 changes: 55 additions & 6 deletions pkg/statistics/component.go
Expand Up @@ -3,31 +3,42 @@ package statistics
import (
"context"

cs "github.com/cloudtrust/common-service"
"github.com/cloudtrust/common-service/log"
api "github.com/cloudtrust/keycloak-bridge/api/statistics"
"github.com/cloudtrust/keycloak-bridge/internal/keycloakb"
kc "github.com/cloudtrust/keycloak-client"
)

// Component is the interface of the events component.
type Component interface {
GetStatistics(context.Context, map[string]string) (api.StatisticsRepresentation, error)
GetStatistics(context.Context, string) (api.StatisticsRepresentation, error)
GetMigrationReport(context.Context, string) (map[string]bool, error)
}

type KeycloakClient interface {
GetUsers(accessToken string, reqRealmName, targetRealmName string, paramKV ...string) (kc.UsersPageRepresentation, error)
}

type component struct {
db keycloakb.EventsDBModule
db keycloakb.EventsDBModule
keycloakClient KeycloakClient
logger log.Logger
}

// NewComponent returns a component
func NewComponent(db keycloakb.EventsDBModule) Component {
func NewComponent(db keycloakb.EventsDBModule, keycloakClient KeycloakClient, logger log.Logger) Component {
return &component{
db: db,
db: db,
keycloakClient: keycloakClient,
logger: logger,
}
}

// Grabs statistics
func (ec *component) GetStatistics(ctx context.Context, m map[string]string) (api.StatisticsRepresentation, error) {
func (ec *component) GetStatistics(ctx context.Context, realmName string) (api.StatisticsRepresentation, error) {
var res api.StatisticsRepresentation
var err error
var realmName = m["realm"]

res.LastConnection, err = ec.db.GetLastConnection(ctx, realmName)

Expand All @@ -49,3 +60,41 @@ func (ec *component) GetStatistics(ctx context.Context, m map[string]string) (ap

return res, err
}

// Compute Migration Report
func (ec *component) GetMigrationReport(ctx context.Context, realmName string) (map[string]bool, error) {
var accessToken = ctx.Value(cs.CtContextAccessToken).(string)
var ctxRealm = ctx.Value(cs.CtContextRealm).(string)

var paramKV = []string{}
paramKV = append(paramKV, "max", "0") //All

usersKc, err := ec.keycloakClient.GetUsers(accessToken, ctxRealm, realmName, paramKV...)

if err != nil {
ec.logger.Warn("err", err.Error())
return map[string]bool{}, err
}

var migratedUsers = map[string]bool{}

for _, user := range usersKc.Users {
migratedUsers[*user.Username] = isMigrated(user)
}

return migratedUsers, nil
}

func isMigrated(user kc.UserRepresentation) bool {
if user.Attributes == nil {
return false
}

var attributes = *(user.Attributes)

if len(attributes["migrated"]) != 0 && attributes["migrated"][0] == "true" {
return true
}

return false
}
16 changes: 10 additions & 6 deletions pkg/statistics/component_test.go
Expand Up @@ -5,8 +5,9 @@ import (
"errors"
"testing"

"github.com/cloudtrust/common-service/log"
api "github.com/cloudtrust/keycloak-bridge/api/statistics"
"github.com/cloudtrust/keycloak-bridge/pkg/events/mock"
"github.com/cloudtrust/keycloak-bridge/pkg/statistics/mock"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)
Expand All @@ -15,19 +16,22 @@ func executeTest(t *testing.T, tester func(mockDBModule *mock.EventsDBModule, co
var mockCtrl = gomock.NewController(t)
defer mockCtrl.Finish()
var mockDBModule = mock.NewEventsDBModule(mockCtrl)
tester(mockDBModule, NewComponent(mockDBModule))
var mockKcClient = mock.NewKcClient(mockCtrl)
var mockLogger = log.NewNopLogger()
tester(mockDBModule, NewComponent(mockDBModule, mockKcClient, mockLogger))
}

func TestGetStatistics(t *testing.T) {
var mockCtrl = gomock.NewController(t)
defer mockCtrl.Finish()

var mockDBModule = mock.NewEventsDBModule(mockCtrl)
component := NewComponent(mockDBModule)
var mockKcClient = mock.NewKcClient(mockCtrl)
var mockLogger = log.NewNopLogger()
component := NewComponent(mockDBModule, mockKcClient, mockLogger)

var errDbModule = errors.New("Dummy error in db module")
var realm = "the_realm_name"
var params = map[string]string{"realm": realm}
var expected = api.StatisticsRepresentation{
LastConnection: 1234567890,
TotalConnections: api.StatisticsConnectionsRepresentation{
Expand All @@ -42,7 +46,7 @@ func TestGetStatistics(t *testing.T) {
{
// db.GetLastConnection fails
mockDBModule.EXPECT().GetLastConnection(gomock.Any(), realm).Return(int64(0), errDbModule).Times(1)
_, err := component.GetStatistics(context.TODO(), params)
_, err := component.GetStatistics(context.TODO(), realm)
assert.Equal(t, errDbModule, err)
}

Expand All @@ -54,7 +58,7 @@ func TestGetStatistics(t *testing.T) {
mockDBModule.EXPECT().GetTotalConnectionsCount(gomock.Any(), realm, "1 WEEK").Return(expected.TotalConnections.LastWeek, nil).Times(1)
mockDBModule.EXPECT().GetTotalConnectionsCount(gomock.Any(), realm, "1 MONTH").Return(expected.TotalConnections.LastMonth, nil).Times(1)
mockDBModule.EXPECT().GetTotalConnectionsCount(gomock.Any(), realm, "1 YEAR").Return(expected.TotalConnections.LastYear, nil).Times(1)
res, err := component.GetStatistics(context.TODO(), params)
res, err := component.GetStatistics(context.TODO(), realm)
assert.Nil(t, err)
assert.Equal(t, expected, res)
}
Expand Down
16 changes: 13 additions & 3 deletions pkg/statistics/endpoint.go
Expand Up @@ -9,12 +9,22 @@ import (

// Endpoints exposed for path /events
type Endpoints struct {
GetStatistics endpoint.Endpoint
GetStatistics endpoint.Endpoint
GetMigrationReport endpoint.Endpoint
}

// MakeGetStatisticsEndpoint makes the events summary endpoint.
// MakeGetStatisticsEndpoint makes the statistic summary endpoint.
func MakeGetStatisticsEndpoint(ec Component) cs.Endpoint {
return func(ctx context.Context, req interface{}) (interface{}, error) {
return ec.GetStatistics(ctx, req.(map[string]string))
var m = req.(map[string]string)
return ec.GetStatistics(ctx, m["realm"])
}
}

// MakeGetMigrationReportEndpoint makes the migration reporting endpoint.
func MakeGetMigrationReportEndpoint(ec Component) cs.Endpoint {
return func(ctx context.Context, req interface{}) (interface{}, error) {
var m = req.(map[string]string)
return ec.GetMigrationReport(ctx, m["realm"])
}
}
3 changes: 2 additions & 1 deletion pkg/statistics/endpoint_test.go
Expand Up @@ -20,8 +20,9 @@ func TestMakeGetStatisticsEndpoint(t *testing.T) {

var ctx = context.Background()
var req = make(map[string]string)
req["realm"] = "realm"

mockComponent.EXPECT().GetStatistics(ctx, req).Return(api.StatisticsRepresentation{}, nil).Times(1)
mockComponent.EXPECT().GetStatistics(ctx, "realm").Return(api.StatisticsRepresentation{}, nil).Times(1)
var res, err = e(ctx, req)
assert.Nil(t, err)
assert.NotNil(t, res)
Expand Down
2 changes: 1 addition & 1 deletion pkg/statistics/http_test.go
Expand Up @@ -7,10 +7,10 @@ import (
"net/http/httptest"
"testing"

"github.com/cloudtrust/common-service/log"
api "github.com/cloudtrust/keycloak-bridge/api/statistics"
"github.com/cloudtrust/keycloak-bridge/internal/keycloakb"
"github.com/cloudtrust/keycloak-bridge/pkg/statistics/mock"
"github.com/cloudtrust/common-service/log"
"github.com/golang/mock/gomock"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
Expand Down
2 changes: 2 additions & 0 deletions pkg/statistics/mock_test.go
@@ -1,5 +1,7 @@
package statistics

//go:generate mockgen -destination=./mock/kc.go -package=mock -mock_names=KeycloakClient=KcClient github.com/cloudtrust/keycloak-bridge/pkg/statistics KeycloakClient
//go:generate mockgen -destination=./mock/component.go -package=mock -mock_names=Component=Component github.com/cloudtrust/keycloak-bridge/pkg/statistics Component
//go:generate mockgen -destination=./mock/dbmodule.go -package=mock -mock_names=EventsDBModule=EventsDBModule github.com/cloudtrust/keycloak-bridge/internal/keycloakb EventsDBModule
//go:generate mockgen -destination=./mock/keycloak_client.go -package=mock -mock_names=KeycloakClient=KeycloakClient github.com/cloudtrust/common-service/security KeycloakClient
//go:generate mockgen -destination=./mock/dbmodule.go -package=mock -mock_names=EventsDBModule=EventsDBModule github.com/cloudtrust/keycloak-bridge/internal/keycloakb EventsDBModule

0 comments on commit edb0b58

Please sign in to comment.