Skip to content

Commit 5bc6b11

Browse files
fix: address PR comments from r4victor
1 parent 806c095 commit 5bc6b11

4 files changed

Lines changed: 13 additions & 39 deletions

File tree

src/dstack/_internal/server/routers/projects.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
)
1717
from dstack._internal.server.security.permissions import (
1818
Authenticated,
19+
ProjectAdmin,
1920
ProjectManager,
20-
ProjectManagerOrPublicJoin,
21+
ProjectManagerOrPublicProject,
2122
ProjectManagerOrSelfLeave,
2223
ProjectMemberOrPublicAccess,
2324
)
@@ -105,7 +106,7 @@ async def set_project_members(
105106
async def add_project_members(
106107
body: AddProjectMemberRequest,
107108
session: AsyncSession = Depends(get_session),
108-
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectManagerOrPublicJoin()),
109+
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectManagerOrPublicProject()),
109110
) -> Project:
110111
user, project = user_project
111112
await projects.add_project_members(
@@ -143,7 +144,7 @@ async def remove_project_members(
143144
async def update_project_visibility(
144145
body: UpdateProjectVisibilityRequest,
145146
session: AsyncSession = Depends(get_session),
146-
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectManager()),
147+
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectAdmin()),
147148
) -> Project:
148149
user, project = user_project
149150
await projects.update_project_visibility(

src/dstack/_internal/server/schemas/projects.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Annotated, List, Optional
1+
from typing import Annotated, List
22

33
from pydantic import Field
44

@@ -8,7 +8,7 @@
88

99
class CreateProjectRequest(CoreModel):
1010
project_name: str
11-
is_public: Optional[bool] = False
11+
is_public: bool = False
1212

1313

1414
class UpdateProjectVisibilityRequest(CoreModel):
@@ -32,10 +32,8 @@ class SetProjectMembersRequest(CoreModel):
3232

3333

3434
class AddProjectMemberRequest(CoreModel):
35-
# Always accept a list of members for cleaner API design
3635
members: List[MemberSetting]
3736

3837

3938
class RemoveProjectMemberRequest(CoreModel):
40-
# Always accept a list of usernames for cleaner API design
4139
usernames: List[str]

src/dstack/_internal/server/security/permissions.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ async def __call__(
5858
raise error_invalid_token()
5959
project = await get_project_model_by_name(session=session, project_name=project_name)
6060
if project is None:
61-
raise error_forbidden()
61+
raise error_not_found()
6262
if user.global_role == GlobalRole.ADMIN:
6363
return user, project
6464
project_role = get_user_project_role(user=user, project=project)
@@ -143,17 +143,19 @@ async def __call__(
143143
if project.is_public:
144144
return user, project
145145

146-
# Neither member nor public project
147146
raise error_forbidden()
148147

149148

150-
class ProjectManagerOrPublicJoin:
149+
class ProjectManagerOrPublicProject:
151150
"""
152151
Allows:
153-
1. Project managers to add any members
154-
2. Any authenticated user to join public projects themselves
152+
1. Project managers to perform member management operations
153+
2. Access to public projects for any authenticated user
155154
"""
156155

156+
def __init__(self):
157+
self.project_manager = ProjectManager()
158+
157159
async def __call__(
158160
self,
159161
project_name: str,

src/tests/_internal/server/routers/test_projects.py

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,33 +1801,6 @@ async def test_project_admin_can_update_visibility(
18011801
assert response.status_code == 200
18021802
assert response.json()["is_public"] == False
18031803

1804-
@pytest.mark.asyncio
1805-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
1806-
async def test_project_manager_can_update_visibility(
1807-
self, test_db, session: AsyncSession, client: AsyncClient
1808-
):
1809-
# Setup project with admin and manager
1810-
admin_user = await create_user(session=session, name="admin", global_role=GlobalRole.USER)
1811-
manager_user = await create_user(
1812-
session=session, name="manager", global_role=GlobalRole.USER
1813-
)
1814-
project = await create_project(session=session, owner=admin_user, is_public=False)
1815-
await add_project_member(
1816-
session=session, project=project, user=admin_user, project_role=ProjectRole.ADMIN
1817-
)
1818-
await add_project_member(
1819-
session=session, project=project, user=manager_user, project_role=ProjectRole.MANAGER
1820-
)
1821-
1822-
# Manager should be able to update visibility
1823-
response = await client.post(
1824-
f"/api/projects/{project.name}/update_visibility",
1825-
headers=get_auth_headers(manager_user.token),
1826-
json={"is_public": True},
1827-
)
1828-
assert response.status_code == 200
1829-
assert response.json()["is_public"] == True
1830-
18311804
@pytest.mark.asyncio
18321805
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
18331806
async def test_regular_user_cannot_update_visibility(

0 commit comments

Comments
 (0)