-
Notifications
You must be signed in to change notification settings - Fork 2
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
Feature/11 combine profile update and fi association #38
Changes from 5 commits
f90c4f1
2ed0d01
a1453c0
e16b624
1057704
face017
fe0a473
562a06f
086a75f
80861ee
ff582cb
df3522f
d5a746c
5a75928
9e6d2aa
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 |
---|---|---|
@@ -1,5 +1,4 @@ | ||
from typing import Any, Dict, List | ||
|
||
from typing import List, Dict, Any, Set | ||
from pydantic import BaseModel | ||
from starlette.authentication import BaseUser | ||
|
||
|
@@ -41,6 +40,15 @@ class Config: | |
orm_mode = True | ||
|
||
|
||
class UserProfile(BaseModel): | ||
firstName: str | ||
lastName: str | ||
leis: Set[str] = None | ||
|
||
def get_user(self) -> Dict[str, Any]: | ||
return {"firstName": self.firstName, "lastName": self.lastName} | ||
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. pydantic should have something built-in that will generate the dict for you, I don't think you need to create a separate method for this. |
||
|
||
|
||
class AuthenticatedUser(BaseUser, BaseModel): | ||
claims: Dict[str, Any] | ||
name: str | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
from http import HTTPStatus | ||
import logging | ||
import os | ||
from typing import Dict, Any | ||
from typing import Dict, Any, Set | ||
|
||
import jose.jwt | ||
import requests | ||
|
@@ -88,5 +88,9 @@ def associate_to_lei(self, user_id: str, lei: str) -> None: | |
detail="No institution found for given LEI", | ||
) | ||
|
||
def associate_to_lei_set(self, user_id, leis: Set[str]): | ||
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. normally if we can, we'd like to keep the implementation detail of the collection out of the var name / method name; this one should be good with just |
||
for lei in leis: | ||
self.associate_to_lei(user_id, lei) | ||
|
||
|
||
oauth2_admin = OAuth2Admin() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
from http import HTTPStatus | ||
from typing import Dict, Any, Set | ||
from typing import Set | ||
from fastapi import Depends, Request | ||
from starlette.authentication import requires | ||
from dependencies import check_domain | ||
from util import Router | ||
from entities.models import UserProfile | ||
|
||
from entities.models import AuthenticatedUser | ||
from oauth2 import oauth2_admin | ||
|
@@ -19,12 +20,13 @@ async def get_me(request: Request): | |
|
||
@router.put("/me/", status_code=HTTPStatus.ACCEPTED, dependencies=[Depends(check_domain)]) | ||
@requires("manage-account") | ||
async def update_me(request: Request, user: Dict[str, Any]): | ||
oauth2_admin.update_user(request.user.id, user) | ||
async def update_me(request: Request, user: UserProfile): | ||
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. Should API functions that use the Keycloak client be Dropping |
||
oauth2_admin.update_user(request.user.id, user.get_user()) | ||
if user.leis: | ||
oauth2_admin.associate_to_lei_set(request.user.id, user.leis) | ||
|
||
|
||
@router.put("/me/institutions/", status_code=HTTPStatus.ACCEPTED, dependencies=[Depends(check_domain)]) | ||
@requires("manage-account") | ||
async def associate_lei(request: Request, leis: Set[str]): | ||
for lei in leis: | ||
oauth2_admin.associate_to_lei(request.user.id, lei) | ||
oauth2_admin.associate_to_lei_set(request.user.id, leis) |
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.
let's keep the variable naming to the python convention of snake case, and use alias_generator to convert them to camel case: https://docs.pydantic.dev/latest/api/config/#pydantic.alias_generators.to_camel
https://stackoverflow.com/questions/67995510/how-to-inflect-from-snake-case-to-camel-case-post-the-pydantic-schema-validation
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 body was to look like:
{ "firstName": "First", "lastName": "Last", "leis": ["LEI1", "LEI2"] }
So, we need aliasing for the request as well as when generating the dict?
To match the request reqs and the syntax that keycloak expects, we would need it in the form above, but to change to snake case would require aliasing to snake when request is sent and when creating dict being sent to keycloak.
Just double-checking here. Should the request body be changed to snake case?
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.
request body to the api should be snake case, we would then alias it to camel case for keycloak.
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.
Just so I understand, our API's User resource will look like:
Meanwhile, the Keycloak Admin API's UserRepresentation expects:
...and to associated LEIs to said User, it's reeeaaally associating a Keycloak User to a Keycloak Group which is named after a given LEI? So, to complete the transaction, and second call that's made to...
PUT /admin/realms/{realm}/users/{id}/groups/{groupId}
If yes, 👍. If no, I may need some help here. 😅
I think what @lchen-2101 is getting at is that we shouldn't couple our API format to Keycloak's, which I agree with.
As to Pydantic's
to_camel
, I'm not sure we want to do that. There's only 2 attributes we care about. Aaaand, what happens toleis
on our model when you do theto_camel
? Wouldn't that still be there in the data we'd be submitting to Keycloak? Seems like that might break.To me, it doesn't seem that gross to just construct the dict at time of request in
update_me()
. 🤷 Looks like we're already doing it that way inupsert_group
now anyway.