Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add security event log #4367

Merged
merged 16 commits into from
Feb 9, 2024
Merged

Add security event log #4367

merged 16 commits into from
Feb 9, 2024

Conversation

segiddins
Copy link
Member

@segiddins segiddins commented Jan 19, 2024

@segiddins segiddins force-pushed the segiddins/event-log branch from f2d0ea1 to 8005337 Compare January 19, 2024 00:57
Copy link

codecov bot commented Jan 19, 2024

Codecov Report

Attention: 9 lines in your changes are missing coverage. Please review.

Comparison is base (a3467f8) 96.99% compared to head (2552351) 97.06%.

Files Patch % Lines
...s/events/rubygem_event/version/pushed_component.rb 81.81% 2 Missing ⚠️
app/avo/fields/event_additional_field.rb 94.44% 1 Missing ⚠️
app/avo/fields/global_id_field.rb 87.50% 1 Missing ⚠️
app/avo/resources/ip_address_resource.rb 92.30% 1 Missing ⚠️
app/policies/events/rubygem_event_policy.rb 87.50% 1 Missing ⚠️
app/policies/events/user_event_policy.rb 87.50% 1 Missing ⚠️
app/policies/ip_address_policy.rb 87.50% 1 Missing ⚠️
...views/components/events/table_details_component.rb 95.45% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4367      +/-   ##
==========================================
+ Coverage   96.99%   97.06%   +0.06%     
==========================================
  Files         349      383      +34     
  Lines        7695     8166     +471     
==========================================
+ Hits         7464     7926     +462     
- Misses        231      240       +9     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@segiddins segiddins force-pushed the segiddins/event-log branch 8 times, most recently from 07a43a9 to f76d884 Compare January 30, 2024 05:01
@segiddins segiddins changed the base branch from master to segiddins/add-records-for-user-rubygem-event-logging January 30, 2024 19:52
@segiddins segiddins force-pushed the segiddins/event-log branch from f76d884 to b005e88 Compare January 31, 2024 09:20

include Events::Tags

VERSION_PUSHED = define_event "rubygem:version:pushed" do
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: rubygem event definitions


include Events::Tags

LOGIN_SUCCESS = define_event "user:login:success" do
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: user event definitions

@segiddins segiddins marked this pull request as ready for review January 31, 2024 09:36
@segiddins segiddins changed the title [WIP] Add security event log Add security event log Jan 31, 2024
Copy link
Member

@martinemde martinemde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partial review. Will come back soon.

@@ -24,7 +24,7 @@ def create

render "multifactor_auths/prompt"
else
do_login
do_login(two_factor_label: nil, two_factor_method: nil, authentication_method: "password")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could use a constant for the magic strings in here to keep it consistent. Minor nitpick.

app/models/api_key.rb Show resolved Hide resolved
app/models/events/recordable.rb Outdated Show resolved Hide resolved
Base automatically changed from segiddins/add-records-for-user-rubygem-event-logging to master February 1, 2024 19:24
@segiddins segiddins force-pushed the segiddins/event-log branch 2 times, most recently from 3a35c17 to 3420808 Compare February 2, 2024 17:51
Copy link
Member

@indirect indirect left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me at a high level, I'm sure we'll discover things that need adjusting in the future but I'm happy to start here.

Copy link
Member

@martinemde martinemde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm most focused on the way this is modeled, serialized and deserialized. I think the underlying storage is ok. I'm just thinking about ways we might be able to make the model a little simpler. However, I think it's possible to refactor this later. The storage structure seems simple enough, so not a blocker.

This is quite a feature. I didn't get to look at it all but we can adjust these parts after this is merged.

Comment on lines 13 to 23
def additional_type
tags.fetch(tag, nil)
end

def additional
additional_type&.new(super) || super
end

def additional=(value)
super(value&.to_h)
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest adding these methods to the module rather than in included do. I think it's better for the method lookup table, so they can be shared by the module instead of unique per singleton.

end

class_methods do
def define_event(string, &blk)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe some of this method would be more local in Event::Tag, since it's almost all about that class?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really seeing what else can be pushed there, since we're dealing with the tags method and setting consts on self

tag = Events::Tag.new(string).freeze
raise ArgumentError, "Tag #{tag.inspect} already defined" if tags.key?(tag)
event = Events::Tag.to_struct(&blk)
tags[tag] = event
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we shift to using the string as key? It's is actually the key since that's how we store and retrieve it from the database. Then deserializing might just be "look it up in the hash table" instead of reconstructing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

except we use the methods on it elsewhere, and then we have to write a custom active record serializer to handle the lookup into the map

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I misunderstood, the methods we use are string manipulations of the key. The code would not change much if they were class methods. It would certainly save complexity and remove a lot of low level code.

I could be totally wrong, you spent more time in this code than me. Just noting complexity where I see it.

Copy link
Member

@martinemde martinemde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still approve as is, but I do think this is worth changing if you're willing.

Comment on lines 7 to 11
values = value.split(":")

source_type = values[0]
subject_type = values[1...-1]&.join(":").presence || source_type
action = values[-1]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the critical code from Tag, the rest is added to load and dump this class. (beside to_struct which is a pure function)

Without this complexity you add a few methods like Event.subject_type(tag) and then the tag can just be a string.

I think this is worthwhile, but I'm open to other approaches or happy to just let this slide. We could make this change even after it merges because this serializes back to the original string.

event = Events::Tag.to_struct(&blk)
tags[tag] = event

const_name = [tag.subject_type == tag.source_type ? nil : tag.subject_type, tag.action].compact.join("_").upcase
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Places where we use tag. methods just become class methods for processing the tag string. It's like a global ID.

@segiddins segiddins force-pushed the segiddins/event-log branch from 3420808 to 9fd9c6c Compare February 8, 2024 19:44
@segiddins segiddins force-pushed the segiddins/event-log branch from 9fd9c6c to 2552351 Compare February 9, 2024 07:11
Copy link
Member

@martinemde martinemde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

@@ -0,0 +1,21 @@
module Events::Tag
module_function
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh neat. So this just turns the following functions into singleton functions?

end

class_methods do
def define_event(tag, &blk)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all looks great. Thanks for hearing me out. You got it even simpler than I had imagined.

@segiddins segiddins merged commit fafb028 into master Feb 9, 2024
17 checks passed
@segiddins segiddins deleted the segiddins/event-log branch February 9, 2024 16:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Event audit log for significant actions
3 participants