summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xipsilon/info/common.py42
-rwxr-xr-xipsilon/info/infoldap.py54
-rwxr-xr-xipsilon/info/nss.py55
-rwxr-xr-xipsilon/login/authldap.py10
-rwxr-xr-xipsilon/login/authtest.py3
-rwxr-xr-xipsilon/login/common.py12
-rwxr-xr-xipsilon/providers/openid/auth.py13
-rwxr-xr-xipsilon/providers/openidp.py2
-rwxr-xr-xipsilon/providers/saml2/auth.py37
-rw-r--r--templates/openid/consent_form.html4
10 files changed, 195 insertions, 37 deletions
diff --git a/ipsilon/info/common.py b/ipsilon/info/common.py
index c4be8fe..92a3ba2 100755
--- a/ipsilon/info/common.py
+++ b/ipsilon/info/common.py
@@ -47,6 +47,48 @@ class InfoProviderBase(PluginObject, Log):
self.debug('Info plugin disabled: %s' % self.name)
+class InfoMapping(Log):
+
+ def __init__(self):
+ self.standard_attributes = {
+ 'fullname': 'Full Name',
+ 'nickname': 'Nickname',
+ 'surname': 'Last Name',
+ 'firstname': 'First Name',
+ 'title': 'Title',
+ 'dob': 'Date of Birth',
+ 'email': 'E-mail Address',
+ 'gender': 'Gender',
+ 'postcode': 'Postal Code',
+ 'street': 'Street Address',
+ 'state': 'State or Province',
+ 'country': 'Country',
+ 'phone': 'Telephone Number',
+ 'language': 'Language',
+ 'timezone': 'Time Zone',
+ }
+ self.mapping = dict()
+
+ def set_mapping(self, attrs_map):
+ self.mapping = attrs_map
+
+ def display_name(self, name):
+ if name in self.standard_attributes:
+ return self.standard_attributes[name]
+ return name
+
+ def map_attrs(self, attrs):
+ s = dict()
+ e = dict()
+ for a in attrs:
+ if a in self.mapping:
+ s[self.mapping[a]] = attrs[a]
+ else:
+ e[a] = attrs[a]
+
+ return s, e
+
+
FACILITY = 'info_config'
diff --git a/ipsilon/info/infoldap.py b/ipsilon/info/infoldap.py
index 6d710bd..fb1c121 100755
--- a/ipsilon/info/infoldap.py
+++ b/ipsilon/info/infoldap.py
@@ -6,15 +6,33 @@
from ipsilon.info.common import InfoProviderBase
from ipsilon.info.common import InfoProviderInstaller
+from ipsilon.info.common import InfoMapping
from ipsilon.util.plugin import PluginObject
from ipsilon.util.log import Log
import ldap
+# TODO: fetch mapping from configuration
+ldap_mapping = {
+ 'cn': 'fullname',
+ 'commonname': 'fullname',
+ 'sn': 'surname',
+ 'mail': 'email',
+ 'destinationindicator': 'country',
+ 'postalcode': 'postcode',
+ 'st': 'state',
+ 'statetorprovincename': 'state',
+ 'streetaddress': 'street',
+ 'telephonenumber': 'phone',
+}
+
+
class InfoProvider(InfoProviderBase, Log):
def __init__(self):
super(InfoProvider, self).__init__()
+ self.mapper = InfoMapping()
+ self.mapper.set_mapping(ldap_mapping)
self.name = 'ldap'
self.description = """
Info plugin that uses LDAP to retrieve user data. """
@@ -92,24 +110,48 @@ Info plugin that uses LDAP to retrieve user data. """
return conn
- def get_user_data_from_conn(self, conn, dn):
+ def _get_user_data(self, conn, dn):
result = conn.search_s(dn, ldap.SCOPE_BASE)
if result is None or result == []:
raise Exception('User object could not be found!')
elif len(result) > 1:
raise Exception('No unique user object could be found!')
- return result[0][1]
+ data = dict()
+ for name, value in result[0][1].iteritems():
+ if type(value) is list and len(value) == 1:
+ value = value[0]
+ data[name] = value
+ return data
+
+ def _get_user_groups(self, conn, dn, ldapattrs):
+ # TODO: fixme to support RFC2307bis schemas
+ if 'memberuid' in ldapattrs:
+ return ldapattrs['memberuid']
+ else:
+ return []
+
+ def get_user_data_from_conn(self, conn, dn):
+ reply = dict()
+ try:
+ ldapattrs = self._get_user_data(conn, dn)
+ userattrs, extras = self.mapper.map_attrs(ldapattrs)
+ groups = self._get_user_groups(conn, dn, ldapattrs)
+ reply['userdata'] = userattrs
+ reply['groups'] = groups
+ reply['extras'] = {'ldap': extras}
+ except Exception, e: # pylint: disable=broad-except
+ self.error(e)
+
+ return reply
def get_user_attrs(self, user):
- userattrs = None
try:
conn = self._ldap_bind()
dn = self.user_dn_tmpl % {'username': user}
- userattrs = self.get_user_data_from_conn(conn, dn)
+ return self.get_user_data_from_conn(conn, dn)
except Exception, e: # pylint: disable=broad-except
self.error(e)
-
- return userattrs
+ return {}
class Installer(InfoProviderInstaller):
diff --git a/ipsilon/info/nss.py b/ipsilon/info/nss.py
index e9a3a96..4208442 100755
--- a/ipsilon/info/nss.py
+++ b/ipsilon/info/nss.py
@@ -6,27 +6,70 @@
from ipsilon.info.common import InfoProviderBase
from ipsilon.info.common import InfoProviderInstaller
+from ipsilon.info.common import InfoMapping
from ipsilon.util.plugin import PluginObject
+import grp
import pwd
+import os
+
+
+posix_map = {
+ 'gecos': 'fullname'
+}
class InfoProvider(InfoProviderBase):
def __init__(self):
super(InfoProvider, self).__init__()
+ self.mapper = InfoMapping()
+ self.mapper.set_mapping(posix_map)
self.name = 'nss'
+ def _get_posix_user(self, user):
+ p = pwd.getpwnam(user)
+ return {'username': p.pw_name, 'uidNumber': p.pw_uid,
+ 'gidNumber': p.pw_gid, 'gecos': p.pw_gecos,
+ 'homeDirectory': p.pw_dir, 'loginShell': p.pw_shell}
+
+ def _get_posix_groups(self, user, group):
+ groups = set()
+ getgrouplist = getattr(os, 'getgrouplist', None)
+ if getgrouplist:
+ ids = getgrouplist(user, group)
+ for i in ids:
+ try:
+ g = grp.getgrgid(i)
+ groups.add(g.gr_name)
+ except KeyError:
+ pass
+
+ else:
+ g = grp.getgrgid(group)
+ groups.add(g.gr_name)
+
+ allg = grp.getgrall()
+ for g in allg:
+ if user in g.gr_mem:
+ groups.add(g.gr_name)
+
+ return list(groups)
+
def get_user_attrs(self, user):
- userattrs = None
+ reply = dict()
try:
- p = pwd.getpwnam(user)
- userattrs = {'uidNumber': p[2], 'gidNumber': p[3],
- 'gecos': p[4], 'homeDirectory': p[5],
- 'loginShell': p[6]}
+ posix_user = self._get_posix_user(user)
+ userattrs, extras = self.mapper.map_attrs(posix_user)
+ groups = self._get_posix_groups(posix_user['username'],
+ posix_user['gidNumber'])
+ reply['userdata'] = userattrs
+ reply['groups'] = groups
+ reply['extras'] = {'posix': extras}
+
except KeyError:
pass
- return userattrs
+ return reply
class Installer(InfoProviderInstaller):
diff --git a/ipsilon/login/authldap.py b/ipsilon/login/authldap.py
index 0d70479..a41d167 100755
--- a/ipsilon/login/authldap.py
+++ b/ipsilon/login/authldap.py
@@ -64,7 +64,15 @@ class LDAP(LoginFormBase, Log):
if username and password:
try:
- userattrs = self._authenticate(username, password)
+ userdata = self._authenticate(username, password)
+ if userdata:
+ userattrs = dict()
+ for d, v in userdata.get('userdata', {}).items():
+ userattrs[d] = v
+ if 'groups' in userdata:
+ userattrs['groups'] = userdata['groups']
+ if 'extras' in userdata:
+ userattrs['extras'] = userdata['extras']
authed = True
except Exception, e: # pylint: disable=broad-except
errmsg = "Authentication failed"
diff --git a/ipsilon/login/authtest.py b/ipsilon/login/authtest.py
index 55b30a4..44492a4 100755
--- a/ipsilon/login/authtest.py
+++ b/ipsilon/login/authtest.py
@@ -33,8 +33,9 @@ class TestAuth(LoginFormBase):
if username and password:
if password == 'ipsilon':
cherrypy.log("User %s successfully authenticated." % username)
+ testdata = {'fullname': 'Test User %s' % username}
return self.lm.auth_successful(self.trans,
- username, 'password')
+ username, 'password', testdata)
else:
cherrypy.log("User %s failed authentication." % username)
error = "Authentication failed"
diff --git a/ipsilon/login/common.py b/ipsilon/login/common.py
index 2fee357..6231997 100755
--- a/ipsilon/login/common.py
+++ b/ipsilon/login/common.py
@@ -49,9 +49,17 @@ class LoginManagerBase(PluginObject, Log):
if self.info:
userattrs = self.info.get_user_attrs(username)
if userdata:
- userdata.update(userattrs or {})
+ userdata.update(userattrs.get('userdata', {}))
else:
- userdata = userattrs
+ userdata = userattrs.get('userdata', {})
+
+ # merge groups and extras from login plugin and info plugin
+ userdata['groups'] = list(set(userdata.get('groups', []) +
+ userattrs.get('groups', [])))
+
+ userdata['extras'] = userdata.get('extras', {})
+ userdata['extras'].update(userattrs.get('extras', {}))
+
self.debug("User %s attributes: %s" % (username, repr(userdata)))
if auth_type:
diff --git a/ipsilon/providers/openid/auth.py b/ipsilon/providers/openid/auth.py
index abf19ae..868daf1 100755
--- a/ipsilon/providers/openid/auth.py
+++ b/ipsilon/providers/openid/auth.py
@@ -162,17 +162,16 @@ class AuthenticateRequest(ProviderPageBase):
'openid_request': json.dumps(kwargs)}
self.trans.store(data)
- # Add extension data to this list of dictionaries
- ad = [
- {
- "Trust Root": request.trust_root,
- },
- ]
+ # Add extension data to this dictionary
+ ad = {
+ "Trust Root": request.trust_root,
+ }
userattrs = us.get_user_attrs()
for n, e in self.cfg.extensions.items():
data = e.get_display_data(request, userattrs)
self.debug('%s returned %s' % (n, repr(data)))
- ad.append(data)
+ for key, value in data.items():
+ ad[self.cfg.mapping.display_name(key)] = value
context = {
"title": 'Consent',
diff --git a/ipsilon/providers/openidp.py b/ipsilon/providers/openidp.py
index 2e41050..a3e1b63 100755
--- a/ipsilon/providers/openidp.py
+++ b/ipsilon/providers/openidp.py
@@ -9,6 +9,7 @@ from ipsilon.providers.common import FACILITY
from ipsilon.providers.openid.auth import OpenID
from ipsilon.providers.openid.extensions.common import LoadExtensions
from ipsilon.util.plugin import PluginObject
+from ipsilon.info.common import InfoMapping
from openid.server.server import Server
# TODO: Move this to the database
@@ -19,6 +20,7 @@ class IdpProvider(ProviderBase):
def __init__(self):
super(IdpProvider, self).__init__('openid', 'openid')
+ self.mapping = InfoMapping()
self.page = None
self.server = None
self.basepath = None
diff --git a/ipsilon/providers/saml2/auth.py b/ipsilon/providers/saml2/auth.py
index cbfeaaa..87f4ac8 100755
--- a/ipsilon/providers/saml2/auth.py
+++ b/ipsilon/providers/saml2/auth.py
@@ -210,18 +210,33 @@ class AuthenticateRequest(ProviderPageBase):
if not attrstat.attribute:
attrstat.attribute = ()
- attributes = us.get_user_attrs()
+ attributes = dict()
+ userattrs = us.get_user_attrs()
+ for key, value in userattrs.get('userdata', {}).iteritems():
+ if type(value) is str:
+ attributes[key] = value
+ if 'groups' in userattrs:
+ attributes['group'] = userattrs['groups']
+ for _, info in userattrs.get('extras', {}).iteritems():
+ for key, value in info.items():
+ attributes[key] = value
+
for key in attributes:
- attr = lasso.Saml2Attribute()
- attr.name = key
- attr.nameFormat = lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC
- value = str(attributes[key]).encode('utf-8')
- node = lasso.MiscTextNode.newWithString(value)
- node.textChild = True
- attrvalue = lasso.Saml2AttributeValue()
- attrvalue.any = [node]
- attr.attributeValue = [attrvalue]
- attrstat.attribute = attrstat.attribute + (attr,)
+ values = attributes[key]
+ if type(values) is not list:
+ values = [values]
+ for value in values:
+ attr = lasso.Saml2Attribute()
+ attr.name = key
+ attr.nameFormat = lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC
+ value = str(value).encode('utf-8')
+ self.debug('value %s' % value)
+ node = lasso.MiscTextNode.newWithString(value)
+ node.textChild = True
+ attrvalue = lasso.Saml2AttributeValue()
+ attrvalue.any = [node]
+ attr.attributeValue = [attrvalue]
+ attrstat.attribute = attrstat.attribute + (attr,)
self.debug('Assertion: %s' % login.assertion.dump())
diff --git a/templates/openid/consent_form.html b/templates/openid/consent_form.html
index 8c3813e..e8ead1d 100644
--- a/templates/openid/consent_form.html
+++ b/templates/openid/consent_form.html
@@ -11,13 +11,11 @@
<form class="form-horizontal" role="form" id="consent_form" action="{{ action }}" method="post" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="ipsilon_transaction_id" id="ipsilon_transaction_id" value="{{ ipsilon_transaction_id }}">
<div class="alert alert-danger">
-{%- for items in authz_details %}
- {%- for item in items|dictsort %}
+{%- for item in authz_details|dictsort %}
<div class="form-group">
<div class="col-sm-10 col-md-10">{{ item[0] }}:</div>
<div class="col-sm-10 col-md-10">{{ item[1] }}</div>
</div>
- {%- endfor %}
{%- endfor %}
</div>
<div class="form-group">