Commit c2b20c67 authored by Tanner Prestegard's avatar Tanner Prestegard Committed by GraceDB

Add AuthGroup model

ligoauth.models.AuthGroup is an enhanced version of the builtin Django
Group model. We add useful attributes like a description, ldap_name,
and Tag.  These can be used to abstract things that are presently
handled manually, like inheriting membership from an LDAP group or
allowing access to view log messages/files with a specific Tag.

I ran some tests comparing this multi-table inheritance setup to
adding a OneToOneField and loading it with/without select_related.
After 1K trials, the DB query times looked comparable and didn't
require multiple queries for this arrangement, so the convenience
seems to be worth it.

The commit creates the model and populates an instance for existing
each existing Django Group.
parent e75bb76f
......@@ -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': {
......
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)
# -*- 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',),
),
]
# -*- 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),
]
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()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment