-
Notifications
You must be signed in to change notification settings - Fork 5
[permissions] Add permission groups management #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"groups": { | ||
"admin": { | ||
"datasources": { | ||
"repository": ["add", "change", "delete", "view"] | ||
}, | ||
"tasks": { | ||
"eventizerjob": ["add", "change", "delete", "view"], | ||
"eventizertask": ["add", "change", "delete", "view"] | ||
} | ||
}, | ||
"user": { | ||
"datasources": { | ||
"repository": ["add", "change", "delete", "view"] | ||
}, | ||
"tasks": { | ||
"eventizerjob": ["add", "change", "delete", "view"], | ||
"eventizertask": ["add", "change", "delete", "view"] | ||
} | ||
}, | ||
"readonly": { | ||
"datasources": { | ||
"repository": ["view"] | ||
}, | ||
"tasks": { | ||
"eventizerjob": ["view"], | ||
"eventizertask": ["view"] | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ | |
from __future__ import annotations | ||
|
||
import getpass | ||
import json | ||
import os | ||
import sys | ||
import typing | ||
|
@@ -61,6 +62,7 @@ def _setup(): | |
|
||
_create_database() | ||
_setup_database() | ||
_setup_group_permissions() | ||
_install_static_files() | ||
|
||
click.secho("\nGrimoirelab configuration completed", fg='bright_cyan') | ||
|
@@ -120,6 +122,44 @@ def _install_static_files(): | |
click.echo() | ||
|
||
|
||
def _setup_group_permissions(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I made this command before but, shouldn't this be a fixture? It can be like what we have in SortingHat for countries. |
||
"""Create groups with the chosen permissions.""" | ||
|
||
from django.conf import settings | ||
from django.contrib.auth.models import Group, Permission | ||
from django.contrib.contenttypes.models import ContentType | ||
|
||
with open(settings.PERMISSION_GROUPS_LIST_PATH, 'r') as f: | ||
groups = json.load(f).get('groups', []) | ||
|
||
for group_name, content_types in groups.items(): | ||
new_group, created = Group.objects.get_or_create(name=group_name) | ||
|
||
for app_label, models in content_types.items(): | ||
for model, permissions in models.items(): | ||
try: | ||
content_type = ContentType.objects.get( | ||
app_label=app_label, | ||
model=model | ||
) | ||
for permission_name in permissions: | ||
codename = f"{permission_name}_{model}" | ||
if model == "custompermissions": | ||
codename = permission_name | ||
try: | ||
permission = Permission.objects.get( | ||
codename=codename, | ||
content_type=content_type | ||
) | ||
new_group.permissions.add(permission) | ||
except Permission.DoesNotExist: | ||
click.echo(f"Permission {permission_name} not found") | ||
continue | ||
except ContentType.DoesNotExist: | ||
click.echo(f"ContentType {model} not found in {app_label}") | ||
continue | ||
|
||
|
||
@admin.command() | ||
@click.option('--username', help="Specifies the login for the user.") | ||
@click.option('--is-admin', is_flag=True, default=False, | ||
|
@@ -186,6 +226,29 @@ def _validate_username(username): | |
return '; '.join(e.messages) | ||
|
||
|
||
@admin.command() | ||
@click.argument('username') | ||
@click.argument('permission_group') | ||
def set_permissions(username, permission_group): | ||
"""Assign a user to a specific permission group""" | ||
|
||
from django.contrib.auth.models import Group | ||
User = get_user_model() | ||
|
||
try: | ||
group = Group.objects.get(name=permission_group) | ||
user = User.objects.get(username=username) | ||
except Group.DoesNotExist: | ||
click.echo(f"Group '{permission_group}' not found") | ||
sys.exit(1) | ||
except User.DoesNotExist: | ||
click.echo(f"User '{username}' not found") | ||
sys.exit(1) | ||
|
||
user.groups.set([group.id]) | ||
click.echo(f"User '{username}' assigned to group '{permission_group}'.") | ||
|
||
|
||
@admin.group() | ||
@click.pass_context | ||
def queues(ctx: Context): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ | |
from unittest.mock import patch | ||
|
||
from django.contrib.auth import get_user_model | ||
from django.contrib.auth.models import Permission | ||
from django.test import TestCase, Client | ||
from django.urls import reverse | ||
|
||
|
@@ -33,7 +34,7 @@ class TestAddRepository(TestCase): | |
|
||
def setUp(self): | ||
self.client = Client() | ||
self.user = get_user_model().objects.create_user(username='testuser', password='testpassword') | ||
self.user = get_user_model().objects.create_user(username='testuser', password='testpassword', is_superuser=True) | ||
self.client.login(username='testuser', password='testpassword') | ||
self.url = reverse('add_repository') | ||
self.valid_data = { | ||
|
@@ -132,3 +133,44 @@ def test_add_repository_authentication_required(self): | |
|
||
self.assertEqual(response.status_code, 403) | ||
self.assertEqual(response.json(), {"detail": "Authentication credentials were not provided."}) | ||
|
||
def test_add_repository_permission_denied(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can have something generic to try the permissions of specific actions. |
||
"""Test adding a repository with insufficient permissions.""" | ||
|
||
# Create a user without permissions | ||
get_user_model().objects.create_user(username='nopermuser', password='nopermpassword') | ||
self.client.login(username='nopermuser', password='nopermpassword') | ||
|
||
response = self.client.post( | ||
self.url, | ||
data=json.dumps(self.valid_data), | ||
content_type='application/json' | ||
) | ||
|
||
self.assertEqual(response.status_code, 403) | ||
self.assertEqual(response.json(), {"message": "You do not have permission to perform this action."}) | ||
|
||
@patch('grimoirelab.core.datasources.views.schedule_task') | ||
def test_add_repository_valid_permissions(self, mock_schedule_task): | ||
"""Test adding a repository with valid permissions.""" | ||
|
||
mock_schedule_task.return_value = self.task | ||
|
||
# Create a user with permissions | ||
user = get_user_model().objects.create_user(username='user', password='password') | ||
perm = Permission.objects.get(codename='add_repository') | ||
user.user_permissions.add(perm) | ||
self.client.login(username='user', password='password') | ||
|
||
response = self.client.post( | ||
self.url, | ||
data=json.dumps(self.valid_data), | ||
content_type='application/json' | ||
) | ||
|
||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual(response.json(), { | ||
'status': 'ok', | ||
'task_id': self.task.uuid, | ||
'message': f"Repository {self.valid_data['uri']} added correctly" | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the implications of this? I'm afraid there could be other ways to call the API, etc and have a security hole because we don't remember this only works for the RestFramework views.