diff --git a/config/settings/base.py b/config/settings/base.py index c75575e1ef624df8b8fb214669104945be426247..b843f2e70d9e86026a8f0e840c86e576ee4ed749 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -128,9 +128,9 @@ LVALERT_OVERSEER_INSTANCES = [ # Access and authorization ---------------------------------------------------- # Some proper names related to authorization -LVC_GROUP = 'Communities:LSCVirgoLIGOGroupMembers' -LVEM_GROUP = 'gw-astronomy:LV-EM' -LVEM_OBSERVERS_GROUP = 'gw-astronomy:LV-EM:Observers' +LVC_GROUP = 'internal_users' +LVEM_GROUP = 'lvem_users' +LVEM_OBSERVERS_GROUP = 'lvem_observers' PUBLIC_GROUP = 'public_users' PRIORITY_USERS_GROUP = 'priority_users' @@ -363,7 +363,7 @@ INSTALLED_APPS = [ # Aliases for django-extensions shell_plus SHELL_PLUS_MODEL_ALIASES = { # Two 'Group' models - auth.Group and gracedb.Group - 'auth': {'Group': 'AuthGroup'}, + 'auth': {'Group': 'DjangoGroup'}, # Superevents models which have the same name as # models in the events app 'superevents': { diff --git a/gracedb/ligoauth/managers.py b/gracedb/ligoauth/managers.py new file mode 100644 index 0000000000000000000000000000000000000000..ed5ed9a5fccba7204aa8f7a90bfdfada388c886b --- /dev/null +++ b/gracedb/ligoauth/managers.py @@ -0,0 +1,15 @@ +from django.db import models + +# Custom managers for the AuthGroup model +class LdapGroupManager(models.Manager): + """Groups whose membership is managed through an LDAP""" + def get_queryset(self): + return super(LdapGroupManager, self).get_queryset().filter( + ldap_name__isnull=False) + + +class TagGroupManager(models.Manager): + """Groups who have an associated tag for allowing log viewing""" + def get_queryset(self): + return super(TagGroupManager, self).get_queryset().filter( + tag__isnull=False) diff --git a/gracedb/ligoauth/migrations/0037_authgroup.py b/gracedb/ligoauth/migrations/0037_authgroup.py new file mode 100644 index 0000000000000000000000000000000000000000..b26228886d73b23227a57b128ca4a1a0646519c7 --- /dev/null +++ b/gracedb/ligoauth/migrations/0037_authgroup.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-05-28 18:16 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0033_pipelinelog_and_pipeline_enabled'), + ('auth', '0023_add_manage_pipeline_permissions'), + ('ligoauth', '0036_add_cwb_cert'), + ] + + operations = [ + migrations.CreateModel( + name='AuthGroup', + fields=[ + ('group_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='auth.Group')), + ('description', models.TextField()), + ('ldap_name', models.CharField(max_length=50, null=True, unique=True)), + ('tag', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='events.Tag')), + ], + bases=('auth.group',), + ), + ] diff --git a/gracedb/ligoauth/migrations/0038_populate_authgroup.py b/gracedb/ligoauth/migrations/0038_populate_authgroup.py new file mode 100644 index 0000000000000000000000000000000000000000..acfcdc19fae2e52c8a824eb3e98c5c33efafe3d1 --- /dev/null +++ b/gracedb/ligoauth/migrations/0038_populate_authgroup.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-05-28 18:18 +from __future__ import unicode_literals + +from django.db import migrations + +GROUP_DATA = [ + { + 'name': 'executives', + 'description': ('"Executive users" who can expose and hide events, ' + 'superevents, and log messages. This is a holdover from O1/O2 and ' + 'should be deleted in favor of the "access_managers" group once ' + 'the permissions for the events app can be reworked to be more ' + 'like the superevents app.'), + }, + { + 'name': 'Communities:LSCVirgoLIGOGroupMembers', + 'ldap_name': 'Communities:LSCVirgoLIGOGroupMembers', + 'new_name': 'internal_users', + 'description': 'Internal users (LIGO, Virgo, and soon, KAGRA).', + }, + { + 'name': 'gw-astronomy:LV-EM', + 'ldap_name': 'gw-astronomy:LV-EM', + 'new_name': 'lvem_users', + 'description': ('LV-EM group; not sure why this exists exactly, but ' + 'these users have been allowed to see MDC events historically.'), + }, + { + 'name': 'public_users', + 'tag_name': 'public', + 'description': ('Public group, used for unauthenticated users to map ' + 'into the django-guardian AnonymousUser account to allow ' + 'object permissions.'), + }, + { + 'name': 'h1_control_room', + 'description': ('Users in the Hanford (H1) control room. Membership ' + 'in this group is set on a per-request basis by some middleware ' + 'which checks the user\'s IP versus a specific IP in the Django ' + 'settings. This group may not be used going forward since ' + 'operator signoffs do not appear to be used in O3.'), + }, + { + 'name': 'l1_control_room', + 'description': ('Users in the Livingston (L1) control room. Membership' + ' in this group is set on a per-request basis by some middleware ' + 'which checks the user\'s IP versus a specific IP in the Django ' + 'settings. This group may not be used going forward since ' + 'operator signoffs do not appear to be used in O3.'), + }, + { + 'name': 'v1_control_room', + 'description': ('Users in the Virgo (V1) control room. Membership ' + 'in this group is set on a per-request basis by some middleware ' + 'which checks the user\'s IP versus a specific IP in the Django ' + 'settings. This group may not be used going forward since ' + 'operator signoffs do not appear to be used in O3.'), + }, + { + 'name': 'gw-astronomy:LV-EM:Observers', + 'ldap_name': 'gw-astronomy:LV-EM:Observers', + 'new_name': 'lvem_observers', + 'tag_name': 'lvem', + 'description': ('LV-EM observers group. This is the primary group ' + 'used for allowing access to LV-EM members.'), + }, + { + 'name': 'em_advocates', + 'ldap_name': 'Communities:LVC:GraceDB:GraceDBAdvocates', + 'description': ('EM follow-up advocates; responsible for managing ' + 'rapid response to new candidate events/superevents and ' + 'performing advocate signoffs.'), + }, + { + 'name': 'superevent_managers', + 'description': ('Users responsible for creating and updating both ' + 'production and MDC superevents.'), + }, + { + 'name': 'access_managers', + 'description': ('Users responsible for allowing non-internal access ' + 'to superevents and log messages. At present, they have no ' + 'ability to modify event permissions, although this should ' + 'change in the future when permissions are consolidated.'), + }, + { + 'name': 'priority_users', + 'description': ('Users allowed to access the priority API (which ' + 'does not exist at present [June 2019]).'), + }, + { + 'name': 'raven_users', + 'description': ('Users in charge of managing the RAVEN pipeline. ' + 'Have permission to upload external events.'), + }, +] + + +def create_authgroups(apps, schema_editor): + DjangoGroup = apps.get_model('auth', 'Group') + AuthGroup = apps.get_model('ligoauth', 'AuthGroup') + Tag = apps.get_model('events', 'Tag') + + # Create AuthGroup instances + for group in GROUP_DATA: + g = DjangoGroup.objects.get(name=group['name']) + ag = AuthGroup(group_ptr=g) + ag.description = group['description'] + + # Update from base class + ag.__dict__.update(g.__dict__) + + # Save + ag.save() + + # Add optional extras and save + if 'new_name' in group: + ag.name = group['new_name'] + if 'ldap_name' in group: + ag.ldap_name = group['ldap_name'] + if 'tag_name' in group: + tag, _ = Tag.objects.get_or_create(name=group['tag_name']) + ag.tag = tag + ag.save() + + +def delete_authgroups(apps, schema_editor): + AuthGroup = apps.get_model('ligoauth', 'AuthGroup') + + # Loop over groups and delete AuthGroup + for group in GROUP_DATA: + # Get AuthGroup + group_name = group['name'] + if 'new_name' in group: + group_name = group['new_name'] + ag = AuthGroup.objects.get(name=group_name) + + # Reset name if needed + if 'new_name' in group: + ag.name = group['name'] + ag.save() + + # Delete AuthGroup and keep DjangoGroup base class + ag.delete(keep_parents=True) + + +class Migration(migrations.Migration): + + dependencies = [ + ('ligoauth', '0037_authgroup'), + ] + + operations = [ + migrations.RunPython(create_authgroups, delete_authgroups), + ] diff --git a/gracedb/ligoauth/models.py b/gracedb/ligoauth/models.py index 4f270fa52a86c939aaa1076c5662ffbeb42e4a3e..ec228312df85dca26de0e25a4ce4b530b1642d60 100644 --- a/gracedb/ligoauth/models.py +++ b/gracedb/ligoauth/models.py @@ -1,8 +1,10 @@ - from __future__ import unicode_literals from django.db import models -from django.contrib.auth.models import User +from django.contrib.auth.models import User, Group + +from .managers import LdapGroupManager, TagGroupManager + # There seems to be a LOT of duplication here and I don't know # if that is good or bad. Normally, that seems bad... @@ -84,3 +86,22 @@ def certdn_to_user(dn, username=None): except IndexError: return None + +class AuthGroup(Group): + """Enhanced version of Django Group model""" + # Description of the group + description = models.TextField(blank=False) + # The group's name in some LDAP (likely the LIGO LDAP). This will be used + # to correlated group memberships in the LDAP as retrieved from an LDAP + # query or from a Shibboleth session to groups in this service + # If this is null, the group is manually managed and does not inherit its + # membership from an LDAP. + ldap_name = models.CharField(max_length=50, unique=True, null=True) + # Tag used to expose access to log messages for group; if null, there is no + # such tag and access is not granted via this mechanism + tag = models.ForeignKey('events.Tag', null=True) + + # Add custom managers, must manually define objects as well + objects = models.Manager() + ldap_objects = LdapGroupManager() + tag_objects = TagGroupManager()