Skip to content
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

[Backport 3.2.x][Fixes #7355] Introduce the adoption of groups to define users permissions, and define add_resource permission for users #7451

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions geonode/base/api/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ def test_users_list(self):
self.assertEqual(response.data['user']['username'], 'admin')
self.assertIsNotNone(response.data['user']['avatar'])

# anonymous users are not in contributors group
url = reverse('users-detail', kwargs={'pk': -1})
response = self.client.get(url, format='json')
self.assertNotIn('add_resource', response.data['user']['perms'])

# Bobby
self.assertTrue(self.client.login(username='bobby', password='bob'))
# Bobby cannot access other users' details
Expand All @@ -192,6 +197,21 @@ def test_users_list(self):
logger.debug(response.data)
self.assertEqual(response.data['user']['username'], 'bobby')
self.assertIsNotNone(response.data['user']['avatar'])
# default contributor group_perm is returned in perms
self.assertIn('add_resource', response.data['user']['perms'])

def test_register_users(self):
"""
Ensure users are created with default groups.
"""
url = reverse('users-list')
user_data = {
'username': 'new_user',
}
response = self.client.post(url, data=user_data, format='json')
self.assertEqual(response.status_code, 201)
# default contributor group_perm is returned in perms
self.assertIn('add_resource', response.data['user']['perms'])

def test_base_resources(self):
"""
Expand Down
4 changes: 2 additions & 2 deletions geonode/base/fixtures/group_test_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
}
},
{
"pk": 2,
"pk": 3,
"model": "auth.group",
"fields": {
"name": "registered-members",
Expand All @@ -22,7 +22,7 @@
"slug": "registered-members",
"last_modified": "2019-09-09 15:45:34",
"created": "2019-09-09 15:45:34",
"group": 2,
"group": 3,
"title": "Registered Members"
},
"model": "groups.groupprofile",
Expand Down
14 changes: 13 additions & 1 deletion geonode/base/populate_test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@
from django.db import transaction
from django.utils import timezone
from django.contrib.gis.geos import Polygon
from django.contrib.auth.models import Group
from django.contrib.auth.models import Permission, Group
from django.core.serializers import serialize
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile

from geonode import geoserver # noqa
Expand Down Expand Up @@ -154,6 +155,14 @@ def create_models(type=None, integration=False):
with transaction.atomic():
map_data, user_data, people_data, layer_data, document_data = create_fixtures()
anonymous_group, created = Group.objects.get_or_create(name='anonymous')
cont_group, created = Group.objects.get_or_create(name='contributors')
ctype = ContentType.objects.get_for_model(cont_group)
perm, created = Permission.objects.get_or_create(
codename='base_addresourcebase',
name='Can add resources',
content_type=ctype
)
cont_group.permissions.add(perm)
logger.debug("[SetUp] Get or create user admin")
u, created = get_user_model().objects.get_or_create(username='admin')
u.set_password('admin')
Expand All @@ -172,6 +181,9 @@ def create_models(type=None, integration=False):
u.last_name = last_name
u.save()
u.groups.add(anonymous_group)

if not (u.is_superuser or u.is_staff or u.is_anonymous):
u.groups.add(cont_group)
users.append(u)

logger.debug(f"[SetUp] Add group {anonymous_group}")
Expand Down
2 changes: 1 addition & 1 deletion geonode/documents/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def document_detail(request, docid):
perms_list = list(
document.get_self_resource().get_user_perms(request.user)
.union(document.get_user_perms(request.user))
)
)

group = None
if document.group:
Expand Down
4 changes: 2 additions & 2 deletions geonode/geoapps/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def geoapp_detail(request, geoappid, template='apps/app_detail.html'):
perms_list = list(
geoapp_obj.get_self_resource().get_user_perms(request.user)
.union(geoapp_obj.get_user_perms(request.user))
)
)
group = None
if geoapp_obj.group:
try:
Expand Down Expand Up @@ -210,7 +210,7 @@ def geoapp_edit(request, geoappid, template='apps/app_edit.html'):
perms_list = list(
geoapp_obj.get_self_resource().get_user_perms(request.user)
.union(geoapp_obj.get_user_perms(request.user))
)
)

group = None
if geoapp_obj.group:
Expand Down
2 changes: 0 additions & 2 deletions geonode/groups/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#########################################################################

from django.contrib import admin
from django.contrib.auth.models import Group
from modeltranslation.admin import TranslationAdmin
from geonode.base.admin import set_user_and_group_layer_permission

Expand All @@ -44,5 +43,4 @@ class GroupProfileAdmin(admin.ModelAdmin):
actions = [set_user_and_group_layer_permission]


admin.site.unregister(Group)
admin.site.register(models.GroupProfile, GroupProfileAdmin)
4 changes: 2 additions & 2 deletions geonode/groups/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ def delete(self, *args, **kwargs):
def promote(self, *args, **kwargs):
self.role = "manager"
if settings.ADMIN_MODERATE_UPLOADS or settings.RESOURCE_PUBLISHING:
from geonode.security.models import ADMIN_PERMISSIONS
from geonode.security.permissions import ADMIN_PERMISSIONS
queryset = get_objects_for_user(
self.user, 'base.view_resourcebase').filter(group=self.group.group)
for _r in queryset.exclude(owner=self.user):
Expand All @@ -279,7 +279,7 @@ def promote(self, *args, **kwargs):
def demote(self, *args, **kwargs):
self.role = "member"
if settings.ADMIN_MODERATE_UPLOADS or settings.RESOURCE_PUBLISHING:
from geonode.security.models import ADMIN_PERMISSIONS
from geonode.security.permissions import ADMIN_PERMISSIONS
queryset = get_objects_for_user(
self.user, 'base.view_resourcebase').filter(group=self.group.group)
for _r in queryset.exclude(owner=self.user):
Expand Down
4 changes: 2 additions & 2 deletions geonode/layers/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ def sld_definition(style):
perms_list = list(
layer.get_self_resource().get_user_perms(request.user)
.union(layer.get_user_perms(request.user))
)
)

group = None
if layer.group:
Expand Down Expand Up @@ -1669,7 +1669,7 @@ def layer_metadata_detail(
perms_list = list(
layer.get_self_resource().get_user_perms(request.user)
.union(layer.get_user_perms(request.user))
)
)

return render(request, template, context={
"resource": layer,
Expand Down
10 changes: 5 additions & 5 deletions geonode/maps/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def map_detail(request, mapid, template='maps/map_detail.html'):
perms_list = list(
map_obj.get_self_resource().get_user_perms(request.user)
.union(map_obj.get_user_perms(request.user))
)
)

group = None
if map_obj.group:
Expand Down Expand Up @@ -554,7 +554,7 @@ def map_view(request, mapid, layer_name=None,
perms_list = list(
map_obj.get_self_resource().get_user_perms(request.user)
.union(map_obj.get_user_perms(request.user))
)
)
if layer_name:
config = add_layers_to_map_config(
request, map_obj, (layer_name, ), False)
Expand Down Expand Up @@ -671,7 +671,7 @@ def map_edit(request, mapid, template='maps/map_edit.html'):
perms_list = list(
map_obj.get_self_resource().get_user_perms(request.user)
.union(map_obj.get_user_perms(request.user))
)
)
return render(request, template, context={
'mapId': mapid,
'config': json.dumps(config),
Expand Down Expand Up @@ -722,14 +722,14 @@ def new_map(request, template='maps/map_new.html'):
perms_list = list(
layer_obj.get_self_resource().get_user_perms(request.user)
.union(layer_obj.get_user_perms(request.user))
)
)
except Exception:
pass
elif map_obj:
perms_list = list(
map_obj.get_self_resource().get_user_perms(request.user)
.union(map_obj.get_user_perms(request.user))
)
)
context_dict = {
'config': config,
'map': map_obj,
Expand Down
2 changes: 1 addition & 1 deletion geonode/people/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class ProfileAdmin(admin.ModelAdmin):
search_fields = (
'username', 'organization', 'profile',
'first_name', 'last_name', 'email')
readonly_fields = ("groups", )
# readonly_fields = ("groups", )
ordering = ('username',)
filter_horizontal = ('groups', 'user_permissions',)
actions = [set_user_and_group_layer_permission]
Expand Down
49 changes: 49 additions & 0 deletions geonode/people/migrations/0032_set_contributors_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Assign the contributors group to users according to #7364
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType

from django.db import migrations
from django.db.migrations.operations import RunPython


def forwards_func(apps, schema_editor):
# assign contributors group to users
cont_group, _ = Group.objects.get_or_create(name='contributors')
ctype = ContentType.objects.get_for_model(cont_group)
perm, _ = Permission.objects.get_or_create(
codename='base_addresourcebase',
name='Can add resources',
content_type=ctype,
)
if perm:
cont_group.permissions.add(perm)
# Exclude admins and anonymous user
users_to_update = get_user_model().objects.filter(
pk__gt=0, is_staff=False, is_superuser=False
)
for user in users_to_update:
cont_group.user_set.add(user)

def reverse_func(apps, schema_editor):
# remove contributors group from users
try:
cont_group = Group.objects.get(name='contributors')
users_to_update = get_user_model().objects.filter(
pk__gt=0, is_staff=False, is_superuser=False
)
for user in users_to_update:
user.groups.remove(cont_group)
except Exception:
pass


class Migration(migrations.Migration):

dependencies = [
('people', '0031_merge_20210205_0824'),
]

operations = [
RunPython(forwards_func, reverse_func)
]
22 changes: 20 additions & 2 deletions geonode/people/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@
from django.urls import reverse
from django.contrib.sites.models import Site
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import AbstractUser, UserManager
from django.contrib.auth.models import AbstractUser, Permission, UserManager
from django.contrib.auth.signals import user_logged_in, user_logged_out

from taggit.managers import TaggableManager

from geonode.base.enumerations import COUNTRIES
from geonode.base.models import Configuration
from geonode.groups.models import GroupProfile
from geonode.security.permissions import PERMISSIONS, READ_ONLY_AFFECTED_PERMISSIONS

from allauth.account.signals import user_signed_up
from allauth.socialaccount.signals import social_account_added
Expand Down Expand Up @@ -196,7 +198,23 @@ def location(self):

@property
def perms(self):
return []
if self.is_superuser or self.is_staff:
# return all permissions for admins
perms = PERMISSIONS.values()
else:
user_groups = self.groups.values_list('name', flat=True)
group_perms = Permission.objects.filter(
group__name__in=user_groups
).distinct().values_list('codename', flat=True)
# return constant names defined by GeoNode
perms = [PERMISSIONS[db_perm] for db_perm in group_perms]

# check READ_ONLY mode
config = Configuration.load()
if config.read_only:
# exclude permissions affected by readonly
perms = [perm for perm in perms if perm not in READ_ONLY_AFFECTED_PERMISSIONS]
return perms

def save(self, *args, **kwargs):
super(Profile, self).save(*args, **kwargs)
Expand Down
27 changes: 18 additions & 9 deletions geonode/people/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@
from django.db import IntegrityError
from django.db.models import Q

from geonode.base.auth import (get_or_create_token,
delete_old_tokens,
set_session_token,
remove_session_token)
from geonode.base.auth import (
get_or_create_token,
delete_old_tokens,
set_session_token,
remove_session_token)

from geonode.groups.models import GroupProfile
from geonode.groups.conf import settings as groups_settings
Expand Down Expand Up @@ -125,15 +126,23 @@ def notify_admins_new_signup(sender, **kwargs):

def profile_post_save(instance, sender, **kwargs):
"""
Make sure the user belongs by default to the anonymous group.
This will make sure that anonymous permissions will be granted to the new users.
Make sure the user belongs by default to the anonymous and contributors groups.
This will make sure that anonymous and contributors permissions will be granted to the new users.
"""
from django.contrib.auth.models import Group
anon_group, created = Group.objects.get_or_create(name='anonymous')
instance.groups.add(anon_group)

created = kwargs.get('created', False)

if created:
anon_group, _ = Group.objects.get_or_create(name='anonymous')
instance.groups.add(anon_group)
is_anonymous = instance.username == 'AnonymousUser'

if Group.objects.filter(name='contributors').count() and not (instance.is_staff or instance.is_superuser or is_anonymous):
cont_group = Group.objects.get(name='contributors')
instance.groups.add(cont_group)

if groups_settings.AUTO_ASSIGN_REGISTERED_MEMBERS_TO_REGISTERED_MEMBERS_GROUP_AT == 'activation':
created = kwargs.get('created', False)
became_active = instance.is_active and (not instance._previous_active_state or created)
if became_active:
_add_user_to_registered_members(instance)
Expand Down
Loading