django-mellon/mellon/utils.py

114 lines
3.5 KiB
Python

import re
import time
import datetime
import importlib
from functools import wraps
from xml.etree import ElementTree as ET
from django.core.urlresolvers import reverse
from django.template.loader import render_to_string
import lasso
from . import app_settings
METADATA = {}
def create_metadata(request):
entity_id = reverse('mellon_metadata')
if entity_id not in METADATA:
login_url = reverse('mellon_login')
logout_url = reverse('mellon_logout')
public_keys = []
for public_key in app_settings.PUBLIC_KEYS:
if public_key.startswith('/'):
public_key = file(public_key).read()
public_keys.append(public_key)
name_id_formats = app_settings.NAME_ID_FORMATS
return render_to_string('mellon/metadata.xml', {
'entity_id': request.build_absolute_uri(entity_id),
'login_url': request.build_absolute_uri(login_url),
'logout_url': request.build_absolute_uri(logout_url),
'public_keys': public_keys,
'name_id_formats': name_id_formats,
})
return METADATA[entity_id]
SERVERS = {}
def create_server(request):
root = request.build_absolute_uri('/')
if root not in SERVERS:
idps = app_settings.IDENTITY_PROVIDERS
metadata = create_metadata(request)
server = lasso.Server.newFromBuffers(metadata,
private_key_content=app_settings.PRIVATE_KEY,
private_key_password=app_settings.PRIVATE_KEY_PASSWORD)
for idp in idps:
metadata = idp['METADATA']
if metadata.startswith('/'):
metadata = file(metadata).read()
idp['ENTITY_ID'] = ET.fromstring(metadata).attrib['entityID']
server.addProviderFromBuffer(lasso.PROVIDER_ROLE_IDP, metadata)
SERVERS[root] = server
return SERVERS[root]
def create_login(request):
server = create_server(request)
login = lasso.Login(server)
if not app_settings.PRIVATE_KEY:
login.setSignatureHint(lasso.PROFILE_SIGNATURE_HINT_FORBID)
return login
def get_idp(entity_id):
for idp in app_settings.IDENTITY_PROVIDERS:
if idp['ENTITY_ID'] == entity_id:
return idp
def flatten_datetime(d):
for key, value in d.iteritems():
if isinstance(value, datetime.datetime):
d[key] = value.isoformat() + 'Z'
return d
def iso8601_to_datetime(date_string):
'''Convert a string formatted as an ISO8601 date into a time_t
value.
This function ignores the sub-second resolution'''
m = re.match(r'(\d+-\d+-\d+T\d+:\d+:\d+)(?:\.\d+)?Z$', date_string)
if not m:
raise ValueError('Invalid ISO8601 date')
tm = time.strptime(m.group(1)+'Z', "%Y-%m-%dT%H:%M:%SZ")
return datetime.datetime.fromtimestamp(time.mktime(tm))
def to_list(func):
@wraps(func)
def f(*args, **kwargs):
return list(func(*args, **kwargs))
return f
def import_object(path):
module, name = path.rsplit('.', 1)
module = importlib.import_module(module)
return getattr(module, name)
@to_list
def get_adapters(idp):
idp = idp or {}
adapters = idp.get('ADAPTER') or app_settings.ADAPTER
for adapter in adapters:
yield import_object(adapter)()
def get_values(saml_attributes, name):
values = saml_attributes.get(name)
if values is None:
return ()
if not isinstance(values, (list, tuple)):
return (values,)
return values
def get_parameter(idp, name):
return idp.get(name) or getattr(app_settings, name)