Skip to content
Snippets Groups Projects
Commit 7662aecc authored by Branson Craig Stephens's avatar Branson Craig Stephens
Browse files

Basic auth path and backend for LV-EM folks.

parent b1984f25
No related branches found
No related tags found
No related merge requests found
......@@ -59,3 +59,17 @@ def internal_user_required(view):
return HttpResponseForbidden("Forbidden")
return view(request, *args, **kwargs)
return inner
#-------------------------------------------------------------------------------
# A wrapper for views that checks whether the user is in the LV-EM group, and if not
# returns a 403.
#-------------------------------------------------------------------------------
def lvem_user_required(view):
@wraps(view)
def inner(request, *args, **kwargs):
# XXX Should probably move this list of internal groups into settings.
lvem_groups = [Group.objects.get(name='gw-astronomy:LV-EM')]
if not set(list(lvem_groups)) & set(list(request.user.groups.all())):
return HttpResponseForbidden("Forbidden")
return view(request, *args, **kwargs)
return inner
import re
from django.conf import settings
from django.contrib.auth import authenticate
from django.contrib.auth.models import User, AnonymousUser, Group
from django.contrib.auth.backends import RemoteUserBackend as DefaultRemoteUserBackend
......@@ -9,10 +10,14 @@ from ligoauth.models import certdn_to_user
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseForbidden
from django.http import HttpResponse, HttpResponseForbidden
proxyPattern = re.compile(r'^(.*?)(/CN=\d+)*$')
from datetime import datetime
from base64 import b64decode
import json
# XXX Hack. This will go away when we get the new perms infrastructure in place.
PUBLIC_URLS = [
'/',
......@@ -108,6 +113,19 @@ class LigoAuthMiddleware:
if not user and dn:
user = authenticate(dn=dn)
authn_header = request.META.get('HTTP_AUTHORIZATION', None)
if not user and 'apibasic' in request.path and authn_header:
user = authenticate(authn_header=authn_header)
# XXX Note: We are using date_joined to store the date
# when the password was set, not when the user joined up.
# This could cause some strange behavior if we ever want to
# actually use 'date_joined' for it's intended purpose.
# check: is now greater than date_joined + time_delta?
if user:
if datetime.now() > user.date_joined + settings.PASSWORD_EXPIRATION_TIME:
msg = "Your password has expired. Please log in and request another."
return HttpResponseForbidden(json.dumps({'error': msg}))
if user and user.is_active:
# Ideal, normal case. Yay!
pass
......@@ -134,6 +152,12 @@ class LigoAuthMiddleware:
if is_cli:
message = "Your credentials are not valid."
return HttpResponseForbidden("{ 'error': '%s' }" % message)
if 'apibasic' in request.path:
# The user was trying to get to the API exposed by basic auth. Send JSON with challenge.
msg = "Login failed: Incorrect username or password."
response = HttpResponse(json.dumps({'error': msg}), status=401)
response['WWW-Authenticate'] = 'Basic realm="/apibasic/"'
return response
return render_to_response(
'forbidden.html',
{'error': message},
......@@ -175,6 +199,47 @@ class LigoShibBackend:
except User.DoesNotExist:
return None
class LigoBasicBackend:
supports_object_permissions = False
supports_anonymous_user = False
supports_inactive_user = False
def authenticate(self, authn_header):
# dig out the username and password from the authn header
username = None
password = None
try:
authn_type, cred = authn_header.strip().split()
except:
return None
if authn_type.lower() != 'basic':
return None
try:
cred = b64decode(cred)
username, password = cred.split(':')
except:
return None
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
return None
if user.check_password(password):
return user
else:
return None
def get_user(self, user_id):
try:
return User.objects.get(id=user_id)
except User.DoesNotExist:
return None
class ModelBackend(DefaultModelBackend):
def authenticate(self, username=None, password=None, **kwargs):
return None
......@@ -229,6 +229,11 @@ LOGGING = {
'propagate': True,
'level': LOG_LEVEL,
},
'ligoauth': {
'handlers': ['debug_file'],
'propagate': True,
'level': LOG_LEVEL,
},
'middleware': {
'handlers': ['performance_file'],
'propagate': True,
......
......@@ -231,6 +231,7 @@ AUTHENTICATION_BACKENDS = (
# 'gracedb.middleware.auth.LigoAuthBackend',
'ligoauth.middleware.auth.LigoX509Backend',
'ligoauth.middleware.auth.LigoShibBackend',
'ligoauth.middleware.auth.LigoBasicBackend',
'ligoauth.middleware.auth.ModelBackend',
# 'ligoauth.middleware.auth.RemoteUserBackend',
# 'ligodjangoauth.LigoShibbolethAuthBackend',
......@@ -315,6 +316,9 @@ SOUTH_MIGRATION_MODULES = {
SOUTH_TESTS_MIGRATE = False
# passwords for LVEM scripted access expire after 365 days.
PASSWORD_EXPIRATION_TIME = timedelta(days=365)
# XXX The following Log settings are for a performance metric.
import logging
LOG_ROOT = '/home/gracedb/logs'
......
{% extends "base.html" %}
{% block title %}Options | Password Manager{% endblock %}
{% block heading %}Password Manager{% endblock %}
{% block pageid %}userprofile{% endblock %}
{% block content %}
<p> Passwords generated here are intended only for scripted access to GraceDB by LV-EM users. Your password allows access to the <a href={% url "basic:api-root" %}>REST API</a>. </p>
<p> Your username is: <span style="color: red"> {{ username }} </span> </p>
{% if password %}
<p> Your password is: <span style="color: red"> {{ password }} </span> </p>
{% endif %}
<br/>
<p> Press the button here to get a new password (or change your existing one): </p>
<form action={% url "userprofile-manage-password" %} method="post">
<input type="submit" value="Get me a password!">
</form>
<p> <b>Note:</b> Clicking this button has the effect of changing your password, and any old
passwords will no longer work. Also, passwords will expire after one year. </p>
{% endblock %}
{% extends "base.html" %}
{% block title %}Options | Notifications{% endblock %}
{% block heading %}Notifications{% endblock %}
{% block heading %}Notifications (LVC Users){% endblock %}
{% block pageid %}userprofile{% endblock %}
{% block content %}
......@@ -16,9 +16,10 @@
</ul>
{% endfor %}
<a href="{% url "userprofile-create" %}">Create New Notification</a>
<a href="{% url "userprofile-create" %}">Create New Notification (LVC users)</a>
<br/><br/>
<h2>Contacts</h2>
<h2>Contacts (LVC Users)</h2>
{% for contact in contacts %}
<ul>
<li>
......@@ -30,5 +31,10 @@
{% endfor %}
<a href="{% url "userprofile-create-contact" %}">Create New Contact</a>
<br/><br/>
<h2>Passwords for Scripted Access (LV-EM users)</h2>
<a href="{% url "userprofile-manage-password" %}">Manage Password</a>
{% endblock %}
......@@ -23,6 +23,7 @@ urlpatterns = patterns('',
(r'^events/', include('gracedb.urls')),
(r'^api/', include('gracedb.urls_rest', app_name="api", namespace="x509")),
(r'^apiweb/', include('gracedb.urls_rest', app_name="api", namespace="shib")),
(r'^apibasic/', include('gracedb.urls_rest', app_name="api", namespace="basic")),
(r'^options/', include('userprofile.urls')),
(r'^cli/create', 'gracedb.views.create'),
(r'^cli/ping', 'gracedb.cli_views.ping'),
......
# Changed for Django 1.6
#from django.conf.urls.defaults import *
from django.conf.urls import patterns, url, include
from django.conf.urls import patterns, url
urlpatterns = patterns('userprofile.views',
url (r'^$', 'index', name="userprofile-home"),
......@@ -14,11 +13,6 @@ urlpatterns = patterns('userprofile.views',
url (r'^trigger/delete/(?P<id>[\d]+)$', 'delete', name="userprofile-delete"),
url (r'^trigger/edit/(?P<id>[\d]+)$', 'edit', name="userprofile-edit"),
# (r'^view/(?P<uid>[\w\d]+)', 'view'),
# (r'^edit/(?P<uid>[\w\d]+)', 'edit'),
# (r'^request_archive/(?P<uid>[\w\d]+)(?P<rescind>/rescind)?', 'request_archive'),
# (r'^approve_archive/(?P<uid>[\w\d]+)(?P<rescind>/rescind)?', 'approve_archive'),
# url (r'^query', 'query', name="search"),
# url (r'^mine/$', 'mine', name="mine"),
# url (r'^myapprovals/$', 'myapprovals', name="myapprovals"),
url (r'^manage_password$', 'managePassword', name="userprofile-manage-password"),
)
......@@ -3,8 +3,8 @@ from django.http import HttpResponse
from django.http import HttpResponseRedirect, HttpResponseNotFound
from django.http import Http404, HttpResponseForbidden
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.template import RequestContext
from django.shortcuts import render_to_response
......@@ -12,9 +12,12 @@ from models import Trigger, Contact
from forms import ContactForm, triggerFormFactory
from gracedb.permission_utils import internal_user_required
from gracedb.permission_utils import internal_user_required, lvem_user_required
@internal_user_required
from datetime import datetime
# Let's let everybody onto the index view.
#@internal_user_required
def index(request):
triggers = Trigger.objects.filter(user=request.user)
contacts = Contact.objects.filter(user=request.user)
......@@ -23,6 +26,19 @@ def index(request):
d,
context_instance=RequestContext(request))
@lvem_user_required
def managePassword(request):
d = { 'username': request.user.username }
if request.method == "POST":
password = User.objects.make_random_password(length=20)
d['password'] = password
request.user.set_password(password)
request.user.date_joined = datetime.now()
request.user.save()
return render_to_response('profile/manage_password.html',
d,
context_instance=RequestContext(request))
@internal_user_required
def create(request):
explanation = ""
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment