Use a SAML2 plugin specific option to specify the database uri
for sessions.
Use a much more robust method to find sessions that need
expiration (thanks Patrick).
https://fedorahosted.org/ipsilon/ticket/90
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-by: Patrick Uiterwijk <puiterwijk@redhat.com>
from ipsilon.providers.saml2.provider import ServiceProvider
from ipsilon.providers.saml2.provider import InvalidProviderId
from ipsilon.providers.saml2.provider import NameIdNotAllowed
from ipsilon.providers.saml2.provider import ServiceProvider
from ipsilon.providers.saml2.provider import InvalidProviderId
from ipsilon.providers.saml2.provider import NameIdNotAllowed
-from ipsilon.providers.saml2.sessions import SAMLSessionFactory
from ipsilon.tools import saml2metadata as metadata
from ipsilon.util.policy import Policy
from ipsilon.util.user import UserSession
from ipsilon.tools import saml2metadata as metadata
from ipsilon.util.policy import Policy
from ipsilon.util.user import UserSession
self.debug('Assertion: %s' % login.assertion.dump())
self.debug('Assertion: %s' % login.assertion.dump())
- saml_sessions = SAMLSessionFactory()
+ saml_sessions = self.cfg.idp.sessionfactory
lasso_session = lasso.Session()
lasso_session.addAssertion(login.remoteProviderId, login.assertion)
lasso_session = lasso.Session()
lasso_session.addAssertion(login.remoteProviderId, login.assertion)
from ipsilon.providers.common import ProviderPageBase
from ipsilon.providers.common import InvalidRequest
from ipsilon.providers.common import ProviderPageBase
from ipsilon.providers.common import InvalidRequest
-from ipsilon.providers.saml2.sessions import SAMLSessionFactory
from ipsilon.providers.saml2.auth import UnknownProvider
from ipsilon.util.user import UserSession
import cherrypy
from ipsilon.providers.saml2.auth import UnknownProvider
from ipsilon.util.user import UserSession
import cherrypy
- saml_sessions = SAMLSessionFactory()
+ saml_sessions = self.cfg.idp.sessionfactory
if lasso.SAML2_FIELD_REQUEST in message:
self._handle_logout_request(us, logout, saml_sessions, message)
if lasso.SAML2_FIELD_REQUEST in message:
self._handle_logout_request(us, logout, saml_sessions, message)
class IdentityProvider(Log):
class IdentityProvider(Log):
- def __init__(self, config):
+ def __init__(self, config, sessionfactory):
self.server = lasso.Server(config.idp_metadata_file,
config.idp_key_file,
None,
config.idp_certificate_file)
self.server.role = lasso.PROVIDER_ROLE_IDP
self.server = lasso.Server(config.idp_metadata_file,
config.idp_key_file,
None,
config.idp_certificate_file)
self.server.role = lasso.PROVIDER_ROLE_IDP
+ self.sessionfactory = sessionfactory
def add_provider(self, sp):
self.server.addProviderFromBuffer(lasso.PROVIDER_ROLE_SP,
def add_provider(self, sp):
self.server.addProviderFromBuffer(lasso.PROVIDER_ROLE_SP,
-def expire_sessions():
- """
- Find all expired sessions and remove them. This is executed as a
- background cherrypy task.
- """
- ss = SAML2SessionStore()
- data = ss.get_data()
- now = datetime.datetime.now()
- for idval in data:
- r = data[idval]
- exp = r.get('expiration_time', None)
- if exp is not None:
- exp = datetime.datetime.strptime(exp, '%Y-%m-%d %H:%M:%S.%f')
- if exp < now:
- ss.remove_session(idval)
-
-
class SAMLSession(Log):
"""
A SAML login session.
class SAMLSession(Log):
"""
A SAML login session.
Returns a SAMLSession object representing the new session.
"""
Returns a SAMLSession object representing the new session.
"""
- def __init__(self):
- self._ss = SAML2SessionStore()
+ def __init__(self, database_url):
+ self._ss = SAML2SessionStore(database_url=database_url)
self.user = None
def _data_to_samlsession(self, uuidval, data):
self.user = None
def _data_to_samlsession(self, uuidval, data):
provider2 = "http://127.0.0.11/saml2"
# temporary values to simulate cherrypy
provider2 = "http://127.0.0.11/saml2"
# temporary values to simulate cherrypy
- cherrypy_config['saml2.sessions.db'] = '/tmp/saml2sessions.sqlite'
cherrypy_config['tools.sessions.timeout'] = 60
cherrypy_config['tools.sessions.timeout'] = 60
- factory = SAMLSessionFactory()
+ factory = SAMLSessionFactory('/tmp/saml2sessions.sqlite')
factory.wipe_data()
sess1 = factory.add_session('_123456', provider1, "admin", "<Login/>")
factory.wipe_data()
sess1 = factory.add_session('_123456', provider1, "admin", "<Login/>")
from ipsilon.providers.saml2.rest import Saml2RestBase
from ipsilon.providers.saml2.provider import IdentityProvider
from ipsilon.providers.saml2.sessions import SAMLSessionFactory
from ipsilon.providers.saml2.rest import Saml2RestBase
from ipsilon.providers.saml2.provider import IdentityProvider
from ipsilon.providers.saml2.sessions import SAMLSessionFactory
-from ipsilon.providers.saml2.sessions import expire_sessions
+from ipsilon.util.data import SAML2SessionStore
from ipsilon.tools.certs import Certificate
from ipsilon.tools import saml2metadata as metadata
from ipsilon.tools import files
from ipsilon.tools.certs import Certificate
from ipsilon.tools import saml2metadata as metadata
from ipsilon.tools import files
self.rest = None
self.page = None
self.idp = None
self.rest = None
self.page = None
self.idp = None
+ self.sessionfactory = None
self.description = """
Provides SAML 2.0 authentication infrastructure. """
self.description = """
Provides SAML 2.0 authentication infrastructure. """
'default allowed attributes',
'Defines a list of allowed attributes, applied after mapping',
['*']),
'default allowed attributes',
'Defines a list of allowed attributes, applied after mapping',
['*']),
+ pconfig.String(
+ 'session database url',
+ 'Database URL for SAML2 sessions',
+ 'saml2.sessions.db.sqlite'),
)
if cherrypy.config.get('debug', False):
import logging
)
if cherrypy.config.get('debug', False):
import logging
logger.addHandler(lh)
logger.setLevel(logging.DEBUG)
logger.addHandler(lh)
logger.setLevel(logging.DEBUG)
- bt = cherrypy.process.plugins.BackgroundTask(60, expire_sessions)
+ store = SAML2SessionStore(
+ database_url=self.get_config_value('session database url')
+ )
+ bt = cherrypy.process.plugins.BackgroundTask(
+ 60, store.remove_expired_sessions
+ )
def init_idp(self):
idp = None
def init_idp(self):
idp = None
+ self.sessionfactory = SAMLSessionFactory(
+ database_url=self.get_config_value('session database url')
+ )
- idp = IdentityProvider(self)
+ idp = IdentityProvider(self,
+ sessionfactory=self.sessionfactory)
except Exception, e: # pylint: disable=broad-except
self.debug('Failed to init SAML2 provider: %r' % e)
return None
except Exception, e: # pylint: disable=broad-except
self.debug('Failed to init SAML2 provider: %r' % e)
return None
us = UserSession()
user = us.get_user()
us = UserSession()
user = us.get_user()
- saml_sessions = SAMLSessionFactory()
+ saml_sessions = self.sessionfactory
session = saml_sessions.get_next_logout()
if session is None:
return
session = saml_sessions.get_next_logout()
if session is None:
return
help=('Metadata validity period in days '
'(default - %d)' %
METADATA_DEFAULT_VALIDITY_PERIOD))
help=('Metadata validity period in days '
'(default - %d)' %
METADATA_DEFAULT_VALIDITY_PERIOD))
+ group.add_argument('--saml2-session-dburl',
+ help='session database URL')
def configure(self, opts, changes):
if opts['saml2'] != 'yes':
def configure(self, opts, changes):
if opts['saml2'] != 'yes':
'idp certificate file': cert.cert,
'idp key file': cert.key,
'idp nameid salt': uuid.uuid4().hex,
'idp certificate file': cert.cert,
'idp key file': cert.key,
'idp nameid salt': uuid.uuid4().hex,
- 'idp metadata validity': opts['saml2_metadata_validity']}
+ 'idp metadata validity': opts['saml2_metadata_validity'],
+ 'session database url': opts['saml2_session_dburl'] or
+ opts['database_url'] % {
+ 'datadir': opts['data_dir'],
+ 'dbname': 'saml2.sessions.db'}}
po.save_plugin_config(config)
# Update global config to add login plugin
po.save_plugin_config(config)
# Update global config to add login plugin
# Copyright (C) 2013 Ipsilon project Contributors, for license see COPYING
import cherrypy
# Copyright (C) 2013 Ipsilon project Contributors, for license see COPYING
import cherrypy
from ipsilon.util.log import Log
from sqlalchemy import create_engine
from sqlalchemy import MetaData, Table, Column, Text
from sqlalchemy.pool import QueuePool, SingletonThreadPool
from ipsilon.util.log import Log
from sqlalchemy import create_engine
from sqlalchemy import MetaData, Table, Column, Text
from sqlalchemy.pool import QueuePool, SingletonThreadPool
-from sqlalchemy.sql import select
+from sqlalchemy.sql import select, and_
import ConfigParser
import os
import uuid
import ConfigParser
import os
import uuid
class SAML2SessionStore(Store):
class SAML2SessionStore(Store):
- def __init__(self, path=None):
- super(SAML2SessionStore, self).__init__('saml2.sessions.db')
+ def __init__(self, database_url):
+ super(SAML2SessionStore, self).__init__(database_url=database_url)
+ # pylint: disable=protected-access
+ table = SqlQuery(self._db, self.table, UNIQUE_DATA_COLUMNS)._table
+ table.create(checkfirst=True)
def _get_unique_id_from_column(self, name, value):
"""
def _get_unique_id_from_column(self, name, value):
"""
raise ValueError("Multiple entries returned")
return data.keys()[0]
raise ValueError("Multiple entries returned")
return data.keys()[0]
+ def remove_expired_sessions(self):
+ # pylint: disable=protected-access
+ table = SqlQuery(self._db, self.table, UNIQUE_DATA_COLUMNS)._table
+ sel = select([table.columns.uuid]). \
+ where(and_(table.c.name == 'expiration_time',
+ table.c.value <= datetime.datetime.now()))
+ # pylint: disable=no-value-for-parameter
+ d = table.delete().where(table.c.uuid.in_(sel))
+ d.execute()
+
def get_data(self, idval=None, name=None, value=None):
return self.get_unique_data(self.table, idval, name, value)
def get_data(self, idval=None, name=None, value=None):
return self.get_unique_data(self.table, idval, name, value)
admin.config.db = "${admindb}"
user.prefs.db = "${usersdb}"
transactions.db = "${transdb}"
admin.config.db = "${admindb}"
user.prefs.db = "${usersdb}"
transactions.db = "${transdb}"
-saml2.sessions.db = "${samlsessionsdb}"
tools.sessions.on = True
tools.sessions.name = "${instance}_ipsilon_session_id"
tools.sessions.on = True
tools.sessions.name = "${instance}_ipsilon_session_id"
self.processes.append(p)
p.wait()
for d in ['adminconfig', 'users', 'transactions', 'sessions',
self.processes.append(p)
p.wait()
for d in ['adminconfig', 'users', 'transactions', 'sessions',
cmd = ['/usr/bin/createdb', '-h', addr, '-p', port, d]
subprocess.check_call(cmd, env=env)
cmd = ['/usr/bin/createdb', '-h', addr, '-p', port, d]
subprocess.check_call(cmd, env=env)