From da18097faaa51a6eb210622b7f063edefac6a0ed Mon Sep 17 00:00:00 2001
From: Brian Moe <brian.moe@ligo.org>
Date: Mon, 10 Jun 2013 16:41:01 -0500
Subject: [PATCH] Updated LIGO auth.  Remove LigoUser model.

---
 gracedb/admin.py                              |   7 +-
 gracedb/api.py                                |   6 +-
 gracedb/feeds.py                              |   2 +-
 gracedb/forms.py                              |   8 +-
 gracedb/middleware/auth.py                    | 246 +++++++++---------
 gracedb/migrations/0004_populate_tags.py      | 106 ++++++++
 ...tage1_rm_ligouser__add_new_foreign_keys.py | 199 ++++++++++++++
 ..._rm_ligouser__populate_new_foreign_keys.py | 234 +++++++++++++++++
 ...e3_rm_ligouser__remove_old_foreign_keys.py | 192 ++++++++++++++
 gracedb/migrations/0008_auto__del_user.py     | 158 +++++++++++
 gracedb/models.py                             |  32 +--
 gracedb/views.py                              |  45 ++--
 ligoauth/__init__.py                          |   0
 ligoauth/admin.py                             |  15 ++
 ligoauth/fixtures/initial.json                |  30 +++
 ligoauth/management/__init__.py               |   0
 ligoauth/management/commands/__init__.py      |   0
 .../commands/refresh_users_from_ldap.py       |  72 +++++
 ligoauth/middleware/__init__.py               |   0
 ligoauth/middleware/auth.py                   | 128 +++++++++
 ligoauth/migrations/0001_initial.py           | 107 ++++++++
 .../migrations/0002_remove_old_auth_users.py  |  70 +++++
 .../migrations/0003_utf8ify_user_tables.py    |  80 ++++++
 ligoauth/migrations/0004_add_localusers.py    | 197 ++++++++++++++
 ligoauth/migrations/0005_add_ldapusers.py     | 148 +++++++++++
 ligoauth/migrations/__init__.py               |   0
 ligoauth/models.py                            |  88 +++++++
 ligoauth/tests.py                             |  16 ++
 ligoauth/views.py                             |   1 +
 settings/default.py                           |  17 +-
 settings/development.py                       |   2 +-
 templates/admin/base_site.html                |   3 +-
 templates/base.html                           |   6 +-
 templates/forbidden.html                      |  11 +
 templates/gracedb/create.html                 |   2 +-
 templates/gracedb/event_detail.html           |   4 +-
 templates/rest_framework/api.html             |   2 +-
 userprofile/migrations/0001_initial.py        | 123 +++++++++
 ...stage1_rm_ligouser__add_new_foreign_key.py | 109 ++++++++
 ...2_rm_ligouser__populate_new_foreign_key.py | 152 +++++++++++
 ...e3_rm_ligouser__remove_old_foreign_keys.py |  68 +++++
 userprofile/migrations/__init__.py            |   0
 userprofile/models.py                         |   6 +-
 userprofile/views.py                          |  16 +-
 44 files changed, 2514 insertions(+), 194 deletions(-)
 create mode 100644 gracedb/migrations/0005_stage1_rm_ligouser__add_new_foreign_keys.py
 create mode 100644 gracedb/migrations/0006_stage2_rm_ligouser__populate_new_foreign_keys.py
 create mode 100644 gracedb/migrations/0007_stage3_rm_ligouser__remove_old_foreign_keys.py
 create mode 100644 gracedb/migrations/0008_auto__del_user.py
 create mode 100644 ligoauth/__init__.py
 create mode 100644 ligoauth/admin.py
 create mode 100644 ligoauth/fixtures/initial.json
 create mode 100644 ligoauth/management/__init__.py
 create mode 100644 ligoauth/management/commands/__init__.py
 create mode 100644 ligoauth/management/commands/refresh_users_from_ldap.py
 create mode 100644 ligoauth/middleware/__init__.py
 create mode 100644 ligoauth/middleware/auth.py
 create mode 100644 ligoauth/migrations/0001_initial.py
 create mode 100644 ligoauth/migrations/0002_remove_old_auth_users.py
 create mode 100644 ligoauth/migrations/0003_utf8ify_user_tables.py
 create mode 100644 ligoauth/migrations/0004_add_localusers.py
 create mode 100644 ligoauth/migrations/0005_add_ldapusers.py
 create mode 100644 ligoauth/migrations/__init__.py
 create mode 100644 ligoauth/models.py
 create mode 100644 ligoauth/tests.py
 create mode 100644 ligoauth/views.py
 create mode 100644 templates/forbidden.html
 create mode 100644 userprofile/migrations/0001_initial.py
 create mode 100644 userprofile/migrations/0002_stage1_rm_ligouser__add_new_foreign_key.py
 create mode 100644 userprofile/migrations/0003_stage2_rm_ligouser__populate_new_foreign_key.py
 create mode 100644 userprofile/migrations/0004_stage3_rm_ligouser__remove_old_foreign_keys.py
 create mode 100644 userprofile/migrations/__init__.py

diff --git a/gracedb/admin.py b/gracedb/admin.py
index 99754ff51..bc37adcea 100644
--- a/gracedb/admin.py
+++ b/gracedb/admin.py
@@ -1,5 +1,5 @@
 
-from models import Event, EventLog, User, Group
+from models import Event, EventLog, Group
 from models import Label, Labelling, Tag
 from django.contrib import admin
 
@@ -15,10 +15,6 @@ class EventAdmin(admin.ModelAdmin):
     list_display = [ graceid, 'group', analysis_type, 'submitter'  ]
     search_fields = [ 'group__name', 'submitter__name' ]
 
-class UserAdmin(admin.ModelAdmin):
-    list_display = [ 'name', 'dn' ]
-    search_fields = [ 'name' ]
-
 class LabelAdmin(admin.ModelAdmin):
     list_display = [ 'name', 'defaultColor' ]
 
@@ -36,7 +32,6 @@ class TagAdmin(admin.ModelAdmin):
 
 admin.site.register(Event, EventAdmin)
 admin.site.register(EventLog, EventLogAdmin)
-admin.site.register(User, UserAdmin)
 admin.site.register(Group)
 admin.site.register(Label, LabelAdmin)
 admin.site.register(Labelling, LabellingAdmin)
diff --git a/gracedb/api.py b/gracedb/api.py
index f2722cb60..10dc622cc 100644
--- a/gracedb/api.py
+++ b/gracedb/api.py
@@ -154,7 +154,7 @@ def eventToDict(event, columns=None, request=None):
     rv = {}
 
     graceid = event.graceid()
-    rv['submitter'] = event.submitter.name
+    rv['submitter'] = event.submitter.username
     rv['created'] = event.created
     rv['group'] = event.group.name
     rv['graceid'] = graceid
@@ -478,7 +478,7 @@ class EventNeighbors(APIView):
 def labelToDict(label, request=None):
     return { 
             "name" : label.label.name,
-            "creator" : label.creator.name,
+            "creator" : label.creator.username,
             "created" : label.created,
             "self" : reverse("labels",
                 args=[label.event.graceid(), label.label.name],
@@ -545,7 +545,7 @@ def eventLogToDict(log, n=None, request=None):
     return {
                 "comment" : log.comment,
                 "created" : log.created,
-                "issuer"  : log.issuer.name,
+                "issuer"  : log.issuer.username,
                 "self"    : uri,
                 "tags"    : taglist_uri,
            }
diff --git a/gracedb/feeds.py b/gracedb/feeds.py
index fd6ca5a5c..807b67960 100644
--- a/gracedb/feeds.py
+++ b/gracedb/feeds.py
@@ -62,7 +62,7 @@ class EventFeed(Feed):
         return reverse(view, args=[obj.graceid()])
 
     def item_author_name(self, obj):
-        return obj.submitter.name
+        return u"{0} {1}".format(obj.submitter.first_name, obj.submitter.last_name)
 
     def item_pubdate(self, obj):
         return obj.created
diff --git a/gracedb/forms.py b/gracedb/forms.py
index b9c905b5e..93448eb71 100644
--- a/gracedb/forms.py
+++ b/gracedb/forms.py
@@ -2,7 +2,8 @@
 from django import forms
 from django.utils.safestring import mark_safe
 from django.utils.html import escape
-from models import Event, User, Group, Label
+from models import Event, Group, Label
+from django.contrib.auth.models import User
 
 from query import parseQuery, ParseException
 
@@ -46,8 +47,9 @@ class EventSearchForm(forms.Form):
     typeChoices= [("","")]+list(Event.ANALYSIS_TYPE_CHOICES)
 
     submitterIds = Event.objects.values_list('submitter',flat=True).distinct()
-    submitterList = User.objects.filter(id__in=submitterIds).order_by('name')
-    submitterChoices = [("","")]+ [ (u.id, u.name) for u in submitterList]
+    submitterList = User.objects.filter(id__in=submitterIds).order_by('last_name', 'first_name')
+    submitterChoices = [("","")]+ \
+            [ (u.id, u"{0} {1}".format(u.first_name, u.last_name)) for u in submitterList]
 
     labelChoices = [ ("hi%d"%n,"bye%d"%n) for n in [1,2,3]]
     labelChoices = [ (label.id, label.name) for label in Label.objects.all() ]
diff --git a/gracedb/middleware/auth.py b/gracedb/middleware/auth.py
index 58dcd8e6e..ed0ee3385 100644
--- a/gracedb/middleware/auth.py
+++ b/gracedb/middleware/auth.py
@@ -1,123 +1,125 @@
-
-from django.contrib.auth import authenticate
-
-from gracedb.models import User
-from django.contrib.auth.models import User as DjangoUser
-
-import re
-proxyPattern = re.compile(r'^(.*?)(/CN=\d+)*$')
-
-def nameFromPrincipal(principal):
-    name = principal.split('@')[0]
-    first, last = name.split('.')
-    return first[0].upper() + first[1:] + " " + last[0].upper() + last[1:]
-
-class LigoAuthMiddleware:
-
-    def process_request(self, request):
-
-        ligouser = None
-        user = None
-        principal = None
-
-        queryResult = []
-        if not request.user.is_anonymous():
-            # Scott's middleware has set the user aready using shib.
-            # Let's add some more attributes.
-            principal = request.user.username
-            request.user.name = nameFromPrincipal(principal)
-            queryResult = User.objects.filter(principal=principal)
-        else:
-            # authenticate with certs
-            certdn = request.META.get('SSL_CLIENT_S_DN')
-            issuer = request.META.get('SSL_CLIENT_I_DN')
-
-            if not certdn:
-                try:
-                    # mod_python is a little off...
-                    # SSL info is in request._req
-                    # Need to try/except because _req is
-                    # not defined in WSGI request.
-                    certdn = request._req.ssl_var_lookup ('SSL_CLIENT_S_DN')
-                    issuer = request._req.ssl_var_lookup ('SSL_CLIENT_I_DN')
-                    pass
-                except:
-                    pass
-
-            if certdn and certdn.startswith(issuer):
-                # proxy.
-                # Proxies can be signed by proxies.
-                # Each level of "proxification" causes the subject
-                # to have a '/CN=[0-9]+ appended to the signers subject.
-                # These must be removed to discover the original identity's
-                # subject DN.
-                issuer = proxyPattern.match(issuer).group(1)
-                queryResult = User.objects.filter(dn=issuer)
-            elif certdn:
-                # cert in browser.
-                queryResult = User.objects.filter(dn=certdn)
-
-        if queryResult:
-            ligouser = queryResult[0]
-            try:
-                user = DjangoUser.objects.get(username=ligouser.unixid)
-            except DjangoUser.DoesNotExist:
-                user = DjangoUser(username=ligouser.unixid, password="")
-                user.is_staff = False
-                user.is_superuser = False
-                user.save()
-        elif principal:
-            # There is no user ... what do we do?
-            # If auth was via cert... nothing, DNs need to be registered.
-            # If auth was via kerberos, we make sure it was a LIGO.ORG
-            #   principal and make a new user and ligouser with no DN.
-            #assert (principal.split('@')[1] == 'LIGO.ORG')
-            ligouser = User(name = nameFromPrincipal(principal),
-                            email = principal.lower(),
-                            principal = principal,
-                            dn = "NONE",
-                            unixid = principal.split('@')[0])
-            ligouser.save()
-            try:
-                user = DjangoUser.objects.get(username=ligouser.unixid)
-            except DjangoUser.DoesNotExist:
-                user = DjangoUser(username=ligouser.unixid, password="")
-                user.is_staff = False
-                user.is_superuser = False
-                user.save()
-
-        request.user = authenticate(ssluser=user)
-        request.ligouser = ligouser
-
-        # Http404 doesn't check for user == None, just hasattr(x,'user')
-        # Why does this matter?  I forget, but it does.
-        if not request.user: del request.user 
-
-        return None
-
-#   def process_view(self, request, view_func, view_args, view_kwargs):
-#       return None
-
-#   def process_response(self, request, response):
-#       return None
-
-#   def process_exception(self, request, exception):
-#       return None
-
-class LigoAuthBackend:
-
-    supports_object_permissions = False
-    supports_anonymous_user = False
-    supports_inactive_user = False
-
-    def authenticate(self, ssluser):
-        return ssluser
-
-    def get_user(self, user_id):
-        try:
-            return DjangoUser.get(id=user_id)
-        except DjangoUser.UserDoesNotExist:
-            return None
-
+#
+#from django.contrib.auth import authenticate
+#
+#from gracedb.models import User
+#from django.contrib.auth.models import User as DjangoUser
+#
+#import re
+#proxyPattern = re.compile(r'^(.*?)(/CN=\d+)*$')
+#
+#def nameFromPrincipal(principal):
+#    name = principal.split('@')[0]
+#    first, last = name.split('.')
+#    return first[0].upper() + first[1:] + " " + last[0].upper() + last[1:]
+#
+#class LigoAuthMiddleware:
+#
+#    def process_request(self, request):
+#
+#        ligouser = None
+#        user = None
+#        principal = None
+#
+#        queryResult = []
+#        if not request.user.is_anonymous():
+#            # Scott's middleware has set the user aready using shib.
+#            # Let's add some more attributes.
+#            principal = request.user.username
+#            request.user.name = nameFromPrincipal(principal)
+#            queryResult = User.objects.filter(principal=principal)
+#        else:
+#            # authenticate with certs
+#            certdn = request.META.get('SSL_CLIENT_S_DN')
+#            issuer = request.META.get('SSL_CLIENT_I_DN')
+#
+#            if not certdn:
+#                try:
+#                    # mod_python is a little off...
+#                    # SSL info is in request._req
+#                    # Need to try/except because _req is
+#                    # not defined in WSGI request.
+#                    certdn = request._req.ssl_var_lookup ('SSL_CLIENT_S_DN')
+#                    issuer = request._req.ssl_var_lookup ('SSL_CLIENT_I_DN')
+#                    pass
+#                except:
+#                    pass
+#
+#            if certdn and certdn.startswith(issuer):
+#                # proxy.
+#                # Proxies can be signed by proxies.
+#                # Each level of "proxification" causes the subject
+#                # to have a '/CN=[0-9]+ appended to the signers subject.
+#                # These must be removed to discover the original identity's
+#                # subject DN.
+#                issuer = proxyPattern.match(issuer).group(1)
+#                queryResult = User.objects.filter(dn=issuer)
+#            elif certdn:
+#                # cert in browser.
+#                queryResult = User.objects.filter(dn=certdn)
+#
+#        if queryResult:
+#            ligouser = queryResult[0]
+#            try:
+#                user = DjangoUser.objects.get(username=ligouser.unixid)
+#            except DjangoUser.DoesNotExist:
+#                user = DjangoUser(username=ligouser.unixid, password="")
+#                user.is_staff = False
+#                user.is_superuser = False
+#                user.save()
+#        elif principal:
+#            # There is no user ... what do we do?
+#            # If auth was via cert... nothing, DNs need to be registered.
+#            # If auth was via kerberos, we make sure it was a LIGO.ORG
+#            #   principal and make a new user and ligouser with no DN.
+#            #assert (principal.split('@')[1] == 'LIGO.ORG')
+#            ligouser = User(name = nameFromPrincipal(principal),
+#                            email = principal.lower(),
+#                            principal = principal,
+#                            dn = "NONE",
+#                            unixid = principal.split('@')[0])
+#            ligouser.save()
+#            try:
+#                user = DjangoUser.objects.get(username=ligouser.unixid)
+#            except DjangoUser.DoesNotExist:
+#                user = DjangoUser(username=ligouser.unixid, password="")
+#                user.is_staff = False
+#                user.is_superuser = False
+#                user.save()
+#
+#        request.user = authenticate(ssluser=user)
+#        request.ligouser = ligouser
+#
+#        # Http404 doesn't check for user == None, just hasattr(x,'user')
+#        # Why does this matter?  I forget, but it does.
+#        if not request.user: del request.user 
+#
+#        return None
+#
+##   def process_view(self, request, view_func, view_args, view_kwargs):
+##       return None
+#
+##   def process_response(self, request, response):
+##       return None
+#
+##   def process_exception(self, request, exception):
+##       return None
+#
+#class LigoAuthBackend:
+#
+#    supports_object_permissions = False
+#    supports_anonymous_user = False
+#    supports_inactive_user = False
+#
+#    def authenticate(self, ssluser):
+#        return ssluser
+#
+#    def get_user(self, user_id):
+#        try:
+#            return DjangoUser.get(id=user_id)
+#        except DjangoUser.UserDoesNotExist:
+#            return None
+#
 def LigoAuthContext(request):
-    return { 'ligouser' : request.ligouser, 'user' : request.user }
+    #return { 'ligouser' : request.ligouser, 'user' : request.user }
+    return { 'user' : request.user }
+    return { 'ligouser' : request.user, 'user' : request.user }
diff --git a/gracedb/migrations/0004_populate_tags.py b/gracedb/migrations/0004_populate_tags.py
index 3a52a1691..5136a8725 100644
--- a/gracedb/migrations/0004_populate_tags.py
+++ b/gracedb/migrations/0004_populate_tags.py
@@ -12,3 +12,109 @@ class Migration(DataMigration):
 
     complete_apps = ['gracedb']
     symmetrical = True
+
+    models = {
+        'gracedb.approval': {
+            'Meta': {'object_name': 'Approval'},
+            'approvedEvent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'approver': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'approvingCollaboration': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'gracedb.coincinspiralevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'CoincInspiralEvent', '_ormbases': ['gracedb.Event']},
+            'combined_far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'end_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'end_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'mass': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'minimum_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'})
+        },
+        'gracedb.event': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'Event'},
+            'analysisType': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'gpstime': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Group']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'instruments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.Label']", 'through': "orm['gracedb.Labelling']", 'symmetrical': 'False'}),
+            'likelihood': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'nevents': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'uid': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'})
+        },
+        'gracedb.eventlog': {
+            'Meta': {'ordering': "['-created']", 'object_name': 'EventLog'},
+            'comment': ('django.db.models.fields.TextField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"})
+        },
+        'gracedb.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'managers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.User']", 'symmetrical': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '20'})
+        },
+        'gracedb.label': {
+            'Meta': {'object_name': 'Label'},
+            'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        'gracedb.labelling': {
+            'Meta': {'object_name': 'Labelling'},
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Label']"})
+        },
+        'gracedb.multiburstevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'MultiBurstEvent', '_ormbases': ['gracedb.Event']},
+            'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'bandwidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'central_freq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'confidence': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'ligo_angle': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_angle_sig': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'peak_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'peak_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'start_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'start_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'})
+        },
+        'gracedb.tag': {
+            'Meta': {'object_name': 'Tag'},
+            'displayName': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'eventlogs': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.EventLog']", 'symmetrical': 'False'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'gracedb.user': {
+            'Meta': {'ordering': "['name']", 'object_name': 'User'},
+            'dn': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'principal': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'unixid': ('django.db.models.fields.CharField', [], {'max_length': '25'})
+        }
+    }
+
+    complete_apps = ['gracedb']
diff --git a/gracedb/migrations/0005_stage1_rm_ligouser__add_new_foreign_keys.py b/gracedb/migrations/0005_stage1_rm_ligouser__add_new_foreign_keys.py
new file mode 100644
index 000000000..d521509d9
--- /dev/null
+++ b/gracedb/migrations/0005_stage1_rm_ligouser__add_new_foreign_keys.py
@@ -0,0 +1,199 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Removing M2M table for field managers on 'Group'
+        db.delete_table('gracedb_group_managers')
+
+        # Adding field 'Event.new_submitter'
+        db.add_column('gracedb_event', 'new_submitter',
+                      self.gf('django.db.models.fields.related.ForeignKey')(default=1, to=orm['auth.User']),
+                      keep_default=False)
+
+        # Adding field 'Labelling.new_creator'
+        db.add_column('gracedb_labelling', 'new_creator',
+                      self.gf('django.db.models.fields.related.ForeignKey')(default=1, to=orm['auth.User']),
+                      keep_default=False)
+
+        # Adding field 'Approval.new_approver'
+        db.add_column('gracedb_approval', 'new_approver',
+                      self.gf('django.db.models.fields.related.ForeignKey')(default=1, to=orm['auth.User']),
+                      keep_default=False)
+
+        # Adding field 'EventLog.new_issuer'
+        db.add_column('gracedb_eventlog', 'new_issuer',
+                      self.gf('django.db.models.fields.related.ForeignKey')(default=1, to=orm['auth.User']),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Adding M2M table for field managers on 'Group'
+        db.create_table('gracedb_group_managers', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('group', models.ForeignKey(orm['gracedb.group'], null=False)),
+            ('user', models.ForeignKey(orm['gracedb.user'], null=False))
+        ))
+        db.create_unique('gracedb_group_managers', ['group_id', 'user_id'])
+
+        # Deleting field 'Event.new_submitter'
+        db.delete_column('gracedb_event', 'new_submitter_id')
+
+        # Deleting field 'Labelling.new_creator'
+        db.delete_column('gracedb_labelling', 'new_creator_id')
+
+        # Deleting field 'Approval.new_approver'
+        db.delete_column('gracedb_approval', 'new_approver_id')
+
+        # Deleting field 'EventLog.new_issuer'
+        db.delete_column('gracedb_eventlog', 'new_issuer_id')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'gracedb.approval': {
+            'Meta': {'object_name': 'Approval'},
+            'approvedEvent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'approver': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'approvingCollaboration': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'new_approver': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'gracedb.coincinspiralevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'CoincInspiralEvent', '_ormbases': ['gracedb.Event']},
+            'combined_far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'end_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'end_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'mass': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'minimum_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'})
+        },
+        'gracedb.event': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'Event'},
+            'analysisType': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'gpstime': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Group']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'instruments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.Label']", 'through': "orm['gracedb.Labelling']", 'symmetrical': 'False'}),
+            'likelihood': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'nevents': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'new_submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'uid': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'})
+        },
+        'gracedb.eventlog': {
+            'Meta': {'ordering': "['-created']", 'object_name': 'EventLog'},
+            'comment': ('django.db.models.fields.TextField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'new_issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'gracedb.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '20'})
+        },
+        'gracedb.label': {
+            'Meta': {'object_name': 'Label'},
+            'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        'gracedb.labelling': {
+            'Meta': {'object_name': 'Labelling'},
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Label']"}),
+            'new_creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'gracedb.multiburstevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'MultiBurstEvent', '_ormbases': ['gracedb.Event']},
+            'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'bandwidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'central_freq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'confidence': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'ligo_angle': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_angle_sig': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'peak_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'peak_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'start_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'start_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'})
+        },
+        'gracedb.tag': {
+            'Meta': {'object_name': 'Tag'},
+            'displayName': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'eventlogs': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.EventLog']", 'symmetrical': 'False'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'gracedb.user': {
+            'Meta': {'ordering': "['name']", 'object_name': 'User'},
+            'dn': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'principal': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'unixid': ('django.db.models.fields.CharField', [], {'max_length': '25'})
+        }
+    }
+
+    complete_apps = ['gracedb']
diff --git a/gracedb/migrations/0006_stage2_rm_ligouser__populate_new_foreign_keys.py b/gracedb/migrations/0006_stage2_rm_ligouser__populate_new_foreign_keys.py
new file mode 100644
index 000000000..73200fa19
--- /dev/null
+++ b/gracedb/migrations/0006_stage2_rm_ligouser__populate_new_foreign_keys.py
@@ -0,0 +1,234 @@
+# -*- coding: utf-8 -*-
+from south.v2 import DataMigration
+import sys
+import re
+
+
+def get_auth_user_for_ligo_user_id(orm, userid):
+
+    LigoUser = orm['gracedb.User']
+    DjangoUser = orm['auth.User']
+
+    service_cert_pattern = re.compile(r'.*CN=([^/]+)/[^/]+')
+
+    try:
+        ligo_user = LigoUser.objects.get(id=userid)
+    except LigoUser.DoesNotExist:
+        print("Can't find Ligo User {0}. (this should not happen)".format(userid))
+        sys.exit(1)
+
+    try:
+        return DjangoUser.objects.get(username=ligo_user.unixid).id
+    except DjangoUser.DoesNotExist:
+        pass
+    try:
+        return DjangoUser.objects.get(username=ligo_user.principal)
+        return DjangoUser.objects.get(username="{0}@LIGO.ORG".format(ligo_user.unixid))
+    except DjangoUser.DoesNotExist:
+        pass
+
+    if ligo_user.unixid.lower() == 'none' or ligo_user.principal.lower() == 'none':
+        # Some service user, likely.
+        name = service_cert_pattern.match(ligo_user.dn).group(1)
+        return DjangoUser.objects.get(username=name)
+
+    print("Can't find Django user named '{0}'\nUnixid: {1}\nPrincipal: ({2})\nDN:({3})".
+            format(ligo_user.name, ligo_user.unixid, ligo_user.principal, ligo_user.dn))
+    sys.exit(1)
+
+
+def get_ligo_user_for_django_user_id(orm, django_id):
+    django_user = orm['auth.User'].objects.get(id=django_id)
+    return orm['gracedb.User'].objects.get(unixid=django_user.username)
+
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        Labelling = orm['gracedb.Labelling'].objects
+        Event = orm['gracedb.Event'].objects
+        EventLog = orm['gracedb.EventLog'].objects
+        Approval = orm['gracedb.Approval'].objects
+
+        # Collect pk's of ligouser entries references by foreignkeys
+        ids = set()
+        ids.update(Labelling.values_list('creator_id', flat=True).distinct())
+        ids.update(Event.values_list('submitter_id', flat=True).distinct())
+        ids.update(EventLog.values_list('issuer_id', flat=True).distinct())
+        ids.update(Approval.values_list('approver_id', flat=True).distinct())
+
+        # Update all ligouser foreign key references (ligo_id)
+        # to refer to djano users (django_id)
+        for ligo_id in ids:
+            django_id = get_auth_user_for_ligo_user_id(orm, ligo_id)
+            Labelling.filter(creator=ligo_id).update(new_creator=django_id)
+            Event.filter(submitter=ligo_id).update(new_submitter=django_id)
+            EventLog.filter(issuer=ligo_id).update(new_issuer=django_id)
+            Approval.filter(approver=ligo_id).update(new_approver=django_id)
+
+    def backwards(self, orm):
+        Labelling = orm['gracedb.Labelling'].objects
+        Event = orm['gracedb.Event'].objects
+        EventLog = orm['gracedb.EventLog'].objects
+        Approval = orm['gracedb.Approval'].objects
+
+        ids = set()
+        ids.update(Labelling.values_list('new_creator_id', flat=True).distinct())
+        ids.update(Event.values_list('new_submitter_id', flat=True).distinct())
+        ids.update(EventLog.values_list('new_issuer_id', flat=True).distinct())
+        ids.update(Approval.values_list('new_approver_id', flat=True).distinct())
+
+        for django_id in ids:
+            ligo_id = get_ligo_user_for_django_user_id(orm, django_id)
+            Labelling.filter(new_creator=django_id).update(creator=ligo_id)
+            Event.filter(new_submitter=django_id).update(submitter=ligo_id)
+            EventLog.filter(new_issuer=django_id).update(issuer=ligo_id)
+            Approval.filter(new_approver=django_id).update(approver=ligo_id)
+
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'gracedb.approval': {
+            'Meta': {'object_name': 'Approval'},
+            'approvedEvent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'approver': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'approvingCollaboration': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'new_approver': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'gracedb.coincinspiralevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'CoincInspiralEvent', '_ormbases': ['gracedb.Event']},
+            'combined_far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'end_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'end_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'mass': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'minimum_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'})
+        },
+        'gracedb.event': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'Event'},
+            'analysisType': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'gpstime': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Group']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'instruments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.Label']", 'through': "orm['gracedb.Labelling']", 'symmetrical': 'False'}),
+            'likelihood': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'nevents': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'new_submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'uid': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'})
+        },
+        'gracedb.eventlog': {
+            'Meta': {'ordering': "['-created']", 'object_name': 'EventLog'},
+            'comment': ('django.db.models.fields.TextField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'new_issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'gracedb.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '20'})
+        },
+        'gracedb.label': {
+            'Meta': {'object_name': 'Label'},
+            'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        'gracedb.labelling': {
+            'Meta': {'object_name': 'Labelling'},
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Label']"}),
+            'new_creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'gracedb.multiburstevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'MultiBurstEvent', '_ormbases': ['gracedb.Event']},
+            'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'bandwidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'central_freq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'confidence': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'ligo_angle': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_angle_sig': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'peak_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'peak_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'start_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'start_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'})
+        },
+        'gracedb.tag': {
+            'Meta': {'object_name': 'Tag'},
+            'displayName': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'eventlogs': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.EventLog']", 'symmetrical': 'False'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'gracedb.user': {
+            'Meta': {'ordering': "['name']", 'object_name': 'User'},
+            'dn': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'principal': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'unixid': ('django.db.models.fields.CharField', [], {'max_length': '25'})
+        }
+    }
+
+    complete_apps = ['gracedb']
+    symmetrical = True
diff --git a/gracedb/migrations/0007_stage3_rm_ligouser__remove_old_foreign_keys.py b/gracedb/migrations/0007_stage3_rm_ligouser__remove_old_foreign_keys.py
new file mode 100644
index 000000000..4abe2f201
--- /dev/null
+++ b/gracedb/migrations/0007_stage3_rm_ligouser__remove_old_foreign_keys.py
@@ -0,0 +1,192 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Remove old fields
+        db.delete_column('gracedb_event', 'submitter_id')
+        db.delete_column('gracedb_labelling', 'creator_id')
+        db.delete_column('gracedb_approval', 'approver_id')
+        db.delete_column('gracedb_eventlog', 'issuer_id')
+
+        #
+        # Move new_* to *  (and make them non-nullable)
+        #
+        db.rename_column('gracedb_event', 'new_submitter_id', 'submitter_id')
+        db.rename_column('gracedb_labelling', 'new_creator_id', 'creator_id')
+        db.rename_column('gracedb_approval', 'new_approver_id', 'approver_id')
+        db.rename_column('gracedb_eventlog', 'new_issuer_id', 'issuer_id')
+
+        db.alter_column('gracedb_event', 'submitter_id',
+            self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'],null=False))
+        db.alter_column('gracedb_labelling', 'creator_id',
+            self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'],null=False))
+        db.alter_column('gracedb_approval', 'approver_id',
+            self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'],null=False))
+        db.alter_column('gracedb_eventlog', 'issuer_id',
+            self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'],null=False))
+
+    def backwards(self, orm):
+        # Move * to new_*
+        db.rename_column('gracedb_event', 'submitter_id', 'new_submitter_id')
+        db.rename_column('gracedb_labelling', 'creator_id', 'new_creator_id')
+        db.rename_column('gracedb_approval', 'approver_id', 'new_approver_id')
+        db.rename_column('gracedb_eventlog', 'issuer_id', 'new_issuer_id')
+
+        # Replace old fields
+        db.add_column('gracedb_event', 'submitter',
+            self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.User'], null=False, default=1),
+            keep_default=False)
+        db.add_column('gracedb_labelling', 'creator',
+            self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.User'], null=False, default=1),
+            keep_default=False)
+        db.add_column('gracedb_approval', 'approver',
+            self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.User'], null=False, default=1),
+            keep_default=False)
+        db.add_column('gracedb_eventlog', 'issuer',
+            self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.User'], null=False, default=1),
+            keep_default=False)
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'gracedb.approval': {
+            'Meta': {'object_name': 'Approval'},
+            'approvedEvent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'approver': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+            'approvingCollaboration': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'gracedb.coincinspiralevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'CoincInspiralEvent', '_ormbases': ['gracedb.Event']},
+            'combined_far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'end_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'end_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'mass': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'minimum_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'})
+        },
+        'gracedb.event': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'Event'},
+            'analysisType': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'gpstime': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Group']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'instruments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.Label']", 'through': "orm['gracedb.Labelling']", 'symmetrical': 'False'}),
+            'likelihood': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'nevents': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+            'uid': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'})
+        },
+        'gracedb.eventlog': {
+            'Meta': {'ordering': "['-created']", 'object_name': 'EventLog'},
+            'comment': ('django.db.models.fields.TextField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'gracedb.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '20'})
+        },
+        'gracedb.label': {
+            'Meta': {'object_name': 'Label'},
+            'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        'gracedb.labelling': {
+            'Meta': {'object_name': 'Labelling'},
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Label']"})
+        },
+        'gracedb.multiburstevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'MultiBurstEvent', '_ormbases': ['gracedb.Event']},
+            'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'bandwidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'central_freq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'confidence': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'ligo_angle': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_angle_sig': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'peak_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'peak_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'start_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'start_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'})
+        },
+        'gracedb.tag': {
+            'Meta': {'object_name': 'Tag'},
+            'displayName': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'eventlogs': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.EventLog']", 'symmetrical': 'False'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'gracedb.user': {
+            'Meta': {'ordering': "['name']", 'object_name': 'User'},
+            'dn': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'principal': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'unixid': ('django.db.models.fields.CharField', [], {'max_length': '25'})
+        }
+    }
+
+    complete_apps = ['gracedb']
diff --git a/gracedb/migrations/0008_auto__del_user.py b/gracedb/migrations/0008_auto__del_user.py
new file mode 100644
index 000000000..697f18291
--- /dev/null
+++ b/gracedb/migrations/0008_auto__del_user.py
@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Deleting model 'User'
+        db.delete_table('gracedb_user')
+
+
+    def backwards(self, orm):
+        # Adding model 'User'
+        db.create_table('gracedb_user', (
+            ('dn', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('unixid', self.gf('django.db.models.fields.CharField')(max_length=25)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('email', self.gf('django.db.models.fields.EmailField')(max_length=75)),
+            ('principal', self.gf('django.db.models.fields.CharField')(max_length=100)),
+        ))
+        db.send_create_signal('gracedb', ['User'])
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'gracedb.approval': {
+            'Meta': {'object_name': 'Approval'},
+            'approvedEvent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'approver': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+            'approvingCollaboration': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'gracedb.coincinspiralevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'CoincInspiralEvent', '_ormbases': ['gracedb.Event']},
+            'combined_far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'end_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'end_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'mass': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'minimum_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'})
+        },
+        'gracedb.event': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'Event'},
+            'analysisType': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'gpstime': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Group']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'instruments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.Label']", 'through': "orm['gracedb.Labelling']", 'symmetrical': 'False'}),
+            'likelihood': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'nevents': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+            'uid': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'})
+        },
+        'gracedb.eventlog': {
+            'Meta': {'ordering': "['-created']", 'object_name': 'EventLog'},
+            'comment': ('django.db.models.fields.TextField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'gracedb.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '20'})
+        },
+        'gracedb.label': {
+            'Meta': {'object_name': 'Label'},
+            'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        'gracedb.labelling': {
+            'Meta': {'object_name': 'Labelling'},
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Label']"})
+        },
+        'gracedb.multiburstevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'MultiBurstEvent', '_ormbases': ['gracedb.Event']},
+            'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'bandwidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'central_freq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'confidence': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'ligo_angle': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_angle_sig': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'peak_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'peak_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'start_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'start_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'})
+        },
+        'gracedb.tag': {
+            'Meta': {'object_name': 'Tag'},
+            'displayName': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'eventlogs': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.EventLog']", 'symmetrical': 'False'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        }
+    }
+
+    complete_apps = ['gracedb']
\ No newline at end of file
diff --git a/gracedb/models.py b/gracedb/models.py
index e7c3afea9..7cceece02 100644
--- a/gracedb/models.py
+++ b/gracedb/models.py
@@ -3,6 +3,9 @@ from django.core.urlresolvers import reverse
 
 from model_utils.managers import InheritanceManager
 
