2018-07-05 06:18:17 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-07-03 07:54:50 -04:00
|
|
|
class AuditEventService
|
2021-12-03 07:10:23 -05:00
|
|
|
include AuditEventSaveType
|
|
|
|
|
2020-02-19 04:08:59 -05:00
|
|
|
# Instantiates a new service
|
|
|
|
#
|
2022-01-20 07:16:19 -05:00
|
|
|
# @param [User, token String] author the entity who authors the change
|
2020-02-25 04:09:10 -05:00
|
|
|
# @param [User, Project, Group] entity the scope which audit event belongs to
|
|
|
|
# This param is also used to determine the visibility of the audit event.
|
|
|
|
# - Project: events are visible at Project and Instance level
|
|
|
|
# - Group: events are visible at Group and Instance level
|
2020-02-19 04:08:59 -05:00
|
|
|
# - User: events are visible at Instance level
|
2020-02-25 04:09:10 -05:00
|
|
|
# @param [Hash] details extra data of audit event
|
2021-12-03 07:10:23 -05:00
|
|
|
# @param [Symbol] save_type the type to save the event
|
|
|
|
# Can be selected from the following, :database, :stream, :database_and_stream .
|
2022-04-08 11:10:26 -04:00
|
|
|
# @params [DateTime] created_at the time the action occured
|
2020-02-19 04:08:59 -05:00
|
|
|
#
|
|
|
|
# @return [AuditEventService]
|
2022-04-08 11:10:26 -04:00
|
|
|
def initialize(author, entity, details = {}, save_type = :database_and_stream, created_at = DateTime.current)
|
2020-04-03 08:09:52 -04:00
|
|
|
@author = build_author(author)
|
2020-02-19 04:08:59 -05:00
|
|
|
@entity = entity
|
|
|
|
@details = details
|
2021-06-29 08:08:48 -04:00
|
|
|
@ip_address = resolve_ip_address(@author)
|
2021-12-03 07:10:23 -05:00
|
|
|
@save_type = save_type
|
2022-04-08 11:10:26 -04:00
|
|
|
@created_at = created_at
|
2015-07-03 07:54:50 -04:00
|
|
|
end
|
|
|
|
|
2020-02-19 04:08:59 -05:00
|
|
|
# Builds the @details attribute for authentication
|
|
|
|
#
|
2020-02-25 04:09:10 -05:00
|
|
|
# This uses the @author as the target object being audited
|
2020-02-19 04:08:59 -05:00
|
|
|
#
|
|
|
|
# @return [AuditEventService]
|
2015-07-03 07:54:50 -04:00
|
|
|
def for_authentication
|
2020-09-18 02:09:31 -04:00
|
|
|
mark_as_authentication_event!
|
|
|
|
|
2015-07-03 07:54:50 -04:00
|
|
|
@details = {
|
|
|
|
with: @details[:with],
|
|
|
|
target_id: @author.id,
|
2016-07-06 09:26:59 -04:00
|
|
|
target_type: 'User',
|
2017-05-03 07:22:03 -04:00
|
|
|
target_details: @author.name
|
2015-07-03 07:54:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2020-02-19 04:08:59 -05:00
|
|
|
# Writes event to a file and creates an event record in DB
|
|
|
|
#
|
2022-01-20 07:16:19 -05:00
|
|
|
# @return [AuditEvent] persisted if saves and non-persisted if fails
|
2015-07-03 07:54:50 -04:00
|
|
|
def security_event
|
Add support for JSON logging for audit events
This will add audit_json.log that writes one line per audit event. For
example:
{
"severity":"INFO",
"time":"2018-10-17T17:38:22.523Z",
"author_id":3,
"entity_id":2,
"entity_type":"Project",
"change":"visibility",
"from":"Private",
"to":"Public",
"author_name":"John Doe4",
"target_id":2,
"target_type":"Project",
"target_details":"namespace2/project2"
}
2018-10-18 15:50:21 -04:00
|
|
|
log_security_event_to_file
|
2020-09-18 02:09:31 -04:00
|
|
|
log_authentication_event_to_database
|
Add support for JSON logging for audit events
This will add audit_json.log that writes one line per audit event. For
example:
{
"severity":"INFO",
"time":"2018-10-17T17:38:22.523Z",
"author_id":3,
"entity_id":2,
"entity_type":"Project",
"change":"visibility",
"from":"Private",
"to":"Public",
"author_name":"John Doe4",
"target_id":2,
"target_type":"Project",
"target_details":"namespace2/project2"
}
2018-10-18 15:50:21 -04:00
|
|
|
log_security_event_to_database
|
|
|
|
end
|
|
|
|
|
2020-02-19 04:08:59 -05:00
|
|
|
# Writes event to a file
|
2019-08-27 17:50:17 -04:00
|
|
|
def log_security_event_to_file
|
|
|
|
file_logger.info(base_payload.merge(formatted_details))
|
|
|
|
end
|
|
|
|
|
Add support for JSON logging for audit events
This will add audit_json.log that writes one line per audit event. For
example:
{
"severity":"INFO",
"time":"2018-10-17T17:38:22.523Z",
"author_id":3,
"entity_id":2,
"entity_type":"Project",
"change":"visibility",
"from":"Private",
"to":"Public",
"author_name":"John Doe4",
"target_id":2,
"target_type":"Project",
"target_details":"namespace2/project2"
}
2018-10-18 15:50:21 -04:00
|
|
|
private
|
|
|
|
|
2020-07-15 11:09:21 -04:00
|
|
|
attr_reader :ip_address
|
|
|
|
|
2020-04-03 08:09:52 -04:00
|
|
|
def build_author(author)
|
2020-05-15 11:08:04 -04:00
|
|
|
case author
|
|
|
|
when User
|
|
|
|
author.impersonated? ? Gitlab::Audit::ImpersonatedAuthor.new(author) : author
|
2020-04-03 08:09:52 -04:00
|
|
|
else
|
|
|
|
Gitlab::Audit::UnauthenticatedAuthor.new(name: author)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-06-29 08:08:48 -04:00
|
|
|
def resolve_ip_address(author)
|
|
|
|
Gitlab::RequestContext.instance.client_ip ||
|
2020-10-27 02:08:27 -04:00
|
|
|
author.current_sign_in_ip
|
|
|
|
end
|
|
|
|
|
Add support for JSON logging for audit events
This will add audit_json.log that writes one line per audit event. For
example:
{
"severity":"INFO",
"time":"2018-10-17T17:38:22.523Z",
"author_id":3,
"entity_id":2,
"entity_type":"Project",
"change":"visibility",
"from":"Private",
"to":"Public",
"author_name":"John Doe4",
"target_id":2,
"target_type":"Project",
"target_details":"namespace2/project2"
}
2018-10-18 15:50:21 -04:00
|
|
|
def base_payload
|
|
|
|
{
|
2015-07-03 07:54:50 -04:00
|
|
|
author_id: @author.id,
|
2020-07-07 08:09:16 -04:00
|
|
|
author_name: @author.name,
|
2015-07-03 07:54:50 -04:00
|
|
|
entity_id: @entity.id,
|
2022-04-08 11:10:26 -04:00
|
|
|
entity_type: @entity.class.name,
|
|
|
|
created_at: @created_at
|
Add support for JSON logging for audit events
This will add audit_json.log that writes one line per audit event. For
example:
{
"severity":"INFO",
"time":"2018-10-17T17:38:22.523Z",
"author_id":3,
"entity_id":2,
"entity_type":"Project",
"change":"visibility",
"from":"Private",
"to":"Public",
"author_name":"John Doe4",
"target_id":2,
"target_type":"Project",
"target_details":"namespace2/project2"
}
2018-10-18 15:50:21 -04:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2020-09-18 02:09:31 -04:00
|
|
|
def authentication_event_payload
|
|
|
|
{
|
|
|
|
# @author can be a User or various Gitlab::Audit authors.
|
|
|
|
# Only capture real users for successful authentication events.
|
|
|
|
user: author_if_user,
|
|
|
|
user_name: @author.name,
|
|
|
|
ip_address: ip_address,
|
|
|
|
result: AuthenticationEvent.results[:success],
|
|
|
|
provider: @details[:with]
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def author_if_user
|
|
|
|
@author if @author.is_a?(User)
|
|
|
|
end
|
|
|
|
|
Add support for JSON logging for audit events
This will add audit_json.log that writes one line per audit event. For
example:
{
"severity":"INFO",
"time":"2018-10-17T17:38:22.523Z",
"author_id":3,
"entity_id":2,
"entity_type":"Project",
"change":"visibility",
"from":"Private",
"to":"Public",
"author_name":"John Doe4",
"target_id":2,
"target_type":"Project",
"target_details":"namespace2/project2"
}
2018-10-18 15:50:21 -04:00
|
|
|
def file_logger
|
|
|
|
@file_logger ||= Gitlab::AuditJsonLogger.build
|
|
|
|
end
|
|
|
|
|
2019-07-03 18:58:05 -04:00
|
|
|
def formatted_details
|
|
|
|
@details.merge(@details.slice(:from, :to).transform_values(&:to_s))
|
|
|
|
end
|
|
|
|
|
2020-09-18 02:09:31 -04:00
|
|
|
def mark_as_authentication_event!
|
2020-09-18 17:10:11 -04:00
|
|
|
@authentication_event = true
|
2020-09-18 02:09:31 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def authentication_event?
|
2020-09-18 17:10:11 -04:00
|
|
|
@authentication_event
|
2020-09-18 02:09:31 -04:00
|
|
|
end
|
|
|
|
|
Add support for JSON logging for audit events
This will add audit_json.log that writes one line per audit event. For
example:
{
"severity":"INFO",
"time":"2018-10-17T17:38:22.523Z",
"author_id":3,
"entity_id":2,
"entity_type":"Project",
"change":"visibility",
"from":"Private",
"to":"Public",
"author_name":"John Doe4",
"target_id":2,
"target_type":"Project",
"target_details":"namespace2/project2"
}
2018-10-18 15:50:21 -04:00
|
|
|
def log_security_event_to_database
|
2021-08-02 05:10:09 -04:00
|
|
|
return if Gitlab::Database.read_only?
|
2020-02-05 22:08:47 -05:00
|
|
|
|
2022-07-07 11:08:37 -04:00
|
|
|
event = build_event
|
2020-09-24 11:09:51 -04:00
|
|
|
save_or_track event
|
|
|
|
event
|
2015-07-03 07:54:50 -04:00
|
|
|
end
|
2020-09-18 02:09:31 -04:00
|
|
|
|
2022-07-07 11:08:37 -04:00
|
|
|
def build_event
|
|
|
|
AuditEvent.new(base_payload.merge(details: @details))
|
|
|
|
end
|
|
|
|
|
2021-11-08 22:42:22 -05:00
|
|
|
def stream_event_to_external_destinations(_event)
|
|
|
|
# Defined in EE
|
|
|
|
end
|
|
|
|
|
2020-09-18 02:09:31 -04:00
|
|
|
def log_authentication_event_to_database
|
2021-08-02 05:10:09 -04:00
|
|
|
return unless Gitlab::Database.read_write? && authentication_event?
|
2020-09-18 02:09:31 -04:00
|
|
|
|
2020-09-24 11:09:51 -04:00
|
|
|
event = AuthenticationEvent.new(authentication_event_payload)
|
|
|
|
save_or_track event
|
|
|
|
|
|
|
|
event
|
|
|
|
end
|
|
|
|
|
|
|
|
def save_or_track(event)
|
2021-12-03 07:10:23 -05:00
|
|
|
event.save! if should_save_database?(@save_type)
|
|
|
|
stream_event_to_external_destinations(event) if should_save_stream?(@save_type)
|
2021-04-26 08:09:44 -04:00
|
|
|
rescue StandardError => e
|
2021-12-21 10:14:09 -05:00
|
|
|
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e, audit_event_type: event.class.to_s)
|
2020-09-18 02:09:31 -04:00
|
|
|
end
|
2015-07-03 07:54:50 -04:00
|
|
|
end
|
2019-09-13 09:26:31 -04:00
|
|
|
|
2021-05-11 17:10:21 -04:00
|
|
|
AuditEventService.prepend_mod_with('AuditEventService')
|