From 7705412b7400e09224506e03f6d7e39d01f26c5a Mon Sep 17 00:00:00 2001 From: TheBurchLog <5104941+TheBurchLog@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:44:42 -0500 Subject: [PATCH] Expanding to support remote user mappings --- brewtils/models.py | 19 ++++++++++----- brewtils/schema_parser.py | 40 ++++++++++++++++++++++++++++++++ brewtils/schemas.py | 46 ++++++++----------------------------- brewtils/test/comparable.py | 8 ++++++- brewtils/test/fixtures.py | 25 ++++++++++++++++---- test/models_test.py | 6 ++--- test/schema_parser_test.py | 37 +++++++++++++++++++++++++++++ 7 files changed, 129 insertions(+), 52 deletions(-) diff --git a/brewtils/models.py b/brewtils/models.py index a2853f61..628ce829 100644 --- a/brewtils/models.py +++ b/brewtils/models.py @@ -1607,15 +1607,17 @@ def __init__( username, password=None, roles=None, - assigned_roles=None, + remote_roles=None, is_remote=False, + remote_user_mapping=None, metadata=None, ): self.username = username self.password = password self.roles = roles or [] - self.assigned_roles = assigned_roles or [] + self.remote_roles = remote_roles or [] self.is_remote = is_remote + self.remote_user_mapping = remote_user_mapping or [] self.metadata = metadata def __str__(self): @@ -1643,7 +1645,6 @@ def __init__( permission=None, description=None, id=None, - is_remote=False, scope_gardens=None, scope_namespaces=None, scope_systems=None, @@ -1655,7 +1656,6 @@ def __init__( self.description = description self.id = id self.name = name - self.is_remote = is_remote self.scope_gardens = scope_gardens or [] self.scope_namespaces = scope_namespaces or [] self.scope_systems = scope_systems or [] @@ -1668,14 +1668,13 @@ def __str__(self): def __repr__(self): return ( - "" ) % ( self.id, self.name, self.permission, - self.is_remote, self.scope_gardens, self.scope_namespaces, self.scope_systems, @@ -1683,3 +1682,11 @@ def __repr__(self): self.scope_verisons, self.scope_commands, ) + + +class RemoteUserMap(BaseModel): + schema = "RemoteUserMapSchema" + + def __init__(self, target_garden, username): + self.target_garden = target_garden + self.username = username diff --git a/brewtils/schema_parser.py b/brewtils/schema_parser.py index 2a6b3783..7462c243 100644 --- a/brewtils/schema_parser.py +++ b/brewtils/schema_parser.py @@ -50,6 +50,7 @@ class SchemaParser(object): "ResolvableSchema": brewtils.models.Resolvable, "RoleSchema": brewtils.models.Role, "UserSchema": brewtils.models.User, + "RemoteUserMapSchema": brewtils.models.RemoteUserMap, } logger = logging.getLogger(__name__) @@ -280,6 +281,25 @@ def parse_role(cls, role, from_string=False, **kwargs): """ return cls.parse(role, brewtils.models.Role, from_string=from_string, **kwargs) + @classmethod + def parse_remote_user_map(cls, remote_user_map, from_string=False, **kwargs): + """Convert raw JSON string or dictionary to a RemoteUserMap model object + + Args: + role: The raw input + from_string: True if input is a JSON string, False if a dictionary + **kwargs: Additional parameters to be passed to the Schema (e.g. many=True) + + Returns: + A RemoteUserMap object + """ + return cls.parse( + remote_user_map, + brewtils.models.RemoteUserMap, + from_string=from_string, + **kwargs + ) + @classmethod def parse_refresh_token(cls, refresh_token, from_string=False, **kwargs): """Convert raw JSON string or dictionary to a refresh token object @@ -703,6 +723,26 @@ def serialize_role(cls, role, to_string=True, **kwargs): role, to_string=to_string, schema_name=brewtils.models.Role.schema, **kwargs ) + @classmethod + def serialize_remote_user_map(cls, remote_user_map, to_string=True, **kwargs): + """Convert a RemoteUserMap model into serialized form + + Args: + RemoteUserMap: The RemoteUserMap object(s) to be serialized + to_string: True to generate a JSON-formatted string, False to generate a + dictionary + **kwargs: Additional parameters to be passed to the Schema (e.g. many=True) + + Returns: + Serialized representation + """ + return cls.serialize( + remote_user_map, + to_string=to_string, + schema_name=brewtils.models.RemoteUserMap.schema, + **kwargs + ) + @classmethod def serialize_refresh_token(cls, refresh_token, to_string=True, **kwargs): """Convert a role model into serialized form diff --git a/brewtils/schemas.py b/brewtils/schemas.py index 743950a8..9ea8ca42 100644 --- a/brewtils/schemas.py +++ b/brewtils/schemas.py @@ -38,8 +38,7 @@ "OperationSchema", "UserSchema", "RoleSchema", - "GardenDomainIdentifierSchema", - "SystemDomainIdentifierSchema", + "RemoteUserMapSchema", ] # This will be updated after all the schema classes are defined @@ -68,26 +67,6 @@ def _deserialize_model(_, data, type_field=None, allowed_types=None): return model_schema_map.get(data[type_field])() -def _domain_identifier_schema_selector(_, role_assignment_domain): - scope_schema_map = { - "Garden": GardenDomainIdentifierSchema, - "System": SystemDomainIdentifierSchema, - "Global": Schema, - } - - if isinstance(role_assignment_domain, dict): - scope = role_assignment_domain.get("scope") - else: - scope = role_assignment_domain.scope - - schema = scope_schema_map.get(scope) - - if schema is None: - raise TypeError("Invalid scope: %s" % scope) - - return schema() - - class ModelField(PolyField): """Field representing a Brewtils model @@ -495,10 +474,6 @@ class GardenSchema(BaseSchema): metadata = fields.Dict(allow_none=True) -class GardenDomainIdentifierSchema(BaseSchema): - name = fields.Str(required=True) - - class JobSchema(BaseSchema): id = fields.Str(allow_none=True) name = fields.Str(allow_none=True) @@ -585,7 +560,6 @@ class RoleSchema(BaseSchema): name = fields.Str() description = fields.Str(allow_none=True) permission = fields.Str() - is_remote = fields.Boolean(allow_none=True) scope_gardens = fields.List(fields.Str(), allow_none=True) scope_namespaces = fields.List(fields.Str(), allow_none=True) scope_systems = fields.List(fields.Str(), allow_none=True) @@ -594,25 +568,22 @@ class RoleSchema(BaseSchema): scope_commands = fields.List(fields.Str(), allow_none=True) +class RemoteUserMapSchema(BaseSchema): + target_garden = fields.Str() + username = fields.Str() + + class UserSchema(BaseSchema): id = fields.Str(allow_none=True) username = fields.Str() password = fields.Str() roles = fields.List(fields.Str(), allow_none=True) - assigned_roles = fields.List(fields.Nested(RoleSchema())) + remote_roles = fields.List(fields.Nested(RoleSchema())) + remote_user_mapping = fields.List(fields.Nested(RemoteUserMapSchema())) is_remote = fields.Boolean(allow_none=True) metadata = fields.Dict(allow_none=True) -# class UserCreateSchema(BaseSchema): -# username = fields.Str(required=True) -# password = fields.Str(required=True, load_only=True) - - -# class UserListSchema(BaseSchema): -# users = fields.List(fields.Nested(UserSchema())) - - model_schema_map.update( { "Choices": ChoicesSchema, @@ -644,6 +615,7 @@ class UserSchema(BaseSchema): "Resolvable": ResolvableSchema, "Role": RoleSchema, "User": UserSchema, + "RemoteUserMap": RemoteUserMapSchema, # Compatibility for the Job trigger types "interval": IntervalTriggerSchema, "date": DateTriggerSchema, diff --git a/brewtils/test/comparable.py b/brewtils/test/comparable.py index 8b320c30..f1197ed6 100644 --- a/brewtils/test/comparable.py +++ b/brewtils/test/comparable.py @@ -31,6 +31,7 @@ PatchOperation, User, Queue, + RemoteUserMap, Request, RequestFile, RequestTemplate, @@ -51,6 +52,7 @@ "assert_command_equal", "assert_parameter_equal", "assert_user_equal", + "assert_remote_user_map_equal", "assert_request_equal", "assert_role_equal", "assert_system_equal", @@ -193,6 +195,7 @@ def _assert_wrapper(obj1, obj2, expected_type=None, do_raise=False, **kwargs): assert_runner_equal = partial(_assert_wrapper, expected_type=Runner) assert_resolvable_equal = partial(_assert_wrapper, expected_type=Resolvable) assert_connection_equal = partial(_assert_wrapper, expected_type=Connection) +assert_remote_user_map_equal = partial(_assert_wrapper, expected_type=RemoteUserMap) def assert_command_equal(obj1, obj2, do_raise=False): @@ -241,7 +244,10 @@ def assert_user_equal(obj1, obj2, do_raise=False): obj1, obj2, expected_type=User, - deep_fields={"assigned_roles": partial(assert_role_equal, do_raise=True)}, + deep_fields={ + "remote_roles": partial(assert_role_equal, do_raise=True), + "remote_user_mapping": partial(assert_remote_user_map_equal, do_raise=True), + }, do_raise=do_raise, ) diff --git a/brewtils/test/fixtures.py b/brewtils/test/fixtures.py index d0c9c28f..5fe6c2c4 100644 --- a/brewtils/test/fixtures.py +++ b/brewtils/test/fixtures.py @@ -22,6 +22,7 @@ Parameter, PatchOperation, Queue, + RemoteUserMap, Request, RequestFile, RequestTemplate, @@ -527,6 +528,19 @@ def bg_queue(queue_dict): return Queue(**queue_dict) +@pytest.fixture +def remote_user_map_dict(): + return { + "target_garden": "test", + "username": "user", + } + + +@pytest.fixture +def bg_remote_user_map(remote_user_map_dict): + return RemoteUserMap(**remote_user_map_dict) + + @pytest.fixture def role_dict(): return { @@ -534,7 +548,6 @@ def role_dict(): "description": "ADMIN ROLE", "id": "1", "name": "ADMIN_ROLE", - "is_remote": False, "scope_gardens": ["FOO"], "scope_namespaces": [], "scope_systems": [], @@ -550,21 +563,23 @@ def bg_role(role_dict): @pytest.fixture -def user_dict(role_dict): +def user_dict(role_dict, remote_user_map_dict): return { "username": "USERNAME", "password": "HASH", "roles": ["ADMIN_ROLE"], - "assigned_roles": [role_dict], + "remote_roles": [role_dict], + "remote_user_mapping": [remote_user_map_dict], "is_remote": False, "metadata": {}, } @pytest.fixture -def bg_user(user_dict, bg_role): +def bg_user(user_dict, bg_role, bg_remote_user_map): dict_copy = copy.deepcopy(user_dict) - dict_copy["assigned_roles"] = [bg_role] + dict_copy["remote_roles"] = [bg_role] + dict_copy["remote_user_mapping"] = [bg_remote_user_map] return User(**dict_copy) diff --git a/test/models_test.py b/test/models_test.py index 64c22cb3..6b136600 100644 --- a/test/models_test.py +++ b/test/models_test.py @@ -535,7 +535,7 @@ def user(self): return User( username="admin", roles=["bg-admin"], - assigned_roles=[Role(name="foo", permission="ADMIN")], + remote_roles=[Role(name="foo", permission="ADMIN")], ) def test_str(self, user): @@ -556,8 +556,8 @@ def test_str(self, role): def test_repr(self, role): assert repr(role) == ( "" + "scope_garden=[], scope_namespaces=[], scope_systems=[], " + "scope_instances=[], scope_versions=[], scope_commands=[]>" ) diff --git a/test/schema_parser_test.py b/test/schema_parser_test.py index aea91843..ab9dab8d 100644 --- a/test/schema_parser_test.py +++ b/test/schema_parser_test.py @@ -23,6 +23,7 @@ assert_patch_equal, assert_user_equal, assert_queue_equal, + assert_remote_user_map_equal, assert_request_equal, assert_request_file_equal, assert_resolvable_equal, @@ -126,6 +127,12 @@ def test_no_modify(self, system_dict): assert_user_equal, lazy_fixture("bg_user"), ), + ( + brewtils.models.RemoteUserMap, + lazy_fixture("remote_user_map_dict"), + assert_remote_user_map_equal, + lazy_fixture("bg_remote_user_map"), + ), ( brewtils.models.Role, lazy_fixture("role_dict"), @@ -254,6 +261,12 @@ def test_single_from_string(self): assert_user_equal, lazy_fixture("bg_user"), ), + ( + "parse_remote_user_map", + lazy_fixture("remote_user_map_dict"), + assert_remote_user_map_equal, + lazy_fixture("bg_remote_user_map"), + ), ( "parse_role", lazy_fixture("role_dict"), @@ -387,6 +400,12 @@ def test_single_specific_from_string(self): assert_user_equal, lazy_fixture("bg_user"), ), + ( + brewtils.models.RemoteUserMap, + lazy_fixture("remote_user_map_dict"), + assert_remote_user_map_equal, + lazy_fixture("bg_remote_user_map"), + ), ( brewtils.models.Role, lazy_fixture("role_dict"), @@ -513,6 +532,12 @@ def test_many(self, model, data, assertion, expected): assert_user_equal, lazy_fixture("bg_user"), ), + ( + "parse_remote_user_map", + lazy_fixture("remote_user_map_dict"), + assert_remote_user_map_equal, + lazy_fixture("bg_remote_user_map"), + ), ( "parse_role", lazy_fixture("role_dict"), @@ -606,6 +631,7 @@ class TestSerialize(object): (lazy_fixture("bg_event"), lazy_fixture("event_dict")), (lazy_fixture("bg_queue"), lazy_fixture("queue_dict")), (lazy_fixture("bg_user"), lazy_fixture("user_dict")), + (lazy_fixture("bg_remote_user_map"), lazy_fixture("remote_user_map_dict")), (lazy_fixture("bg_role"), lazy_fixture("role_dict")), (lazy_fixture("bg_job"), lazy_fixture("job_dict")), (lazy_fixture("bg_cron_job"), lazy_fixture("cron_job_dict")), @@ -669,6 +695,11 @@ def test_single(self, model, expected): lazy_fixture("bg_user"), lazy_fixture("user_dict"), ), + ( + "serialize_remote_user_map", + lazy_fixture("bg_remote_user_map"), + lazy_fixture("remote_user_map_dict"), + ), ( "serialize_role", lazy_fixture("bg_role"), @@ -735,6 +766,7 @@ def test_single_specific(self, method, data, expected): (lazy_fixture("bg_event"), lazy_fixture("event_dict")), (lazy_fixture("bg_queue"), lazy_fixture("queue_dict")), (lazy_fixture("bg_user"), lazy_fixture("user_dict")), + (lazy_fixture("bg_remote_user_map"), lazy_fixture("remote_user_map_dict")), (lazy_fixture("bg_role"), lazy_fixture("role_dict")), (lazy_fixture("bg_job"), lazy_fixture("job_dict")), (lazy_fixture("bg_cron_job"), lazy_fixture("cron_job_dict")), @@ -815,6 +847,11 @@ class TestRoundTrip(object): assert_user_equal, lazy_fixture("bg_user"), ), + ( + brewtils.models.RemoteUserMap, + assert_remote_user_map_equal, + lazy_fixture("bg_remote_user_map"), + ), (brewtils.models.Role, assert_role_equal, lazy_fixture("bg_role")), (brewtils.models.Job, assert_job_equal, lazy_fixture("bg_job")), (brewtils.models.Job, assert_job_equal, lazy_fixture("bg_cron_job")),