Skip to content

Commit

Permalink
Merge 139991f into c9557f9
Browse files Browse the repository at this point in the history
  • Loading branch information
bsoniam committed Apr 16, 2019
2 parents c9557f9 + 139991f commit 9ae28eb
Show file tree
Hide file tree
Showing 15 changed files with 605 additions and 394 deletions.
236 changes: 116 additions & 120 deletions Gopkg.lock

Large diffs are not rendered by default.

21 changes: 18 additions & 3 deletions cmd/keycloakb/keycloak_bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,19 @@ func main() {
{
var managementLogger = log.With(logger, "svc", "management")

// module to store API calls of the back office to the DB
var eventsDBModule event.EventsDBModule
{
eventsDBModule = event.NewEventsDBModule(eventsDBConn)
eventsDBModule = event.MakeEventsDBModuleInstrumentingMW(influxMetrics.NewHistogram("eventsDB_module"))(eventsDBModule)
eventsDBModule = event.MakeEventsDBModuleLoggingMW(log.With(managementLogger, "mw", "module", "unit", "eventsDB"))(eventsDBModule)
eventsDBModule = event.MakeEventsDBModuleTracingMW(tracer)(eventsDBModule)

}

var keycloakComponent management.Component
{
keycloakComponent = management.NewComponent(keycloakClient)
keycloakComponent = management.NewComponent(keycloakClient, eventsDBModule)
}

managementEndpoints = management.Endpoints{
Expand Down Expand Up @@ -527,13 +537,18 @@ func config(logger log.Logger) *viper.Viper {
v.SetDefault("keycloak-timeout", "5s")

//Storage events in DB
v.SetDefault("events-DB", false)
v.SetDefault("events-db", false)

// DB
v.SetDefault("db-host-port", "")
v.SetDefault("db-username", "")
v.SetDefault("db-password", "")
v.SetDefault("db-database", "")
v.SetDefault("db-table", "")
v.SetDefault("protocol", "")
v.SetDefault("db-protocol", "")
v.SetDefault("db-max-open-conns", 10)
v.SetDefault("db-max-idle-conns", 2)
v.SetDefault("db-conn-max-lifetime", 3600)

// Rate limiting (in requests/second)
v.SetDefault("rate-event", 1000)
Expand Down
124 changes: 74 additions & 50 deletions pkg/event/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@ import (
"context"
"encoding/json"
"fmt"
"regexp"
"strings"
"sync"
"time"

"github.com/cloudtrust/keycloak-bridge/api/event/fb"
)

const (
timeFormat = "2006-01-02 15:04:05.000"
)

// MuxComponent is the Mux component interface.
type MuxComponent interface {
Event(ctx context.Context, eventType string, obj []byte) error
Expand Down Expand Up @@ -122,48 +127,44 @@ func (c *adminComponent) AdminEvent(ctx context.Context, adminEvent *fb.AdminEve
func addCTtypeToEvent(event map[string]string) map[string]string {
// add the ct_event_type

switch opType := event["operationType"]; opType {
addInfo := []byte(event["additional_info"])
var f map[string]string
_ = json.Unmarshal(addInfo, &f)

switch opType := event["kc_operation_type"]; opType {
case "CREATE":
//ACCOUNT_CREATED
// check if the resourcePath starts with prefix users
if strings.HasPrefix(event["resourcePath"], "users") {
if strings.HasPrefix(f["resource_path"], "users") {
event["ct_event_type"] = "ACCOUNT_CREATED"
return event
}
case "ACTION":
//ACTIVATION_EMAIL_SENT
// check if the resourcePath ends with sufix send-verify-email
if strings.HasSuffix(event["resourcePath"], "send-verify-email") {
// check if the resourcePath ends with suffix send-verify-email
if strings.HasSuffix(f["resource_path"], "send-verify-email") {
event["ct_event_type"] = "ACTIVATION_EMAIL_SENT"
return event
}
default:

// Nothing to do here
}

switch t := event["type"]; t {
switch t := event["kc_event_type"]; t {
case "CUSTOM_REQUIRED_ACTION":
//EMAIL_CONFIRMED
eventDetails := []byte(event["details"])
var f map[string]string
_ = json.Unmarshal(eventDetails, &f)

if f["custom_required_action"] == "VERIFY_EMAIL" {
event["ct_event_type"] = "EMAIL_CONFIRMED"
return event
}
case "EXECUTE_ACTION_TOKEN_ERROR":
//CONFIRM_EMAIL_EXPIRED
if event["error"] == "expired_code" {
if f["error"] == "expired_code" {
event["ct_event_type"] = "CONFIRM_EMAIL_EXPIRED"
return event
}
case "UPDATE_PASSWORD":
//PASSWORD_RESET
eventDetails := []byte(event["details"])
var f map[string]string
_ = json.Unmarshal(eventDetails, &f)

if f["custom_required_action"] == "sms-password-set" {
event["ct_event_type"] = "PASSWORD_RESET"
return event
Expand All @@ -182,7 +183,7 @@ func addCTtypeToEvent(event map[string]string) map[string]string {
event["ct_event_type"] = "LOGOUT"
return event
default:

// Nothing to do here
}

// for all those events that don't have set the ct_event_type, we assign an empty ct_event_type
Expand All @@ -195,33 +196,40 @@ func addCTtypeToEvent(event map[string]string) map[string]string {

func adminEventToMap(adminEvent *fb.AdminEvent) map[string]string {
var adminEventMap = make(map[string]string)
adminEventMap["uid"] = fmt.Sprint(adminEvent.Uid())
var addInfo = make(map[string]string)

time := epochMilliToTime(adminEvent.Time())
adminEventMap["time"] = time.Format("2006-01-02T15:04:05.000Z")
addInfo["uid"] = fmt.Sprint(adminEvent.Uid())

adminEventMap["realmId"] = string(adminEvent.RealmId())
time := epochMilliToTime(adminEvent.Time()).UTC()
adminEventMap["audit_time"] = time.Format(timeFormat) //audit_time

adminEventMap["realm_name"] = string(adminEvent.RealmId()) //realm_name
adminEventMap["origin"] = "keycloak" //origin

authDetails := adminEvent.AuthDetails(nil)
var authDetailsMap map[string]string
authDetailsMap = make(map[string]string)
authDetailsMap["clientId"] = string(authDetails.ClientId())
authDetailsMap["ipAddress"] = string(authDetails.IpAddress())
authDetailsMap["realmId"] = string(authDetails.RealmId())
authDetailsMap["userId"] = string(authDetails.UserId())

// BE AWARE: error is not treated
authDetailsJson, _ := json.Marshal(authDetailsMap)
adminEventMap["authDetails"] = string(authDetailsJson)

adminEventMap["resourceType"] = string(adminEvent.ResourceType())
adminEventMap["operationType"] = fb.EnumNamesOperationType[int8(adminEvent.OperationType())]
adminEventMap["resourcePath"] = string(adminEvent.ResourcePath())
adminEventMap["representation"] = string(adminEvent.Representation())
adminEventMap["error"] = string(adminEvent.Error())
adminEventMap["client_id"] = string(authDetails.ClientId()) //client_id
addInfo["ip_address"] = string(authDetails.IpAddress())
adminEventMap["agent_realm_name"] = string(authDetails.RealmId()) // agent_realm_name
adminEventMap["agent_user_id"] = string(authDetails.UserId()) //agent_user_id

addInfo["resource_type"] = string(adminEvent.ResourceType())
adminEventMap["kc_operation_type"] = fb.EnumNamesOperationType[int8(adminEvent.OperationType())] //kc_operation_type
addInfo["resource_path"] = string(adminEvent.ResourcePath())
reg := regexp.MustCompile(`[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}`)
if strings.HasPrefix(addInfo["resource_path"], "users") {
adminEventMap["user_id"] = string(reg.Find([]byte(addInfo["resource_path"]))) //user_id
}

addInfo["representation"] = string(adminEvent.Representation())
addInfo["error"] = string(adminEvent.Error())
//all the admin events have, by default, the ct_event_type set to admin
adminEventMap["ct_event_type"] = "ADMIN"

// BE AWARE: error is not treated
infoJSON, _ := json.Marshal(addInfo)
adminEventMap["additional_info"] = string(infoJSON)

//set the correct ct_event_type for actions like create_account, etc.
adminEventMap = addCTtypeToEvent(adminEventMap)

Expand All @@ -230,36 +238,52 @@ func adminEventToMap(adminEvent *fb.AdminEvent) map[string]string {

func eventToMap(event *fb.Event) map[string]string {
var eventMap = make(map[string]string)
eventMap["uid"] = fmt.Sprint(event.Uid())
var addInfo = make(map[string]string)
// if an event has the ct_event_type set already, the flag avoids rewriting it
var doNotSetCTEventType bool = false

addInfo["uid"] = fmt.Sprint(event.Uid())

time := epochMilliToTime(event.Time())
eventMap["time"] = time.Format("2006-01-02T15:04:05.000Z")
time := epochMilliToTime(event.Time()).UTC()
eventMap["audit_time"] = time.Format(timeFormat) //audit_time

eventMap["type"] = fb.EnumNamesEventType[int8(event.Type())]
eventMap["realmId"] = string(event.RealmId())
eventMap["clientId"] = string(event.ClientId())
eventMap["userId"] = string(event.UserId())
eventMap["sessionId"] = string(event.SessionId())
eventMap["ipAddress"] = string(event.IpAddress())
eventMap["error"] = string(event.Error())
eventMap["kc_event_type"] = fb.EnumNamesEventType[int8(event.Type())] // kc_event_type
eventMap["realm_name"] = string(event.RealmId()) //realm_name
eventMap["client_id"] = string(event.ClientId()) //client_id
eventMap["agent_user_id"] = string(event.UserId()) //agent_user_id
eventMap["user_id"] = string(event.UserId()) //user_id
//Note: we make the assumption that the agent and the user are the same in the case of the events that are not admin events

addInfo["session_id"] = string(event.SessionId())
addInfo["ip_address"] = string(event.IpAddress())
addInfo["error"] = string(event.Error())
eventMap["origin"] = "keycloak" //origin

var detailsMap = make(map[string]string)
var detailsLength = event.DetailsLength()
for i := 0; i < detailsLength; i++ {
var tuple = new(fb.Tuple)
event.Details(tuple, i)
if string(tuple.Key()) == "ct_event_type" {
eventMap[string(tuple.Key())] = string(tuple.Value())
doNotSetCTEventType = true
} else {
detailsMap[string(tuple.Key())] = string(tuple.Value())
if string(tuple.Key()) == "username" {
eventMap["agent_username"] = string(tuple.Value()) //agent_username
eventMap[string(tuple.Key())] = string(tuple.Value()) //username
} else {
addInfo[string(tuple.Key())] = string(tuple.Value())
}

}
}

// BE AWARE: error is not treated
detailsJson, _ := json.Marshal(detailsMap)
eventMap["details"] = string(detailsJson)
infoJSON, _ := json.Marshal(addInfo)
eventMap["additional_info"] = string(infoJSON)

eventMap = addCTtypeToEvent(eventMap)
if !doNotSetCTEventType {
eventMap = addCTtypeToEvent(eventMap)
}

return eventMap
}
Expand Down
82 changes: 62 additions & 20 deletions pkg/event/component_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package event

import (
"context"
"encoding/json"
"errors"
"strconv"
"testing"
Expand Down Expand Up @@ -123,13 +124,14 @@ func TestAdminComponent(t *testing.T) {
func TestEventToMap(t *testing.T) {
var uid int64 = 1234
var epoch = int64(1547127600485)
var etype int8
var etype int8 = 6
var realmID = "realm"
var clientID = "client"
var userID = "user"
var sessionID = "session"
var ipAddr = "ipAddress"
var error = "error"
var username = "test_username"

var event *fb.Event
{
Expand All @@ -143,7 +145,7 @@ func TestEventToMap(t *testing.T) {
var error = builder.CreateString(error)

var key1 = builder.CreateString("username")
var value1 = builder.CreateString("test_username")
var value1 = builder.CreateString(username)
fb.TupleStart(builder)
fb.TupleAddKey(builder, key1)
fb.TupleAddValue(builder, value1)
Expand Down Expand Up @@ -178,20 +180,56 @@ func TestEventToMap(t *testing.T) {
}

var m = eventToMap(event)
assert.Equal(t, strconv.FormatInt(uid, 10), m["uid"])
assert.Equal(t, time.Unix(0, epoch*1000000).Format("2006-01-02T15:04:05.000Z"), m["time"])
assert.Equal(t, fb.EnumNamesEventType[int8(etype)], m["type"])
assert.Equal(t, realmID, m["realmId"])
assert.Equal(t, clientID, m["clientId"])
assert.Equal(t, userID, m["userId"])
assert.Equal(t, sessionID, m["sessionId"])
assert.Equal(t, ipAddr, m["ipAddress"])
assert.Equal(t, error, m["error"])
assert.Equal(t, time.Unix(0, epoch*1000000).UTC().Format("2006-01-02 15:04:05.000"), m["audit_time"])
assert.Equal(t, fb.EnumNamesEventType[int8(etype)], m["kc_event_type"])
assert.Equal(t, realmID, m["realm_name"])
assert.Equal(t, clientID, m["client_id"])
assert.Equal(t, userID, m["user_id"])
assert.Equal(t, username, m["username"])
var f = make(map[string]string)
err := json.Unmarshal([]byte(m["additional_info"]), &f)
assert.Nil(t, err)
assert.Equal(t, strconv.FormatInt(uid, 10), f["uid"])
assert.Equal(t, sessionID, f["session_id"])
assert.Equal(t, ipAddr, f["ip_address"])
assert.Equal(t, error, f["error"])
assert.Equal(t, "", m["ct_event_type"])

}

func TestEventToMapNewCTEvent(t *testing.T) {
var customEvent = "CUSTOM_EVENT"
var etype int8 = 6

var event *fb.Event
{
var builder = flatbuffers.NewBuilder(0)
var key1 = builder.CreateString("ct_event_type")
var value1 = builder.CreateString(customEvent)
fb.TupleStart(builder)
fb.TupleAddKey(builder, key1)
fb.TupleAddValue(builder, value1)
var detail1 = fb.TupleEnd(builder)

fb.EventStartDetailsVector(builder, 1)
builder.PrependUOffsetT(detail1)
var details = builder.EndVector(1)

fb.EventStart(builder)
fb.EventAddDetails(builder, details)
fb.EventAddType(builder, etype)
var eventOffset = fb.EventEnd(builder)
builder.Finish(eventOffset)
event = fb.GetRootAsEvent(builder.FinishedBytes(), 0)
}

var m = eventToMap(event)
assert.Equal(t, customEvent, m["ct_event_type"])

}

func TestEventToMapLogon(t *testing.T) {
var etype int8 = 0
var etype int8

var event *fb.Event
{
Expand Down Expand Up @@ -422,20 +460,24 @@ func TestAdminEventToMap(t *testing.T) {
}

var m = adminEventToMap(adminEvent)
assert.Equal(t, strconv.FormatInt(uid, 10), m["uid"])
assert.Equal(t, time.Unix(0, epoch*1000000).Format("2006-01-02T15:04:05.000Z"), m["time"])
assert.Equal(t, fb.EnumNamesOperationType[int8(optype)], m["operationType"])
assert.Equal(t, realmID, m["realmId"])
assert.Equal(t, resourcePath, m["resourcePath"])
assert.Equal(t, representation, m["representation"])
assert.Equal(t, error, m["error"])

assert.Equal(t, time.Unix(0, epoch*1000000).UTC().Format("2006-01-02 15:04:05.000"), m["audit_time"])
assert.Equal(t, fb.EnumNamesOperationType[int8(optype)], m["kc_operation_type"])
assert.Equal(t, realmID, m["realm_name"])
var f = make(map[string]string)
err := json.Unmarshal([]byte(m["additional_info"]), &f)
assert.Nil(t, err)
assert.Equal(t, strconv.FormatInt(uid, 10), f["uid"])
assert.Equal(t, resourcePath, f["resource_path"])
assert.Equal(t, representation, f["representation"])
assert.Equal(t, error, f["error"])
assert.Equal(t, "ADMIN", m["ct_event_type"])

}

func TestAdminEventToMapAccountCreated(t *testing.T) {
var resourcePath = "users/8caefab3-90d1-492e-87e0-1bf6cecc76ea/role-mappings/realm "
var optype int8 = 0
var optype int8

var adminEvent *fb.AdminEvent
{
Expand Down
Loading

0 comments on commit 9ae28eb

Please sign in to comment.