1+ import re
12from http import HTTPStatus
2- from fastapi import APIRouter , Depends , HTTPException
3+ from fastapi import APIRouter , Depends , HTTPException , Body
34from sqlmodel import Session
45
5- from authentication import KeycloakUser , get_user_or_none
6+ from authentication import KeycloakUser , get_user_or_none , get_user_or_raise , get_user_by_username
7+ from database .authorization import user_can_administer , set_permission , Permission , register_user
68from database .session import get_session
79from routers .helper_functions import get_asset_type_by_abbreviation
810from routers .resource_routers import versioned_routers
911from database .model .concept .aiod_entry import EntryStatus
10- from database .authorization import user_can_read
12+ from database .authorization import user_can_read , PermissionType
1113from versioning import Version
1214
1315
1416def create (url_prefix : str = "" , version : Version = Version .LATEST ) -> APIRouter :
1517 router = APIRouter ()
1618
19+ @router .post (
20+ "/assets/permissions" ,
21+ tags = ["Assets" ],
22+ description = "Manage permissions that a user has for an asset." ,
23+ )
24+ def add_or_update_permission (
25+ asset_identifier : str = Body (
26+ description = "The identifier of the asset for which to update the permission."
27+ ),
28+ user : str = Body (
29+ description = "The username or subject identifier of the user." ,
30+ examples = ["jsmith01" , "4a80f256-3928-4cfa-ba66-5e22bb36fc01" ],
31+ ),
32+ permission_type : PermissionType | None = Body (
33+ description = "The permission to add for the user. "
34+ "If not set, their permissions will be removed." ,
35+ default = None ,
36+ ),
37+ session : Session = Depends (get_session ),
38+ current_user : KeycloakUser = Depends (get_user_or_raise ),
39+ ):
40+ _ , resource = get_asset_by_identifier (asset_identifier , session )
41+ if not user_can_administer (current_user , resource .aiod_entry ):
42+ raise HTTPException (
43+ status_code = HTTPStatus .FORBIDDEN ,
44+ detail = f"You are not allowed to update permissions for asset { asset_identifier } ." ,
45+ )
46+ sub_pattern = r"\S{8}(-\S{4}){3}-\S{12}"
47+ if re .match (sub_pattern , user ):
48+ other = KeycloakUser (name = "unknown" , roles = set (), _subject_identifier = user )
49+ else :
50+ other = get_user_by_username (user ) # type: ignore[assignment]
51+ if not other :
52+ raise HTTPException (
53+ status_code = HTTPStatus .NOT_FOUND ,
54+ detail = f"User with name { user !r} not found." ,
55+ )
56+
57+ register_user (other , session ) # Should be replaced by KC pushing to REST API
58+ if other ._subject_identifier == current_user ._subject_identifier :
59+ # This request is more likely to be an accident than on purpose.
60+ # Additionally, we do not want to allow people to accidentally remove all
61+ # administrators from an asset which this restriction ensures.
62+ raise HTTPException (
63+ status_code = HTTPStatus .UNPROCESSABLE_ENTITY ,
64+ detail = "You cannot change permissions that pertain to yourself." ,
65+ )
66+ if permission_type :
67+ set_permission (other , resource .aiod_entry , session , type_ = permission_type )
68+ session .commit ()
69+ else :
70+ key = {
71+ "user_identifier" : other ._subject_identifier ,
72+ "aiod_entry_identifier" : resource .aiod_entry .identifier ,
73+ }
74+ permission = session .get (Permission , key )
75+ if permission :
76+ session .delete (permission )
77+ session .commit ()
78+
1779 @router .get (
1880 f"/assets/{{identifier}}" ,
1981 tags = ["Assets" ],
@@ -27,17 +89,7 @@ def asset(
2789 """
2890 Get the resource identified by AIoD identifier, return in aiod schema.
2991 """
30- asset_type_map = get_asset_type_by_abbreviation ()
31- prefix = identifier .split ("_" )[0 ]
32- model_class = asset_type_map .get (prefix )
33-
34- if not model_class :
35- raise HTTPException (
36- status_code = HTTPStatus .NOT_FOUND ,
37- detail = f"Unknown asset type with identifier '{ identifier } '" ,
38- )
39-
40- resource = session .get (model_class , identifier )
92+ model_class , resource = get_asset_by_identifier (identifier , session )
4193
4294 if not resource or resource .date_deleted is not None :
4395 raise HTTPException (
@@ -66,4 +118,16 @@ def asset(
66118 detail = f"No router found to deserialize asset of type '{ model_class .__name__ } '" ,
67119 )
68120
121+ def get_asset_by_identifier (identifier , session ):
122+ asset_type_map = get_asset_type_by_abbreviation ()
123+ prefix = identifier .split ("_" )[0 ]
124+ model_class = asset_type_map .get (prefix )
125+ if not model_class :
126+ raise HTTPException (
127+ status_code = HTTPStatus .NOT_FOUND ,
128+ detail = f"Unknown asset type with identifier '{ identifier } '" ,
129+ )
130+ resource = session .get (model_class , identifier )
131+ return model_class , resource
132+
69133 return router
0 commit comments