66from fastapi import APIRouter , Depends , HTTPException
77from fastapi .security import HTTPBearer
88
9- from app .deps .authorization_deps import Authorization
9+ from app .deps .authorization_deps import Authorization , ProjectAuthorization
1010from app .keycloak_auth import get_current_user , get_user
1111from app .models .datasets import DatasetDB
12- from app .models .files import FileDB
13- from app .models .folders import FolderDB
14- from app .models .groups import GroupDB
12+ from app .models .groups import GroupDB , Member , GroupType
1513from app .models .pages import Paged , _construct_page_metadata , _get_page_query
16- from app .models .projects import ProjectMember , ProjectDB , ProjectIn , ProjectOut
14+ from app .models .projects import ProjectDB , ProjectIn , ProjectOut
1715from app .models .users import UserDB , UserOut
1816
1917router = APIRouter ()
@@ -30,18 +28,29 @@ async def save_project(
3028 project = ProjectDB (** project_in .dict ())
3129 await project .insert ()
3230
33- # Automatically create a group to go with this project
34- group = GroupDB ({
35- "name" : project .name ,
36- "description" : f"Automatically created for members of { project .name } project." ,
31+ # Automatically create viewer and editor groups to go with this project
32+ viewer_group = GroupDB ({
33+ "name" : project .name + " (Viewers)" ,
34+ "description" : f"Automatically created for viewers of { project .name } project." ,
35+ "users" : [],
36+ "project_id" : project .id ,
37+ "type" : GroupType .PROJECT
38+ }, creator = user .email )
39+ await viewer_group .insert ()
40+
41+ editor_group = GroupDB ({
42+ "name" : project .name + " (Editors)" ,
43+ "description" : f"Automatically created for editors of { project .name } project." ,
3744 "users" : [
3845 {"user" : user , "editor" : True }
3946 ],
40- "project_id" : project .id
47+ "project_id" : project .id ,
48+ "type" : GroupType .PROJECT
4149 }, creator = user .email )
42- await group .insert ()
50+ await editor_group .insert ()
4351
44- project .group_id = group .id
52+ project .viewers_group = viewer_group .id
53+ project .editors_group = editor_group .id
4554 await project .save ()
4655
4756 return project .dict ()
@@ -51,8 +60,8 @@ async def save_project(
5160async def add_dataset (
5261 project_id : str ,
5362 dataset_id : str ,
54- # allow_proj: bool = Depends(ProjectAuthorization("editor")),
55- allow_ds : bool = Depends (Authorization ("editor " )),
63+ allow_proj : bool = Depends (ProjectAuthorization ("editor" )),
64+ allow_ds : bool = Depends (Authorization ("viewer " )),
5665):
5766 if (project := await ProjectDB .get (PydanticObjectId (project_id ))) is not None :
5867 if (dataset := await DatasetDB .get (PydanticObjectId (dataset_id ))) is not None :
@@ -93,118 +102,6 @@ async def remove_dataset(
93102 raise HTTPException (status_code = 404 , detail = f"Project { project_id } not found" )
94103
95104
96- @router .post ("/{project_id}/add_folder/{folder_id}" , response_model = ProjectOut )
97- async def add_folder (
98- project_id : str ,
99- folder_id : str ,
100- ):
101- if (
102- project := await ProjectDB .find_one (
103- Or (
104- ProjectDB .id == PydanticObjectId (project_id ),
105- )
106- )
107- ) is not None :
108- if (
109- folder := await FolderDB .find_one (
110- Or (
111- FolderDB .id == FolderDB (PydanticObjectId (folder_id )),
112- )
113- )
114- ) is not None :
115- if folder_id not in project .folder_ids :
116- project .folder_ids .append (PydanticObjectId (folder_id ))
117- await project .replace ()
118- return project .dict ()
119- raise HTTPException (status_code = 404 , detail = f"Folder { folder_id } not found" )
120- raise HTTPException (status_code = 404 , detail = f"Project { project_id } not found" )
121-
122-
123- @router .post ("/{project_id}/remove_folder/{folder_id}" , response_model = ProjectOut )
124- async def remove_folder (
125- project_id : str ,
126- folder_id : str ,
127- ):
128- if (
129- project := await ProjectDB .find_one (
130- Or (
131- ProjectDB .id == PydanticObjectId (project_id ),
132- )
133- )
134- ) is not None :
135- if (
136- folder := await FolderDB .find_one (
137- Or (
138- FolderDB .id == FolderDB (PydanticObjectId (folder_id )),
139- )
140- )
141- ) is not None :
142- if folder_id in project .folder_ids :
143- project .folder_ids .remove (PydanticObjectId (folder_id ))
144- await project .replace ()
145- return project .dict ()
146- else :
147- return project .dict ()
148- raise HTTPException (status_code = 404 , detail = f"Folder { folder_id } not found" )
149- raise HTTPException (status_code = 404 , detail = f"Project { project_id } not found" )
150-
151-
152- @router .post ("/{project_id}/add_file/{file_id}" , response_model = ProjectOut )
153- async def add_file (
154- project_id : str ,
155- file_id : str ,
156- ):
157- if (
158- project := await ProjectDB .find_one (
159- Or (
160- ProjectDB .id == PydanticObjectId (project_id ),
161- )
162- )
163- ) is not None :
164- if (
165- file := await FolderDB .find_one (
166- Or (
167- FileDB .id == FileDB (PydanticObjectId (file_id )),
168- )
169- )
170- ) is not None :
171- if file_id not in project .file_ids :
172- project .file_ids .append (PydanticObjectId (file_id ))
173- await project .replace ()
174- return project .dict ()
175- raise HTTPException (status_code = 404 , detail = f"File { file_id } not found" )
176- raise HTTPException (status_code = 404 , detail = f"Project { project_id } not found" )
177-
178-
179- @router .post ("/{project_id}/remove_file/{file_id}" , response_model = ProjectOut )
180- async def remove_file (
181- project_id : str ,
182- file_id : str ,
183- ):
184- if (
185- project := await ProjectDB .find_one (
186- Or (
187- ProjectDB .id == PydanticObjectId (project_id ),
188- )
189- )
190- ) is not None :
191- if (
192- file := await FolderDB .find_one (
193- Or (
194- FileDB .id == FileDB (PydanticObjectId (file_id )),
195- )
196- )
197- ) is not None :
198- if file_id in project .file_ids :
199- project .file_ids .remove (PydanticObjectId (file_id ))
200- await project .replace ()
201- return project .dict ()
202- else :
203- return project .dict ()
204- raise HTTPException (status_code = 404 , detail = f"File { file_id } not found" )
205- raise HTTPException (status_code = 404 , detail = f"Project { project_id } not found" )
206-
207-
208105@router .get ("" , response_model = Paged )
209106async def get_projects (
210107 user_id = Depends (get_user ),
@@ -263,22 +160,59 @@ async def delete_project(
263160async def add_member (
264161 project_id : str ,
265162 username : str ,
266- role : Optional [str ] = None ,
163+ role : Optional [str ] = "viewer" ,
164+ allow : bool = Depends (ProjectAuthorization ("editor" )),
267165):
268- """Add a new user to a group ."""
166+ """Add a new user to the project individually - this is routed to one of the project's hidden groups ."""
269167 if (user := await UserDB .find_one (UserDB .email == username )) is not None :
270- new_member = ProjectMember (user = UserOut (** user .dict ()))
168+ # Add to viewers group if role is none, otherwise add to appropriate group
169+ new_member = Member (user = UserOut (** user .dict ()), editor = (role == "editor" ))
271170 if (project := await ProjectDB .get (PydanticObjectId (project_id ))) is not None :
272- found_already = False
273- for u in project .users :
274- if u .user .email == username :
275- found_already = True
276- break
277- if not found_already :
278- # If user is already in the group, skip directly to returning the group
279- # else add role and attach this member
280- project .users .append (new_member )
281- await project .replace ()
171+ viewers_group = await GroupDB .get (PydanticObjectId (project .viewers_group_id ))
172+ editors_group = await GroupDB .get (PydanticObjectId (project .editors_group_id ))
173+
174+ if role == "viewer" :
175+ found_in_viewers = False
176+ for u in viewers_group .users :
177+ if u .user .email == username :
178+ found_in_viewers = True
179+ break
180+ if not found_in_viewers :
181+ viewers_group .users .append (new_member )
182+ await viewers_group .save ()
183+
184+ found_in_editors = False
185+ clean_users = []
186+ for u in editors_group .users :
187+ if u .user .email == username :
188+ found_in_editors = True
189+ else :
190+ clean_users .append (u )
191+ if found_in_editors :
192+ editors_group .users = clean_users
193+ await editors_group .save ()
194+
195+ elif role == "editor" :
196+ found_in_editors = False
197+ for u in editors_group .users :
198+ if u .user .email == username :
199+ found_in_editors = True
200+ break
201+ if not found_in_editors :
202+ editors_group .users .append (new_member )
203+ await editors_group .save ()
204+
205+ found_in_viewers = False
206+ clean_users = []
207+ for u in viewers_group .users :
208+ if u .user .email == username :
209+ found_in_viewers = True
210+ else :
211+ clean_users .append (u )
212+ if found_in_viewers :
213+ viewers_group .users = clean_users
214+ await viewers_group .save ()
215+
282216 return project .dict ()
283217 raise HTTPException (status_code = 404 , detail = f"Project { project_id } not found" )
284218 raise HTTPException (status_code = 404 , detail = f"User { username } not found" )
@@ -288,20 +222,35 @@ async def add_member(
288222async def remove_member (
289223 project_id : str ,
290224 username : str ,
225+ allow : bool = Depends (ProjectAuthorization ("editor" )),
291226):
292227 """Remove a user from a group."""
293228
294229 if (project := await ProjectDB .get (PydanticObjectId (project_id ))) is not None :
295- # Is the user actually in the group already?
296- found_user = None
297- for u in project .users :
230+ viewers_group = await GroupDB .get (PydanticObjectId (project .viewers_group_id ))
231+ editors_group = await GroupDB .get (PydanticObjectId (project .editors_group_id ))
232+
233+ found_in_editors = False
234+ clean_users = []
235+ for u in editors_group .users :
236+ if u .user .email == username :
237+ found_in_editors = True
238+ else :
239+ clean_users .append (u )
240+ if found_in_editors :
241+ editors_group .users = clean_users
242+ await editors_group .save ()
243+
244+ found_in_viewers = False
245+ clean_users = []
246+ for u in viewers_group .users :
298247 if u .user .email == username :
299- found_user = u
300- if not found_user :
301- # TODO: User wasn't in group, should this throw an error instead? Either way, the user is removed...
302- return project
303- # Update group itself
304- project . users . remove ( found_user )
305- await project . replace ()
248+ found_in_viewers = True
249+ else :
250+ clean_users . append ( u )
251+ if found_in_viewers :
252+ viewers_group . users = clean_users
253+ await viewers_group . save ( )
254+
306255 return project .dict ()
307256 raise HTTPException (status_code = 404 , detail = f"Project { project_id } not found" )
0 commit comments