diff --git a/api/account/api.go b/api/account/api.go index 128a785f6..14e374d88 100644 --- a/api/account/api.go +++ b/api/account/api.go @@ -4,6 +4,7 @@ import ( "errors" "regexp" + internal "github.com/cloudtrust/keycloak-bridge/internal/keycloakb" kc "github.com/cloudtrust/keycloak-client" ) @@ -107,23 +108,23 @@ func ConvertToKCUser(user AccountRepresentation) kc.UserRepresentation { // Validate is a validator for AccountRepresentation func (user AccountRepresentation) Validate() error { if user.Username != nil && !matchesRegExp(*user.Username, RegExpUsername) { - return errors.New("Invalid username") + return errors.New(internal.MsgErrInvalidParam + "." + internal.Username) } if user.Email != nil && !matchesRegExp(*user.Email, RegExpEmail) { - return errors.New("Invalid email") + return errors.New(internal.MsgErrInvalidParam + "." + internal.Email) } if user.FirstName != nil && !matchesRegExp(*user.FirstName, RegExpFirstName) { - return errors.New("Invalid firstname") + return errors.New(internal.MsgErrInvalidParam + "." + internal.Firstname) } if user.LastName != nil && !matchesRegExp(*user.LastName, RegExpLastName) { - return errors.New("Invalid lastname") + return errors.New(internal.MsgErrInvalidParam + "." + internal.Lastname) } if user.PhoneNumber != nil && !matchesRegExp(*user.PhoneNumber, RegExpPhoneNumber) { - return errors.New("Invalid phone number") + return errors.New(internal.MsgErrInvalidParam + "." + internal.PhoneNumber) } return nil @@ -132,15 +133,15 @@ func (user AccountRepresentation) Validate() error { // Validate is a validator for UpdatePasswordBody func (updatePwd UpdatePasswordBody) Validate() error { if !matchesRegExp(updatePwd.CurrentPassword, RegExpPassword) { - return errors.New("Invalid current Password") + return errors.New(internal.MsgErrInvalidParam + "." + internal.CurrentPassword) } if !matchesRegExp(updatePwd.NewPassword, RegExpPassword) { - return errors.New("Invalid new Password") + return errors.New(internal.MsgErrInvalidParam + "." + internal.NewPassword) } if !matchesRegExp(updatePwd.ConfirmPassword, RegExpPassword) { - return errors.New("Invalid confirm Password") + return errors.New(internal.MsgErrInvalidParam + "." + internal.ConfirmPassword) } return nil @@ -149,15 +150,15 @@ func (updatePwd UpdatePasswordBody) Validate() error { // Validate is a validator for CredentialRepresentation func (credential CredentialRepresentation) Validate() error { if credential.ID != nil && !matchesRegExp(*credential.ID, RegExpID) { - return errors.New("Invalid Id") + return errors.New(internal.MsgErrInvalidParam + "." + internal.ID) } if credential.Type != nil && !matchesRegExp(*credential.Type, RegExpType) { - return errors.New("Invalid Type") + return errors.New(internal.MsgErrInvalidParam + "." + internal.Type) } if credential.UserLabel != nil && !matchesRegExp(*credential.UserLabel, RegExpLabel) { - return errors.New("Invalid Label") + return errors.New(internal.MsgErrInvalidParam + "." + internal.Label) } return nil diff --git a/internal/keycloakb/errormessages.go b/internal/keycloakb/errormessages.go index 7f08c5396..d021b4c0f 100644 --- a/internal/keycloakb/errormessages.go +++ b/internal/keycloakb/errormessages.go @@ -24,6 +24,7 @@ const ( UserID = "userId" Username = "username" User = "user" + UserLabel = "userLabel" Email = "email" Firstname = "firstname" Lastname = "lastname" diff --git a/internal/keycloakb/logging.go b/internal/keycloakb/logging.go index 9aec2f0a6..881481611 100644 --- a/internal/keycloakb/logging.go +++ b/internal/keycloakb/logging.go @@ -1,5 +1,10 @@ package keycloakb +import ( + "context" + "encoding/json" +) + // Logger interface for logging with level type Logger interface { Debug(keyvals ...interface{}) error @@ -7,3 +12,20 @@ type Logger interface { Warn(keyvals ...interface{}) error Error(keyvals ...interface{}) error } + +// LogUnrecordedEvent logs the events that could not be reported in the DB +func LogUnrecordedEvent(ctx context.Context, logger Logger, eventName string, errorMessage string, values ...string) { + if len(values)%2 != 0 { + logger.Error("err", "When logging an unrecorded event the number of parameters should be even") + } + m := map[string]interface{}{"event_name": eventName} + for i := 0; i < len(values); i += 2 { + m[values[i]] = values[i+1] + } + eventJSON, errMarshal := json.Marshal(m) + if errMarshal == nil { + logger.Error("err", errorMessage, "event", string(eventJSON)) + } else { + logger.Error("err", errorMessage) + } +} diff --git a/pkg/account/component.go b/pkg/account/component.go index 344a6c174..f83fff6fd 100644 --- a/pkg/account/component.go +++ b/pkg/account/component.go @@ -67,8 +67,12 @@ func NewComponent(keycloakAccountClient KeycloakAccountClient, eventDBModule dat } } -func (c *component) reportEvent(ctx context.Context, apiCall string, values ...string) error { - return c.eventDBModule.ReportEvent(ctx, apiCall, "self-service", values...) +func (c *component) reportEvent(ctx context.Context, apiCall string, values ...string) { + errEvent := c.eventDBModule.ReportEvent(ctx, apiCall, "self-service", values...) + if errEvent != nil { + //store in the logs also the event that failed to be stored in the DB + internal.LogUnrecordedEvent(ctx, c.logger, apiCall, errEvent.Error(), values...) + } } func (c *component) UpdatePassword(ctx context.Context, currentPassword, newPassword, confirmPassword string) error { @@ -91,18 +95,7 @@ func (c *component) UpdatePassword(ctx context.Context, currentPassword, newPass } //store the API call into the DB - errEvent := c.reportEvent(ctx, "PASSWORD_RESET", database.CtEventRealmName, realm, database.CtEventUserID, userID, database.CtEventUsername, username) - if errEvent != nil { - //store in the logs also the event that failed to be stored in the DB - m := map[string]interface{}{"event_name": "PASSWORD_RESET", database.CtEventRealmName: realm, database.CtEventUserID: userID, database.CtEventUsername: username} - eventJSON, errMarshal := json.Marshal(m) - if errMarshal == nil { - c.logger.Error("err", errEvent.Error(), "event", string(eventJSON)) - } else { - c.logger.Error("err", errEvent.Error()) - } - - } + c.reportEvent(ctx, "PASSWORD_RESET", database.CtEventRealmName, realm, database.CtEventUserID, userID, database.CtEventUsername, username) return nil } @@ -194,7 +187,7 @@ func (c *component) UpdateAccount(ctx context.Context, user api.AccountRepresent } //store the API call into the DB - _ = c.reportEvent(ctx, "UPDATE_ACCOUNT", database.CtEventRealmName, realm, database.CtEventUserID, userID, database.CtEventUsername, username) + c.reportEvent(ctx, "UPDATE_ACCOUNT", database.CtEventRealmName, realm, database.CtEventUserID, userID, database.CtEventUsername, username) return nil } @@ -210,6 +203,9 @@ func (c *component) DeleteAccount(ctx context.Context) error { return err } + //store the API call into the DB + c.reportEvent(ctx, "SELF_DELETE_ACCOUNT", database.CtEventRealmName, realm) + return nil } @@ -263,7 +259,8 @@ func (c *component) UpdateLabelCredential(ctx context.Context, credentialID stri //store the API call into the DB // the error should be treated additionalInfos, _ := json.Marshal(map[string]string{"credentialID": credentialID, "label": label}) - _ = c.reportEvent(ctx, "SELF_UPDATE_CREDENTIAL", database.CtEventRealmName, currentRealm, database.CtEventUserID, userID, database.CtEventUsername, username, database.CtEventAdditionalInfo, string(additionalInfos)) + + c.reportEvent(ctx, "SELF_UPDATE_CREDENTIAL", database.CtEventRealmName, currentRealm, database.CtEventUserID, userID, database.CtEventUsername, username, database.CtEventAdditionalInfo, string(additionalInfos)) return nil } @@ -281,10 +278,10 @@ func (c *component) DeleteCredential(ctx context.Context, credentialID string) e return err } - //store the API call into the DB - // the error should be treated additionalInfos, _ := json.Marshal(map[string]string{"credentialID": credentialID}) - _ = c.reportEvent(ctx, "SELF_DELETE_CREDENTIAL", database.CtEventRealmName, currentRealm, database.CtEventUserID, userID, database.CtEventUsername, username, database.CtEventAdditionalInfo, string(additionalInfos)) + + //store the API call into the DB + c.reportEvent(ctx, "SELF_DELETE_CREDENTIAL", database.CtEventRealmName, currentRealm, database.CtEventUserID, userID, database.CtEventUsername, username, database.CtEventAdditionalInfo, string(additionalInfos)) return nil } @@ -307,10 +304,10 @@ func (c *component) MoveCredential(ctx context.Context, credentialID string, pre return err } + additionalInfos, err := json.Marshal(map[string]string{"credentialID": credentialID, "previousCredentialID": previousCredentialID}) + //store the API call into the DB - // the error should be treated - additionalInfos, _ := json.Marshal(map[string]string{"credentialID": credentialID, "previousCredentialID": previousCredentialID}) - _ = c.reportEvent(ctx, "SELF_MOVE_CREDENTIAL", database.CtEventRealmName, currentRealm, database.CtEventUserID, userID, database.CtEventUsername, username, database.CtEventAdditionalInfo, string(additionalInfos)) + c.reportEvent(ctx, "SELF_MOVE_CREDENTIAL", database.CtEventRealmName, currentRealm, database.CtEventUserID, userID, database.CtEventUsername, username, database.CtEventAdditionalInfo, string(additionalInfos)) return nil } diff --git a/pkg/account/component_test.go b/pkg/account/component_test.go index 8a8e34707..86abb2eec 100644 --- a/pkg/account/component_test.go +++ b/pkg/account/component_test.go @@ -391,6 +391,7 @@ func TestDeleteUser(t *testing.T) { // Delete user with succces { mockKeycloakAccountClient.EXPECT().DeleteAccount(accessToken, realmName).Return(nil).Times(1) + mockEventDBModule.EXPECT().ReportEvent(ctx, "SELF_DELETE_ACCOUNT", "self-service", gomock.Any(), gomock.Any()).Return(nil).AnyTimes() err := accountComponent.DeleteAccount(ctx) diff --git a/pkg/account/endpoint.go b/pkg/account/endpoint.go index d4f0fed80..67622dce8 100644 --- a/pkg/account/endpoint.go +++ b/pkg/account/endpoint.go @@ -88,7 +88,7 @@ func MakeUpdateLabelCredentialEndpoint(component AccountComponent) cs.Endpoint { err := json.Unmarshal([]byte(m["body"]), &body) if err != nil { - return nil, errrorhandler.CreateBadRequestError("Invalid body") + return nil, errrorhandler.CreateBadRequestError(internal.MsgErrInvalidParam + "." + internal.Body) } if err = body.Validate(); err != nil { @@ -96,7 +96,7 @@ func MakeUpdateLabelCredentialEndpoint(component AccountComponent) cs.Endpoint { } if body.UserLabel == nil { - return nil, errrorhandler.CreateBadRequestError("User label missing") + return nil, errrorhandler.CreateBadRequestError(internal.MsgErrMissingParam + "." + internal.UserLabel) } return nil, component.UpdateLabelCredential(ctx, m["credentialID"], *body.UserLabel) diff --git a/pkg/events/component.go b/pkg/events/component.go index 918ae4a43..f0df14c4f 100644 --- a/pkg/events/component.go +++ b/pkg/events/component.go @@ -2,7 +2,6 @@ package events import ( "context" - "encoding/json" "github.com/cloudtrust/common-service/database" errorhandler "github.com/cloudtrust/common-service/errors" @@ -32,8 +31,13 @@ func NewComponent(db app.EventsDBModule, eventDBModule database.EventsDBModule, } } -func (ec *component) reportEvent(ctx context.Context, apiCall string, values ...string) error { - return ec.eventDBModule.ReportEvent(ctx, apiCall, "back-office", values...) +func (ec *component) reportEvent(ctx context.Context, apiCall string, values ...string) { + errEvent := ec.eventDBModule.ReportEvent(ctx, apiCall, "back-office", values...) + if errEvent != nil { + //store in the logs also the event that failed to be stored in the DB + app.LogUnrecordedEvent(ctx, ec.logger, apiCall, errEvent.Error(), values...) + } + } // Get events according to optional parameters @@ -65,16 +69,6 @@ func (ec *component) GetUserEvents(ctx context.Context, params map[string]string return api.AuditEventsRepresentation{}, errorhandler.CreateMissingParameterError(app.UserID) } - err := ec.reportEvent(ctx, "GET_ACTIVITY", database.CtEventRealmName, params["realm"], database.CtEventUserID, params["userID"]) - if err != nil { - //store in the logs also the event that failed to be stored in the DB - m := map[string]interface{}{"event_name": "GET_ACTIVITY", database.CtEventRealmName: params["realm"], database.CtEventUserID: params["userID"]} - eventJSON, errMarshal := json.Marshal(m) - if errMarshal == nil { - ec.logger.Error("err", err.Error(), "event", string(eventJSON)) - } else { - ec.logger.Error("err", err.Error()) - } - } + ec.reportEvent(ctx, "GET_ACTIVITY", database.CtEventRealmName, params["realm"], database.CtEventUserID, params["userID"]) return ec.GetEvents(ctx, params) } diff --git a/pkg/management/component.go b/pkg/management/component.go index 25f244d79..534e3fe87 100644 --- a/pkg/management/component.go +++ b/pkg/management/component.go @@ -2,7 +2,6 @@ package management import ( "context" - "encoding/json" "regexp" "strings" @@ -107,8 +106,12 @@ func NewComponent(keycloakClient KeycloakClient, eventDBModule database.EventsDB } } -func (c *component) reportEvent(ctx context.Context, apiCall string, values ...string) error { - return c.eventDBModule.ReportEvent(ctx, apiCall, "back-office", values...) +func (c *component) reportEvent(ctx context.Context, apiCall string, values ...string) { + errEvent := c.eventDBModule.ReportEvent(ctx, apiCall, "back-office", values...) + if errEvent != nil { + //store in the logs also the event that failed to be stored in the DB + internal.LogUnrecordedEvent(ctx, c.logger, apiCall, errEvent.Error(), values...) + } } func (c *component) GetRealms(ctx context.Context) ([]api.RealmRepresentation, error) { @@ -227,17 +230,7 @@ func (c *component) CreateUser(ctx context.Context, realmName string, user api.U userID := string(reg.Find([]byte(locationURL))) //store the API call into the DB - err = c.reportEvent(ctx, "API_ACCOUNT_CREATION", database.CtEventRealmName, realmName, database.CtEventUserID, userID, database.CtEventUsername, username) - if err != nil { - //store in the logs also the event that failed to be stored in the DB - m := map[string]interface{}{"event_name": "API_ACCOUNT_CREATION", database.CtEventRealmName: realmName, database.CtEventUserID: userID, database.CtEventUsername: username} - eventJSON, errMarshal := json.Marshal(m) - if errMarshal == nil { - c.logger.Error("err", err.Error(), "event", string(eventJSON)) - } else { - c.logger.Error("err", err.Error()) - } - } + c.reportEvent(ctx, "API_ACCOUNT_CREATION", database.CtEventRealmName, realmName, database.CtEventUserID, userID, database.CtEventUsername, username) return locationURL, nil } @@ -253,17 +246,7 @@ func (c *component) DeleteUser(ctx context.Context, realmName, userID string) er } //store the API call into the DB - err = c.reportEvent(ctx, "API_ACCOUNT_DELETION", database.CtEventRealmName, realmName, database.CtEventUserID, userID) - if err != nil { - //store in the logs also the event that failed to be stored in the DB - m := map[string]interface{}{"event_name": "API_ACCOUNT_DELETION", database.CtEventRealmName: realmName, database.CtEventUserID: userID} - eventJSON, errMarshal := json.Marshal(m) - if errMarshal == nil { - c.logger.Error("err", err.Error(), "event", string(eventJSON)) - } else { - c.logger.Error("err", err.Error()) - } - } + c.reportEvent(ctx, "API_ACCOUNT_DELETION", database.CtEventRealmName, realmName, database.CtEventUserID, userID) return nil } @@ -287,17 +270,8 @@ func (c *component) GetUser(ctx context.Context, realmName, userID string) (api. } //store the API call into the DB - err = c.reportEvent(ctx, "GET_DETAILS", database.CtEventRealmName, realmName, database.CtEventUserID, userID, database.CtEventUsername, username) - if err != nil { - //store in the logs also the event that failed to be stored in the DB - m := map[string]interface{}{"event_name": "GET_DETAILS", database.CtEventRealmName: realmName, database.CtEventUserID: userID, database.CtEventUsername: username} - eventJSON, errMarshal := json.Marshal(m) - if errMarshal == nil { - c.logger.Error("err", err.Error(), "event", string(eventJSON)) - } else { - c.logger.Error("err", err.Error()) - } - } + c.reportEvent(ctx, "GET_DETAILS", database.CtEventRealmName, realmName, database.CtEventUserID, userID, database.CtEventUsername, username) + return userRep, nil } @@ -376,17 +350,8 @@ func (c *component) UpdateUser(ctx context.Context, realmName, userID string, us ctEventType = "LOCK_ACCOUNT" } - err = c.reportEvent(ctx, ctEventType, database.CtEventRealmName, realmName, database.CtEventUserID, userID, database.CtEventUsername, username) - if err != nil { - //store in the logs also the event that failed to be stored in the DB - m := map[string]interface{}{"event_name": ctEventType, database.CtEventRealmName: realmName, database.CtEventUserID: userID, database.CtEventUsername: username} - eventJSON, errMarshal := json.Marshal(m) - if errMarshal == nil { - c.logger.Error("err", err.Error(), "event", string(eventJSON)) - } else { - c.logger.Error("err", err.Error()) - } - } + c.reportEvent(ctx, ctEventType, database.CtEventRealmName, realmName, database.CtEventUserID, userID, database.CtEventUsername, username) + } return nil @@ -578,17 +543,7 @@ func (c *component) ResetPassword(ctx context.Context, realmName string, userID } //store the API call into the DB - err = c.reportEvent(ctx, "INIT_PASSWORD", database.CtEventRealmName, realmName, database.CtEventUserID, userID) - if err != nil { - //store in the logs also the event that failed to be stored in the DB - m := map[string]interface{}{"event_name": "INIT_PASSWORD", database.CtEventRealmName: realmName, database.CtEventUserID: userID} - eventJSON, errMarshal := json.Marshal(m) - if errMarshal == nil { - c.logger.Error("err", err.Error(), "event", string(eventJSON)) - } else { - c.logger.Error("err", err.Error()) - } - } + c.reportEvent(ctx, "INIT_PASSWORD", database.CtEventRealmName, realmName, database.CtEventUserID, userID) return pwd, nil } @@ -614,18 +569,7 @@ func (c *component) ExecuteActionsEmail(ctx context.Context, realmName string, u actions = append(actions, string(requiredAction)) if string(requiredAction) == initPasswordAction { //store the API call into the DB - err := c.reportEvent(ctx, "INIT_PASSWORD", database.CtEventRealmName, realmName, database.CtEventUserID, userID) - if err != nil { - //store in the logs also the event that failed to be stored in the DB - m := map[string]interface{}{"event_name": "INIT_PASSWORD", database.CtEventRealmName: realmName, database.CtEventUserID: userID} - eventJSON, errMarshal := json.Marshal(m) - if errMarshal == nil { - c.logger.Error("err", err.Error(), "event", string(eventJSON)) - } else { - c.logger.Error("err", err.Error()) - } - - } + c.reportEvent(ctx, "INIT_PASSWORD", database.CtEventRealmName, realmName, database.CtEventUserID, userID) } } @@ -649,17 +593,7 @@ func (c *component) SendNewEnrolmentCode(ctx context.Context, realmName string, } // store the API call into the DB - errEvent := c.reportEvent(ctx, "SMS_CHALLENGE", database.CtEventRealmName, realmName, database.CtEventUserID, userID) - if errEvent != nil { - //store in the logs also the event that failed to be stored in the DB - m := map[string]interface{}{"event_name": "SMS_CHALLENGE", database.CtEventRealmName: realmName, database.CtEventUserID: userID} - eventJSON, errMarshal := json.Marshal(m) - if errMarshal == nil { - c.logger.Error("err", errEvent.Error(), "event", string(eventJSON)) - } else { - c.logger.Error("err", errEvent.Error()) - } - } + c.reportEvent(ctx, "SMS_CHALLENGE", database.CtEventRealmName, realmName, database.CtEventUserID, userID) return *smsCodeKc.Code, err } @@ -713,17 +647,7 @@ func (c *component) DeleteCredentialsForUser(ctx context.Context, realmName stri for _, credKc := range credsKc { if *credKc.Id == credentialID && *credKc.Type != "password" { - errEvent := c.reportEvent(ctx, "2ND_FACTOR_REMOVED", database.CtEventRealmName, realmName, database.CtEventUserID, userID) - if errEvent != nil { - //store in the logs also the event that failed to be stored in the DB - m := map[string]interface{}{"event_name": "2ND_FACTOR_REMOVED", database.CtEventRealmName: realmName, database.CtEventUserID: userID} - eventJSON, errMarshal := json.Marshal(m) - if errMarshal == nil { - c.logger.Error("err", errEvent.Error(), "event", string(eventJSON)) - } else { - c.logger.Error("err", errEvent.Error()) - } - } + c.reportEvent(ctx, "2ND_FACTOR_REMOVED", database.CtEventRealmName, realmName, database.CtEventUserID, userID) break } }