-
Notifications
You must be signed in to change notification settings - Fork 154
feat: Support Traceable Sessions #442
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
base: main
Are you sure you want to change the base?
feat: Support Traceable Sessions #442
Conversation
4f0ecdf to
0c4f06f
Compare
dillonwelch
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like merge conflicts now since you so kindly fixed the test suite in a separate PR.
Can you look at some of the changes you made to the test config and see if they could be pulled out separately as well? Would make the review on this one easier, which I want to be thorough on since it's a new module and impacts an existing module. Also, I want to get other changes in so I can cut a new minor version and then have this in a new major version.
Note that I'm reviewing this while very sleepy and will review again in the near future with a more clear mind.
| end | ||
| end | ||
|
|
||
| # Removes old/inactive session if allowed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should be deactivating / updating records in a boolean method like this. It should just be returning true / false without any side effects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The purpose of allow_limitable_authentication? is to:
- If
session_traceablemodule is not included then it will always betrue. - If didn't matched to 1 and number of sessions is still within the allowable number of session then it will allow authentication.
- If didn't matched to 2 and will only reject authentication on limit then it will try to remove any expired session to accomodate new authentication using
deactivate_timeout_sessions!. - If didn't matched to 3 (which reject authentication is disabled) then it will expire the oldest session regardless if its already expired.
- If didn't matched to 4 then disallow authentication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Steps 3-5 don't feel like they belong in this method - boolean methods should only be returning true / false based on whatever logic you're adding. Steps 1-2 seem fine and what this method should be used for.
| return true unless devise_modules.include?(:session_traceable) | ||
|
|
||
| opts = session_traceable_condition(active: true) | ||
| return true if max_active_sessions > session_traceable_adapter.find_all(opts).size |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a little trouble following here - what is the return of session_traceable_adapter and what are you doing with find_all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
session_traceable_adapter will always be the session_history_class ORM compatible on session_traceable module. It will count the number of active sessions to check whether it is already on its maximum allowable number of sessions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unclear to me why we can't just use SessionHistory.
|
|
||
| # Log user session. | ||
| # @return [true] if session is allowed to log. | ||
| # @return [false] if session isn't allowed to log when already exceeded to `:max_active_sessions` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should indicate the allow_limitable_authentication? part as well.
| end | ||
|
|
||
| # Update the `:last_accessed_at` to current time. | ||
| def update_traceable_token(token) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thoughts on making these update / expire methods bang methods?
|
|
||
| # Allow customization of the session history class | ||
| mattr_accessor :session_history_class | ||
| @@session_history_class = 'SessionHistory' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate on why you added this? It adds a lot of complexity and I'm not sure what benefit it gives.
| # Update the `:last_accessed_at` to current time. | ||
| def update_traceable_token(token) | ||
| record = find_traceable_by_token(token) | ||
| record.update_attribute_without_validatons_or_callbacks(:last_accessed_at, Time.current.utc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like you should have a presence check like you have for expire_session_token.
dillonwelch
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pressed the button before I was done :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading through this again, this feels separate from the SessionTraceable module you're adding.
If this is the case, it feels like it would be better as a separate PR.
If this is not the case, please let me know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changes will allow to maintain the original logic of session_limitable which only allow 1 session at a time when session_traceable module but when session_traceable is added then it add flexibility on session limitable to customize maximum sessions per user and allowing session to expire. On scenarios that a user already hit the limit then its either reject authentication or expire the oldest session.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that this is supposed to be a history record, it seems like it would be more granular to create a new record each time the session was accessed instead of having one record for the session with last_accessed_at being updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we create a session history every time a user is fetch (on warden.authenticate or on devise current_user) then it will create a lot of session histories. Let's say in a single session of a user every time rails controller is hit, a history which likely in a single session it will be thousand of requests.
The main purpose of last_accessed_at is to track when the specific session (which track using token) is being used. The data relationship will always be a user has many sessions and last activity is tracked.
20d9954 to
5c1dee4
Compare
|
Thank you for the quick responses. I will get to them soon. |
724168b to
6ee1cee
Compare
dillonwelch
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can fix the merge conflicts now since I merged in the other PR.
| # @return [1] by default. This can be overridden by application logic as necessary. | ||
| def max_active_sessions | ||
| # Always fallback to default logic when session_traceable is not supported. | ||
| return 1 unless devise_modules.include?(:session_traceable) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to have session_traceable turned on before allowing more than 1 active session?
| return true unless devise_modules.include?(:session_traceable) | ||
|
|
||
| opts = session_traceable_condition(active: true) | ||
| return true if max_active_sessions > session_traceable_adapter.find_all(opts).size |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unclear to me why we can't just use SessionHistory.
| end | ||
| end | ||
|
|
||
| # Removes old/inactive session if allowed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Steps 3-5 don't feel like they belong in this method - boolean methods should only be returning true / false based on whatever logic you're adding. Steps 1-2 seem fine and what this method should be used for.
6ee1cee to
882d2a5
Compare
Allows having a history of login sessions with flexible active session limits.
Related #71