From 9d162b309160e624fa96533fa99077acc27db50b Mon Sep 17 00:00:00 2001 From: Alexandros Sigalas Date: Tue, 1 Dec 2020 11:24:55 +0200 Subject: [PATCH] Port OneLogin.Events to pantherlog and add username tag (#2123) * Port OneLogin.Events to pantherlog and add username tag * mage gen fmt * fix test * Remove timestamp Co-authored-by: panther-bot Co-authored-by: Kostas Papageorgiou --- .../parsers/oneloginlogs/onelogin.go | 170 +++++------ .../parsers/oneloginlogs/onelogin_test.go | 106 +------ .../oneloginlogs/testdata/onelogin_tests.yml | 264 ++++++++++++++++++ .../parsers/timestamp/timestamp.go | 23 -- 4 files changed, 333 insertions(+), 230 deletions(-) create mode 100644 internal/log_analysis/log_processor/parsers/oneloginlogs/testdata/onelogin_tests.yml diff --git a/internal/log_analysis/log_processor/parsers/oneloginlogs/onelogin.go b/internal/log_analysis/log_processor/parsers/oneloginlogs/onelogin.go index 424da1b929..1bfc69988f 100644 --- a/internal/log_analysis/log_processor/parsers/oneloginlogs/onelogin.go +++ b/internal/log_analysis/log_processor/parsers/oneloginlogs/onelogin.go @@ -19,11 +19,8 @@ package oneloginlogs */ import ( - jsoniter "github.com/json-iterator/go" - "github.com/panther-labs/panther/internal/log_analysis/log_processor/logtypes" - "github.com/panther-labs/panther/internal/log_analysis/log_processor/parsers" - "github.com/panther-labs/panther/internal/log_analysis/log_processor/parsers/timestamp" + "github.com/panther-labs/panther/internal/log_analysis/log_processor/pantherlog" ) const TypeOneLogin = "OneLogin.Events" @@ -32,113 +29,78 @@ func LogTypes() logtypes.Group { return logTypes } -var logTypes = logtypes.Must("OneLogin", logtypes.Config{ +var logTypes = logtypes.Must("OneLogin", logtypes.ConfigJSON{ Name: TypeOneLogin, Description: `OneLogin provides single sign-on and identity management for organizations Panther Enterprise Only `, ReferenceURL: `https://developers.onelogin.com/api-docs/1/events/event-resource`, - Schema: OneLogin{}, - NewParser: parsers.AdapterFactory(&OneLoginParser{}), + NewEvent: func() interface{} { + return &OneLogin{} + }, }) // nolint:lll type OneLogin struct { - UUID *string `json:"uuid" validate:"required,uuid" description:"The Universal Unique Identifier for this message generated by OneLogin."` - AccountID *int `json:"account_id" validate:"required" description:"Account that triggered the event."` - EventTimestamp *timestamp.OneLoginTimestamp `json:"event_timestamp" validate:"required" description:"Time and date at which the event was created. This value is autogenerated by OneLogin."` - ErrorDescription *string `json:"error_description,omitempty" description:"Provisioning error details, if applicable."` - LoginName *string `json:"login_name,omitempty" description:"The name of the login user"` - AppName *string `json:"app_name,omitempty" description:"Name of the app involved in the event, if applicable."` - AuthenticationFactorDescription *string `json:"authentication_factor_description,omitempty" description:"More details about the authentication factor used."` - CertificateName *string `json:"certificate_name,omitempty" description:"The name of the certificate that was included in the request."` - CertificateID *string `json:"certificate_id,omitempty" description:"The ID of the certificate that was included in the request."` - AssumedBySuperadminOrReseller *bool `json:"assumed_by_superadmin_or_reseller,omitempty" description:"Indicates that the operation was performed by superadmin or reseller."` - DirectoryName *string `json:"directory_name,omitempty" description:"The directory name."` - ActorUserID *int `json:"actor_user_id,omitempty" description:"ID of the user whose action triggered the event."` - UserName *string `json:"user_name,omitempty" description:"Name of the user that was acted upon to trigger the event."` - MappingID *int `json:"mapping_id,omitempty" description:"The ID of the mapping included in the operation."` - RadiusConfigID *int `json:"radius_config_id,omitempty" description:"The ID of the Radius configuration included in the operation."` - RiskScore *int `json:"risk_score,omitempty" description:"The higher thiss number, the higher the risk."` - OtpDeviceID *int `json:"otp_device_id,omitempty" description:"ID of a device involved in the event."` - ImportedUserID *int `json:"imported_user_id,omitempty" description:"The ID of the imported user."` - Resolution *int `json:"resolution,omitempty" description:"The resolution."` - DirectoryID *int `json:"directory_id,omitempty" description:"The directory ID."` - AuthenticationFactorID *int `json:"authentication_factor_id,omitempty" description:"The ID of the authentication factor used."` - RiskCookieID *string `json:"risk_cookie_id,omitempty" description:"The ID of the risk cookie."` - AppID *int `json:"app_id,omitempty" description:"ID of the app involved in the event, if applicable."` - CustomMessage *string `json:"custom_message,omitempty" description:"More details about the event."` - BrowserFingerprint *string `json:"browser_fingerprint,omitempty" description:"The fingerprint of the browser."` - OtpDeviceName *string `json:"otp_device_name,omitempty" description:"Name of a device involved in the event."` - ActorUserName *string `json:"actor_user_name,omitempty" description:"First and last name of the user whose action triggered the event."` - ActorSystem *string `json:"actor_system,omitempty" description:"Acting system that triggered the event when the actor is not a user."` - UserFieldName *string `json:"user_field_name,omitempty" description:"The name of the custom user field."` - UserFieldID *string `json:"user_field_id,omitempty" description:"The ID of the custom user field."` - AssumingActingUserID *int `json:"assuming_acting_user_id,omitempty" description:"ID of the user who assumed the role of the acting user to trigger the event, if applicable."` - APICredentialName *string `json:"api_credential_name,omitempty" description:"The name of the API credential used."` - ImportedUserName *string `json:"imported_user_name,omitempty" description:"The name of the imported user."` - NoteTitle *string `json:"note_title,omitempty" description:"The title of the note."` - TrustedIdpName *string `json:"trusted_idp_name,omitempty" description:"The name of the trusted IDP."` - PolicyID *int `json:"policy_id,omitempty" description:"ID of the policy involved in the event."` - RoleName *string `json:"role_name,omitempty" description:"Name of a role involved in the event."` - ResolvedByUserID *int `json:"resolved_by_user_id,omitempty" description:"The ID of the user that resolved the issue."` - GroupID *int `json:"group_id,omitempty" description:"ID of a group involved in the event."` - ClientID *string `json:"client_id,omitempty" description:"Client ID used to generate the access token that made the API call that generated the event."` - IPAddr *string `json:"ipaddr,omitempty" description:"IP address of the machine used to trigger the event."` - Notes *string `json:"notes,omitempty" description:"More details about the event."` - EventTypeID *int `json:"event_type_id" validate:"required" description:"Type of event triggered."` - UserID *int `json:"user_id,omitempty" description:"ID of the user that was acted upon to trigger the event."` - RiskReasons *string `json:"risk_reasons,omitempty" description:"This is not an exhaustive list of the reasons for the risk score and should only be used as a guide"` - ProxyAgentName *string `json:"proxy_agent_name,omitempty" description:"The name of the proxy agent."` - PolicyType *string `json:"policy_type,omitempty" description:"The type of the policy."` - RoleID *int `json:"role_id,omitempty" description:"ID of a role involved in the event."` - UserAgent *string `json:"user_agent,omitempty" description:"The user agent from which the request was invoke"` - PrivilegeName *string `json:"privilege_name,omitempty" description:"The name of the privilege."` - GroupName *string `json:"group_name,omitempty" description:"Name of a group involved in the event."` - Entity *string `json:"entity,omitempty" description:"The entity involved in this request."` - ResourceTypeID *int `json:"resource_type_id,omitempty" description:"ID of the resource (user, role, group, and so forth) associated with the event."` - MappingName *string `json:"mapping_name,omitempty" description:"The name of the mapping."` - TaskName *string `json:"task_name,omitempty" description:"The name of the task."` - AuthenticationFactorType *int `json:"authentication_factor_type,omitempty" description:"The type of the authentication type."` - RadiusConfigName *string `json:"radius_config_name,omitempty" description:"The name of the Radius configuration used."` - PolicyName *string `json:"policy_name,omitempty" description:"Name of the policy involved in the event."` - PrivilegeID *int `json:"privilege_id,omitempty" description:"The id of the privilege."` - DirectorySyncRunID *int `json:"directory_sync_run_id,omitempty" description:"Directory sync run ID."` - OperationName *string `json:"operation_name,omitempty" description:"The name of the operation"` - - // NOTE: added to end of struct to allow expansion later - parsers.PantherLog -} - -// OneLogin parser parses OneLogin logs -type OneLoginParser struct{} - -func (p *OneLoginParser) New() parsers.LogParser { - return &OneLoginParser{} -} - -// Parse returns the parsed events or nil if parsing failed -func (p *OneLoginParser) Parse(log string) ([]*parsers.PantherLog, error) { - var event OneLogin - err := jsoniter.UnmarshalFromString(log, &event) - if err != nil { - return nil, err - } - - event.updatePantherFields(p) - - if err := parsers.Validator.Struct(event); err != nil { - return nil, err - } - return event.Logs(), nil -} - -// LogType returns the log type supported by this parser -func (p *OneLoginParser) LogType() string { - return TypeOneLogin -} - -func (event *OneLogin) updatePantherFields(p *OneLoginParser) { - event.SetCoreFields(p.LogType(), (*timestamp.RFC3339)(event.EventTimestamp), event) - event.AppendAnyIPAddressPtr(event.IPAddr) + UUID pantherlog.String `json:"uuid" validate:"required,uuid" description:"The Universal Unique Identifier for this message generated by OneLogin."` + AccountID pantherlog.Int64 `json:"account_id" validate:"required" description:"Account that triggered the event."` + EventTimestamp pantherlog.Time `json:"event_timestamp" event_time:"true" tcodec:"layout=2006-01-02 15:04:05 MST" validate:"required" description:"Time and date at which the event was created. This value is autogenerated by OneLogin."` + ErrorDescription pantherlog.String `json:"error_description" description:"Provisioning error details, if applicable."` + LoginName pantherlog.String `json:"login_name" description:"The name of the login user"` + AppName pantherlog.String `json:"app_name" description:"Name of the app involved in the event, if applicable."` + AuthenticationFactorDescription pantherlog.String `json:"authentication_factor_description" description:"More details about the authentication factor used."` + CertificateName pantherlog.String `json:"certificate_name" description:"The name of the certificate that was included in the request."` + CertificateID pantherlog.String `json:"certificate_id" description:"The ID of the certificate that was included in the request."` + AssumedBySuperadminOrReseller pantherlog.Bool `json:"assumed_by_superadmin_or_reseller" description:"Indicates that the operation was performed by superadmin or reseller."` + DirectoryName pantherlog.String `json:"directory_name" description:"The directory name."` + ActorUserID pantherlog.Int64 `json:"actor_user_id" description:"ID of the user whose action triggered the event."` + UserName pantherlog.String `json:"user_name" panther:"username" description:"Name of the user that was acted upon to trigger the event."` + MappingID pantherlog.Int64 `json:"mapping_id" description:"The ID of the mapping included in the operation."` + RadiusConfigID pantherlog.Int64 `json:"radius_config_id" description:"The ID of the Radius configuration included in the operation."` + RiskScore pantherlog.Int64 `json:"risk_score" description:"The higher thiss number, the higher the risk."` + OtpDeviceID pantherlog.Int64 `json:"otp_device_id" description:"ID of a device involved in the event."` + ImportedUserID pantherlog.Int64 `json:"imported_user_id" description:"The ID of the imported user."` + Resolution pantherlog.Int64 `json:"resolution" description:"The resolution."` + DirectoryID pantherlog.Int64 `json:"directory_id" description:"The directory ID."` + AuthenticationFactorID pantherlog.Int64 `json:"authentication_factor_id" description:"The ID of the authentication factor used."` + RiskCookieID pantherlog.String `json:"risk_cookie_id" description:"The ID of the risk cookie."` + AppID pantherlog.Int64 `json:"app_id" description:"ID of the app involved in the event, if applicable."` + CustomMessage pantherlog.String `json:"custom_message" description:"More details about the event."` + BrowserFingerprint pantherlog.String `json:"browser_fingerprint" description:"The fingerprint of the browser."` + OtpDeviceName pantherlog.String `json:"otp_device_name" description:"Name of a device involved in the event."` + ActorUserName pantherlog.String `json:"actor_user_name" description:"First and last name of the user whose action triggered the event."` + ActorSystem pantherlog.String `json:"actor_system" description:"Acting system that triggered the event when the actor is not a user."` + UserFieldName pantherlog.String `json:"user_field_name" description:"The name of the custom user field."` + UserFieldID pantherlog.String `json:"user_field_id" description:"The ID of the custom user field."` + AssumingActingUserID pantherlog.Int64 `json:"assuming_acting_user_id" description:"ID of the user who assumed the role of the acting user to trigger the event, if applicable."` + APICredentialName pantherlog.String `json:"api_credential_name" description:"The name of the API credential used."` + ImportedUserName pantherlog.String `json:"imported_user_name" description:"The name of the imported user."` + NoteTitle pantherlog.String `json:"note_title" description:"The title of the note."` + TrustedIdpName pantherlog.String `json:"trusted_idp_name" description:"The name of the trusted IDP."` + PolicyID pantherlog.Int64 `json:"policy_id" description:"ID of the policy involved in the event."` + RoleName pantherlog.String `json:"role_name" description:"Name of a role involved in the event."` + ResolvedByUserID pantherlog.Int64 `json:"resolved_by_user_id" description:"The ID of the user that resolved the issue."` + GroupID pantherlog.Int64 `json:"group_id" description:"ID of a group involved in the event."` + ClientID pantherlog.String `json:"client_id" description:"Client ID used to generate the access token that made the API call that generated the event."` + IPAddr pantherlog.String `json:"ipaddr" panther:"ip" description:"IP address of the machine used to trigger the event."` + Notes pantherlog.String `json:"notes" description:"More details about the event."` + EventTypeID pantherlog.Int64 `json:"event_type_id" validate:"required" description:"Type of event triggered."` + UserID pantherlog.Int64 `json:"user_id" description:"ID of the user that was acted upon to trigger the event."` + RiskReasons pantherlog.String `json:"risk_reasons" description:"This is not an exhaustive list of the reasons for the risk score and should only be used as a guide"` + ProxyAgentName pantherlog.String `json:"proxy_agent_name" description:"The name of the proxy agent."` + PolicyType pantherlog.String `json:"policy_type" description:"The type of the policy."` + RoleID pantherlog.Int64 `json:"role_id" description:"ID of a role involved in the event."` + UserAgent pantherlog.String `json:"user_agent" description:"The user agent from which the request was invoke"` + PrivilegeName pantherlog.String `json:"privilege_name" description:"The name of the privilege."` + GroupName pantherlog.String `json:"group_name" description:"Name of a group involved in the event."` + Entity pantherlog.String `json:"entity" description:"The entity involved in this request."` + ResourceTypeID pantherlog.Int64 `json:"resource_type_id" description:"ID of the resource (user, role, group, and so forth) associated with the event."` + MappingName pantherlog.String `json:"mapping_name" description:"The name of the mapping."` + TaskName pantherlog.String `json:"task_name" description:"The name of the task."` + AuthenticationFactorType pantherlog.Int64 `json:"authentication_factor_type" description:"The type of the authentication type."` + RadiusConfigName pantherlog.String `json:"radius_config_name" description:"The name of the Radius configuration used."` + PolicyName pantherlog.String `json:"policy_name" description:"Name of the policy involved in the event."` + PrivilegeID pantherlog.Int64 `json:"privilege_id" description:"The id of the privilege."` + DirectorySyncRunID pantherlog.Int64 `json:"directory_sync_run_id" description:"Directory sync run ID."` + OperationName pantherlog.String `json:"operation_name" description:"The name of the operation"` } diff --git a/internal/log_analysis/log_processor/parsers/oneloginlogs/onelogin_test.go b/internal/log_analysis/log_processor/parsers/oneloginlogs/onelogin_test.go index cebd32ad1f..cc4a6d6ed7 100644 --- a/internal/log_analysis/log_processor/parsers/oneloginlogs/onelogin_test.go +++ b/internal/log_analysis/log_processor/parsers/oneloginlogs/onelogin_test.go @@ -20,110 +20,10 @@ package oneloginlogs import ( "testing" - "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/stretchr/testify/require" - - "github.com/panther-labs/panther/internal/log_analysis/log_processor/parsers/testutil" - "github.com/panther-labs/panther/internal/log_analysis/log_processor/parsers/timestamp" + "github.com/panther-labs/panther/internal/log_analysis/log_processor/logtypes/logtesting" ) -func TestOneLoginAuthEvent(t *testing.T) { - // nolint: lll - log := `{"create":{"_id":"6ce16f26-b25d-4070-9a30-4f259675a745"},"event_timestamp":"2020-04-15 22:51:24 UTC","otp_device_id":1159999,"user_id":50361999,"event_type_id":1400,"actor_user_name":"Jon Bixton","notes":"Authentication method: OTP (Valid OTP). Factor name: Duo Security","actor_system":"","uuid":"6ce16f26-b25d-4070-9a30-4f259675a745","user_name":"Jon Bixton","ipaddr":"136.25.254.254","account_id":142999,"authentication_factor_type":12,"authentication_factor_description":"Duo Security","user_agent":"Swagger-Codegen/1.0.0/ruby","authentication_factor_id":20402,"actor_user_id":50361999,"otp_device_name":"Duo Duo Security"}` - expectedTime := time.Date(2020, 4, 15, 22, 51, 24, 0, time.UTC) - expectedEvent := &OneLogin{ - EventTimestamp: (*timestamp.OneLoginTimestamp)(&expectedTime), - OtpDeviceID: aws.Int(1159999), - UserID: aws.Int(50361999), - EventTypeID: aws.Int(1400), - ActorSystem: aws.String(""), - ActorUserName: aws.String("Jon Bixton"), - Notes: aws.String("Authentication method: OTP (Valid OTP). Factor name: Duo Security"), - UUID: aws.String("6ce16f26-b25d-4070-9a30-4f259675a745"), - UserName: aws.String("Jon Bixton"), - IPAddr: aws.String("136.25.254.254"), - AccountID: aws.Int(142999), - AuthenticationFactorType: aws.Int(12), - AuthenticationFactorDescription: aws.String("Duo Security"), - UserAgent: aws.String("Swagger-Codegen/1.0.0/ruby"), - AuthenticationFactorID: aws.Int(20402), - ActorUserID: aws.Int(50361999), - OtpDeviceName: aws.String("Duo Duo Security"), - } - expectedEvent.SetCoreFields("OneLogin.Events", (*timestamp.RFC3339)(&expectedTime), expectedEvent) - expectedEvent.AppendAnyIPAddress("136.25.254.254") - parser := (&OneLoginParser{}).New() - logs, err := parser.Parse(log) - testutil.EqualPantherLog(t, expectedEvent.Log(), logs, err) -} - -func TestOneLoginPrivilegeEvent(t *testing.T) { - // nolint: lll - log := `{"create":{"_id":"aca33051-d938-40cf-8dec-c382e397b4d7"},"event_timestamp":"2020-04-14 23:34:15 UTC","ipaddr":"136.25.254.254","privilege_id":338999,"privilege_name":"Manage Panther Cognito (Test)","account_id":142999,"event_type_id":72,"uuid":"aca33051-d938-40cf-8dec-c382e397b4d7","app_name":"Panther Cognito (Test)","actor_user_name":"Jon Bixton","user_id":50361999,"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36","user_name":"Jon Bixton","app_id":1120551,"actor_user_id":50361999,"actor_system":""}` - expectedTime := time.Date(2020, 4, 14, 23, 34, 15, 0, time.UTC) - expectedEvent := &OneLogin{ - EventTimestamp: (*timestamp.OneLoginTimestamp)(&expectedTime), - UserID: aws.Int(50361999), - UserName: aws.String("Jon Bixton"), - EventTypeID: aws.Int(72), - ActorSystem: aws.String(""), - ActorUserName: aws.String("Jon Bixton"), - UUID: aws.String("aca33051-d938-40cf-8dec-c382e397b4d7"), - IPAddr: aws.String("136.25.254.254"), - AccountID: aws.Int(142999), - // nolint: lll - UserAgent: aws.String("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36"), - ActorUserID: aws.Int(50361999), - AppID: aws.Int(1120551), - AppName: aws.String("Panther Cognito (Test)"), - PrivilegeID: aws.Int(338999), - PrivilegeName: aws.String("Manage Panther Cognito (Test)"), - } - expectedEvent.SetCoreFields("OneLogin.Events", (*timestamp.RFC3339)(&expectedTime), expectedEvent) - expectedEvent.AppendAnyIPAddress("136.25.254.254") - parser := (&OneLoginParser{}).New() - logs, err := parser.Parse(log) - testutil.EqualPantherLog(t, expectedEvent.Log(), logs, err) -} - -func TestOneLoginDirectorySync(t *testing.T) { - // nolint: lll - log := `{"create":{"_id":"5c103afb-bcca-4d5b-8eae-a23e675cae82"},"event_type_id":14,"uuid":"5c103afb-bcca-4d5b-8eae-a23e67588888","resolution": 10,"event_timestamp":"2020-04-09 18:37:33 UTC","directory_sync_run_id":1586458888,"account_id":142999,"actor_user_name":"G Suite","actor_system":"G Suite","ipaddr":"","user_agent":"","user_id":53642999,"user_name":"Jon Bixton","notes":"Updated user"}` - expectedTime := time.Date(2020, 4, 9, 18, 37, 33, 0, time.UTC) - expectedEvent := &OneLogin{ - Resolution: aws.Int(10), - EventTimestamp: (*timestamp.OneLoginTimestamp)(&expectedTime), - DirectorySyncRunID: aws.Int(1586458888), - UserID: aws.Int(53642999), - UserName: aws.String("Jon Bixton"), - EventTypeID: aws.Int(14), - ActorSystem: aws.String("G Suite"), - ActorUserName: aws.String("G Suite"), - UUID: aws.String("5c103afb-bcca-4d5b-8eae-a23e67588888"), - IPAddr: aws.String(""), - AccountID: aws.Int(142999), - UserAgent: aws.String(""), - Notes: aws.String("Updated user"), - } - expectedEvent.SetCoreFields("OneLogin.Events", (*timestamp.RFC3339)(&expectedTime), expectedEvent) - parser := (&OneLoginParser{}).New() - logs, err := parser.Parse(log) - testutil.EqualPantherLog(t, expectedEvent.Log(), logs, err) -} - -func TestOneLoginRiskReasons(t *testing.T) { - // keep sure we parse logs with risk_reason set correctly - logs := []string{ - `{"create":{"_id":"9d038ce7-12a9-4548-9835-6c07d13dcb63"},"resource_type_id":null,"resolved_at":null,"object_id":null,"job_name":null,"authentication_factor_type":null,"radius_config_id":null,"mapping_id":null,"client_id":null,"risk_cookie_id":"dccab460dde68a296872a01204976b5e6a755671aa6234d14ccedbea10fd4173","param":null,"proxy_ip":null,"authentication_factor_description":null,"directory_id":null,"risk_reasons":"Boulder, Colorado, United States is a new location (20%)\nAccessed from a new IP address (20%)\nUnexpectedly high velocity (4587.45 km/hr) (20%)\nInfrequent access from 70.39.110.83 (20%)\nInfrequent access from Boulder, Colorado, United States (20%)","api_credential_name":null,"safe_to_unescape":null,"report_id":null,"app_name":null,"proxy_agent_name":null,"user_field_name":null,"otp_device_id":null,"directory_name":null,"notes":"Authentication method: Login App.\nTransitions: desktop_login -> username -> password -> mfa_login\nCorrelation_id: fc7f0a67-6970-4919-8a73-e5a3b4c76189","custom_message":null,"policy_name":null,"note_id":null,"user_field_id":null,"user_id":73503145,"service_job_name":null,"assumed_by_superadmin_or_reseller":null,"assuming_acting_user_id":null,"otp_device_name":null,"authentication_factor_id":null,"browser_fingerprint":"0335332a287a43e05e0e7cd8ec9eb44c","adc_name":null,"user_name":"Somebody","trusted_idp_id":null,"trusted_idp_name":null,"service_directory_id":null,"actor_user_id":73503145,"note_title":null,"login_name":null,"adc_id":null,"certificate_name":null,"privilege_id":null,"solved":null,"uuid":"9d038ce7-12a9-4548-9835-6c07d13dcb63","directory_sync_run_id":null,"proxy_agent_id":null,"service_job_id":null,"app_id":null,"error_description":null,"actor_system":"","privilege_name":null,"imported_user_id":null,"job_id":null,"role_id":null,"policy_type":null,"actor_user_name":"Somebody","account_id":148935,"resolution":null,"event_type_id":5,"risk_score":30,"report_name":null,"resolved_by_user_id":null,"imported_user_name":null,"group_name":null,"ipaddr":"70.39.110.83","group_id":null,"policy_id":null,"entity":null,"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36","certificate_id":null,"mapping_name":null,"task_name":null,"login_id":null,"role_name":null,"event_timestamp":"2020-08-21 00:32:38 UTC","radius_config_name":null}`, // nolint: lll - `{"create":{"_id":"5cb13a5c-2280-4155-8b70-45a2a8c40e4d"},"resource_type_id":null,"resolved_at":null,"object_id":null,"job_name":null,"authentication_factor_type":null,"radius_config_id":null,"mapping_id":null,"client_id":null,"risk_cookie_id":"dccab460dde68a296872a01204976b5e6a755671aa6234d14ccedbea10fd4173","param":null,"proxy_ip":null,"authentication_factor_description":null,"directory_id":null,"risk_reasons":"Boulder, Colorado, United States is a new location (20%)\nAccessed from a new IP address (20%)\nUnexpectedly high velocity (4587.45 km/hr) (20%)\nInfrequent access from 70.39.110.83 (20%)\nInfrequent access from Boulder, Colorado, United States (20%)","api_credential_name":null,"safe_to_unescape":null,"report_id":null,"app_name":null,"proxy_agent_name":null,"user_field_name":null,"otp_device_id":null,"directory_name":null,"notes":null,"custom_message":null,"policy_name":null,"note_id":null,"user_field_id":null,"user_id":73503145,"service_job_name":null,"assumed_by_superadmin_or_reseller":null,"assuming_acting_user_id":null,"otp_device_name":null,"authentication_factor_id":null,"browser_fingerprint":"0335332a287a43e05e0e7cd8ec9eb44c","adc_name":null,"user_name":"Somebody","trusted_idp_id":null,"trusted_idp_name":null,"service_directory_id":null,"actor_user_id":73503145,"note_title":null,"login_name":null,"adc_id":null,"certificate_name":null,"privilege_id":null,"solved":null,"uuid":"5cb13a5c-2280-4155-8b70-45a2a8c40e4d","directory_sync_run_id":null,"proxy_agent_id":null,"service_job_id":null,"app_id":null,"error_description":null,"actor_system":"","privilege_name":null,"imported_user_id":null,"job_id":null,"role_id":null,"policy_type":null,"actor_user_name":"Somebody","account_id":148935,"resolution":null,"event_type_id":1001,"risk_score":30,"report_name":null,"resolved_by_user_id":null,"imported_user_name":null,"group_name":null,"ipaddr":"70.39.110.83","group_id":null,"policy_id":null,"entity":null,"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36","certificate_id":null,"mapping_name":null,"task_name":null,"login_id":null,"role_name":null,"event_timestamp":"2020-08-21 00:32:29 UTC","radius_config_name":null}`, // nolint: lll - } - parser := (&OneLoginParser{}).New() - for _, log := range logs { - parsedLogs, err := parser.Parse(log) - require.NoError(t, err, log) - require.Len(t, parsedLogs, 1) // parsing worked - require.NotNil(t, parsedLogs[0].Event().(*OneLogin).RiskReasons, log) // risk_reason populated - } +func TestOneLoginEvents(t *testing.T) { + logtesting.RunTestsFromYAML(t, LogTypes(), "./testdata/onelogin_tests.yml") } diff --git a/internal/log_analysis/log_processor/parsers/oneloginlogs/testdata/onelogin_tests.yml b/internal/log_analysis/log_processor/parsers/oneloginlogs/testdata/onelogin_tests.yml new file mode 100644 index 0000000000..657e911233 --- /dev/null +++ b/internal/log_analysis/log_processor/parsers/oneloginlogs/testdata/onelogin_tests.yml @@ -0,0 +1,264 @@ +# Panther is a Cloud-Native SIEM for the Modern Security Team. +# Copyright (C) 2020 Panther Labs Inc +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +name: TestOneLogin +logType: OneLogin.Events +input: | + { + "create": { + "_id": "6ce16f26-b25d-4070-9a30-4f259675a745" + }, + "event_timestamp": "2020-04-15 22:51:24 UTC", + "otp_device_id": 1159999, + "user_id": 50361999, + "event_type_id": 1400, + "actor_user_name": "Jon Bixton", + "notes": "Authentication method: OTP (Valid OTP). Factor name: Duo Security", + "actor_system": "", + "uuid": "6ce16f26-b25d-4070-9a30-4f259675a745", + "user_name": "Jon Bixton", + "ipaddr": "136.25.254.254", + "account_id": 142999, + "authentication_factor_type": 12, + "authentication_factor_description": "Duo Security", + "user_agent": "Swagger-Codegen/1.0.0/ruby", + "authentication_factor_id": 20402, + "actor_user_id": 50361999, + "otp_device_name": "Duo Duo Security" + } +result: | + { + "event_timestamp": "2020-04-15 22:51:24 UTC", + "otp_device_id": 1159999, + "user_id": 50361999, + "event_type_id": 1400, + "actor_user_name": "Jon Bixton", + "notes": "Authentication method: OTP (Valid OTP). Factor name: Duo Security", + "actor_system": "", + "uuid": "6ce16f26-b25d-4070-9a30-4f259675a745", + "user_name": "Jon Bixton", + "ipaddr": "136.25.254.254", + "account_id": 142999, + "authentication_factor_type": 12, + "authentication_factor_description": "Duo Security", + "user_agent": "Swagger-Codegen/1.0.0/ruby", + "authentication_factor_id": 20402, + "actor_user_id": 50361999, + "otp_device_name": "Duo Duo Security", + "p_event_time": "2020-04-15T22:51:24Z", + "p_any_usernames": ["Jon Bixton"], + "p_any_ip_addresses":["136.25.254.254"], + "p_log_type": "OneLogin.Events" + } +--- +name: TestOneLoginPriviledgeEvent +logType: OneLogin.Events +input: | + { + "create": { + "_id": "aca33051-d938-40cf-8dec-c382e397b4d7" + }, + "event_timestamp": "2020-04-14 23:34:15 UTC", + "ipaddr": "136.25.254.254", + "privilege_id": 338999, + "privilege_name": "Manage Panther Cognito (Test)", + "account_id": 142999, + "event_type_id": 72, + "uuid": "aca33051-d938-40cf-8dec-c382e397b4d7", + "app_name": "Panther Cognito (Test)", + "actor_user_name": "Jon Bixton", + "user_id": 50361999, + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36", + "user_name": "Jon Bixton", + "app_id": 1120551, + "actor_user_id": 50361999, + "actor_system": "" + } +result: | + { + "event_timestamp": "2020-04-14 23:34:15 UTC", + "ipaddr": "136.25.254.254", + "privilege_id": 338999, + "privilege_name": "Manage Panther Cognito (Test)", + "account_id": 142999, + "event_type_id": 72, + "uuid": "aca33051-d938-40cf-8dec-c382e397b4d7", + "app_name": "Panther Cognito (Test)", + "actor_user_name": "Jon Bixton", + "user_id": 50361999, + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36", + "user_name": "Jon Bixton", + "app_id": 1120551, + "actor_user_id": 50361999, + "actor_system": "", + "p_event_time": "2020-04-14T23:34:15Z", + "p_any_usernames": ["Jon Bixton"], + "p_any_ip_addresses":["136.25.254.254"], + "p_log_type": "OneLogin.Events" + } +--- +name: TestOneLoginDirectorySync +logType: OneLogin.Events +input: | + { + "create": { + "_id": "5c103afb-bcca-4d5b-8eae-a23e675cae82" + }, + "event_type_id": 14, + "uuid": "5c103afb-bcca-4d5b-8eae-a23e67588888", + "resolution": 10, + "event_timestamp": "2020-04-09 18:37:33 UTC", + "directory_sync_run_id": 1586458888, + "account_id": 142999, + "actor_user_name": "G Suite", + "actor_system": "G Suite", + "ipaddr": "", + "user_agent": "", + "user_id": 53642999, + "user_name": "Jon Bixton", + "notes": "Updated user" + } +result: | + { + "event_type_id": 14, + "uuid": "5c103afb-bcca-4d5b-8eae-a23e67588888", + "resolution": 10, + "event_timestamp": "2020-04-09 18:37:33 UTC", + "directory_sync_run_id": 1586458888, + "account_id": 142999, + "actor_user_name": "G Suite", + "actor_system": "G Suite", + "ipaddr": "", + "user_agent": "", + "user_id": 53642999, + "user_name": "Jon Bixton", + "notes": "Updated user", + "p_event_time": "2020-04-09T18:37:33Z", + "p_any_usernames": ["Jon Bixton"], + "p_log_type": "OneLogin.Events" + } + +--- +name: TestOneLoginRiskReasons +logType: OneLogin.Events +input: | + { + "create": { + "_id": "9d038ce7-12a9-4548-9835-6c07d13dcb63" + }, + "resource_type_id": null, + "resolved_at": null, + "object_id": null, + "job_name": null, + "authentication_factor_type": null, + "radius_config_id": null, + "mapping_id": null, + "client_id": null, + "risk_cookie_id": "dccab460dde68a296872a01204976b5e6a755671aa6234d14ccedbea10fd4173", + "param": null, + "proxy_ip": null, + "authentication_factor_description": null, + "directory_id": null, + "risk_reasons": "Boulder, Colorado, United States is a new location (20%)\nAccessed from a new IP address (20%)\nUnexpectedly high velocity (4587.45 km/hr) (20%)\nInfrequent access from 70.39.110.83 (20%)\nInfrequent access from Boulder, Colorado, United States (20%)", + "api_credential_name": null, + "safe_to_unescape": null, + "report_id": null, + "app_name": null, + "proxy_agent_name": null, + "user_field_name": null, + "otp_device_id": null, + "directory_name": null, + "notes": "Authentication method: Login App.\nTransitions: desktop_login -> username -> password -> mfa_login\nCorrelation_id: fc7f0a67-6970-4919-8a73-e5a3b4c76189", + "custom_message": null, + "policy_name": null, + "note_id": null, + "user_field_id": null, + "user_id": 73503145, + "service_job_name": null, + "assumed_by_superadmin_or_reseller": null, + "assuming_acting_user_id": null, + "otp_device_name": null, + "authentication_factor_id": null, + "browser_fingerprint": "0335332a287a43e05e0e7cd8ec9eb44c", + "adc_name": null, + "user_name": "Somebody", + "trusted_idp_id": null, + "trusted_idp_name": null, + "service_directory_id": null, + "actor_user_id": 73503145, + "note_title": null, + "login_name": null, + "adc_id": null, + "certificate_name": null, + "privilege_id": null, + "solved": null, + "uuid": "9d038ce7-12a9-4548-9835-6c07d13dcb63", + "directory_sync_run_id": null, + "proxy_agent_id": null, + "service_job_id": null, + "app_id": null, + "error_description": null, + "actor_system": "", + "privilege_name": null, + "imported_user_id": null, + "job_id": null, + "role_id": null, + "policy_type": null, + "actor_user_name": "Somebody", + "account_id": 148935, + "resolution": null, + "event_type_id": 5, + "risk_score": 30, + "report_name": null, + "resolved_by_user_id": null, + "imported_user_name": null, + "group_name": null, + "ipaddr": "70.39.110.83", + "group_id": null, + "policy_id": null, + "entity": null, + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36", + "certificate_id": null, + "mapping_name": null, + "task_name": null, + "login_id": null, + "role_name": null, + "event_timestamp": "2020-08-21 00:32:38 UTC", + "radius_config_name": null + } +result: | + { + "risk_cookie_id": "dccab460dde68a296872a01204976b5e6a755671aa6234d14ccedbea10fd4173", + "risk_reasons": "Boulder, Colorado, United States is a new location (20%)\nAccessed from a new IP address (20%)\nUnexpectedly high velocity (4587.45 km/hr) (20%)\nInfrequent access from 70.39.110.83 (20%)\nInfrequent access from Boulder, Colorado, United States (20%)", + "notes": "Authentication method: Login App.\nTransitions: desktop_login -> username -> password -> mfa_login\nCorrelation_id: fc7f0a67-6970-4919-8a73-e5a3b4c76189", + "browser_fingerprint": "0335332a287a43e05e0e7cd8ec9eb44c", + "user_name": "Somebody", + "actor_user_id": 73503145, + "uuid": "9d038ce7-12a9-4548-9835-6c07d13dcb63", + "actor_system": "", + "actor_user_name": "Somebody", + "account_id": 148935, + "event_type_id": 5, + "risk_score": 30, + "ipaddr": "70.39.110.83", + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36", + "user_id": 73503145, + "event_timestamp": "2020-08-21 00:32:38 UTC", + "p_event_time": "2020-08-21T00:32:38Z", + "p_any_usernames": ["Somebody"], + "p_any_ip_addresses":["70.39.110.83"], + "p_log_type": "OneLogin.Events" + } diff --git a/internal/log_analysis/log_processor/parsers/timestamp/timestamp.go b/internal/log_analysis/log_processor/parsers/timestamp/timestamp.go index 03f7905a23..26605dc7bc 100644 --- a/internal/log_analysis/log_processor/parsers/timestamp/timestamp.go +++ b/internal/log_analysis/log_processor/parsers/timestamp/timestamp.go @@ -40,8 +40,6 @@ const ( suricataTimestampLayout = `"2006-01-02T15:04:05.999999999Z0700"` - oneLoginTimestampLayout = `"2006-01-02 15:04:05 MST"` - //08 Jul 2020 09:00 GMT laceworkTimestampLayout = `"02 Jan 2006 15:04 MST"` ) @@ -54,7 +52,6 @@ func init() { typSuricata := reflect.TypeOf(SuricataTimestamp{}) typUnixFloat := reflect.TypeOf(UnixFloat{}) typUnixMillis := reflect.TypeOf(UnixMillisecond{}) - typOneLogin := reflect.TypeOf(OneLoginTimestamp{}) typLacework := reflect.TypeOf(LaceworkTimestamp{}) // Add glue table mappings glueschema.MustRegisterMapping(typANSICwithTZ, glueschema.TypeTimestamp) @@ -64,7 +61,6 @@ func init() { glueschema.MustRegisterMapping(typUnixFloat, glueschema.TypeTimestamp) glueschema.MustRegisterMapping(typUnixMillis, glueschema.TypeTimestamp) glueschema.MustRegisterMapping(typLacework, glueschema.TypeTimestamp) - glueschema.MustRegisterMapping(typOneLogin, glueschema.TypeTimestamp) } // use these functions to parse all incoming dates to ensure UTC consistency @@ -194,25 +190,6 @@ func (ts *UnixFloat) UnmarshalJSON(jsonBytes []byte) (err error) { return nil } -type OneLoginTimestamp time.Time - -func (ts *OneLoginTimestamp) String() string { - return (*time.Time)(ts).UTC().String() // ensure UTC -} - -func (ts *OneLoginTimestamp) MarshalJSON() ([]byte, error) { - return []byte((*time.Time)(ts).UTC().Format(gluetimestamp.LayoutJSON)), nil // ensure UTC -} - -func (ts *OneLoginTimestamp) UnmarshalJSON(jsonBytes []byte) (err error) { - t, err := time.Parse(oneLoginTimestampLayout, string(jsonBytes)) - if err != nil { - return - } - *ts = (OneLoginTimestamp)(t.UTC()) - return -} - type LaceworkTimestamp time.Time func (ts *LaceworkTimestamp) String() string {