Skip to content

Commit

Permalink
Multiple groups in a provider (#1416)
Browse files Browse the repository at this point in the history
* Added groups to manage a function

* Fix program tasks

* admin_groups support for mockuser

* Modified upload serializer to support groups

* Support admin_groups in logs

* Support admin_groups in the tasks

* Added support for M2M iterations

* Added a registry in the mockprovider

* Fix linter and black

* Fix last references to old admin_group

* Update migration from last merge
  • Loading branch information
Tansito authored Jul 31, 2024
1 parent 000dc60 commit cedb70a
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 42 deletions.
2 changes: 1 addition & 1 deletion gateway/api/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def on_migrations_applied(sender, **kwargs): # pylint: disable=unused-argument
providers,
)

providers.assign_admin_group()
providers.assign_admin_groups()
programs.assign_run_permission()


Expand Down
4 changes: 2 additions & 2 deletions gateway/api/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,11 @@ def authenticate(self, request):
group.permissions.add(run_program)
group.user_set.add(user)
logger.info("New group created")
Provider.objects.create(
provider = Provider.objects.create(
name="mockprovider",
admin_group=group,
registry=settings.SETTINGS_AUTH_MOCKPROVIDER_REGISTRY,
)
provider.admin_groups.add(group)
logger.info("New provider created")

return user, CustomToken(token.encode()) if token else None
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.0.7 on 2024-07-31 17:33

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("api", "0027_remove_jobconfig_python_version"),
("auth", "0012_alter_user_first_name_max_length"),
]

operations = [
migrations.RemoveField(
model_name="provider",
name="admin_group",
),
migrations.AddField(
model_name="provider",
name="admin_groups",
field=models.ManyToManyField(to="auth.group"),
),
]
8 changes: 1 addition & 7 deletions gateway/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,7 @@ class Provider(models.Model):

name = models.CharField(max_length=255, db_index=True, unique=True)
registry = models.CharField(max_length=255, null=True, blank=True, default=None)
admin_group = models.ForeignKey(
to=Group,
on_delete=models.SET_NULL,
default=None,
null=True,
blank=True,
)
admin_groups = models.ManyToManyField(Group)

def __str__(self):
return f"{self.name}"
Expand Down
6 changes: 5 additions & 1 deletion gateway/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,15 @@ def check_provider_access(self, provider_name, author):
if provider is None:
logger.error("Provider [%s] does not exist.", provider_name)
return False
has_access = provider.admin_group in author.groups.all()

author_groups = author.groups.all()
admin_groups = provider.admin_groups.all()
has_access = any(group in admin_groups for group in author_groups)
if not has_access:
logger.error(
"User [%s] has no access to provider [%s].", author.id, provider_name
)

return has_access

def retrieve_private_function(self, title, author):
Expand Down
3 changes: 1 addition & 2 deletions gateway/api/tasks/programs.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,11 @@ def assign_run_permission():
if group is None:
logger.warning("Group [%s] does not exist", instance_title)
else:
logger.info("Group [%s] does not exist", instance_title)
group.permissions.add(run_permission)
groups.append(group)

logger.info(
"Program [%s] is going to be updated with [%s] groups",
"Program [%s] is going to be updated with [%s] group(s)",
program.title,
len(groups),
)
Expand Down
45 changes: 29 additions & 16 deletions gateway/api/tasks/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
logger = logging.getLogger("gateway")