+from django.contrib.auth.models import User as DjangoUser
+
+
 import datetime
 import thread
 import string
@@ -23,23 +26,22 @@ SERVER_TZ = pytz.timezone(settings.TIME_ZONE)
 #
 schema_version = "1.1"
 
-class User(models.Model):
-    name = models.CharField(max_length=100)
-    email = models.EmailField()
-    principal = models.CharField(max_length=100)
-    dn = models.CharField(max_length=100)
-    unixid = models.CharField(max_length=25)
+#class User(models.Model):
+    #name = models.CharField(max_length=100)
+    #email = models.EmailField()
+    #principal = models.CharField(max_length=100)
+    #dn = models.CharField(max_length=100)
+    #unixid = models.CharField(max_length=25)
 
-    class Meta:
-        ordering = ["name"]
+    #class Meta:
+        #ordering = ["name"]
 
-    def __unicode__(self):
-        return self.name
+    #def __unicode__(self):
+        #return self.name
 
 
 class Group(models.Model):
     name = models.CharField(max_length=20)
-    managers = models.ManyToManyField(User)
     def __unicode__(self):
         return self.name
 
@@ -68,7 +70,7 @@ class Event(models.Model):
     )
     DEFAULT_EVENT_NEIGHBORHOOD = (5,5)
 
