mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
Use an ACL table to check for privileges
This commit is contained in:
1 parent
6bd855b3c5
commit
60ce1172e0
30 files changed
+1195
-1423
No files matched your search
@@ -0,0 +1,214 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2023 GNS3 Technologies Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
import uuid
|
||||
|
||||
from fastapi import FastAPI, status
|
||||
from httpx import AsyncClient
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from gns3server.db.repositories.users import UsersRepository
|
||||
from gns3server.db.repositories.rbac import RbacRepository
|
||||
from gns3server.controller import Controller
|
||||
from gns3server.controller.project import Project
|
||||
from gns3server.schemas.controller.users import User
|
||||
from gns3server.schemas.controller.rbac import ACECreate
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
class TestACLRoutes:
|
||||
|
||||
# @pytest_asyncio.fixture
|
||||
# async def project(
|
||||
# self,
|
||||
# app: FastAPI,
|
||||
# authorized_client: AsyncClient,
|
||||
# test_user: User,
|
||||
# db_session: AsyncSession,
|
||||
# controller: Controller
|
||||
# ) -> Project:
|
||||
#
|
||||
# # add an ACE to allow user to create a project
|
||||
# user_id = test_user.user_id
|
||||
# rbac_repo = RbacRepository(db_session)
|
||||
# role_in_db = await rbac_repo.get_role_by_name("User")
|
||||
# role_id = role_in_db.role_id
|
||||
# ace = ACECreate(
|
||||
# path="/projects",
|
||||
# type="user",
|
||||
# user_id=user_id,
|
||||
# role_id=role_id
|
||||
# )
|
||||
# await rbac_repo.create_ace(ace)
|
||||
# project_uuid = str(uuid.uuid4())
|
||||
# params = {"name": "test", "project_id": project_uuid}
|
||||
# response = await authorized_client.post(app.url_path_for("create_project"), json=params)
|
||||
# assert response.status_code == status.HTTP_201_CREATED
|
||||
# return controller.get_project(project_uuid)
|
||||
|
||||
#@pytest_asyncio.fixture
|
||||
# async def project(
|
||||
# self,
|
||||
# app: FastAPI,
|
||||
# client: AsyncClient,
|
||||
# controller: Controller
|
||||
# ) -> Project:
|
||||
#
|
||||
# project_uuid = str(uuid.uuid4())
|
||||
# params = {"name": "test", "project_id": project_uuid}
|
||||
# response = await client.post(app.url_path_for("create_project"), json=params)
|
||||
# assert response.status_code == status.HTTP_201_CREATED
|
||||
# return controller.get_project(project_uuid)
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def group_id(self, db_session: AsyncSession) -> str:
|
||||
|
||||
users_repo = UsersRepository(db_session)
|
||||
group_in_db = await users_repo.get_user_group_by_name("Users")
|
||||
group_id = str(group_in_db.user_group_id)
|
||||
return group_id
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def role_id(self, db_session: AsyncSession) -> str:
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
role_in_db = await rbac_repo.get_role_by_name("User")
|
||||
role_id = str(role_in_db.role_id)
|
||||
return role_id
|
||||
|
||||
async def test_create_ace(
|
||||
self,
|
||||
app: FastAPI,
|
||||
authorized_client: AsyncClient,
|
||||
db_session: AsyncSession,
|
||||
test_user: User,
|
||||
role_id: str
|
||||
) -> None:
|
||||
|
||||
# add an ACE on /projects to allow user to create a project
|
||||
path = f"/projects"
|
||||
new_ace = {
|
||||
"path": path,
|
||||
"type": "user",
|
||||
"user_id": str(test_user.user_id),
|
||||
"role_id": role_id
|
||||
}
|
||||
|
||||
response = await authorized_client.post(app.url_path_for("create_ace"), json=new_ace)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
assert await rbac_repo.check_user_has_privilege(test_user.user_id, path, "Project.Allocate") is True
|
||||
|
||||
response = await authorized_client.post(app.url_path_for("create_project"), json={"name": "test"})
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
async def test_create_ace_not_existing_endpoint(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
group_id: str,
|
||||
role_id: str
|
||||
) -> None:
|
||||
|
||||
new_ace = {
|
||||
"path": "/projects/invalid",
|
||||
"type": "group",
|
||||
"group_id": group_id,
|
||||
"role_id": role_id
|
||||
}
|
||||
response = await client.post(app.url_path_for("create_ace"), json=new_ace)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
# async def test_create_ace_not_existing_resource(
|
||||
# self,
|
||||
# app: FastAPI,
|
||||
# client: AsyncClient,
|
||||
# group_id: str,
|
||||
# role_id: str
|
||||
# ) -> None:
|
||||
#
|
||||
# new_ace = {
|
||||
# "path": f"/projects/{str(uuid.uuid4())}",
|
||||
# "group_id": group_id,
|
||||
# "role_id": role_id
|
||||
# }
|
||||
# response = await client.post(app.url_path_for("create_ace"), json=new_ace)
|
||||
# assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
async def test_get_ace(self, app: FastAPI, client: AsyncClient, db_session: AsyncSession) -> None:
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
ace_in_db = await rbac_repo.get_ace_by_path(f"/projects")
|
||||
response = await client.get(app.url_path_for("get_ace", ace_id=ace_in_db.ace_id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["ace_id"] == str(ace_in_db.ace_id)
|
||||
|
||||
async def test_list_aces(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("get_aces"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 1
|
||||
|
||||
async def test_update_ace(
|
||||
self, app: FastAPI,
|
||||
client: AsyncClient,
|
||||
db_session: AsyncSession,
|
||||
test_user: User,
|
||||
role_id: str
|
||||
) -> None:
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
ace_in_db = await rbac_repo.get_ace_by_path(f"/projects")
|
||||
|
||||
update_ace = {
|
||||
"path": f"/appliances",
|
||||
"type": "user",
|
||||
"user_id": str(test_user.user_id),
|
||||
"role_id": role_id
|
||||
}
|
||||
response = await client.put(
|
||||
app.url_path_for("update_ace", ace_id=ace_in_db.ace_id),
|
||||
json=update_ace
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
updated_ace_in_db = await rbac_repo.get_ace(ace_in_db.ace_id)
|
||||
assert updated_ace_in_db.path == f"/appliances"
|
||||
|
||||
async def test_delete_ace(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
db_session: AsyncSession,
|
||||
) -> None:
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
ace_in_db = await rbac_repo.get_ace_by_path(f"/appliances")
|
||||
response = await client.delete(app.url_path_for("delete_ace", ace_id=ace_in_db.ace_id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
# async def test_prune_permissions(self, app: FastAPI, client: AsyncClient, db_session: AsyncSession) -> None:
|
||||
#
|
||||
# response = await client.post(app.url_path_for("prune_permissions"))
|
||||
# assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
#
|
||||
# rbac_repo = RbacRepository(db_session)
|
||||
# permissions_in_db = await rbac_repo.get_permissions()
|
||||
# assert len(permissions_in_db) == 10 # 6 default permissions + 4 custom permissions
|
||||
@@ -16,17 +16,13 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from fastapi import FastAPI, status
|
||||
from httpx import AsyncClient
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from gns3server.db.repositories.users import UsersRepository
|
||||
from gns3server.db.repositories.rbac import RbacRepository
|
||||
from gns3server.schemas.controller.users import User
|
||||
from gns3server.schemas.controller.rbac import Role
|
||||
from gns3server import schemas
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
@@ -170,84 +166,3 @@ class TestGroupMembersRoutes:
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
members = await user_repo.get_user_group_members(group_in_db.user_group_id)
|
||||
assert len(members) == 0
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_role(db_session: AsyncSession) -> Role:
|
||||
|
||||
new_role = schemas.RoleCreate(
|
||||
name="TestRole",
|
||||
description="This is my test role"
|
||||
)
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
existing_role = await rbac_repo.get_role_by_name(new_role.name)
|
||||
if existing_role:
|
||||
return existing_role
|
||||
return await rbac_repo.create_role(new_role)
|
||||
|
||||
|
||||
class TestGroupRolesRoutes:
|
||||
|
||||
async def test_add_role_to_group(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
test_role: Role,
|
||||
db_session: AsyncSession
|
||||
) -> None:
|
||||
|
||||
user_repo = UsersRepository(db_session)
|
||||
group_in_db = await user_repo.get_user_group_by_name("Users")
|
||||
response = await client.put(
|
||||
app.url_path_for(
|
||||
"add_role_to_group",
|
||||
user_group_id=group_in_db.user_group_id,
|
||||
role_id=str(test_role.role_id)
|
||||
)
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
roles = await user_repo.get_user_group_roles(group_in_db.user_group_id)
|
||||
assert len(roles) == 2 # 1 default role + 1 custom role
|
||||
for role in roles:
|
||||
if not role.is_builtin:
|
||||
assert role.name == test_role.name
|
||||
|
||||
async def test_get_user_group_roles(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
db_session: AsyncSession
|
||||
) -> None:
|
||||
|
||||
user_repo = UsersRepository(db_session)
|
||||
group_in_db = await user_repo.get_user_group_by_name("Users")
|
||||
response = await client.get(
|
||||
app.url_path_for(
|
||||
"get_user_group_roles",
|
||||
user_group_id=group_in_db.user_group_id)
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 2 # 1 default role + 1 custom role
|
||||
|
||||
async def test_remove_role_from_group(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
test_role: Role,
|
||||
db_session: AsyncSession
|
||||
) -> None:
|
||||
|
||||
user_repo = UsersRepository(db_session)
|
||||
group_in_db = await user_repo.get_user_group_by_name("Users")
|
||||
|
||||
response = await client.delete(
|
||||
app.url_path_for(
|
||||
"remove_role_from_group",
|
||||
user_group_id=group_in_db.user_group_id,
|
||||
role_id=test_role.role_id
|
||||
),
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
roles = await user_repo.get_user_group_roles(group_in_db.user_group_id)
|
||||
assert len(roles) == 1 # 1 default role
|
||||
assert roles[0].name != test_role.name
|
||||
@@ -1,136 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2021 GNS3 Technologies Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
import uuid
|
||||
|
||||
from fastapi import FastAPI, status
|
||||
from httpx import AsyncClient
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from gns3server.db.repositories.rbac import RbacRepository
|
||||
from gns3server.controller import Controller
|
||||
from gns3server.controller.project import Project
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
class TestPermissionRoutes:
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def project(self, app: FastAPI, client: AsyncClient, controller: Controller) -> Project:
|
||||
|
||||
project_uuid = str(uuid.uuid4())
|
||||
params = {"name": "test", "project_id": project_uuid}
|
||||
await client.post(app.url_path_for("create_project"), json=params)
|
||||
return controller.get_project(project_uuid)
|
||||
|
||||
async def test_create_permission(self, app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
new_permission = {
|
||||
"methods": ["GET"],
|
||||
"path": f"/projects/{project.id}",
|
||||
"action": "ALLOW"
|
||||
}
|
||||
response = await client.post(app.url_path_for("create_permission"), json=new_permission)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
async def test_create_wildcard_permission(self, app: FastAPI, client: AsyncClient, project: Project) -> None:
|
||||
|
||||
new_permission = {
|
||||
"methods": ["POST"],
|
||||
"path": f"/projects/{project.id}/*",
|
||||
"action": "ALLOW"
|
||||
}
|
||||
|
||||
response = await client.post(app.url_path_for("create_permission"), json=new_permission)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
async def test_create_permission_not_existing_endpoint(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
new_permission = {
|
||||
"methods": ["GET"],
|
||||
"path": "/projects/invalid",
|
||||
"action": "ALLOW"
|
||||
}
|
||||
response = await client.post(app.url_path_for("create_permission"), json=new_permission)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
async def test_create_permission_not_existing_object(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
new_permission = {
|
||||
"methods": ["GET"],
|
||||
"path": f"/projects/{str(uuid.uuid4())}/*",
|
||||
"action": "ALLOW"
|
||||
}
|
||||
response = await client.post(app.url_path_for("create_permission"), json=new_permission)
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
async def test_get_permission(self, app: FastAPI, client: AsyncClient, db_session: AsyncSession, project: Project) -> None:
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
permission_in_db = await rbac_repo.get_permission_by_path(f"/projects/{project.id}/*")
|
||||
response = await client.get(app.url_path_for("get_permission", permission_id=permission_in_db.permission_id))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["permission_id"] == str(permission_in_db.permission_id)
|
||||
|
||||
async def test_list_permissions(self, app: FastAPI, client: AsyncClient) -> None:
|
||||
|
||||
response = await client.get(app.url_path_for("get_permissions"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 11 # 6 default permissions + 5 custom permissions
|
||||
|
||||
async def test_update_permission(self, app: FastAPI, client: AsyncClient, db_session: AsyncSession, project: Project) -> None:
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
permission_in_db = await rbac_repo.get_permission_by_path(f"/projects/{project.id}/*")
|
||||
|
||||
update_permission = {
|
||||
"methods": ["GET"],
|
||||
"path": f"/projects/{project.id}/*",
|
||||
"action": "ALLOW"
|
||||
}
|
||||
response = await client.put(
|
||||
app.url_path_for("update_permission", permission_id=permission_in_db.permission_id),
|
||||
json=update_permission
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
updated_permission_in_db = await rbac_repo.get_permission(permission_in_db.permission_id)
|
||||
assert updated_permission_in_db.path == f"/projects/{project.id}/*"
|
||||
|
||||
async def test_delete_permission(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
db_session: AsyncSession,
|
||||
project: Project,
|
||||
) -> None:
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
permission_in_db = await rbac_repo.get_permission_by_path(f"/projects/{project.id}/*")
|
||||
response = await client.delete(app.url_path_for("delete_permission", permission_id=permission_in_db.permission_id))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
async def test_prune_permissions(self, app: FastAPI, client: AsyncClient, db_session: AsyncSession) -> None:
|
||||
|
||||
response = await client.post(app.url_path_for("prune_permissions"))
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
permissions_in_db = await rbac_repo.get_permissions()
|
||||
assert len(permissions_in_db) == 10 # 6 default permissions + 4 custom permissions
|
||||
@@ -16,15 +16,12 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from fastapi import FastAPI, status
|
||||
from httpx import AsyncClient
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from gns3server.db.repositories.rbac import RbacRepository
|
||||
from gns3server.schemas.controller.rbac import Permission, HTTPMethods, PermissionAction
|
||||
from gns3server import schemas
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
@@ -49,7 +46,7 @@ class TestRolesRoutes:
|
||||
|
||||
response = await client.get(app.url_path_for("get_roles"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 3 # 2 default roles + role1
|
||||
assert len(response.json()) == 5 # 4 default roles + role1
|
||||
|
||||
async def test_update_role(self, app: FastAPI, client: AsyncClient, db_session: AsyncSession) -> None:
|
||||
|
||||
@@ -106,46 +103,31 @@ class TestRolesRoutes:
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_permission(db_session: AsyncSession) -> Permission:
|
||||
class TestRolesPrivilegesRoutes:
|
||||
|
||||
new_permission = schemas.PermissionCreate(
|
||||
methods=[HTTPMethods.get],
|
||||
path="/statistics",
|
||||
action=PermissionAction.allow
|
||||
)
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
existing_permission = await rbac_repo.get_permission_by_path("/statistics")
|
||||
if existing_permission:
|
||||
return existing_permission
|
||||
return await rbac_repo.create_permission(new_permission)
|
||||
|
||||
|
||||
class TestRolesPermissionsRoutes:
|
||||
|
||||
async def test_add_permission_to_role(
|
||||
async def test_add_privilege_to_role(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
test_permission: Permission,
|
||||
db_session: AsyncSession
|
||||
) -> None:
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
role_in_db = await rbac_repo.get_role_by_name("User")
|
||||
privilege = await rbac_repo.get_privilege_by_name("Template.Allocate")
|
||||
|
||||
response = await client.put(
|
||||
app.url_path_for(
|
||||
"add_permission_to_role",
|
||||
"add_privilege_to_role",
|
||||
role_id=role_in_db.role_id,
|
||||
permission_id=str(test_permission.permission_id)
|
||||
privilege_id=str(privilege.privilege_id)
|
||||
)
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
permissions = await rbac_repo.get_role_permissions(role_in_db.role_id)
|
||||
assert len(permissions) == 6 # 5 default permissions + 1 custom permission
|
||||
privileges = await rbac_repo.get_role_privileges(role_in_db.role_id)
|
||||
assert len(privileges) == 21 # 20 default privileges + 1 custom privilege
|
||||
|
||||
async def test_get_role_permissions(
|
||||
async def test_get_role_privileges(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
@@ -157,30 +139,30 @@ class TestRolesPermissionsRoutes:
|
||||
|
||||
response = await client.get(
|
||||
app.url_path_for(
|
||||
"get_role_permissions",
|
||||
"get_role_privileges",
|
||||
role_id=role_in_db.role_id)
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 6 # 5 default permissions + 1 custom permission
|
||||
assert len(response.json()) == 21 # 20 default privileges + 1 custom privilege
|
||||
|
||||
async def test_remove_role_from_group(
|
||||
async def test_remove_privilege_from_role(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
test_permission: Permission,
|
||||
db_session: AsyncSession
|
||||
) -> None:
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
role_in_db = await rbac_repo.get_role_by_name("User")
|
||||
privilege = await rbac_repo.get_privilege_by_name("Template.Allocate")
|
||||
|
||||
response = await client.delete(
|
||||
app.url_path_for(
|
||||
"remove_permission_from_role",
|
||||
"remove_privilege_from_role",
|
||||
role_id=role_in_db.role_id,
|
||||
permission_id=str(test_permission.permission_id)
|
||||
privilege_id=str(privilege.privilege_id)
|
||||
),
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
permissions = await rbac_repo.get_role_permissions(role_in_db.role_id)
|
||||
assert len(permissions) == 5 # 5 default permissions
|
||||
privileges = await rbac_repo.get_role_privileges(role_in_db.role_id)
|
||||
assert len(privileges) == 20 # 20 default privileges
|
||||
@@ -16,7 +16,6 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from typing import Optional
|
||||
from fastapi import FastAPI, HTTPException, status
|
||||
@@ -26,12 +25,9 @@ from jose import jwt
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from gns3server.db.repositories.users import UsersRepository
|
||||
from gns3server.db.repositories.rbac import RbacRepository
|
||||
from gns3server.schemas.controller.rbac import Permission, HTTPMethods, PermissionAction
|
||||
from gns3server.services import auth_service
|
||||
from gns3server.config import Config
|
||||
from gns3server.schemas.controller.users import User
|
||||
from gns3server import schemas
|
||||
import gns3server.db.models as models
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
@@ -352,7 +348,7 @@ class TestUserMe:
|
||||
assert user.email == test_user.email
|
||||
assert user.user_id == test_user.user_id
|
||||
|
||||
# logged in users can only change their email, full name and password
|
||||
# logged-in users can only change their email, full name and password
|
||||
@pytest.mark.parametrize(
|
||||
"attr, value, status_code",
|
||||
(
|
||||
@@ -426,92 +422,3 @@ class TestSuperAdmin:
|
||||
response = await unauthorized_client.post(app.url_path_for("login"), data=login_data)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
# async def test_super_admin_belongs_to_admin_group(
|
||||
# self,
|
||||
# app: FastAPI,
|
||||
# client: AsyncClient,
|
||||
# db_session: AsyncSession
|
||||
# ) -> None:
|
||||
#
|
||||
# user_repo = UsersRepository(db_session)
|
||||
# admin_in_db = await user_repo.get_user_by_username("admin")
|
||||
# response = await client.get(app.url_path_for("get_user_memberships", user_id=admin_in_db.user_id))
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
# assert len(response.json()) == 1
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def test_permission(db_session: AsyncSession) -> Permission:
|
||||
|
||||
new_permission = schemas.PermissionCreate(
|
||||
methods=[HTTPMethods.get],
|
||||
path="/statistics",
|
||||
action=PermissionAction.allow
|
||||
)
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
existing_permission = await rbac_repo.get_permission_by_path("/statistics")
|
||||
if existing_permission:
|
||||
return existing_permission
|
||||
return await rbac_repo.create_permission(new_permission)
|
||||
|
||||
|
||||
class TestUserPermissionsRoutes:
|
||||
|
||||
async def test_add_permission_to_user(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
test_user: User,
|
||||
test_permission: Permission,
|
||||
db_session: AsyncSession
|
||||
) -> None:
|
||||
|
||||
response = await client.put(
|
||||
app.url_path_for(
|
||||
"add_permission_to_user",
|
||||
user_id=str(test_user.user_id),
|
||||
permission_id=str(test_permission.permission_id)
|
||||
)
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
permissions = await rbac_repo.get_user_permissions(test_user.user_id)
|
||||
assert len(permissions) == 1
|
||||
assert permissions[0].permission_id == test_permission.permission_id
|
||||
|
||||
async def test_get_user_permissions(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
test_user: User,
|
||||
db_session: AsyncSession
|
||||
) -> None:
|
||||
|
||||
response = await client.get(
|
||||
app.url_path_for(
|
||||
"get_user_permissions",
|
||||
user_id=str(test_user.user_id))
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()) == 1
|
||||
|
||||
async def test_remove_permission_from_user(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
test_user: User,
|
||||
test_permission: Permission,
|
||||
db_session: AsyncSession
|
||||
) -> None:
|
||||
|
||||
response = await client.delete(
|
||||
app.url_path_for(
|
||||
"remove_permission_from_user",
|
||||
user_id=str(test_user.user_id),
|
||||
permission_id=str(test_permission.permission_id)
|
||||
),
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
permissions = await rbac_repo.get_user_permissions(test_user.user_id)
|
||||
assert len(permissions) == 0
|
||||
+1
-1
@@ -115,7 +115,7 @@ async def test_user(db_session: AsyncSession) -> User:
|
||||
return existing_user
|
||||
user = await user_repo.create_user(new_user)
|
||||
|
||||
# add new user to "Users group
|
||||
# add new user to the "Users" group
|
||||
group = await user_repo.get_user_group_by_name("Users")
|
||||
await user_repo.add_member_to_user_group(group.user_group_id, user)
|
||||
return user
|
||||
|
||||
+174
-174
@@ -27,177 +27,177 @@ from gns3server.db.models import User
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
class TestPermissions:
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"method, path, result",
|
||||
(
|
||||
("GET", "/users", False),
|
||||
("GET", "/projects", True),
|
||||
("GET", "/projects/e451ad73-2519-4f83-87fe-a8e821792d44", False),
|
||||
("POST", "/projects", True),
|
||||
("GET", "/templates", True),
|
||||
("GET", "/templates/62e92cf1-244a-4486-8dae-b95439b54da9", False),
|
||||
("POST", "/templates", True),
|
||||
("GET", "/computes", True),
|
||||
("GET", "/computes/local", True),
|
||||
("GET", "/symbols", True),
|
||||
("GET", "/symbols/default_symbols", True),
|
||||
),
|
||||
)
|
||||
async def test_default_permissions_user_group(
|
||||
self,
|
||||
app: FastAPI,
|
||||
authorized_client: AsyncClient,
|
||||
test_user: User,
|
||||
db_session: AsyncSession,
|
||||
method: str,
|
||||
path: str,
|
||||
result: bool
|
||||
) -> None:
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
authorized = await rbac_repo.check_user_is_authorized(test_user.user_id, method, path)
|
||||
assert authorized == result
|
||||
|
||||
|
||||
class TestProjectsWithRbac:
|
||||
|
||||
async def test_admin_create_project(self, app: FastAPI, client: AsyncClient):
|
||||
|
||||
params = {"name": "Admin project"}
|
||||
response = await client.post(app.url_path_for("create_project"), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
async def test_user_only_access_own_projects(
|
||||
self,
|
||||
app: FastAPI,
|
||||
authorized_client: AsyncClient,
|
||||
test_user: User,
|
||||
db_session: AsyncSession
|
||||
) -> None:
|
||||
|
||||
params = {"name": "User project"}
|
||||
response = await authorized_client.post(app.url_path_for("create_project"), json=params)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
project_id = response.json()["project_id"]
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
permissions_in_db = await rbac_repo.get_user_permissions(test_user.user_id)
|
||||
assert len(permissions_in_db) == 1
|
||||
assert permissions_in_db[0].path == f"/projects/{project_id}/*"
|
||||
|
||||
response = await authorized_client.get(app.url_path_for("get_projects"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
projects = response.json()
|
||||
assert len(projects) == 1
|
||||
|
||||
async def test_admin_access_all_projects(self, app: FastAPI, client: AsyncClient):
|
||||
|
||||
response = await client.get(app.url_path_for("get_projects"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
projects = response.json()
|
||||
assert len(projects) == 2
|
||||
|
||||
async def test_admin_user_give_permission_on_project(
|
||||
self,
|
||||
app: FastAPI,
|
||||
client: AsyncClient,
|
||||
test_user: User
|
||||
):
|
||||
|
||||
response = await client.get(app.url_path_for("get_projects"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
projects = response.json()
|
||||
project_id = None
|
||||
for project in projects:
|
||||
if project["name"] == "Admin project":
|
||||
project_id = project["project_id"]
|
||||
break
|
||||
|
||||
new_permission = {
|
||||
"methods": ["GET"],
|
||||
"path": f"/projects/{project_id}",
|
||||
"action": "ALLOW"
|
||||
}
|
||||
response = await client.post(app.url_path_for("create_permission"), json=new_permission)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
permission_id = response.json()["permission_id"]
|
||||
|
||||
response = await client.put(
|
||||
app.url_path_for(
|
||||
"add_permission_to_user",
|
||||
user_id=test_user.user_id,
|
||||
permission_id=permission_id
|
||||
)
|
||||
)
|
||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
|
||||
async def test_user_access_admin_project(
|
||||
self,
|
||||
app: FastAPI,
|
||||
authorized_client: AsyncClient,
|
||||
test_user: User,
|
||||
db_session: AsyncSession
|
||||
) -> None:
|
||||
|
||||
response = await authorized_client.get(app.url_path_for("get_projects"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
projects = response.json()
|
||||
assert len(projects) == 2
|
||||
|
||||
|
||||
class TestTemplatesWithRbac:
|
||||
|
||||
async def test_admin_create_template(self, app: FastAPI, client: AsyncClient):
|
||||
|
||||
new_template = {"base_script_file": "vpcs_base_config.txt",
|
||||
"category": "guest",
|
||||
"console_auto_start": False,
|
||||
"console_type": "telnet",
|
||||
"default_name_format": "PC{0}",
|
||||
"name": "ADMIN_VPCS_TEMPLATE",
|
||||
"compute_id": "local",
|
||||
"symbol": ":/symbols/vpcs_guest.svg",
|
||||
"template_type": "vpcs"}
|
||||
|
||||
response = await client.post(app.url_path_for("create_template"), json=new_template)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
async def test_user_only_access_own_templates(
|
||||
self, app: FastAPI,
|
||||
authorized_client: AsyncClient,
|
||||
test_user: User,
|
||||
db_session: AsyncSession
|
||||
) -> None:
|
||||
|
||||
new_template = {"base_script_file": "vpcs_base_config.txt",
|
||||
"category": "guest",
|
||||
"console_auto_start": False,
|
||||
"console_type": "telnet",
|
||||
"default_name_format": "PC{0}",
|
||||
"name": "USER_VPCS_TEMPLATE",
|
||||
"compute_id": "local",
|
||||
"symbol": ":/symbols/vpcs_guest.svg",
|
||||
"template_type": "vpcs"}
|
||||
|
||||
response = await authorized_client.post(app.url_path_for("create_template"), json=new_template)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
template_id = response.json()["template_id"]
|
||||
|
||||
rbac_repo = RbacRepository(db_session)
|
||||
permissions_in_db = await rbac_repo.get_user_permissions(test_user.user_id)
|
||||
assert len(permissions_in_db) == 1
|
||||
assert permissions_in_db[0].path == f"/templates/{template_id}/*"
|
||||
|
||||
response = await authorized_client.get(app.url_path_for("get_templates"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
templates = [template for template in response.json() if template["builtin"] is False]
|
||||
assert len(templates) == 1
|
||||
|
||||
async def test_admin_access_all_templates(self, app: FastAPI, client: AsyncClient):
|
||||
|
||||
response = await client.get(app.url_path_for("get_templates"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
templates = [template for template in response.json() if template["builtin"] is False]
|
||||
assert len(templates) == 2
|
||||
# class TestPermissions:
|
||||
#
|
||||
# @pytest.mark.parametrize(
|
||||
# "method, path, result",
|
||||
# (
|
||||
# ("GET", "/users", False),
|
||||
# ("GET", "/projects", True),
|
||||
# ("GET", "/projects/e451ad73-2519-4f83-87fe-a8e821792d44", False),
|
||||
# ("POST", "/projects", True),
|
||||
# ("GET", "/templates", True),
|
||||
# ("GET", "/templates/62e92cf1-244a-4486-8dae-b95439b54da9", False),
|
||||
# ("POST", "/templates", True),
|
||||
# ("GET", "/computes", True),
|
||||
# ("GET", "/computes/local", True),
|
||||
# ("GET", "/symbols", True),
|
||||
# ("GET", "/symbols/default_symbols", True),
|
||||
# ),
|
||||
# )
|
||||
# async def test_default_permissions_user_group(
|
||||
# self,
|
||||
# app: FastAPI,
|
||||
# authorized_client: AsyncClient,
|
||||
# test_user: User,
|
||||
# db_session: AsyncSession,
|
||||
# method: str,
|
||||
# path: str,
|
||||
# result: bool
|
||||
# ) -> None:
|
||||
#
|
||||
# rbac_repo = RbacRepository(db_session)
|
||||
# authorized = await rbac_repo.check_user_is_authorized(test_user.user_id, method, path)
|
||||
# assert authorized == result
|
||||
#
|
||||
#
|
||||
# class TestProjectsWithRbac:
|
||||
#
|
||||
# async def test_admin_create_project(self, app: FastAPI, client: AsyncClient):
|
||||
#
|
||||
# params = {"name": "Admin project"}
|
||||
# response = await client.post(app.url_path_for("create_project"), json=params)
|
||||
# assert response.status_code == status.HTTP_201_CREATED
|
||||
#
|
||||
# async def test_user_only_access_own_projects(
|
||||
# self,
|
||||
# app: FastAPI,
|
||||
# authorized_client: AsyncClient,
|
||||
# test_user: User,
|
||||
# db_session: AsyncSession
|
||||
# ) -> None:
|
||||
#
|
||||
# params = {"name": "User project"}
|
||||
# response = await authorized_client.post(app.url_path_for("create_project"), json=params)
|
||||
# assert response.status_code == status.HTTP_201_CREATED
|
||||
# project_id = response.json()["project_id"]
|
||||
#
|
||||
# rbac_repo = RbacRepository(db_session)
|
||||
# permissions_in_db = await rbac_repo.get_user_permissions(test_user.user_id)
|
||||
# assert len(permissions_in_db) == 1
|
||||
# assert permissions_in_db[0].path == f"/projects/{project_id}/*"
|
||||
#
|
||||
# response = await authorized_client.get(app.url_path_for("get_projects"))
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
# projects = response.json()
|
||||
# assert len(projects) == 1
|
||||
#
|
||||
# async def test_admin_access_all_projects(self, app: FastAPI, client: AsyncClient):
|
||||
#
|
||||
# response = await client.get(app.url_path_for("get_projects"))
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
# projects = response.json()
|
||||
# assert len(projects) == 2
|
||||
#
|
||||
# async def test_admin_user_give_permission_on_project(
|
||||
# self,
|
||||
# app: FastAPI,
|
||||
# client: AsyncClient,
|
||||
# test_user: User
|
||||
# ):
|
||||
#
|
||||
# response = await client.get(app.url_path_for("get_projects"))
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
# projects = response.json()
|
||||
# project_id = None
|
||||
# for project in projects:
|
||||
# if project["name"] == "Admin project":
|
||||
# project_id = project["project_id"]
|
||||
# break
|
||||
#
|
||||
# new_permission = {
|
||||
# "methods": ["GET"],
|
||||
# "path": f"/projects/{project_id}",
|
||||
# "action": "ALLOW"
|
||||
# }
|
||||
# response = await client.post(app.url_path_for("create_permission"), json=new_permission)
|
||||
# assert response.status_code == status.HTTP_201_CREATED
|
||||
# permission_id = response.json()["permission_id"]
|
||||
#
|
||||
# response = await client.put(
|
||||
# app.url_path_for(
|
||||
# "add_permission_to_user",
|
||||
# user_id=test_user.user_id,
|
||||
# permission_id=permission_id
|
||||
# )
|
||||
# )
|
||||
# assert response.status_code == status.HTTP_204_NO_CONTENT
|
||||
#
|
||||
# async def test_user_access_admin_project(
|
||||
# self,
|
||||
# app: FastAPI,
|
||||
# authorized_client: AsyncClient,
|
||||
# test_user: User,
|
||||
# db_session: AsyncSession
|
||||
# ) -> None:
|
||||
#
|
||||
# response = await authorized_client.get(app.url_path_for("get_projects"))
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
# projects = response.json()
|
||||
# assert len(projects) == 2
|
||||
#
|
||||
#
|
||||
# class TestTemplatesWithRbac:
|
||||
#
|
||||
# async def test_admin_create_template(self, app: FastAPI, client: AsyncClient):
|
||||
#
|
||||
# new_template = {"base_script_file": "vpcs_base_config.txt",
|
||||
# "category": "guest",
|
||||
# "console_auto_start": False,
|
||||
# "console_type": "telnet",
|
||||
# "default_name_format": "PC{0}",
|
||||
# "name": "ADMIN_VPCS_TEMPLATE",
|
||||
# "compute_id": "local",
|
||||
# "symbol": ":/symbols/vpcs_guest.svg",
|
||||
# "template_type": "vpcs"}
|
||||
#
|
||||
# response = await client.post(app.url_path_for("create_template"), json=new_template)
|
||||
# assert response.status_code == status.HTTP_201_CREATED
|
||||
#
|
||||
# async def test_user_only_access_own_templates(
|
||||
# self, app: FastAPI,
|
||||
# authorized_client: AsyncClient,
|
||||
# test_user: User,
|
||||
# db_session: AsyncSession
|
||||
# ) -> None:
|
||||
#
|
||||
# new_template = {"base_script_file": "vpcs_base_config.txt",
|
||||
# "category": "guest",
|
||||
# "console_auto_start": False,
|
||||
# "console_type": "telnet",
|
||||
# "default_name_format": "PC{0}",
|
||||
# "name": "USER_VPCS_TEMPLATE",
|
||||
# "compute_id": "local",
|
||||
# "symbol": ":/symbols/vpcs_guest.svg",
|
||||
# "template_type": "vpcs"}
|
||||
#
|
||||
# response = await authorized_client.post(app.url_path_for("create_template"), json=new_template)
|
||||
# assert response.status_code == status.HTTP_201_CREATED
|
||||
# template_id = response.json()["template_id"]
|
||||
#
|
||||
# rbac_repo = RbacRepository(db_session)
|
||||
# permissions_in_db = await rbac_repo.get_user_permissions(test_user.user_id)
|
||||
# assert len(permissions_in_db) == 1
|
||||
# assert permissions_in_db[0].path == f"/templates/{template_id}/*"
|
||||
#
|
||||
# response = await authorized_client.get(app.url_path_for("get_templates"))
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
# templates = [template for template in response.json() if template["builtin"] is False]
|
||||
# assert len(templates) == 1
|
||||
#
|
||||
# async def test_admin_access_all_templates(self, app: FastAPI, client: AsyncClient):
|
||||
#
|
||||
# response = await client.get(app.url_path_for("get_templates"))
|
||||
# assert response.status_code == status.HTTP_200_OK
|
||||
# templates = [template for template in response.json() if template["builtin"] is False]
|
||||
# assert len(templates) == 2
|
||||
Reference in new issue
Block a user