def assign_admin_group():
def assign_admin_groups():
"""
This method will assign a group to a provider.
If the provider does not exist it will be created.
Expand All @@ -30,21 +30,34 @@ def assign_admin_group():
return

for provider_name, provider_attributes in providers_configuration.items():
admin_group_name = provider_attributes["admin_group"]
groups = []
admin_groups = provider_attributes["admin_groups"]
registry = provider_attributes["registry"]
group = Group.objects.filter(name=admin_group_name).first()
if group is None:
logger.warning("Group [%s] does not exist", admin_group_name)

for admin_group_name in admin_groups:
group = Group.objects.filter(name=admin_group_name).first()
if group is None:
logger.warning("Group [%s] does not exist", admin_group_name)
else:
groups.append(group)

provider, created = Provider.objects.update_or_create(
name=provider_name,
defaults={"registry": registry},
)
provider.admin_groups.set(groups)

if created:
logger.info(
"Provider [%s] created for [%s] admin(s) with registry [%s]",
provider.name,
len(admin_groups),
registry,
)
else:
provider, created = Provider.objects.update_or_create(
name=provider_name,
defaults={"admin_group": group, "registry": registry},
logger.info(
"Provider [%s] updated for [%s] admin(s) with registry [%s]",
provider.name,
len(admin_groups),
registry,
)

if created:
logger.info( # pylint: disable=logging-too-many-args
"Provider [%s] created for admin [%s] with registrt [%s]",
provider.name,
admin_group_name,
registry,
)
25 changes: 19 additions & 6 deletions gateway/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,10 +373,14 @@ def get_jobs(
{"message": f"program [{pk}] was not found."},
status=status.HTTP_404_NOT_FOUND,
)
if (
program.provider
and program.provider.admin_group in request.user.groups.all()
):

user_is_provider = False
if program.provider:
admin_groups = program.provider.admin_groups.all()
user_groups = request.user.groups.all()
user_is_provider = any(group in admin_groups for group in user_groups)

if user_is_provider:
jobs = Job.objects.filter(program=program)
else:
jobs = Job.objects.filter(program=program, author=request.user)
Expand Down Expand Up @@ -468,13 +472,19 @@ def logs(self, request, pk=None): # pylint: disable=invalid-name,unused-argumen
with tracer.start_as_current_span("gateway.job.logs", context=ctx):
job = Job.objects.filter(pk=pk).first()
if job is None:
logger.warning("Job [%s] not found", pk)
return Response(status=404)

logs = job.logs
author = self.request.user
if job.program and job.program.provider:
if job.program.provider.admin_group in author.groups.all():
provider_groups = job.program.provider.admin_groups.all()
author_groups = author.groups.all()
has_access = any(group in provider_groups for group in author_groups)
if has_access:
return Response({"logs": logs})
return Response({"logs": "No available logs"})

if author == job.author:
return Response({"logs": logs})
return Response({"logs": "No available logs"})
Expand Down Expand Up @@ -586,7 +596,10 @@ def list_user_providers(self, user):
provider_list = []
providers = Provider.objects.all()
for instance in providers:
if instance.admin_group in user.groups.all():
user_groups = user.groups.all()
admin_groups = instance.admin_groups.all()
provider_found = any(group in admin_groups for group in user_groups)
if provider_found:
provider_list.append(instance.name)
return provider_list

Expand Down
13 changes: 8 additions & 5 deletions gateway/tests/api/tasks/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,27 @@ class TestProgramApi(APITestCase):
fixtures = ["tests/fixtures/tasks_fixtures.json"]

@override_settings(
PROVIDERS_CONFIGURATION='{"test_provider": {"admin_group": "runner", "registry": "docker.io/"}}'
PROVIDERS_CONFIGURATION='{"test_provider": {"admin_groups": ["runner"], "registry": "docker.io"}}'
)
def test_assign_admin_group(self):
"""This test will check assign admin group task"""

providers.assign_admin_group()
providers.assign_admin_groups()

provider = Provider.objects.get(name="test_provider")
self.assertEqual(provider.admin_group.name, "runner")
admin_groups = provider.admin_groups.all()
self.assertEqual(len(admin_groups), 1)
self.assertEqual(admin_groups[0].name, "runner")
self.assertEqual(provider.registry, "docker.io")

@override_settings(
PROVIDERS_CONFIGURATION='{"test_provider": {"admin_group": "runner", "registry": "docker.io/"}}'
PROVIDERS_CONFIGURATION='{"test_provider": {"admin_groups": ["runner"], "registry": "docker.io"}}'
)
@override_settings(
FUNCTIONS_PERMISSIONS='{"function_provider": {"provider": "test_provider", "instances": ["runner", "manager"]}}'
)
def test_assign_run_permission(self):
providers.assign_admin_group()
providers.assign_admin_groups()

user = models.User.objects.get(username="test_user")
self.client.force_authenticate(user=user)
Expand Down
4 changes: 2 additions & 2 deletions gateway/tests/fixtures/fixtures.json
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@
"fields": {
"name": "default",
"created": "2023-02-01T15:30:43.281796Z",
"admin_group": 105,
"admin_groups": [105],
"registry": "docker.io/awesome"
}
},
Expand All @@ -195,7 +195,7 @@
"fields": {
"name": "ibm",
"created": "2023-02-01T15:30:43.281796Z",
"admin_group": 101,
"admin_groups": [101],
"registry": "docker.io/awesome"
}
}
Expand Down

0 comments on commit cedb70a

Please sign in to comment.