-
Notifications
You must be signed in to change notification settings - Fork 640
Defer user authentication until requested by endpoint #2077
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
Conversation
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.
nice work!
req.mut_extensions().insert(api_token); | ||
} | ||
if let Some(id) = req.session().get("user_id").and_then(|s| s.parse().ok()) { | ||
req.mut_extensions().insert(TrustedUserId(id)); |
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.
out of curiosity, why is it not possible to put this into the authenticate call too?
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.
(in response to tobias) I think this req.session()
is the call that needs the mutable reference to the request
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 think this req.session() is the call that needs the mutable reference to the request
Yes, that's exactly the problem I ran into. session()
would probably be better called session_mut()
, since it calls mut_extensions()
and find_mut()
internally to return a &mut HashMap<_, _>
.
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 is nice!!! I just have a few questions :)
//! Due to lifetimes it is not possible to call `mut_extensions()` while a reference obtained from | ||
//! `extensions()` is still live. The call to `conduit_cookie::RequestSession::session` needs | ||
//! mutable access to the request and its extensions, and there is no read-only alternative in | ||
//! `conduit_cookie` to access the session cookie. This means that it is not possible to access |
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.
Would our code be better if there was a read-only alternative to get the session cookie? It looks like the write access is used for caching the results of decoding the cookie, if we don't care about that, would that be a better alternative? Or is this PR acceptable/better? Just making sure that enhancing conduit-cookie isn't what we actually want :)
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.
Would our code be better if there was a read-only alternative to get the session cookie?
It would be nice to support that upstream in conduit-cookie
. We only use the session cookie here and during the login oauth workflow, and in both cases we don't need a database connection at that particular moment so it hasn't been a problem, yet. Although I don't really see us adding more complex cookie logic anytime soon.
It looks like the write access is used for caching the results of decoding the cookie.
As I understand it, the middleware layer from conduit-cookie
decodes the cookie before the request is processed by the endpoint, and then the middleware adds the (possibly updated) cookie back into every response. So the &mut HashMap<_, _>
returned here is what provides the API for mutating the session cookie.
There are some changes (especially with respect to bumping old deps, and more especially wrt our very old dependency on semver
) that I would like make to the conduit-*
ecosystem. Last I looked we (crates.io) are the only public users of these crates, so it would be awesome if we could make some improvements and release new versions of these crates. Longer term, it would be nice if we could add async/await support in our middleware stack.
For now, I think its fine to document the workaround. This also provides an easy way for the test harness to simulate cookie authenticated requests, although it would certainly be nice to construct real cookies in the test harness instead.
req.mut_extensions().insert(api_token); | ||
} | ||
if let Some(id) = req.session().get("user_id").and_then(|s| s.parse().ok()) { | ||
req.mut_extensions().insert(TrustedUserId(id)); |
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.
(in response to tobias) I think this req.session()
is the call that needs the mutable reference to the request
user_id: user.id, | ||
crate_id, | ||
}) | ||
Ok(Follow { user_id, crate_id }) |
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 like that this abstraction gets rid of a User
lookup in cases like this spot when it's not needed 👍
|
||
let conn = app.diesel_database.get()?; | ||
let ids = req.authenticate(&conn)?; | ||
let user = ids.find_user(&conn)?; |
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.
LOVE that this is out of parse_new_headers
now!!!!
use crate::models::{Crate, CrateVersions, Version}; | ||
use crate::schema::versions; | ||
|
||
fn version_and_crate(req: &mut dyn Request) -> AppResult<(Version, Crate)> { | ||
fn version_and_crate(req: &dyn Request) -> AppResult<(DieselPooledConn<'_>, Version, Crate)> { |
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 function is a little strange now that it returns a connection too, but 🤷♀ it's not exactly the cleanest abstraction to begin with anyway given that half the time we don't even use the returned crate. Always more to do.
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 function is a little strange now that it returns a connection too
I would be cleaner to obtain a connection and pass it to this function, but it just isn't wasn't possible because of the lifetimes here. As I write this, I now see that I eventually dropped the mut
ness of the &dyn Request
, so it should now be possible to pass in a &PgConnection
as well, at least I think.
Always more to do.
Yup!
☔ The latest upstream changes (presumably #2072) made this pull request unmergeable. Please resolve the merge conflicts. |
6458a59
to
d13d22a
Compare
I've rebased to resolve merge conflicts, and added a 2nd commit that addresses the review comments. |
☔ The latest upstream changes (presumably #2032) made this pull request unmergeable. Please resolve the merge conflicts. |
d13d22a
to
b3281f0
Compare
☔ The latest upstream changes (presumably #1955) made this pull request unmergeable. Please resolve the merge conflicts. |
Many endpoints do not require user authentication, and by moving the logic out of the middleware layer these endpoints now avoid obtaining a database connection from the pool and 1 or 2 queries. Due to lifetime issues a small middlware component remains. Further details are described in the middleware's documentation.
b3281f0
to
c06e7c2
Compare
Rebased to resolve merge conflicts. I believe all review comments have been addressed. I'd like to deploy to staging tonight, and possibly production tomorrow. @bors r=carols10cents |
📌 Commit c06e7c2 has been approved by |
Defer user authentication until requested by endpoint Many endpoints do not require user authentication, and by moving the logic out of the middleware layer these endpoints now avoid obtaining a database connection from the pool and 1 or 2 queries. Due to lifetime issues a small middlware component remains. Further details are described in the middleware's documentation. r? @carols10cents
☀️ Test successful - checks-travis |
Fixes rust-lang#2166. In rust-lang#2077 (d0b9bcc), we changed the type of the authentication data stored in the extensions from `User` to `TrustedUserId`, but we missed this spot :( We're allowed to insert anything we want into the extensions, but if we use a different type than what we use when looking up data in the extensions, it won't find what we inserted.
Fix auth Set the TrustedUserId in the extensions when logging in Fixes #2166. In #2077 (d0b9bcc), we changed the type of the authentication data stored in the extensions from `User` to `TrustedUserId`, but we missed this spot :( We're allowed to insert anything we want into the extensions, but if we use a different type than what we use when looking up data in the extensions, it won't find what we inserted. I checked all the other locations of `mut_extensions` and `extensions` and I think they're good, but overall this system feels a bit loosey-goosey to me. Not sure how to improve it, though. r? @jtgeibel
Many endpoints do not require user authentication, and by moving the
logic out of the middleware layer these endpoints now avoid obtaining a
database connection from the pool and 1 or 2 queries.
Due to lifetime issues a small middlware component remains. Further
details are described in the middleware's documentation.
r? @carols10cents