Add required privileges to all endpoints

This commit is contained in:
grossmj committed 2023-09-02 17:54:24 +07:00
1 parent f3a4ad49f4
commit 0077fd98aa
24 files changed
+1125 -357

No files matched your search

+56 -58
View File
@@ -17,7 +17,6 @@
import pytest
import pytest_asyncio
import uuid
from fastapi import FastAPI, status
from httpx import AsyncClient
@@ -25,58 +24,15 @@ 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
from gns3server.controller import Controller
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:
@@ -102,11 +58,22 @@ class TestACLRoutes:
role_id: str
) -> None:
# allow the user to create an ACE
rbac_repo = RbacRepository(db_session)
admin_role_id = (await rbac_repo.get_role_by_name("Administrator")).role_id
ace = ACECreate(
path="/acl",
ace_type="user",
user_id=test_user.user_id,
role_id=admin_role_id
)
await rbac_repo.create_ace(ace)
# add an ACE on /projects to allow user to create a project
path = f"/projects"
new_ace = {
"path": path,
"type": "user",
"ace_type": "user",
"user_id": str(test_user.user_id),
"role_id": role_id
}
@@ -130,14 +97,14 @@ class TestACLRoutes:
new_ace = {
"path": "/projects/invalid",
"type": "group",
"ace_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(
# async def test_create_ace_non_existing_resource(
# self,
# app: FastAPI,
# client: AsyncClient,
@@ -147,6 +114,7 @@ class TestACLRoutes:
#
# new_ace = {
# "path": f"/projects/{str(uuid.uuid4())}",
# "ace_type": "group",
# "group_id": group_id,
# "role_id": role_id
# }
@@ -165,7 +133,7 @@ class TestACLRoutes:
response = await client.get(app.url_path_for("get_aces"))
assert response.status_code == status.HTTP_200_OK
assert len(response.json()) == 1
assert len(response.json()) == 2
async def test_update_ace(
self, app: FastAPI,
@@ -180,7 +148,7 @@ class TestACLRoutes:
update_ace = {
"path": f"/appliances",
"type": "user",
"ace_type": "user",
"user_id": str(test_user.user_id),
"role_id": role_id
}
@@ -204,11 +172,41 @@ class TestACLRoutes:
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
async def test_ace_cleanup(
self,
app: FastAPI,
authorized_client: AsyncClient,
db_session: AsyncSession,
test_user: User,
role_id: str,
) -> None:
# allow the user to create projects
rbac_repo = RbacRepository(db_session)
ace = ACECreate(
path="/projects",
ace_type="user",
user_id=test_user.user_id,
role_id=role_id
)
await rbac_repo.create_ace(ace)
response = await authorized_client.post(app.url_path_for("create_project"), json={"name": "test2"})
assert response.status_code == status.HTTP_201_CREATED
project_id = response.json()["project_id"]
path = f"/projects/{project_id}"
ace = ACECreate(
path=path,
ace_type="user",
user_id=test_user.user_id,
role_id=role_id
)
await rbac_repo.create_ace(ace)
assert await rbac_repo.get_ace_by_path(path)
response = await authorized_client.delete(app.url_path_for("delete_project", project_id=project_id))
assert response.status_code == status.HTTP_204_NO_CONTENT
# the ACE should have been deleted after deleting the project
assert not await rbac_repo.get_ace_by_path(path)
+3 -3
View File
@@ -125,7 +125,7 @@ class TestRolesPrivilegesRoutes:
)
assert response.status_code == status.HTTP_204_NO_CONTENT
privileges = await rbac_repo.get_role_privileges(role_in_db.role_id)
assert len(privileges) == 21 # 20 default privileges + 1 custom privilege
assert len(privileges) == 25 # 24 default privileges + 1 custom privilege
async def test_get_role_privileges(
self,
@@ -143,7 +143,7 @@ class TestRolesPrivilegesRoutes:
role_id=role_in_db.role_id)
)
assert response.status_code == status.HTTP_200_OK
assert len(response.json()) == 21 # 20 default privileges + 1 custom privilege
assert len(response.json()) == 25 # 24 default privileges + 1 custom privilege
async def test_remove_privilege_from_role(
self,
@@ -165,4 +165,4 @@ class TestRolesPrivilegesRoutes:
)
assert response.status_code == status.HTTP_204_NO_CONTENT
privileges = await rbac_repo.get_role_privileges(role_in_db.role_id)
assert len(privileges) == 20 # 20 default privileges
assert len(privileges) == 24 # 24 default privileges
+1 -1
View File
@@ -305,7 +305,7 @@ class TestUserLogin:
assert response.status_code == status.HTTP_200_OK
token = response.json().get("access_token")
response = await unauthorized_client.get(app.url_path_for("get_projects"), params={"token": token})
response = await unauthorized_client.get(app.url_path_for("statistics"), params={"token": token})
assert response.status_code == status.HTTP_200_OK
+162 -94
View File
@@ -16,51 +16,118 @@
# 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.db.repositories.users import UsersRepository
from gns3server.schemas.controller.rbac import ACECreate
from gns3server.db.models import User
pytestmark = pytest.mark.asyncio
# class TestPermissions:
# @pytest_asyncio.fixture
# async def project_ace(db_session: AsyncSession):
#
# @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),
# ),
# group_id = (await UsersRepository(db_session).get_user_group_by_name("Users")).user_group_id
# role_id = (await RbacRepository(db_session).get_role_by_name("User")).role_id
# ace = ACECreate(
# path="/projects",
# ace_type="group",
# propagate=False,
# group_id=str(group_id),
# role_id=str(role_id)
# )
# 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
#
#
# await RbacRepository(db_session).create_ace(ace)
class TestPrivileges:
@pytest.mark.parametrize(
"privilege, path, result",
(
("User.Allocate", "/users", False),
("Project.Allocate", "/projects", False),
("Project.Allocate", "/projects", True),
("Project.Audit", "/projects/e451ad73-2519-4f83-87fe-a8e821792d44", True),
("Project.Audit", "/templates", False),
("Template.Audit", "/templates", True),
("Template.Allocate", "/templates", False),
("Compute.Audit", "/computes", True),
("Compute.Audit", "/computes/local", True),
("Symbol.Audit", "/symbols", True),
("Symbol.Audit", "/symbols/default_symbols", True),
),
)
async def test_default_privileges_user_group(
self,
test_user: User,
db_session: AsyncSession,
privilege: str,
path: str,
result: bool
) -> None:
# add an ACE for path
if result:
group_id = (await UsersRepository(db_session).get_user_group_by_name("Users")).user_group_id
role_id = (await RbacRepository(db_session).get_role_by_name("User")).role_id
ace = ACECreate(
path=path,
ace_type="group",
propagate=False,
group_id=str(group_id),
role_id=str(role_id)
)
await RbacRepository(db_session).create_ace(ace)
authorized = await RbacRepository(db_session).check_user_has_privilege(test_user.user_id, path, privilege)
assert authorized == result
async def test_propagate(self, test_user: User, db_session: AsyncSession):
privilege = "Project.Audit"
path = "/projects/44929147-47bb-460a-90ae-c782c4dbb6ef"
authorized = await RbacRepository(db_session).check_user_has_privilege(test_user.user_id, path, privilege)
assert authorized is False
ace = await RbacRepository(db_session).get_ace_by_path("/projects")
ace.propagate = True
await db_session.commit()
authorized = await RbacRepository(db_session).check_user_has_privilege(test_user.user_id, path, privilege)
assert authorized is True
async def test_allowed(self, test_user: User, db_session: AsyncSession):
ace = await RbacRepository(db_session).get_ace_by_path("/projects")
ace.allowed = False
ace.propagate = True
await db_session.commit()
privilege = "Project.Audit"
path = "/projects/44929147-47bb-460a-90ae-c782c4dbb6ef"
authorized = await RbacRepository(db_session).check_user_has_privilege(test_user.user_id, path, privilege)
assert authorized is False
# privileges on deeper levels replace those inherited from an upper level.
group_id = (await UsersRepository(db_session).get_user_group_by_name("Users")).user_group_id
role_id = (await RbacRepository(db_session).get_role_by_name("User")).role_id
ace = ACECreate(
path=path,
ace_type="group",
propagate=False,
group_id=str(group_id),
role_id=str(role_id)
)
await RbacRepository(db_session).create_ace(ace)
authorized = await RbacRepository(db_session).check_user_has_privilege(test_user.user_id, path, privilege)
assert authorized is True
# class TestProjectsWithRbac:
#
# async def test_admin_create_project(self, app: FastAPI, client: AsyncClient):
@@ -73,6 +140,7 @@ pytestmark = pytest.mark.asyncio
# self,
# app: FastAPI,
# authorized_client: AsyncClient,
# project_ace,
# test_user: User,
# db_session: AsyncSession
# ) -> None:
@@ -86,67 +154,67 @@ pytestmark = pytest.mark.asyncio
# 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
#
#
#
# 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):