From c7ea45c1f3e11ff255275e5a74f286fadf2bcde4 Mon Sep 17 00:00:00 2001 From: mgcam Date: Fri, 4 Mar 2022 12:40:17 +0000 Subject: [PATCH] Added a model to describe requestor's permissions Added a pydantic model to represent the details about the requestor, the roles and the scope it is allowed to act within. The model is agnostic about the method that was used to authentication and authorisation. --- .flake8 | 4 +-- server/npg/porch/models/permission.py | 50 +++++++++++++++++++++++++++ server/tests/model_permission_test.py | 46 ++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 server/npg/porch/models/permission.py create mode 100644 server/tests/model_permission_test.py diff --git a/.flake8 b/.flake8 index 7584aa6..eb84764 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,5 @@ [flake8] max-line-length = 100 -ignore = E251, E265, E261, E302 +ignore = E251, E265, E261, E302, W503 per-file-ignores = __init__.py:F401 -exclude = server/tests/conftest.py \ No newline at end of file +exclude = server/tests/conftest.py diff --git a/server/npg/porch/models/permission.py b/server/npg/porch/models/permission.py new file mode 100644 index 0000000..f4d1cd9 --- /dev/null +++ b/server/npg/porch/models/permission.py @@ -0,0 +1,50 @@ +# Copyright (C) 2021, 2022 Genome Research Ltd. +# +# Author: Kieron Taylor kt19@sanger.ac.uk +# Author: Marina Gourtovaia mg8@sanger.ac.uk +# +# This file is part of npg_porch +# +# npg_porch 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 . + +from enum import Enum +from pydantic import BaseModel, Field, validator +from typing import Optional + +from npg.porch.models.pipeline import Pipeline + +class RolesEnum(str, Enum): + POWER_USER = 'power_user' + REGULAR_USER = 'regular_user' + +class Permission(BaseModel): + pipeline: Optional[Pipeline] = Field( + None, + title = 'An optional pipeline object', + description = 'The scope is limited to this pipeline if undefined' + ) + requestor_id: int = Field( + title = 'ID that corresponds to the presented credentials', + description = 'A validated internal ID that corresponds to the presented credentials' + ) + role: RolesEnum = Field( + title = 'A role associated with the presented credentials', + ) + + @validator('role') + def no_pipeline4special_users(cls, v, values): + if (v == RolesEnum.POWER_USER + and ('pipeline' in values and values['pipeline'] is not None)): + raise ValueError('Power user cannot be associated with a pipeline') + return v diff --git a/server/tests/model_permission_test.py b/server/tests/model_permission_test.py new file mode 100644 index 0000000..ff103e5 --- /dev/null +++ b/server/tests/model_permission_test.py @@ -0,0 +1,46 @@ +import pytest + +from npg.porch.models.pipeline import Pipeline +from npg.porch.models.permission import Permission +from pydantic.error_wrappers import ValidationError + +def test_model_create(): + '''' + Test objects can be created. + ''' + + p = Permission(requestor_id = 3, role = 'power_user') + assert type(p) is Permission + p = Permission( + requestor_id = 1, + role = 'regular_user', + pipeline = Pipeline(name='number one') + ) + assert type(p) is Permission + +def test_xvalidation_role_pipeline(): + ''' + Test cross validation for the role and pipeline fields. + ''' + + with pytest.raises( + ValidationError, + match = r'Power user cannot be associated with a pipeline'): + Permission( + requestor_id = 3, + role = 'power_user', + pipeline = Pipeline(name='number one') + ) + +def test_error_with_insufficient_args(): + + with pytest.raises(ValidationError, match=r'requestor_id\s+field required'): + Permission( + role = 'regular_user', + pipeline = Pipeline(name='number one') + ) + with pytest.raises(ValidationError, match=r'role\s+field required'): + Permission( + requestor_id = 1, + pipeline = Pipeline(name='number one') + )