-    submitter = models.ForeignKey(User)
+    submitter = models.ForeignKey(DjangoUser)
     created = models.DateTimeField(auto_now_add=True)
     group = models.ForeignKey(Group)
     uid = models.CharField(max_length=20, default="")  # XXX deprecated.  should be removed.
@@ -197,7 +199,7 @@ class EventLog(models.Model):
         ordering = ["-created"]
     event = models.ForeignKey(Event, null=False)
     created = models.DateTimeField(auto_now_add=True)
-    issuer = models.ForeignKey(User)
+    issuer = models.ForeignKey(DjangoUser)
     filename = models.CharField(max_length=100, default="")
     comment = models.TextField(null=False)
 
@@ -225,14 +227,14 @@ class EventLog(models.Model):
 class Labelling(models.Model):
     event = models.ForeignKey(Event)
     label = models.ForeignKey(Label)
-    creator = models.ForeignKey(User)
+    creator = models.ForeignKey(DjangoUser)
     created = models.DateTimeField(auto_now_add=True)
 
 # XXX Deprecated?  Is this used *anywhere*?
 # Appears to only be used in models.py.  Here and Event class as approval_set
 class Approval(models.Model):
     COLLABORATION_CHOICES = ( ('L','LIGO'), ('V','Virgo'), )
-    approver = models.ForeignKey(User)
+    approver = models.ForeignKey(DjangoUser)
     created = models.DateTimeField(auto_now_add=True)
     approvedEvent = models.ForeignKey(Event, null=False)
     approvingCollaboration = models.CharField(max_length=1, choices=COLLABORATION_CHOICES)
diff --git a/gracedb/views.py b/gracedb/views.py
index b78030ca3..9dfbcf7c3 100644
--- a/gracedb/views.py
+++ b/gracedb/views.py
@@ -1,6 +1,7 @@
 
 from django.http import HttpResponse
 from django.http import HttpResponseRedirect, HttpResponseNotFound, HttpResponseBadRequest, Http404
+from django.http import HttpResponseForbidden
 from django.template import RequestContext
 from django.core.urlresolvers import reverse, get_script_prefix
 from django.shortcuts import render_to_response, get_object_or_404
@@ -9,14 +10,17 @@ from django.utils.html import strip_tags, escape, urlize
 from django.utils.safestring import mark_safe
 
 from django.views.generic.list_detail import object_detail, object_list
+from django.contrib.auth.decorators import login_required
 
-from models import Event, Group, EventLog, Labelling, Label, User, Tag
+from models import Event, Group, EventLog, Labelling, Label, Tag
 from models import CoincInspiralEvent
 from models import MultiBurstEvent
 from forms import CreateEventForm, EventSearchForm, SimpleSearchForm
 from alert import issueAlert, issueAlertForLabel, issueAlertForUpdate
 from translator import handle_uploaded_data
 
+from django.contrib.auth.models import User
+
 import urllib
 
 from utils.vfile import VersionedFile
@@ -38,7 +42,7 @@ GRACEDB_DATA_DIR = settings.GRACEDB_DATA_DIR
 import json
 
 def index(request):
-#   assert request.ligouser
+#   assert request.user
     return render_to_response(
             'gracedb/index.html',
             {},
@@ -46,7 +50,7 @@ def index(request):
 
 def skyalert_authorized(request):
     try:
-        return request.ligouser.name in settings.SKYALERT_SUBMITTERS
+        return u"{0} {1}".format(request.user.first_name, request.user.last_name) in settings.SKYALERT_SUBMITTERS
     except:
         return False
 
@@ -120,7 +124,7 @@ def skyalert(request, graceid):
     request.session['flash_msg'] = flashmessage or message
 
     if createLogEntry:
-        logentry = EventLog(event=event, issuer=request.ligouser, comment=message)
+        logentry = EventLog(event=event, issuer=request.user, comment=message)
         logentry.save()
 
     return HttpResponseRedirect(reverse(view, args=[graceid]))
@@ -156,7 +160,7 @@ def create(request):
                     context_instance=RequestContext(request))
 
 def _create(request):
-    assert request.ligouser
+    assert request.user
 
     rv = {}
 
@@ -211,7 +215,7 @@ def _createEventFromForm(request, form):
             event = MultiBurstEvent()
         else:
             event = Event()
-        event.submitter = request.ligouser
+        event.submitter = request.user
         event.group = group[0]
         event.analysisType = atype
         #  ARGH.  We don't get a graceid until we save,
@@ -290,7 +294,7 @@ def _createLog(request, graceid, comment, uploadedFile=None):
         rdict['error'] = "Missing argument(s)"
     else:
         logEntry = EventLog(event=event,
-                            issuer=request.ligouser,
+                            issuer=request.user,
                             comment=comment)
         if uploadedFile:
             try:
@@ -335,7 +339,7 @@ def upload(request):
         #event issuer comment
         # XXX Note:  filename or comment oughta have a version
         log = EventLog(event=event,
-                       issuer=request.ligouser,
+                       issuer=request.user,
                        filename=uploadedfile.name,
                        comment=comment)
         try:
@@ -411,7 +415,7 @@ def cli_label(request):
     labelName = request.POST.get('label')
 
     doxmpp = request.POST.get('alert') == "True"
-    d = create_label(graceid, labelName, request.ligouser, doXMPP=doxmpp)
+    d = create_label(graceid, labelName, request.user, doXMPP=doxmpp)
 
     msg = str(d)
     response = HttpResponse(mimetype='application/json')
@@ -449,7 +453,7 @@ def logentry(request, graceid, num=None):
         raise Http404
     if request.method == "POST":
         # create a log entry
-        elog = EventLog(event=event, issuer=request.ligouser)
+        elog = EventLog(event=event, issuer=request.user)
         elog.comment = request.POST.get('comment') or request.GET.get('comment')
         elog.save()
         tagname = request.POST.get('tagname')
@@ -467,7 +471,7 @@ def logentry(request, graceid, num=None):
             num = elog.getN()
             msg = "Tagged message %s: %s " % (num, tagname)
             tlog = EventLog(event=event,
-                               issuer=request.ligouser,
+                               issuer=request.user,
                                comment=msg)
             tlog.save()
     else:
@@ -481,7 +485,7 @@ def logentry(request, graceid, num=None):
 
     rv = {}
     rv['comment'] = elog.comment
-    rv['issuer'] = elog.issuer.name
+    rv['issuer'] = elog.issuer.username
     rv['created'] = elog.created.isoformat()
     rv['comment'] = elog.comment
     if tagname:
@@ -509,7 +513,7 @@ def log(request):
         msg = "ERROR: Event '%s' does not exist" % graceid
     else:
         #event issuer comment
-        log = EventLog(event=event, issuer=request.ligouser, comment=message)
+        log = EventLog(event=event, issuer=request.user, comment=message)
         try:
             log.save()
             msg = "OK"
@@ -565,7 +569,7 @@ def view(request, graceid):
         context_instance=RequestContext(request))
 
 def cli_search(request):
-    assert request.ligouser
+    assert request.user
     form = SimpleSearchForm(request.POST)
     if form.is_valid():
         query = form.cleaned_data['query']
@@ -635,7 +639,8 @@ def assembleLigoLw(objects):
 
 
 def search(request, format=""):
-    assert request.ligouser
+    if not request.user or not request.user.is_authenticated():
+        return HttpResponseForbidden("Forbidden")
     # XXX DO NOT HARDCODE THIS
     # Also, user should be notified if their result hits this limit.
     limit = MAX_QUERY_RESULTS
@@ -734,7 +739,7 @@ def search(request, format=""):
             context_instance=RequestContext(request))
 
 def oldsearch(request):
-    assert request.ligouser
+    assert request.user
     if request.method == 'GET':
         form = EventSearchForm()
     else:
