parent
85cc82c4e2
commit
5294fd40c4
|
@ -0,0 +1,93 @@
|
|||
# vim:et:sw=4
|
||||
#
|
||||
# CUD Schema
|
||||
#
|
||||
# Date : 20140908
|
||||
# Révision :
|
||||
# * 20140908 - Jérôme Schneider <jschneider@entrouvert.com> - Création initiale
|
||||
#
|
||||
|
||||
objectIdentifier EoRoot 1.3.6.1.4.1.36560
|
||||
objectIdentifier EoClientRoot EoRoot:3
|
||||
objectIdentifier MandayeRoot EoClientRoot:1127
|
||||
objectIdentifier MandayeLdap MandayeRoot:1
|
||||
objectIdentifier MandayeLdapAttributes MandayeLdap:1
|
||||
objectIdentifier MandayeLdapObjectClass MandayeLdap:2
|
||||
|
||||
objectIdentifier Int 1.3.6.1.4.1.1466.115.121.1.27
|
||||
objectIdentifier UTF8 1.3.6.1.4.1.1466.115.121.1.15
|
||||
objectIdentifier Boolean 1.3.6.1.4.1.1466.115.121.1.7
|
||||
objectIdentifier Binary 1.3.6.1.4.1.1466.115.121.1.40
|
||||
objectIdentifier DateTime 1.3.6.1.4.1.1466.115.121.1.24
|
||||
objectIdentifier IA5String 1.3.6.1.4.1.1466.115.121.1.26
|
||||
|
||||
attributetype (MandayeLdapAttributes:1
|
||||
NAME 'uniqueID'
|
||||
DESC 'Mandaye user unique identifier'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SYNTAX UTF8
|
||||
SINGLE-VALUE )
|
||||
|
||||
attributetype (MandayeLdapAttributes:2
|
||||
NAME 'idpUniqueID'
|
||||
DESC 'IDP unique id (like NameID)'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX UTF8
|
||||
SINGLE-VALUE )
|
||||
|
||||
attributetype (MandayeLdapAttributes:3
|
||||
NAME 'idpName'
|
||||
DESC 'Name of the idp'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SYNTAX UTF8
|
||||
SINGLE-VALUE )
|
||||
|
||||
attributetype (MandayeLdapAttributes:4
|
||||
NAME 'spLogin'
|
||||
DESC 'SP login'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SYNTAX UTF8
|
||||
SINGLE-VALUE )
|
||||
|
||||
attributetype (MandayeLdapAttributes:5
|
||||
NAME 'spPostValues'
|
||||
DESC 'SP post values'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SYNTAX UTF8
|
||||
SINGLE-VALUE )
|
||||
|
||||
attributetype (MandayeLdapAttributes:6
|
||||
NAME 'spName'
|
||||
DESC 'name of the service provider'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SYNTAX UTF8
|
||||
SINGLE-VALUE )
|
||||
|
||||
attributetype (MandayeLdapAttributes:7
|
||||
NAME 'creationDate'
|
||||
DESC 'creation date of the association (ISO8601 format)'
|
||||
EQUALITY generalizedTimeMatch
|
||||
SYNTAX DateTime
|
||||
SINGLE-VALUE )
|
||||
|
||||
attributetype (MandayeLdapAttributes:8
|
||||
NAME 'lastConnectionDate'
|
||||
DESC 'Last connection date ISO8601'
|
||||
EQUALITY generalizedTimeMatch
|
||||
ORDERING generalizedTimeOrderingMatch
|
||||
SYNTAX DateTime
|
||||
SINGLE-VALUE )
|
||||
|
||||
#
|
||||
# Classes d'objets:
|
||||
#
|
||||
|
||||
objectclass (MandayeLdapObjectClass:1
|
||||
NAME 'MandayeUser'
|
||||
DESC 'Mandaye user Objectclass'
|
||||
SUP top
|
||||
STRUCTURAL
|
||||
MUST ( uniqueID $ idpUniqueId $ idpName $ spLogin $ spPostValues $ creationDate $ spName )
|
||||
MAY ( lastConnectionDate ) )
|
||||
|
|
@ -28,6 +28,11 @@ if config.storage_backend == "mandaye.backends.sql":
|
|||
bind=create_engine(config.db_url)
|
||||
)
|
||||
)
|
||||
elif config.storage_backend == "mandaye.backends.ldap_back":
|
||||
import ldap
|
||||
storage_conn = ldap.initialize(config.ldap_url)
|
||||
storage_conn.protocol_version = ldap.VERSION3
|
||||
storage_conn.simple_bind(config.ldap_bind_dn, config.ldap_bind_password)
|
||||
|
||||
backend = import_backend(config.storage_backend)
|
||||
Association = backend.Association
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
|
||||
import datetime
|
||||
import ldap
|
||||
import ldap.modlist
|
||||
import random
|
||||
|
||||
from mandaye import config
|
||||
from mandaye.log import logger
|
||||
from mandaye.backends.default import storage_conn
|
||||
|
||||
class Association(object):
|
||||
"""
|
||||
association dictionary return by the following methods:
|
||||
{
|
||||
'id': '', # identifier of your association (must be unique)
|
||||
'sp_name': '', # name of the service provider (defined in the mappers)
|
||||
'sp_login': '', # login on the service provider
|
||||
'sp_post_values': '', # the post values for sp login form
|
||||
'idp_unique_id:': '', # the unique identifier of the identity provider (ex.: a saml NameID)
|
||||
'idp_name': '', # identity provide name
|
||||
'last_connection': datetime.datetime, # last connection with this association
|
||||
'creation_date': datetime.datetime, # creation date of this association
|
||||
}
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def ldap2association(ldap_object):
|
||||
return {
|
||||
'id': ldap_object['uniqueID'][0],
|
||||
'sp_name': ldap_object['spName'][0],
|
||||
'sp_login': ldap_object['spLogin'][0],
|
||||
'sp_post_values': ldap_object['spPostValues'][0],
|
||||
'idp_unique_id': ldap_object['idpUniqueID'][0],
|
||||
'idp_name': ldap_object['idpName'][0],
|
||||
'last_connection': datetime.datetime.strptime(
|
||||
ldap_object['lastConnectionDate'][0][:14],
|
||||
'%Y%m%d%H%M%S'),
|
||||
'creation_date': datetime.datetime.strptime(
|
||||
ldap_object['creationDate'][0][:14],
|
||||
'%Y%m%d%H%M%S'),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get(sp_name, idp_unique_id, idp_name='default'):
|
||||
""" return a list of dict with associations matching all of this options """
|
||||
associations = []
|
||||
results = storage_conn.search_s(config.ldap_base_dn, ldap.SCOPE_ONELEVEL,
|
||||
filterstr='(&(objectClass=MandayeUser)(spName=%s)(idpUniqueID=%s)(idpName=%s))' % (sp_name, idp_unique_id, idp_name))
|
||||
for result in results:
|
||||
associations.append(Association.ldap2association(result[1]))
|
||||
return associations
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_by_id(asso_id):
|
||||
""" return an dict of the association with the id or None if it doesn't exist """
|
||||
results = storage_conn.search_s(config.ldap_base_dn, ldap.SCOPE_ONELEVEL,
|
||||
filterstr='(&(objectClass=MandayeUser)(uniqueID=%s))' %\
|
||||
(asso_id))
|
||||
if results:
|
||||
return Association.ldap2association(results[0][1])
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def has_id(asso_id):
|
||||
""" check the given user is present in the directory """
|
||||
results = storage_conn.search_s(config.ldap_base_dn, ldap.SCOPE_ONELEVEL,
|
||||
filterstr='(&(objectClass=MandayeUser)(uniqueID=%s))' %\
|
||||
(asso_id))
|
||||
if results:
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def update_or_create(sp_name, sp_login, sp_post_values, idp_unique_id, idp_name='default'):
|
||||
""" update or create an associtaion which match the following values
|
||||
return the association id
|
||||
"""
|
||||
results = storage_conn.search_s(config.ldap_base_dn, ldap.SCOPE_ONELEVEL,
|
||||
filterstr='(&(objectClass=MandayeUser)(spName=%s)(spLogin=%s)(idpUniqueID=%s)(idpName=%s))' %\
|
||||
(sp_name, sp_login, idp_unique_id, idp_name))
|
||||
if not results:
|
||||
association = {'spName': sp_name,
|
||||
'spLogin': sp_login,
|
||||
'spPostValues': sp_post_values,
|
||||
'idpUniqueID': idp_unique_id,
|
||||
'idpName': idp_name,
|
||||
'creationDate': datetime.datetime.utcnow().strftime('%Y%m%d%H%M%SZ'),
|
||||
'lastConnectionDate': datetime.datetime.utcnow().strftime('%Y%m%d%H%M%SZ'),
|
||||
'objectClass': 'MandayeUser'
|
||||
}
|
||||
mod_list = ldap.modlist.addModlist(association)
|
||||
while True:
|
||||
unique_id = random.randint(1, 5000000)
|
||||
dn = "uniqueID=%s,%s" % (unique_id, config.ldap_base_dn)
|
||||
try:
|
||||
result = storage_conn.add_s(dn, mod_list)
|
||||
except ldap.ALREADY_EXISTS:
|
||||
continue
|
||||
break
|
||||
logger.info("New association %r with %r", sp_login, idp_unique_id)
|
||||
return unique_id
|
||||
else:
|
||||
dn = results[0][0]
|
||||
mod_list = [(ldap.MOD_REPLACE, 'spPostValues', sp_post_values)]
|
||||
storage_conn.modify_s(dn, mod_list)
|
||||
logger.info("Update post values for %r (%r)", sp_login, idp_unique_id)
|
||||
return results[0][1]['uniqueID'][0]
|
||||
|
||||
@staticmethod
|
||||
def delete(asso_id):
|
||||
""" delete the association which has the following asso_id """
|
||||
dn = "uniqueID=%s,%s" % (asso_id, config.ldap_base_dn)
|
||||
storage_conn.delete_s(dn)
|
||||
logger.info('Delete %r association', dn)
|
||||
|
||||
@staticmethod
|
||||
def get_last_connected(sp_name, idp_unique_id, idp_name='default'):
|
||||
""" get the last connecting association which match the parameters
|
||||
return a dict of the association
|
||||
"""
|
||||
results = storage_conn.search_s(config.ldap_base_dn, ldap.SCOPE_ONELEVEL,
|
||||
filterstr='(&(objectClass=MandayeUser)(spName=%s)(idpUniqueID=%s)(idpName=%s))' % (sp_name, idp_unique_id, idp_name))
|
||||
if results:
|
||||
return Association.ldap2association(results[0][1])
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def update_last_connection(asso_id):
|
||||
""" update the association last connection time with the current time
|
||||
return a dict of the association
|
||||
"""
|
||||
last_connection = datetime.datetime.utcnow().strftime("%Y%m%d%H%M%SZ")
|
||||
dn = "uniqueID=%s,%s" % (asso_id, config.ldap_base_dn)
|
||||
mod_list = [(ldap.MOD_REPLACE, 'lastConnectionDate', last_connection)]
|
||||
storage_conn.modify_s(dn, mod_list)
|
||||
|
|
@ -4,16 +4,21 @@ import os
|
|||
_PROJECT_PATH = os.path.join(os.path.dirname(__file__), '..')
|
||||
|
||||
# Choose storage
|
||||
# Only sql at the moment
|
||||
# mandaye.backends.ldap_back or mandaye.backends.sql
|
||||
storage_backend = "mandaye.backends.sql"
|
||||
|
||||
## SQL Backend config
|
||||
# Database configuration
|
||||
# http://docs.sqlalchemy.org/en/rel_0_7/core/engines.html
|
||||
# http://docs.sqlalchemy.org/en/rel_0_8/core/engines.html
|
||||
# rfc 1738 https://tools.ietf.org/html/rfc1738
|
||||
# dialect+driver://username:password@host:port/database
|
||||
db_url = 'sqlite:///test.db'
|
||||
|
||||
## LDAP Backend config
|
||||
ldap_url = 'ldap://127.0.0.1'
|
||||
ldap_bind_dn = 'cn=admin,dc=acompany,dc=org'
|
||||
ldap_bind_password = 'MyPassword'
|
||||
ldap_base_dn = 'ou=mandaye,dc=acompany,dc=org'
|
||||
|
||||
# urllib2 debug mode
|
||||
debug = False
|
||||
|
|
|
@ -30,6 +30,12 @@ config.read(SETTINGS_INI)
|
|||
# dialect+driver://username:password@host:port/database
|
||||
db_url = config.get('database', 'url')
|
||||
|
||||
## LDAP Backend config
|
||||
ldap_url = config.get('ldap', 'url')
|
||||
ldap_bind_dn = config.get('ldap', 'base_dn')
|
||||
ldap_bind_password = config.get('ldap', 'bind_password')
|
||||
ldap_base_dn = config.get('ldap', 'base_dn')
|
||||
|
||||
debug = config.getboolean('debug', 'debug')
|
||||
|
||||
# Log configuration
|
||||
|
@ -137,12 +143,13 @@ mandaye_offline_toolbar = config.getboolean('mandaye', 'offline_toolbar')
|
|||
# Authentic 2 auto connection
|
||||
a2_auto_connection = config.getboolean('mandaye', 'a2_auto_connection')
|
||||
|
||||
# Choose storage
|
||||
# Only mandaye.backends.sql at the moment
|
||||
# Choose storage (sql or ldap)
|
||||
if config.get('mandaye', 'storage_backend') == 'sql':
|
||||
storage_backend = "mandaye.backends.sql"
|
||||
elif config.get('mandaye', 'storage_backend') == 'ldap':
|
||||
storage_backend = "mandaye.backends.ldap_back"
|
||||
else:
|
||||
ImproperlyConfigured('Storage backend must be sql')
|
||||
ImproperlyConfigured('Storage backend must be sql or ldap')
|
||||
|
||||
# Encrypt service provider passwords with a secret
|
||||
# You should install pycypto to use this feature
|
||||
|
|
|
@ -2,9 +2,17 @@
|
|||
base_dir: .
|
||||
|
||||
[database]
|
||||
; use by sql backend
|
||||
; http://docs.sqlalchemy.org/en/rel_0_8/core/engines.html
|
||||
url: sqlite:///%(base_dir)s/{project_name}.db
|
||||
|
||||
[ldap]
|
||||
; use by ldap backend
|
||||
url: ldap://127.0.0.1
|
||||
bind_dn: cn=admin,dc=acompany,dc=org
|
||||
bind_password: AdminPassword
|
||||
base_dn: ou=mandaye,dc=acompany,dc=org
|
||||
|
||||
[dirs]
|
||||
config_root: %(base_dir)s/conf.d
|
||||
data_dir: %(base_dir)s/data
|
||||
|
@ -23,7 +31,7 @@ sentry_dsn:
|
|||
toolbar: true
|
||||
offline_toolbar: true
|
||||
a2_auto_connection: false
|
||||
; only sql at the moment
|
||||
; sql or ldap
|
||||
storage_backend: sql
|
||||
auto_decompress: true
|
||||
; if you want to encypt password set to true
|
||||
|
|
Reference in New Issue