-
Notifications
You must be signed in to change notification settings - Fork 12
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
ID-690 Get User(s) Endpoint v2 #1208
Conversation
required: | ||
- id | ||
- enabled | ||
- createdAt | ||
- updatedAt | ||
properties: |
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 enabled did not get updated to allowed here
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.
Done!
// api/users/v2/self | ||
pathEndOrSingleSlash { | ||
getSamUserResponse(samUser, samRequestContext) | ||
} ~ | ||
// api/users/v2/self/allowed | ||
pathPrefix("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 appreciate these comments, akka formatting is not easy to read...
.withEnabledUser(defaultUser) // "persisted/enabled" user we will check the status of | ||
.withAllowedUser(defaultUser) // "allowed" user we will check the status of |
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.
Since these tests are on the API routing layer, they should not have a distinction between and allowed and enabled user. I think we should defer to using withAllowedUser
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 not possible otherwise, this may be a place where we need to mock out the only routes for backwards compatibility.
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 wasn't sure what to do here. I think it is valuable, because a user being enabled doesn't necessarily mean they can use Terra. I think there's value in being able to test and respond accordingly. I do agree that this is clunky, having to specify both things. Maybe all usages of withEnabledUser(s)
should be migrated to a new withReadyUser(s)
call, which would do both enabling and allowing, leaving the other two methods there for testing the specifics if need be.
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 in general, the api mocking builder should only care about mocking things from the api layer's perspective. So the distinction between allowed and enabled isnt a detail which the api is concerned about, so I dont think the mock should have any logic on it in this layer. In the service layer, it is important to make that distinction, but I dont believe so in the api layer.
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.
Good point. I'll change things.
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.
Actually, I'm going to take it back. As I was rewriting, I realized that the difference between enabled
and allowed
is significant to the api layer. They mean different things. allowed
for the v2 endpoints, means the user is able to use the system, which means they're both enabled
in the db AND have accepted the Terms of Service. These things are spelled out in the API responses, so we need to be able to test them. The test code as written doesn't actually mandate that the test "know" that allowed
means enabled
and ToS accepted, but the responses should look right.
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.
It looks like this has been modified, but IIRC I was trying to have these things call each other so that if I wanted to say .withEnabledUser
it would take care of creating the user automatically. So carrying that logic forward, I would expect .withAllowedUser
to make the user enabled as well, since for a user to be Allowed it must also be enabled.
"GET /api/users/v2/{sam_user_id}" should "return the regular user if they're getting themselves" in { | ||
// Arrange | ||
val samRoutes = new MockSamRoutesBuilder(allUsersGroup) | ||
.withEnabledUsers(Seq(defaultUser, otherUser)) | ||
.withAllowedUsers(Seq(defaultUser, otherUser)) | ||
.callAsNonAdminUser() | ||
.build |
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 now that we have logic dependant on if the calling user's idenitity is an admin or not, we are going to need to pass args into callAsNonAdminUser
since it would not be true for any arbitrary non admin, it is only true of the nonAdmin who is calling is in fact the user that is being gotten
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.
Since we're testing the logic based on the calling user, not the requested user, I think the functionality is still valid. It is kinda un-intuitive though, making all calls to the routes be as an Admin user. I think it could be refactored, but I don't think its necessary to do in this PR
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 guess what I am trying to get as is: how can we know if the calling non admin user cannot make requests for any other enabled/allowed user? It seems like there would not be any distinction in this test
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.
Added the samUser
arg to callAs(Non)AdminUser
methods
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.
@tlangs you're 100% right that needing to construct the routes WITH the one and only 1 user that we will be calling those routes with is indeed gross. It's a function of how the routes are written and where we do the permissions checks (pro-tip: don't sprinkle business logic like authorization checks in your routing logic). This is definitely a refactoring we should do at some point, and for now, the callAsFooUser
was the best solution I could come up with that read somewhat nicely and got us around the yucky.
src/main/scala/org/broadinstitute/dsde/workbench/sam/api/OldUserRoutes.scala
Show resolved
Hide resolved
src/main/scala/org/broadinstitute/dsde/workbench/sam/api/UserRoutesV1.scala
Show resolved
Hide resolved
…te/sam into tl_ID-690_get_user_endpoint
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 looks good to me, but just in case we should do some manual testing in dev/staging before the prod deploy of this stuff. We def don't want non-admin users to be able to grab each-other's info.
@Ghost-in-a-Jar Absolutely. I'll test this on a BEE before merging. |
|
@@ -33,7 +32,7 @@ trait UserRoutes extends SamUserDirectives with SamRequestContextDirectives { | |||
}) | |||
} | |||
|
|||
def userRoutes(samRequestContext: SamRequestContext): server.Route = | |||
def oldUserRoutes(samRequestContext: SamRequestContext): server.Route = |
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.
We should mark this as deprecated
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 we should only mark as deprecated once all the functionality is implemented in the V2 routes
trait UserRoutesV1 extends SamUserDirectives with SamRequestContextDirectives { | ||
val userService: UserService | ||
|
||
def userRoutesV1(samUser: SamUser, samRequestContext: SamRequestContext): server.Route = pathPrefix("users") { |
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 have happened before, but we should mark this as deprecated
pathPrefix("users") { | ||
pathPrefix("v2") { | ||
pathPrefix("self") { | ||
// api/users/v2/self |
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.
Hahaha ❤️ I was literally just doing this 5 minutes ago in my own PR so I could understand what the hell the routes are. This syntax is brutal.
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.
Its sooooo bad
def apply(allowed: Boolean, enabled: Boolean, termsOfService: Boolean): SamUserAllowances = | ||
SamUserAllowances(allowed, Map("enabled" -> enabled, "termsOfService" -> termsOfService)) | ||
} | ||
final case class SamUserAllowances( |
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.
or maybe SamUserAllowance
?
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 it being plural, since it details out all the reasons someone might not be allowed to use the system.
"GET /api/users/v2/{sam_user_id}" should "return the regular user if they're getting themselves" in { | ||
// Arrange | ||
val samRoutes = new MockSamRoutesBuilder(allUsersGroup) | ||
.withEnabledUsers(Seq(defaultUser, otherUser)) | ||
.withAllowedUsers(Seq(defaultUser, otherUser)) | ||
.callAsNonAdminUser() | ||
.build |
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.
@tlangs you're 100% right that needing to construct the routes WITH the one and only 1 user that we will be calling those routes with is indeed gross. It's a function of how the routes are written and where we do the permissions checks (pro-tip: don't sprinkle business logic like authorization checks in your routing logic). This is definitely a refactoring we should do at some point, and for now, the callAsFooUser
was the best solution I could come up with that read somewhat nicely and got us around the yucky.
.withEnabledUser(defaultUser) // "persisted/enabled" user we will check the status of | ||
.withAllowedUser(defaultUser) // "allowed" user we will check the status of |
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.
It looks like this has been modified, but IIRC I was trying to have these things call each other so that if I wanted to say .withEnabledUser
it would take care of creating the user automatically. So carrying that logic forward, I would expect .withAllowedUser
to make the user enabled as well, since for a user to be Allowed it must also be enabled.
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
Ticket:
What:
Some new
users/v2
endpoints to get users, via oneself and via admin powers, all in the same API!Why:
We want to get rid of some of the separate
/admin
endpoints and gate behavior on standard CRUD endpoints.How:
SamUserResponse
object, which replacesenabled
withallowed
.SamUserAllowances
object, which details the reasons a user might be allowed/unallowed./api/users/v2/self
endpoint. This just gets the SamUserResponse object of the calling user/api/users/v2/{sam_user_id}
. A regular user can only get themselves via their ID. An Admin user can get any SamUserResponse object./api/users/v2/self/allowed
endpoint to allow a user to interrogate why they might be allowed/unallowed/api/users/v2/{sam_user_id}/allowed
endpoint to allow an admin to interrogate why another user might be allowed/unallowedPR checklist