@@ -909,7 +914,7 @@ def flexigridResponse(request, objects):
               'cell': [ '<a href="%s">%s</a>' %
                             (reverse(view, args=[object.graceid()]), object.graceid()),
                          #Labels
-                        " ".join(["""<span onmouseover="tooltip.show(tooltiptext('%s', '%s', '%s'));" onmouseout="tooltip.hide();"  style="color: %s"> %s </span>""" % (label.label.name, label.creator.name, label.created, label.label.defaultColor, label.label.name)
+                        " ".join(["""<span onmouseover="tooltip.show(tooltiptext('%s', '%s', '%s'));" onmouseout="tooltip.hide();"  style="color: %s"> %s </span>""" % (label.label.name, label.creator.username, label.created, label.label.defaultColor, label.label.name)
                                 for label in object.labelling_set.all()]),
                         # Links to neighbors
                         ', '.join([
@@ -932,7 +937,7 @@ def flexigridResponse(request, objects):
                         #created_times['gps'],
                         created_times.get('utc',""),
 
-                        object.submitter.name,
+                        "{0} {1}".format(object.submitter.first_name, object.submitter.last_name)
 
                       ]
             }
@@ -992,7 +997,7 @@ def latest(request):
         form = SimpleSearchForm(request.POST)
 
     template = 'gracedb/latest.html'
-    if not request.ligouser:
+    if not request.user or not request.user.is_authenticated():
         limit = LimitedEvent
         template = 'gracedb/latest_public.html'
     else:
@@ -1048,7 +1053,7 @@ def taglogentry(request, graceid, num, tagname):
             # Create a log entry to document the tag creation.
             msg = "Tagged message %s: %s " % (num, tagname)
             logentry = EventLog(event=event,
-                               issuer=request.ligouser,
+                               issuer=request.user,
                                comment=msg)
             logentry.save()
     else:
diff --git a/ligoauth/__init__.py b/ligoauth/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/ligoauth/admin.py b/ligoauth/admin.py
new file mode 100644
index 000000000..a1b1a5bf3
--- /dev/null
+++ b/ligoauth/admin.py
@@ -0,0 +1,15 @@
+
+from django.contrib import admin
+from models import LocalUser, LigoLdapUser, X509Cert
+
+class LigoLdapUserAdmin(admin.ModelAdmin):
+    list_display = ['username', 'first_name', 'last_name']
+    search_fields = ['username']
+
+class X509CertAdmin(admin.ModelAdmin):
+    list_display = ['subject']
+    search_fields = ['subject']
+
+admin.site.register(LocalUser)
+admin.site.register(LigoLdapUser, LigoLdapUserAdmin)
+admin.site.register(X509Cert, X509CertAdmin)
diff --git a/ligoauth/fixtures/initial.json b/ligoauth/fixtures/initial.json
new file mode 100644
index 000000000..050de1d29
--- /dev/null
+++ b/ligoauth/fixtures/initial.json
@@ -0,0 +1,30 @@
+
+/C=IT/O=INFN/OU=Service/L=EGO/CN=MbtaAlert/lscgw.virgo.infn.it
+/C=IT/O=INFN/OU=Service/L=EGO/CN=MbtaAlert/olnode04.virgo.infn.it
+/C=IT/O=INFN/OU=Service/L=EGO/CN=MbtaAlert/olnode33.virgo.infn.it
+
+/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-grid.ligo-la.caltech.edu
+/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-grid.ligo-wa.caltech.edu
+/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-grid.ligo.caltech.edu
+/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev1.ligo-la.caltech.edu
+/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev1.ligo-wa.caltech.edu
+/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev1.ligo.caltech.edu
+/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev2.ligo-la.caltech.edu
+/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev2.ligo-wa.caltech.edu
+/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev2.ligo.caltech.edu
+/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev3.ligo.caltech.edu
+/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev4.ligo.caltech.edu
+
+/DC=org/DC=doegrids/OU=Services/CN=excesspower-processor/marlin.phys.uwm.edu
+
+/DC=org/DC=doegrids/OU=Services/CN=gdb-processor/marlin.phys.uwm.edu
+
+/DC=org/DC=doegrids/OU=Services/CN=gis/lscgis.phys.uwm.edu
+
+/DC=org/DC=doegrids/OU=Services/CN=luminrobot/ldas-pcdev1.ligo.caltech.edu
+
+/DC=org/DC=doegrids/OU=Services/CN=omegarobot/node499.ldas-cit.ligo.caltech.edu
+
+/DC=org/DC=doegrids/OU=Services/CN=waveburst/ldas-pcdev1.ligo.caltech.edu
+
+/DC=org/DC=ligo/O=LIGO/OU=Services/CN=gstlalcbc/ldas-pcdev1.ligo.caltech.edu
diff --git a/ligoauth/management/__init__.py b/ligoauth/management/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/ligoauth/management/commands/__init__.py b/ligoauth/management/commands/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/ligoauth/management/commands/refresh_users_from_ldap.py b/ligoauth/management/commands/refresh_users_from_ldap.py
new file mode 100644
index 000000000..4799e23ad
--- /dev/null
+++ b/ligoauth/management/commands/refresh_users_from_ldap.py
@@ -0,0 +1,72 @@
+
+from django.core.management.base import NoArgsCommand
+
+from ligoauth.models import LigoLdapUser, X509Cert
+
+import ldap
+
+baseDN = "ou=people,dc=ligo,dc=org"
+searchScope = ldap.SCOPE_SUBTREE
+searchFilter = "(employeeNumber=*)"
+retrieveAttributes = ["krbPrincipalName",
+                      "gridX509subject",
+                      "givenName",
+                      "sn",
+                      "mail",
+                      "isMemberOf"]
+
+class Command(NoArgsCommand):
+    help = "Update ligoauth.models.LigoUser and django.contrib.auth.models.User from LIGO LDAP"
+
+    def handle_noargs(self, **options):
+        l = ldap.open("ldap.ligo.org")
+        l.protocol_version = ldap.VERSION3
+        ldap_result_id = l.search(baseDN, searchScope, searchFilter, retrieveAttributes)
+        while 1:
+            result_type, result_data = l.result(ldap_result_id, 0)
+            if (result_data == []):
+                break
+            else:
+                if result_type == ldap.RES_SEARCH_ENTRY:
+                    for (ldap_dn, ldap_result) in result_data:
+
+                        first_name = unicode(ldap_result['givenName'][0], 'utf-8')
+                        last_name = unicode(ldap_result['sn'][0], 'utf-8')
+                        email = ldap_result['mail'][0]
+                        new_dns = set(ldap_result.get('gridX509subject',[]))
+                        is_active = "Communities:LVC:LVCGroupMembers" \
+                                    in ldap_result.get('isMemberOf',[])
+                        principal = ldap_result['krbPrincipalName'][0]
+
+                        # Update/Create LigoLdapUser entry
+                        user, created = LigoLdapUser.objects.get_or_create(ldap_dn=ldap_dn)
+
+                        changed = created \
+                                or (user.first_name != first_name) \
+                                or (user.last_name != last_name) \
+                                or (user.email != email) \
+                                or (user.username != principal) \
+                                or (user.is_active != is_active)
+
+                        if changed:
+                            user.first_name = first_name
+                            user.last_name = last_name
+                            user.email = email
+                            user.username = principal
+                            user.is_active = is_active
+                            # revoke staff/superuser if not active.
+                            user.is_staff = user.is_staff and is_active
+                            user.is_superuser = user.is_superuser and is_active
+                            user.save()
+
+                        # update X509 certs for user
+                        current_dns = set([ cert.subject for cert in user.x509cert_set.all() ])
+
+                        if current_dns != new_dns:
+                            for dn in current_dns - new_dns:
+                                X509Cert.objects.get(subject=dn).delete()
+                            for dn in new_dns - current_dns:
+                                cert, created = X509Cert.objects.get_or_create(subject=dn)
+                                if created:
+                                    cert.save()
+                                cert.users.add(user)
diff --git a/ligoauth/middleware/__init__.py b/ligoauth/middleware/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/ligoauth/middleware/auth.py b/ligoauth/middleware/auth.py
new file mode 100644
index 000000000..cf5ac5248
--- /dev/null
+++ b/ligoauth/middleware/auth.py
@@ -0,0 +1,128 @@
+
+import re
+from django.contrib.auth import authenticate
+from django.contrib.auth.models import User, AnonymousUser
+from django.contrib.auth.backends import RemoteUserBackend as DefaultRemoteUserBackend
+from ligoauth.models import certdn_to_user
+
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+
+from django.http import HttpResponseForbidden
+
+proxyPattern = re.compile(r'^(.*?)(/CN=\d+)*$')
+
+def cert_dn_from_request(request):
+    """Take a request, rummage through SSL_* headers, return the DN for the user."""
+    certdn = request.META.get('SSL_CLIENT_S_DN')
+    issuer = request.META.get('SSL_CLIENT_I_DN')
+
+    if not certdn:
+        try:
+            # mod_python is a little off...
+            # SSL info is in request._req
+            # Need to try/except because _req is
+            # not defined in WSGI request.
+            certdn = request._req.ssl_var_lookup ('SSL_CLIENT_S_DN')
+            issuer = request._req.ssl_var_lookup ('SSL_CLIENT_I_DN')
+            pass
+        except:
+            pass
+
+    if certdn and certdn.startswith(issuer):
+        # proxy.
+        # Proxies can be signed by proxies.
+        # Each level of "proxification" causes the subject
+        # to have a '/CN=[0-9]+ appended to the signers subject.
+        # These must be removed to discover the original identity's cert DN.
+        certdn = proxyPattern.match(issuer).group(1)
+
+    return certdn
+
+class LigoAuthMiddleware:
+    """This is the ultimate gatekeeper for GraceDb auth/authz.
+    Ideally, Apache will do all authentication and the GraceDb
+    Django code will do authorization.  That is for the future.
+    Today -- it all happens here."""
+
+    def process_request(self, request):
+        user = None
+
+        # An authenticated LIGO user will have one of these set.
+
+        remote_user = request.META.get('REMOTE_USER')
+        dn = cert_dn_from_request(request)
+
+        if remote_user:
+            user = authenticate(principal=remote_user)
+            if not (user and user.is_authenticated()):
+                # XXX THIS SHOULD NEVER HAPPEN
+                pass
+
+        if not user and dn:
+            user = authenticate(dn=dn)
+
+        if user and user.is_active:
+            # Ideal, normal case.  Yay!
+            pass
+        elif request.path.find('latest') > 0:
+            # We are on a "public" page.
+            # XXX Needs to be better refined. (dealt with elsewhere, in fact)
+            user = AnonymousUser()
+        elif user:
+            # Grotesque case.  User authenticates, but is not active.
+            # How is this?  Good credentials, but LDAP says you shouldn't
+            # have credentials?
+            user = None
+        else:
+            user = None
+
+        request.user = user
+
+        if user is None:
+            # Forbidden!
+            is_cli = request.POST.get('cli_version') or \
+                     request.GET.get('cli_version')
+            if is_cli:
+                message = "Your credentials are not valid."
+                return HttpResponseForbidden("{ 'error': '%s'  }" % message)
+            return render_to_response(
+                    'forbidden.html',
+                    {},
+                    context_instance=RequestContext(request))
+
+class RemoteUserBackend(DefaultRemoteUserBackend):
+    create_unknown_user = False
+
+class LigoX509Backend:
+
+    supports_object_permissions = False
+    supports_anonymous_user = False
+    supports_inactive_user = False
+
+    def authenticate(self, dn, username=None):
+        return certdn_to_user(dn)
+
+    def get_user(self, user_id):
+        try:
+            return User.objects.get(id=user_id)
+        except User.DoesNotExist:
+            return None
+
+class LigoShibBackend:
+
+    supports_object_permissions = False
+    supports_anonymous_user = False
+    supports_inactive_user = False
+
+    def authenticate(self, principal):
+        try:
+            return User.objects.get(username=principal)
+        except User.DoesNotExist:
+            return None
+
+    def get_user(self, user_id):
+        try:
+            return User.objects.get(id=user_id)
+        except User.DoesNotExist:
+            return None
diff --git a/ligoauth/migrations/0001_initial.py b/ligoauth/migrations/0001_initial.py
new file mode 100644
index 000000000..6b86b5cc7
--- /dev/null
+++ b/ligoauth/migrations/0001_initial.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'LigoLdapUser'
+        db.create_table('ligoauth_ligoldapuser', (
+            ('user_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True, primary_key=True)),
+            ('ldap_dn', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)),
+        ))
+        db.send_create_signal('ligoauth', ['LigoLdapUser'])
+
+        # Adding model 'LocalUser'
+        db.create_table('ligoauth_localuser', (
+            ('user_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True, primary_key=True)),
+        ))
+        db.send_create_signal('ligoauth', ['LocalUser'])
+
+        # Adding model 'X509Cert'
+        db.create_table('ligoauth_x509cert', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('subject', self.gf('django.db.models.fields.CharField')(max_length=200)),
+        ))
+        db.send_create_signal('ligoauth', ['X509Cert'])
+
+        # Adding M2M table for field users on 'X509Cert'
+        db.create_table('ligoauth_x509cert_users', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('x509cert', models.ForeignKey(orm['ligoauth.x509cert'], null=False)),
+            ('user', models.ForeignKey(orm['auth.user'], null=False))
+        ))
+        db.create_unique('ligoauth_x509cert_users', ['x509cert_id', 'user_id'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'LigoLdapUser'
+        db.delete_table('ligoauth_ligoldapuser')
+
+        # Deleting model 'LocalUser'
+        db.delete_table('ligoauth_localuser')
+
+        # Deleting model 'X509Cert'
+        db.delete_table('ligoauth_x509cert')
+
+        # Removing M2M table for field users on 'X509Cert'
+        db.delete_table('ligoauth_x509cert_users')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'ligoauth.ligoldapuser': {
+            'Meta': {'object_name': 'LigoLdapUser', '_ormbases': ['auth.User']},
+            'ldap_dn': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'ligoauth.localuser': {
+            'Meta': {'object_name': 'LocalUser', '_ormbases': ['auth.User']},
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'ligoauth.x509cert': {
+            'Meta': {'object_name': 'X509Cert'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'subject': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'})
+        }
+    }
+
+    complete_apps = ['ligoauth']
diff --git a/ligoauth/migrations/0002_remove_old_auth_users.py b/ligoauth/migrations/0002_remove_old_auth_users.py
new file mode 100644
index 000000000..aad2071b2
--- /dev/null
+++ b/ligoauth/migrations/0002_remove_old_auth_users.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        db.clear_table("auth_user")
+
+    def backwards(self, orm):
+        "Nothing to do"
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'ligoauth.ligoldapuser': {
+            'Meta': {'object_name': 'LigoLdapUser', '_ormbases': ['auth.User']},
+            'ldap_dn': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'ligoauth.localuser': {
+            'Meta': {'object_name': 'LocalUser', '_ormbases': ['auth.User']},
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'ligoauth.x509cert': {
+            'Meta': {'object_name': 'X509Cert'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'subject': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'})
+        }
+    }
+
+    complete_apps = ['ligoauth']
+    symmetrical = True
diff --git a/ligoauth/migrations/0003_utf8ify_user_tables.py b/ligoauth/migrations/0003_utf8ify_user_tables.py
new file mode 100644
index 000000000..de37a6458
--- /dev/null
+++ b/ligoauth/migrations/0003_utf8ify_user_tables.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import DataMigration
+
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        # XXX MySQL specific.
+        db.execute("ALTER TABLE ligoauth_ligoldapuser DEFAULT CHARACTER SET UTF8")
+        db.execute("ALTER TABLE ligoauth_ligoldapuser CONVERT TO CHARACTER SET UTF8")
+        db.execute("ALTER TABLE auth_user DEFAULT CHARACTER SET UTF8")
+        db.execute("ALTER TABLE auth_user CONVERT TO CHARACTER SET UTF8")
+
+    def backwards(self, orm):
+        # We can't go back!
+        pass
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.message': {
+            'Meta': {'object_name': 'Message'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'message': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'_message_set'", 'to': "orm['auth.User']"})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'ligoauth.ligoldapuser': {
+            'Meta': {'object_name': 'LigoLdapUser'},
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'ldap_dn': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}),
+            'principal': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'ligoauth.x509cert': {
+            'Meta': {'object_name': 'X509Cert'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ligouser': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ligoauth.LigoLdapUser']"}),
+            'subject': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+        }
+    }
+
+    complete_apps = ['auth', 'ligoauth']
+    symmetrical = True
diff --git a/ligoauth/migrations/0004_add_localusers.py b/ligoauth/migrations/0004_add_localusers.py
new file mode 100644
index 000000000..83fa02d0a
--- /dev/null
+++ b/ligoauth/migrations/0004_add_localusers.py
@@ -0,0 +1,197 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+users = [
+        {
+            'username' : 'MbtaAlert',
+            'first_name' : '',
+            'last_name' : 'MBTA Alert',
+            'email' : 'mours@lapp.in2p3.fr',
+            'dns' : [
+                "/C=IT/O=INFN/OU=Service/L=EGO/CN=MbtaAlert/lscgw.virgo.infn.it",
+                "/C=IT/O=INFN/OU=Service/L=EGO/CN=MbtaAlert/olnode04.virgo.infn.it",
+                "/C=IT/O=INFN/OU=Service/L=EGO/CN=MbtaAlert/olnode33.virgo.infn.it",
+            ]
+        },
+        {
+            'username' : 'detchar',
+            'first_name' : '',
+            'last_name' : 'Detchar',
+            'email' : 'pankow@gravity.phys.uwm.edu',
+            'dns' : [
+                "/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-grid.ligo-la.caltech.edu",
+                "/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-grid.ligo-wa.caltech.edu",
+                "/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-grid.ligo.caltech.edu",
+                "/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev1.ligo-la.caltech.edu",
+                "/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev1.ligo-wa.caltech.edu",
+                "/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev1.ligo.caltech.edu",
+                "/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev2.ligo-la.caltech.edu",
+                "/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev2.ligo-wa.caltech.edu",
+                "/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev2.ligo.caltech.edu",
+                "/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev3.ligo.caltech.edu",
+                "/DC=org/DC=doegrids/OU=Services/CN=detchar/ldas-pcdev4.ligo.caltech.edu",
+            ]
+        },
+        {
+            'username' : 'excesspower-processor',
+            'first_name' : '',
+            'last_name' : 'Excess Power Processor',
+            'email' : 'pankow@gravity.phys.uwm.edu',
+            'dns' : [
+                '/DC=org/DC=doegrids/OU=Services/CN=excesspower-processor/marlin.phys.uwm.edu',
+            ]
+        },
+        {
+            'username' : 'gdb-processor',
+            'first_name' : '',
+            'last_name' : 'GDB Processor',
+            'email' : 'gdb_processor@gravity.phys.uwm.edu',
+            'dns' : [
+                "/DC=org/DC=doegrids/OU=Services/CN=gdb-processor/marlin.phys.uwm.edu",
+            ]
+        },
+        {
+            'username' : 'gis',
+            'first_name' : '',
+            'last_name' : 'GIS',
+            'email' : 'xavier.amador@ligo.org',
+            'dns' : [
+                "/DC=org/DC=doegrids/OU=Services/CN=gis/lscgis.phys.uwm.edu",
+            ]
+        },
+        {
+            'username' : 'lumin',
+            'first_name' : '',
+            'last_name' : 'LUMIN',
+            'email' : 'bmoe@gravity.phys.uwm.edu',
+            'dns' : [
+                "/DC=org/DC=doegrids/OU=Services/CN=luminrobot/ldas-pcdev1.ligo.caltech.edu",
+            ]
+        },
+        {
+            'username' : 'omega',
+            'first_name' : '',
+            'last_name' : 'Omega Analysis',
+            'email' : '',
+            'dns' : [
+                "/DC=org/DC=doegrids/OU=Services/CN=omegarobot/node499.ldas-cit.ligo.caltech.edu",
+            ]
+        },
+        {
+            'username' : 'waveburst',
+            'first_name' : '',
+            'last_name' : 'Cwb Analysis',
+            'email' : 'bmoe@gravity.phys.uwm.edu',
+            'dns' : [
+                "/DC=org/DC=doegrids/OU=Services/CN=waveburst/ldas-pcdev1.ligo.caltech.edu",
+            ]
+        },
+        {
+            'username' : 'gstlalcbc',
+            'first_name' : '',
+            'last_name' : 'GstLal CBC',
+            'email' : 'chad.r.hanna@gmail.com',
+            'dns' : [
+                "/DC=org/DC=ligo/O=LIGO/OU=Services/CN=gstlalcbc/ldas-pcdev1.ligo.caltech.edu",
+            ]
+        },
+]
+
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        LocalUser = orm['ligoauth.LocalUser']
+        X509Cert = orm['ligoauth.X509Cert']
+
+        # Local Users
+        for entry in users:
+            user, created = LocalUser.objects.get_or_create(username=entry['username'])
+            if created:
+                user.first_name = entry['first_name']
+                user.last_name = entry['last_name']
+                user.email = entry['email']
+                user.is_active = True
+                user.is_staff = False
+                user.is_superuser = False
+                user.save()
+            current_dns = set([cert.subject for cert in user.x509cert_set.all()])
+            new_dns = set(entry['dns'])
+
+            missing_dns = new_dns - current_dns
+            redundant_dns = current_dns - new_dns
+
+            for dn in missing_dns:
+                cert, created = X509Cert.objects.get_or_create(subject=dn)
+                if created:
+                    cert.save()
+                cert.users.add(user)
+
+            for dn in redundant_dns:
+                cert = X509Cert.objects.get(subject=dn)
+                cert.users.remove(user)
+
+    def backwards(self, orm):
+        LocalUser = orm['ligoauth.LocalUser']
+        for user in LocalUser.objects.all():
+            user.delete()
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'ligoauth.ligoldapuser': {
+            'Meta': {'object_name': 'LigoLdapUser', '_ormbases': ['auth.User']},
+            'ldap_dn': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'ligoauth.localuser': {
+            'Meta': {'object_name': 'LocalUser', '_ormbases': ['auth.User']},
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'ligoauth.x509cert': {
+            'Meta': {'object_name': 'X509Cert'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'subject': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'})
+        }
+    }
+
+    complete_apps = ['ligoauth']
+    symmetrical = True
diff --git a/ligoauth/migrations/0005_add_ldapusers.py b/ligoauth/migrations/0005_add_ldapusers.py
new file mode 100644
index 000000000..91b89ef95
--- /dev/null
+++ b/ligoauth/migrations/0005_add_ldapusers.py
@@ -0,0 +1,148 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+import ldap
+
+
+def update_from_ldap(LigoLdapUser, X509Cert):
+    # Copied here from ligoauth/management/commands/refresh_from_ldap
+    # because it might not be there or be crazy different in the future.
+    # Of course, it might still be broken in the future anyway, but hey.
+    baseDN = "ou=people,dc=ligo,dc=org"
+    searchScope = ldap.SCOPE_SUBTREE
+    searchFilter = "(employeeNumber=*)"
+    retrieveAttributes = ["krbPrincipalName",
+                          "gridX509subject",
+                          "givenName",
+                          "sn",
+                          "mail",
+                          "isMemberOf"]
+
+    l = ldap.open("ldap.ligo.org")
+    l.protocol_version = ldap.VERSION3
+    ldap_result_id = l.search(baseDN, searchScope, searchFilter, retrieveAttributes)
+    while 1:
+        result_type, result_data = l.result(ldap_result_id, 0)
+        if (result_data == []):
+            break
+        else:
+            if result_type == ldap.RES_SEARCH_ENTRY:
+                for (ldap_dn, ldap_result) in result_data:
+
+                    first_name = unicode(ldap_result['givenName'][0], 'utf-8')
+                    last_name = unicode(ldap_result['sn'][0], 'utf-8')
+                    email = ldap_result['mail'][0]
+                    new_dns = set(ldap_result.get('gridX509subject',[]))
+                    is_active = "Communities:LVC:LVCGroupMembers" \
+                                in ldap_result.get('isMemberOf',[])
+                    principal = ldap_result['krbPrincipalName'][0]
+
+                    # Update/Create LigoLdapUser entry
+                    user, created = LigoLdapUser.objects.get_or_create(ldap_dn=ldap_dn)
+
+                    changed = created \
+                            or (user.first_name != first_name) \
+                            or (user.last_name != last_name) \
+                            or (user.email != email) \
+                            or (user.username != principal) \
+                            or (user.is_active != is_active)
+
+                    if changed:
+                        user.first_name = first_name
+                        user.last_name = last_name
+                        user.email = email
+                        user.username = principal
+                        user.is_active = is_active
+                        # revoke staff/superuser if not active.
+                        user.is_staff = user.is_staff and is_active
+                        user.is_superuser = user.is_superuser and is_active
+                        user.save()
+
+                    # update X509 certs for user
+                    current_dns = set([ cert.subject for cert in user.x509cert_set.all() ])
+
+                    if current_dns != new_dns:
+                        for dn in current_dns - new_dns:
+                            X509Cert.objects.get(subject=dn).delete()
+                        for dn in new_dns - current_dns:
+                            cert, created = X509Cert.objects.get_or_create(subject=dn)
+                            if created:
+                                cert.save()
+                            cert.users.add(user)
+
+class Migration(DataMigration):
+
+    needed_by = (
+                ("gracedb", "0005_stage1_rm_ligouser__add_new_foreign_keys"),
+                ("userprofile", "0002_stage1_rm_ligouser__add_new_foreign_key"),
+            )
+
+    def forwards(self, orm):
+        LigoLdapUser = orm['ligoauth.LigoLdapUser']
+        X509Cert = orm['ligoauth.X509Cert']
+        update_from_ldap(LigoLdapUser, X509Cert)
+
+    def backwards(self, orm):
+        LigoLdapUser = orm['ligoauth.LigoLdapUser']
+        for user in LigoLdapUser.objects.all():
+            user.delete()
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'ligoauth.ligoldapuser': {
+            'Meta': {'object_name': 'LigoLdapUser', '_ormbases': ['auth.User']},
+            'ldap_dn': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'ligoauth.localuser': {
+            'Meta': {'object_name': 'LocalUser', '_ormbases': ['auth.User']},
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'ligoauth.x509cert': {
+            'Meta': {'object_name': 'X509Cert'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'subject': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'})
+        }
+    }
+
+    complete_apps = ['ligoauth']
+    symmetrical = True
diff --git a/ligoauth/migrations/__init__.py b/ligoauth/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/ligoauth/models.py b/ligoauth/models.py
new file mode 100644
index 000000000..eb8efc716
--- /dev/null
+++ b/ligoauth/models.py
@@ -0,0 +1,88 @@
+
+from __future__ import unicode_literals
+
+from django.db import models
+from django.contrib.auth.models import User
+
+# There seems to be a LOT of duplication here and I don't know
+# if that is good or bad.  Normally, that seems bad...
+#
+# The thing is, LigoLdapUser and LocalUser (and whatever we might add later)
+# are actual entities that we want synched with the Django User, so they *are*
+# separate could conceivably have different first_name's, say, yet still refer
+# to the same (abstract) user entity.  Not likely, and not initially, but
+# conceivably.
+#
+# Whatever.  Just am not fully pleased with this.
+
+#from exceptions import NotImplementedError
+
+#class MetaUser(models.Model):
+    #class Meta:
+        #abstract = True
+
+    #first_name = models.CharField(max_length=50, blank=False, null=False, unique=True)
+    #last_name = models.CharField(max_length=50, blank=False, null=False, unique=True)
+    #email = models.EmailField()
+    #username = models.CharField(max_length=100, unique=True)
+    #is_active = models.BooleanField(default=True)
+    #auth_user = models.ForeignKey(User, null=False)
+
+    #def _get_or_create_auth_user(self):
+        #raise NotImplementedError(str(self.__class__) + " _get_auth_user")
+
+    #def save(self):
+        #user, created = self._get_auth_user()
+        #changed = created \
+                #or (user.first_name != self.first_name) \
+                #or (user.last_name != self.last_name) \
+                #or (user.email != self.email) \
+                #or (user.is_active == self.is_active)
+        #if changed:
+            #user.first_name = self.first_name
+            #user.last_name = self.last_name
+            #user.email = self.email
+            #user.is_active = self.is_active
+            #user.save()
+        #models.Model.save(self)
+
+class LigoLdapUser(User):
+    ldap_dn = models.CharField(max_length=100, null=False, unique=True)
+
+#   def _get_or_create_auth_user(self):
+#       return User.get_or_create(username=self.principal)
+
+    def name(self):
+        # XXX I really don't freaking understand WHY THIS SEEMS NECESSARY.
+        # print user.name()  gives an idiotic ascii coding error otherwise. WHY!?
+        return u"{0} {1}".format(self.first_name, self.last_name).encode('utf-8')
+
+
+class LocalUser(User):
+    pass
+
+class X509Cert(models.Model):
+    subject = models.CharField(max_length=200)
+    users = models.ManyToManyField(User)
+
+
+def shibid_to_user(shibid):
+    try:
+        return User.objects.get(username=shibid)
+    except User.DoesNotExist:
+        return None
+
+def certdn_to_user(dn, username=None):
+    try:
+        possible_users = X509Cert.objects.get(subject=dn).users
+    except:
+        return None
+
+    if username:
+        possible_users.filter(username=username)
+
+    try:
+        return possible_users.all()[0]
+    except IndexError:
+        return None
+
diff --git a/ligoauth/tests.py b/ligoauth/tests.py
new file mode 100644
index 000000000..501deb776
--- /dev/null
+++ b/ligoauth/tests.py
@@ -0,0 +1,16 @@
+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+
+class SimpleTest(TestCase):
+    def test_basic_addition(self):
+        """
+        Tests that 1 + 1 always equals 2.
+        """
+        self.assertEqual(1 + 1, 2)
diff --git a/ligoauth/views.py b/ligoauth/views.py
new file mode 100644
index 000000000..60f00ef0e
--- /dev/null
+++ b/ligoauth/views.py
@@ -0,0 +1 @@
+# Create your views here.
diff --git a/settings/default.py b/settings/default.py
index d3d09bbb4..d6620570b 100644
--- a/settings/default.py
+++ b/settings/default.py
@@ -169,9 +169,12 @@ TEMPLATE_CONTEXT_PROCESSORS = (
 )
 
 AUTHENTICATION_BACKENDS = (
-    'gracedb.middleware.auth.LigoAuthBackend',
-    'ligodjangoauth.LigoShibbolethAuthBackend',
-    'django.contrib.auth.backends.ModelBackend',
+#   'gracedb.middleware.auth.LigoAuthBackend',
+    'ligoauth.middleware.auth.LigoX509Backend',
+    'ligoauth.middleware.auth.LigoShibBackend',
+#   'ligoauth.middleware.auth.RemoteUserBackend',
+#   'ligodjangoauth.LigoShibbolethAuthBackend',
+#   'django.contrib.auth.backends.ModelBackend',
 )
 
 SHIB_AUTHENTICATION_SESSION_INITIATOR = 'https://moe.phys.uwm.edu/Shibboleth.sso/Login'
@@ -187,9 +190,10 @@ MIDDLEWARE_CLASSES = [
     'django.middleware.common.CommonMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
     'django.contrib.messages.middleware.MessageMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'ligodjangoauth.LigoShibbolethMiddleware',
-    'gracedb.middleware.auth.LigoAuthMiddleware',
+#   'django.contrib.auth.middleware.AuthenticationMiddleware',
+#   'ligodjangoauth.LigoShibbolethMiddleware',
+    'ligoauth.middleware.auth.LigoAuthMiddleware',
+#   'django.contrib.auth.middleware.RemoteUserMiddleware',
 ]
 
 ROOT_URLCONF = 'urls'
@@ -210,6 +214,7 @@ INSTALLED_APPS = (
     'django.contrib.staticfiles',
     'gracedb',
     'userprofile',
+    'ligoauth',
     'rest_framework',
     'south',
 )
diff --git a/settings/development.py b/settings/development.py
index 8569d0169..dbd0163ce 100644
--- a/settings/development.py
+++ b/settings/development.py
@@ -35,7 +35,7 @@ LATENCY_REPORT_WEB_PAGE_FILE_PATH = LATENCY_REPORT_DEST_DIR + "/latency.inc"
 UPTIME_REPORT_DIR = "/home/bmoe/data/uptime"
 
 
-SITE_ID = 4
+SITE_ID = 1
 
 TEMPLATE_DIRS = (
     # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
diff --git a/templates/admin/base_site.html b/templates/admin/base_site.html
index d2d77abe3..76e911a67 100644
--- a/templates/admin/base_site.html
+++ b/templates/admin/base_site.html
@@ -20,6 +20,7 @@
     <li id="nav-reports"><a href="{% url reports %}">Reports</a></li>
     <li id="nav-feeds"><a href="{% url feeds %}">RSS</a></li>
     <li id="nav-userprofile"><a href="{% url userprofile-home %}">Options</a></li>
-    {% if ligouser %}<li id="nav-user">Authenticated as: {{ ligouser.name }}</li>{% endif %}
+    {% if user %}<li id="nav-user">Authenticated as:
+                 {{ user.first_name }} {{ user.last_name }}</li>{% endif %}
 <ul>
 {% endblock %}
diff --git a/templates/base.html b/templates/base.html
index dc32d8af6..82a670103 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -37,7 +37,7 @@ function changeTime(obj, label) {
     <li id="nav-feeds"><a href="{% url feeds %}">RSS</a></li>
     <li id="nav-latest"><a href="{% url latest %}">Latest</a></li>
     <li id="nav-userprofile"><a href="{% url userprofile-home %}">Options</a></li>
-    {% if ligouser %}<li id="nav-user">Authenticated as: {{ ligouser.name }}</li>{% endif %}
+    {% if user %}<li id="nav-user">Authenticated as: {{ user.first_name }} {{user.last_name }}</li>{% endif %}
 </ul>
 <center>
     {% if config_name %}
@@ -78,9 +78,9 @@ function changeTime(obj, label) {
    </td>
 
    <td align="center">
-    {% if ligouser and ligouser.name == "Chad Hanna" %}
+    {% if user and user.username == "chad.hanna@LIGO.ORG" %}
         <img width="194" height="37" src="{{MEDIA_URL}}images/PI_Logo-anim.gif">
-    {% else %}{% if ligouser and ligouser.name == "Kipp Cannon" %}
+    {% else %}{% if user and user.username == "kipp.cannon@LIGO.ORG" %}
         <img width="194" height="37" src="{{MEDIA_URL}}images/CITA_logo-anim.gif">
     {% else %}
         <img width="107" height="37" src="{{MEDIA_URL}}images/anim2.gif">
diff --git a/templates/forbidden.html b/templates/forbidden.html
new file mode 100644
index 000000000..e383758b9
--- /dev/null
+++ b/templates/forbidden.html
@@ -0,0 +1,11 @@
+
+
+{% extends "base.html" %}
+
+{% block title %}403 &ndash; Forbidden{{ object.graceid }}{% endblock %}
+{% block heading %}Forbidden{{ object.graceid }}{% endblock %}
+
+{% block content %}
+    <p>The item you requested is not for you.</p> {{ message|safe }}
+{% endblock %}
+
diff --git a/templates/gracedb/create.html b/templates/gracedb/create.html
index 604421bf3..90573335e 100644
--- a/templates/gracedb/create.html
+++ b/templates/gracedb/create.html
@@ -10,7 +10,7 @@
 <form enctype="multipart/form-data" action="" method="POST">
 <table>
     {{ form }}
-    <tr><td>Submitter:</td><td>{{ ligouser }}</td></tr>
+    <tr><td>Submitter:</td><td>{{ user }}</td></tr>
     <tr><td><input type="submit" value="Submit"></td></tr>
 </table>
 </form>
diff --git a/templates/gracedb/event_detail.html b/templates/gracedb/event_detail.html
index b224a02ed..a8645d92e 100644
--- a/templates/gracedb/event_detail.html
+++ b/templates/gracedb/event_detail.html
@@ -491,7 +491,7 @@
                         <tr class="{% cycle 'odd' 'even'%}">
                            <td>{{log.getN}}</td>
                            <td>{{log.created|multiTime:"logtime"}}</td>
-                           <td>{{log.issuer}}</td>
+			   <td>{{log.issuer.first_name}} {{log.issuer.last_name}}</td>
                            <td>{{log.comment|sanitize}}
                                {% if log.fileurl %}
                                 <a href="{{log.fileurl}}">{{log.filename}}</a>
@@ -567,7 +567,7 @@
         <tr class="{% cycle 'odd' 'even'%}">
            <td>{{log.getN}}
            <td>{{log.created|multiTime:"logtime"}}</td>
-           <td>{{log.issuer}}</td>
+           <td>{{log.issuer.first_name}} {{ log.issuer.last_name}}</td>
   {% if object.getAvailableTags %}
            <td> 
               <table> 
diff --git a/templates/rest_framework/api.html b/templates/rest_framework/api.html
index 608e7f005..e89675dea 100644
--- a/templates/rest_framework/api.html
+++ b/templates/rest_framework/api.html
@@ -12,7 +12,7 @@ GraceDB &mdash; REST API
 {% endblock %}
 
 {% block userlinks %}
-    {% if ligouser %}<li>Authenticated as: {{ ligouser.name }}</li>{% endif %}
+   {% if user %}<li>Authenticated as: {{ user.first_name }} {{ user.last_name }}</li>{% endif %}
 {% endblock %}
 
     {% block breadcrumbs %}
diff --git a/userprofile/migrations/0001_initial.py b/userprofile/migrations/0001_initial.py
new file mode 100644
index 000000000..def4e474c
--- /dev/null
+++ b/userprofile/migrations/0001_initial.py
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'AnalysisType'
+        db.create_table('userprofile_analysistype', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('code', self.gf('django.db.models.fields.CharField')(unique=True, max_length=20)),
+            ('display', self.gf('django.db.models.fields.CharField')(unique=True, max_length=20)),
+        ))
+        db.send_create_signal('userprofile', ['AnalysisType'])
+
+        # Adding model 'Contact'
+        db.create_table('userprofile_contact', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.User'])),
+            ('desc', self.gf('django.db.models.fields.CharField')(max_length=20)),
+            ('email', self.gf('django.db.models.fields.EmailField')(max_length=75)),
+        ))
+        db.send_create_signal('userprofile', ['Contact'])
+
+        # Adding model 'Trigger'
+        db.create_table('userprofile_trigger', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.User'])),
+            ('triggerType', self.gf('django.db.models.fields.CharField')(max_length=20, blank=True)),
+            ('farThresh', self.gf('django.db.models.fields.FloatField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('userprofile', ['Trigger'])
+
+        # Adding M2M table for field labels on 'Trigger'
+        db.create_table('userprofile_trigger_labels', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('trigger', models.ForeignKey(orm['userprofile.trigger'], null=False)),
+            ('label', models.ForeignKey(orm['gracedb.label'], null=False))
+        ))
+        db.create_unique('userprofile_trigger_labels', ['trigger_id', 'label_id'])
+
+        # Adding M2M table for field atypes on 'Trigger'
+        db.create_table('userprofile_trigger_atypes', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('trigger', models.ForeignKey(orm['userprofile.trigger'], null=False)),
+            ('analysistype', models.ForeignKey(orm['userprofile.analysistype'], null=False))
+        ))
+        db.create_unique('userprofile_trigger_atypes', ['trigger_id', 'analysistype_id'])
+
+        # Adding M2M table for field contacts on 'Trigger'
+        db.create_table('userprofile_trigger_contacts', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('trigger', models.ForeignKey(orm['userprofile.trigger'], null=False)),
+            ('contact', models.ForeignKey(orm['userprofile.contact'], null=False))
+        ))
+        db.create_unique('userprofile_trigger_contacts', ['trigger_id', 'contact_id'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'AnalysisType'
+        db.delete_table('userprofile_analysistype')
+
+        # Deleting model 'Contact'
+        db.delete_table('userprofile_contact')
+
+        # Deleting model 'Trigger'
+        db.delete_table('userprofile_trigger')
+
+        # Removing M2M table for field labels on 'Trigger'
+        db.delete_table('userprofile_trigger_labels')
+
+        # Removing M2M table for field atypes on 'Trigger'
+        db.delete_table('userprofile_trigger_atypes')
+
+        # Removing M2M table for field contacts on 'Trigger'
+        db.delete_table('userprofile_trigger_contacts')
+
+
+    models = {
+        'gracedb.label': {
+            'Meta': {'object_name': 'Label'},
+            'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        'gracedb.user': {
+            'Meta': {'ordering': "['name']", 'object_name': 'User'},
+            'dn': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'principal': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'unixid': ('django.db.models.fields.CharField', [], {'max_length': '25'})
+        },
+        'userprofile.analysistype': {
+            'Meta': {'object_name': 'AnalysisType'},
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
+            'display': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'userprofile.contact': {
+            'Meta': {'object_name': 'Contact'},
+            'desc': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"})
+        },
+        'userprofile.trigger': {
+            'Meta': {'object_name': 'Trigger'},
+            'atypes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['userprofile.AnalysisType']", 'symmetrical': 'False', 'blank': 'True'}),
+            'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['userprofile.Contact']", 'symmetrical': 'False', 'blank': 'True'}),
+            'farThresh': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.Label']", 'symmetrical': 'False', 'blank': 'True'}),
+            'triggerType': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"})
+        }
+    }
+
+    complete_apps = ['userprofile']
\ No newline at end of file
diff --git a/userprofile/migrations/0002_stage1_rm_ligouser__add_new_foreign_key.py b/userprofile/migrations/0002_stage1_rm_ligouser__add_new_foreign_key.py
new file mode 100644
index 000000000..8143bf92b
--- /dev/null
+++ b/userprofile/migrations/0002_stage1_rm_ligouser__add_new_foreign_key.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'Trigger.new_user'
+        db.add_column('userprofile_trigger', 'new_user',
+                      self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True),
+                      keep_default=False)
+
+        # Adding field 'Contact.new_user'
+        db.add_column('userprofile_contact', 'new_user',
+                      self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Trigger.new_user'
+        db.delete_column('userprofile_trigger', 'new_user_id')
+
+        # Deleting field 'Contact.new_user'
+        db.delete_column('userprofile_contact', 'new_user_id')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'gracedb.label': {
+            'Meta': {'object_name': 'Label'},
+            'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        'gracedb.user': {
+            'Meta': {'ordering': "['name']", 'object_name': 'User'},
+            'dn': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'principal': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'unixid': ('django.db.models.fields.CharField', [], {'max_length': '25'})
+        },
+        'userprofile.analysistype': {
+            'Meta': {'object_name': 'AnalysisType'},
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
+            'display': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'userprofile.contact': {
+            'Meta': {'object_name': 'Contact'},
+            'desc': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'new_user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"})
+        },
+        'userprofile.trigger': {
+            'Meta': {'object_name': 'Trigger'},
+            'atypes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['userprofile.AnalysisType']", 'symmetrical': 'False', 'blank': 'True'}),
+            'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['userprofile.Contact']", 'symmetrical': 'False', 'blank': 'True'}),
+            'farThresh': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.Label']", 'symmetrical': 'False', 'blank': 'True'}),
+            'new_user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+            'triggerType': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"})
+        }
+    }
+
+    complete_apps = ['userprofile']
\ No newline at end of file
diff --git a/userprofile/migrations/0003_stage2_rm_ligouser__populate_new_foreign_key.py b/userprofile/migrations/0003_stage2_rm_ligouser__populate_new_foreign_key.py
new file mode 100644
index 000000000..5a8cc65dd
--- /dev/null
+++ b/userprofile/migrations/0003_stage2_rm_ligouser__populate_new_foreign_key.py
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+#import datetime
+#from south.db import db
+from south.v2 import DataMigration
+#from django.db import models
+import sys
+import re
+
+def get_auth_user_for_ligo_user_id(orm, userid):
+
+    LigoUser = orm['gracedb.User']
+    DjangoUser = orm['auth.User']
+
+    service_cert_pattern = re.compile(r'.*CN=([^/]+)/[^/]+')
+
+    try:
+        ligo_user = LigoUser.objects.get(id=userid)
+    except LigoUser.DoesNotExist:
+        print("Can't find Ligo User {0}. (this should not happen)".format(userid))
+        sys.exit(1)
+
+    try:
+        return DjangoUser.objects.get(username=ligo_user.unixid).id
+    except DjangoUser.DoesNotExist:
+        pass
+    try:
+        return DjangoUser.objects.get(username=ligo_user.principal)
+        return DjangoUser.objects.get(username="{0}@LIGO.ORG".format(ligo_user.unixid))
+    except DjangoUser.DoesNotExist:
+        pass
+
+    if ligo_user.unixid.lower() == 'none' or ligo_user.principal.lower() == 'none':
+        # Some service user, likely.
+        name = service_cert_pattern.match(ligo_user.dn).group(1)
+        return DjangoUser.objects.get(username=name)
+
+    print("Can't find Django user named '{0}'\nUnixid: {1}\nPrincipal: ({2})\nDN:({3})".
+            format(ligo_user.name, ligo_user.unixid, ligo_user.principal, ligo_user.dn))
+    sys.exit(1)
+
+#def get_auth_user_for_ligo_user_id(orm, userid):
+    #ligo_user = orm['gracedb.User'].objects.get(id=userid)
+    #return orm['auth.User'].objects.get(username=ligo_user.unixid).id
+
+def get_ligo_user_for_django_user_id(orm, django_id):
+    django_user = orm['auth.User'].objects.get(id=django_id)
+    return orm['gracedb.User'].objects.get(unixid=django_user.username)
+    return 1
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        ids = set()
+        ids.update(orm['userprofile.Contact'].objects.values_list('user_id', flat=True).distinct())
+        ids.update(orm['userprofile.Trigger'].objects.values_list('user_id', flat=True).distinct())
+
+        for ligo_id in ids:
+            django_id = get_auth_user_for_ligo_user_id(orm, ligo_id)
+            orm['userprofile.Contact'].objects.filter(user=ligo_id).update(new_user=django_id)
+            orm['userprofile.Trigger'].objects.filter(user=ligo_id).update(new_user=django_id)
+
+    def backwards(self, orm):
+        ids = set()
+
+        ids.update(orm['userprofile.Contact'].objects.values_list('new_user_id', flat=True).distinct())
+        ids.update(orm['userprofile.Trigger'].objects.values_list('new_user_id', flat=True).distinct())
+        for django_id in ids:
+            ligo_id = get_ligo_user_for_django_user_id(orm, django_id)
+            orm['userprofile.Contact'].objects.filter(new_user=django_id).update(user=ligo_id)
+            orm['userprofile.Trigger'].objects.filter(new_user=django_id).update(user=ligo_id)
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'gracedb.label': {
+            'Meta': {'object_name': 'Label'},
+            'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        'gracedb.user': {
+            'Meta': {'ordering': "['name']", 'object_name': 'User'},
+            'dn': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'principal': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'unixid': ('django.db.models.fields.CharField', [], {'max_length': '25'})
+        },
+        'userprofile.analysistype': {
+            'Meta': {'object_name': 'AnalysisType'},
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
+            'display': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'userprofile.contact': {
+            'Meta': {'object_name': 'Contact'},
+            'desc': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'new_user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"})
+        },
+        'userprofile.trigger': {
+            'Meta': {'object_name': 'Trigger'},
+            'atypes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['userprofile.AnalysisType']", 'symmetrical': 'False', 'blank': 'True'}),
+            'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['userprofile.Contact']", 'symmetrical': 'False', 'blank': 'True'}),
+            'farThresh': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.Label']", 'symmetrical': 'False', 'blank': 'True'}),
+            'new_user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+            'triggerType': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"})
+        }
+    }
+
+    complete_apps = ['userprofile']
+    symmetrical = True
diff --git a/userprofile/migrations/0004_stage3_rm_ligouser__remove_old_foreign_keys.py b/userprofile/migrations/0004_stage3_rm_ligouser__remove_old_foreign_keys.py
new file mode 100644
index 000000000..8bfc19126
--- /dev/null
+++ b/userprofile/migrations/0004_stage3_rm_ligouser__remove_old_foreign_keys.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+
+
+class Migration(SchemaMigration):
+    needed_by = (("gracedb","0008_auto__del_user"),)
+
+    def forwards(self, orm):
+        db.delete_column('userprofile_contact', 'user_id')
+        db.delete_column('userprofile_trigger', 'user_id')
+
+        db.rename_column('userprofile_contact', 'new_user_id', 'user_id')
+        db.rename_column('userprofile_trigger', 'new_user_id', 'user_id')
+
+    def backwards(self, orm):
+        db.rename_column('userprofile_contact', 'user_id', 'new_user_id')
+        db.rename_column('userprofile_trigger', 'user_id', 'new_user_id')
+
+        db.add_column('userprofile_contact', 'trigger',
+            self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.User'], null=False, default=1),
+            keep_default=False)
+        db.add_column('userprofile_trigger', 'trigger',
+            self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.User'], null=False, default=1),
+            keep_default=False)
+
+    models = {
+        'gracedb.label': {
+            'Meta': {'object_name': 'Label'},
+            'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        'gracedb.user': {
+            'Meta': {'ordering': "['name']", 'object_name': 'User'},
+            'dn': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'principal': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'unixid': ('django.db.models.fields.CharField', [], {'max_length': '25'})
+        },
+        'userprofile.analysistype': {
+            'Meta': {'object_name': 'AnalysisType'},
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
+            'display': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'userprofile.contact': {
+            'Meta': {'object_name': 'Contact'},
+            'desc': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"})
+        },
+        'userprofile.trigger': {
+            'Meta': {'object_name': 'Trigger'},
+            'atypes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['userprofile.AnalysisType']", 'symmetrical': 'False', 'blank': 'True'}),
+            'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['userprofile.Contact']", 'symmetrical': 'False', 'blank': 'True'}),
+            'farThresh': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.Label']", 'symmetrical': 'False', 'blank': 'True'}),
+            'triggerType': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"})
+        }
+    }
+
+    complete_apps = ['userprofile']
diff --git a/userprofile/migrations/__init__.py b/userprofile/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/userprofile/models.py b/userprofile/models.py
index 50eeecc12..95e9f66d0 100644
--- a/userprofile/models.py
+++ b/userprofile/models.py
@@ -1,7 +1,9 @@
 
 from django.db import models
 
-from gracedb.models import User, Label, Event
+from gracedb.models import Label, Event
+
+from django.contrib.auth.models import User
 
 
 #class Notification(models.Model):
@@ -33,6 +35,7 @@ def populateAnalysisType():
 
 class Contact(models.Model):
     user = models.ForeignKey(User, null=False)
+    #new_user = models.ForeignKey(DjangoUser, null=True)
     desc = models.CharField(max_length=20)
     email = models.EmailField()
 
@@ -42,6 +45,7 @@ class Contact(models.Model):
 class Trigger(models.Model):
     TYPES = ( ("create", "create"), ("change","change"), ("label","label") )
     user = models.ForeignKey(User, null=False)
+    #new_user = models.ForeignKey(DjangoUser, null=True)
     triggerType = models.CharField(max_length=20, choices=TYPES, blank=True)
     labels = models.ManyToManyField(Label, blank=True)
     atypes = models.ManyToManyField(AnalysisType, blank=True, verbose_name="Analysis Types")
diff --git a/userprofile/views.py b/userprofile/views.py
index 7256f500d..615793be4 100644
--- a/userprofile/views.py
+++ b/userprofile/views.py
@@ -13,8 +13,8 @@ from models import Trigger, Contact
 from forms import ContactForm, triggerFormFactory
 
 def index(request):
-    triggers = Trigger.objects.filter(user=request.ligouser)
-    contacts = Contact.objects.filter(user=request.ligouser)
+    triggers = Trigger.objects.filter(user=request.user)
+    contacts = Contact.objects.filter(user=request.user)
     d = { 'triggers' : triggers, 'contacts': contacts }
     return render_to_response('profile/notifications.html',
                               d,
@@ -24,10 +24,10 @@ def create(request):
     explanation = ""
     message = ""
     if request.method == "POST":
-        form = triggerFormFactory(request.POST, user=request.ligouser)
+        form = triggerFormFactory(request.POST, user=request.user)
         if form.is_valid():
             # Create the Trigger
-            t = Trigger(user=request.ligouser)
+            t = Trigger(user=request.user)
             labels = form.cleaned_data['labels']
             atypes = form.cleaned_data['atypes']
             contacts = form.cleaned_data['contacts']
@@ -56,7 +56,7 @@ def create(request):
             # hopefully, there are error messages in the form.
             pass
     else:
-        form = triggerFormFactory(user=request.ligouser)
+        form = triggerFormFactory(user=request.user)
     if message:
         request.session['flash_msg'] = message
     return render_to_response('profile/createNotification.html',
@@ -74,7 +74,7 @@ def delete(request, id):
         t = Trigger.objects.get(id=id)
     except Trigger.DoesNotExist:
         raise Http404
-    if request.ligouser != t.user:
+    if request.user != t.user:
         return HttpResponseForbidden("NO!")
     request.session['flash_msg'] = "Notification Deleted: %s" % t.userlessDisplay()
     t.delete()
@@ -90,7 +90,7 @@ def createContact(request):
         if form.is_valid():
             # Create the Contact
             c = Contact(
-                    user=request.ligouser,
+                    user=request.user,
                     desc = form.cleaned_data['desc'],
                     email = form.cleaned_data['email']
                 )
@@ -114,7 +114,7 @@ def deleteContact(request, id):
         c = Contact.objects.get(id=id)
     except Contact.DoesNotExist:
         raise Http404
-    if request.ligouser != c.user:
+    if request.user != c.user:
         return HttpResponseForbidden("NO!")
     request.session['flash_msg'] = "Notification Deleted: %s" % c
     c.delete()
-- 
GitLab