-
Notifications
You must be signed in to change notification settings - Fork 29
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
rsc: Add route to check for valid api key #1549
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -171,6 +171,13 @@ fn create_router( | |
move |req, next| api_key_check::api_key_check_middleware(req, next, conn.clone()) | ||
})), | ||
) | ||
.route( | ||
"/auth/check", | ||
post(axum::http::StatusCode::OK).layer(axum::middleware::from_fn({ | ||
let conn = conn.clone(); | ||
move |req, next| api_key_check::api_key_check_middleware(req, next, conn.clone()) | ||
})), | ||
) | ||
// Unauthorized Routes | ||
.route( | ||
"/job/matching", | ||
|
@@ -508,6 +515,36 @@ mod tests { | |
|
||
assert_eq!(res.status(), StatusCode::BAD_REQUEST); | ||
|
||
// Authorization check with invalid auth should 401 | ||
let res = router | ||
.call( | ||
Request::builder() | ||
.uri("/auth/check") | ||
.method(http::Method::POST) | ||
.header("Authorization", "badauth") | ||
.body(Body::empty()) | ||
.unwrap(), | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
assert_eq!(res.status(), StatusCode::UNAUTHORIZED); | ||
|
||
// Authorization check with valid auth should 200 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one is probably the best code. There's potential in 204 No Content, but I'm not sure the connotations there exactly match up. |
||
let res = router | ||
.call( | ||
Request::builder() | ||
.uri("/auth/check") | ||
.method(http::Method::POST) | ||
.header("Authorization", api_key.clone()) | ||
.body(Body::empty()) | ||
.unwrap(), | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
assert_eq!(res.status(), StatusCode::OK); | ||
|
||
// Correctly inserting a job should 200 | ||
let res = router | ||
.call( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -199,9 +199,18 @@ export def makeRemoteCacheApi (config: String): Result RemoteCacheApi Error = | |
else failWithError "Remote cache config was set with non-integer port. Saw: {portStr}" | ||
|
||
def auth = if authStr ==* "" then None else Some authStr | ||
def api = RemoteCacheApi domain port auth | ||
|
||
RemoteCacheApi domain port auth | ||
| Pass | ||
# TODO: check for compatable wake version | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought this version check would be on the server side? What could we check for here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see followup PR :) but tl;dr we send our version to the server and it tells us if it thinks we are compatible |
||
|
||
# If auth is not set we are done. Just return the api | ||
require Some _ = auth | ||
else Pass api | ||
|
||
# Auth was set so it must be validated. | ||
api | ||
| rscApiCheckAuthorization | ||
| rmap (\_ api) | ||
|
||
# rscApiPostStringBlob: Posts a named string as a blob to the remote server defined by *api* | ||
# then returns the id associated to the blob. Requires authorization. | ||
|
@@ -325,6 +334,27 @@ export def rscApiFindMatchingJob (req: CacheSearchRequest) (api: RemoteCacheApi) | |
|
||
mkCacheSearchResponse json | ||
|
||
# rscApiCheckAuthorization: Checks if the provided authorization key is valid. | ||
# | ||
# ``` | ||
# api | rscApiCheckAuthorization = Pass Unit | ||
# ``` | ||
export def rscApiCheckAuthorization (api: RemoteCacheApi): Result Unit Error = | ||
require Some auth = api.getRemoteCacheApiAuthorization | ||
else failWithError "rsc: Checking authorization requires authorization but none was provided" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know what sort of logic chokepoints you have closer to the surface and with this being Footnotes
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I consider any HTTP web route as a public part of the Since
I'll need you to give an example of what you mean, but maybe given the above comment this doesn't make sense anymore? The important thing to keep in mind is that this library is just a wrapper around what is available as the public interface on the server. What the consumer does with it (in this case remote_cache_runner) is entirely up to them There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, part 1 makes sense and with that clarification, part 2 is indeed invalid. I basically just meant that if you had a lot of potential errors which were not in directly-exported functions but instead were only accessible by going through a small number of entry points, you wouldn't have to add the |
||
|
||
require Pass response = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How frequently is this function called? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Two answers here
Aside:
This isn't the case. Web requests are never safe to cache as the result can change at any time. Because of that web request jobs are never stored in the db There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SGTM. |
||
makeRoute api "auth/check" | ||
| buildHttpRequest | ||
| setMethod HttpMethodPost | ||
| addAuthorizationHeader auth | ||
| makeRequest | ||
|
||
match response.getHttpResponseStatusCode | ||
Some 200 -> Pass Unit | ||
Some x -> failWithError "Invalid auth key. Status code: {str x}" | ||
None -> failWithError "Invalid auth key. Unable to determine status code" | ||
|
||
# rscApiGetStringBlob: Downloads a blob and returns the contents as a string | ||
# | ||
# ``` | ||
|
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.
MDN suggests this often means "unauthenticated" in practice rather than a literal "unauthorized", and while there's definitely no reason not to follow the literal meaning since we're using a very specific client software, I might have gone with a 403 Forbidden as an unambiguous response.
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.
Yep, I agree that 401 means "unauthenticated" which imo is correct here since the point is to check if the provided auth key grants 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.
Makes sense -- I was reading it as the auth key basically being the credentials themselves, in which case you've got valid authentication and just not authorization (i.e. this would be the gate for a restricted-push store), but sounds like my model of what's going on was slightly off.