Skip to content

Commit 881ec0f

Browse files
authored
Basic implementation of admin in user collection in mongodb (#809)
* Basic implementation of admin in user collection in mongodb * black formatting * adding codegen files * minor fix to boolean logic * Adding get_admin dependency black formatting adding codegen file removing redundant print statement fixing pytest failure fixing pytest failure ran black * fixed pytest failure * using function count
1 parent 847b570 commit 881ec0f

File tree

4 files changed

+107
-14
lines changed

4 files changed

+107
-14
lines changed

backend/app/models/users.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class Settings:
3131
class UserDB(UserDoc):
3232
hashed_password: str = Field()
3333
keycloak_id: Optional[str] = None
34+
admin: bool
3435

3536
def verify_password(self, password):
3637
return pwd_context.verify(password, self.hashed_password)

backend/app/routers/authentication.py

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
)
99
from passlib.hash import bcrypt
1010

11-
from app import dependencies
12-
from app.keycloak_auth import create_user
11+
from beanie import PydanticObjectId
12+
13+
from app.keycloak_auth import create_user, get_current_user
1314
from app.keycloak_auth import keycloak_openid
15+
from app.models.datasets import DatasetDB
1416
from app.models.users import UserDB, UserIn, UserOut, UserLogin
1517

1618
router = APIRouter()
@@ -38,11 +40,25 @@ async def save_user(userIn: UserIn):
3840

3941
# create local user
4042
hashed_password = bcrypt.hash(userIn.password)
41-
user = UserDB(
42-
**userIn.dict(),
43-
hashed_password=hashed_password,
44-
keycloak_id=keycloak_user,
45-
)
43+
44+
# check if this is the 1st user, make it admin
45+
count = await UserDB.count()
46+
47+
if count == 0:
48+
user = UserDB(
49+
**userIn.dict(),
50+
admin=True,
51+
hashed_password=hashed_password,
52+
keycloak_id=keycloak_user,
53+
)
54+
else:
55+
user = UserDB(
56+
**userIn.dict(),
57+
admin=False,
58+
hashed_password=hashed_password,
59+
keycloak_id=keycloak_user,
60+
)
61+
4662
await user.insert()
4763
return user.dict()
4864

@@ -75,3 +91,41 @@ async def authenticate_user(email: str, password: str):
7591
if not user.verify_password(password):
7692
return None
7793
return user
94+
95+
96+
async def get_admin(dataset_id: str = None, current_username=Depends(get_current_user)):
97+
if (
98+
dataset_id
99+
and (dataset_db := await DatasetDB.get(PydanticObjectId(dataset_id)))
100+
is not None
101+
):
102+
return DatasetDB.creator.email == current_username.email
103+
else:
104+
if (
105+
current_user := await UserDB.find_one(
106+
UserDB.email == current_username.email
107+
)
108+
) is not None:
109+
return current_user.admin
110+
else:
111+
raise HTTPException(
112+
status_code=404, detail=f"User {current_username.email} not found"
113+
)
114+
115+
116+
@router.post("/users/set_admin/{useremail}", response_model=UserOut)
117+
async def set_admin(
118+
useremail: str, current_username=Depends(get_current_user), admin=Depends(get_admin)
119+
):
120+
if admin:
121+
if (user := await UserDB.find_one(UserDB.email == useremail)) is not None:
122+
user.admin = True
123+
await user.replace()
124+
return user.dict()
125+
else:
126+
raise HTTPException(status_code=404, detail=f"User {useremail} not found")
127+
else:
128+
raise HTTPException(
129+
status_code=403,
130+
detail=f"User {current_username.email} is not an admin. Only admin can make others admin.",
131+
)

backend/app/routers/keycloak.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,28 @@ async def auth(code: str) -> RedirectResponse:
117117
given_name = userinfo.get("given_name", " ")
118118
family_name = userinfo.get("family_name", " ")
119119
email = userinfo["email"]
120-
user = UserDB(
121-
email=email,
122-
first_name=given_name,
123-
last_name=family_name,
124-
hashed_password="",
125-
keycloak_id=keycloak_id,
126-
)
120+
121+
# check if this is the 1st user, make it admin
122+
count = await UserDB.count()
123+
124+
if count == 0:
125+
user = UserDB(
126+
email=email,
127+
first_name=given_name,
128+
last_name=family_name,
129+
hashed_password="",
130+
keycloak_id=keycloak_id,
131+
admin=True,
132+
)
133+
else:
134+
user = UserDB(
135+
email=email,
136+
first_name=given_name,
137+
last_name=family_name,
138+
hashed_password="",
139+
keycloak_id=keycloak_id,
140+
admin=False,
141+
)
127142
matched_user = await UserDB.find_one(UserDB.email == email)
128143
if matched_user is None:
129144
await user.insert()

frontend/src/openapi/v2/services/LoginService.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,27 @@ export class LoginService {
4949
});
5050
}
5151

52+
/**
53+
* Set Admin
54+
* @param useremail
55+
* @param datasetId
56+
* @returns UserOut Successful Response
57+
* @throws ApiError
58+
*/
59+
public static setAdminApiV2UsersSetAdminUseremailPost(
60+
useremail: string,
61+
datasetId?: string,
62+
): CancelablePromise<UserOut> {
63+
return __request({
64+
method: 'POST',
65+
path: `/api/v2/users/set_admin/${useremail}`,
66+
query: {
67+
'dataset_id': datasetId,
68+
},
69+
errors: {
70+
422: `Validation Error`,
71+
},
72+
});
73+
}
74+
5275
}

0 commit comments

Comments
 (0)