summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2015-05-11 18:14:42 -0400
committerPatrick Uiterwijk <puiterwijk@redhat.com>2015-05-12 00:39:31 +0200
commit8445b3297cd0b25989f2575c21bf3426aee7c5ad (patch)
tree3ce2b9aa142f9946548fc6175dcc07fa2561ace0
parent6437f6c9385e5e59cb21de7a3addedd904ee2825 (diff)
downloadipsilon-8445b3297cd0b25989f2575c21bf3426aee7c5ad.tar.gz
ipsilon-8445b3297cd0b25989f2575c21bf3426aee7c5ad.tar.xz
ipsilon-8445b3297cd0b25989f2575c21bf3426aee7c5ad.zip
Use plugin-specific configuration, better expiration
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>
-rw-r--r--ipsilon/providers/saml2/auth.py3
-rw-r--r--ipsilon/providers/saml2/logout.py3
-rw-r--r--ipsilon/providers/saml2/provider.py3
-rw-r--r--ipsilon/providers/saml2/sessions.py24
-rw-r--r--ipsilon/providers/saml2idp.py30
-rw-r--r--ipsilon/util/data.py20
-rw-r--r--templates/install/ipsilon.conf1
-rwxr-xr-xtests/helpers/common.py2
8 files changed, 50 insertions, 36 deletions
diff --git a/ipsilon/providers/saml2/auth.py b/ipsilon/providers/saml2/auth.py
index 495e5a9..c46d604 100644
--- a/ipsilon/providers/saml2/auth.py
+++ b/ipsilon/providers/saml2/auth.py
@@ -5,7 +5,6 @@ from ipsilon.providers.common import AuthenticationError, InvalidRequest
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
@@ -275,7 +274,7 @@ class AuthenticateRequest(ProviderPageBase):
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)
diff --git a/ipsilon/providers/saml2/logout.py b/ipsilon/providers/saml2/logout.py
index d20370a..cc9b777 100644
--- a/ipsilon/providers/saml2/logout.py
+++ b/ipsilon/providers/saml2/logout.py
@@ -2,7 +2,6 @@
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
@@ -204,7 +203,7 @@ class LogoutRequest(ProviderPageBase):
us = UserSession()
- 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)
diff --git a/ipsilon/providers/saml2/provider.py b/ipsilon/providers/saml2/provider.py
index c8425bb..3dea631 100644
--- a/ipsilon/providers/saml2/provider.py
+++ b/ipsilon/providers/saml2/provider.py
@@ -266,12 +266,13 @@ class ServiceProviderCreator(object):
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.sessionfactory = sessionfactory
def add_provider(self, sp):
self.server.addProviderFromBuffer(lasso.PROVIDER_ROLE_SP,
diff --git a/ipsilon/providers/saml2/sessions.py b/ipsilon/providers/saml2/sessions.py
index 6b3d860..1000a87 100644
--- a/ipsilon/providers/saml2/sessions.py
+++ b/ipsilon/providers/saml2/sessions.py
@@ -11,23 +11,6 @@ LOGGING_OUT = 4
LOGGED_OUT = 8
-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.
@@ -118,8 +101,8 @@ class SAMLSessionFactory(Log):
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):
@@ -288,10 +271,9 @@ if __name__ == '__main__':
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
- factory = SAMLSessionFactory()
+ factory = SAMLSessionFactory('/tmp/saml2sessions.sqlite')
factory.wipe_data()
sess1 = factory.add_session('_123456', provider1, "admin", "<Login/>")
diff --git a/ipsilon/providers/saml2idp.py b/ipsilon/providers/saml2idp.py
index efaf67e..f771ef7 100644
--- a/ipsilon/providers/saml2idp.py
+++ b/ipsilon/providers/saml2idp.py
@@ -8,7 +8,7 @@ from ipsilon.providers.saml2.admin import Saml2AdminPage
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
@@ -215,6 +215,7 @@ class IdpProvider(ProviderBase):
self.rest = None
self.page = None
self.idp = None
+ self.sessionfactory = None
self.description = """
Provides SAML 2.0 authentication infrastructure. """
@@ -272,6 +273,10 @@ Provides SAML 2.0 authentication infrastructure. """
'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
@@ -281,7 +286,12 @@ Provides SAML 2.0 authentication infrastructure. """
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
+ )
bt.start()
@property
@@ -344,9 +354,13 @@ Provides SAML 2.0 authentication infrastructure. """
def init_idp(self):
idp = None
+ self.sessionfactory = SAMLSessionFactory(
+ database_url=self.get_config_value('session database url')
+ )
# Init IDP data
try:
- 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
@@ -385,7 +399,7 @@ Provides SAML 2.0 authentication infrastructure. """
us = UserSession()
user = us.get_user()
- saml_sessions = SAMLSessionFactory()
+ saml_sessions = self.sessionfactory
session = saml_sessions.get_next_logout()
if session is None:
return
@@ -459,6 +473,8 @@ class Installer(ProviderInstaller):
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':
@@ -497,7 +513,11 @@ class Installer(ProviderInstaller):
'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
diff --git a/ipsilon/util/data.py b/ipsilon/util/data.py
index f90519d..53a1756 100644
--- a/ipsilon/util/data.py
+++ b/ipsilon/util/data.py
@@ -1,11 +1,12 @@
# Copyright (C) 2013 Ipsilon project Contributors, for license see COPYING
import cherrypy
+import datetime
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
@@ -513,9 +514,12 @@ class TranStore(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)
self.table = 'sessions'
+ # 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):
"""
@@ -533,6 +537,16 @@ class SAML2SessionStore(Store):
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)
diff --git a/templates/install/ipsilon.conf b/templates/install/ipsilon.conf
index c688f0b..b57aa55 100644
--- a/templates/install/ipsilon.conf
+++ b/templates/install/ipsilon.conf
@@ -10,7 +10,6 @@ base.dir = "${staticdir}"
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"
diff --git a/tests/helpers/common.py b/tests/helpers/common.py
index 93d0f17..cbd516b 100755
--- a/tests/helpers/common.py
+++ b/tests/helpers/common.py
@@ -187,7 +187,7 @@ class IpsilonTestBase(object):
self.processes.append(p)
p.wait()
for d in ['adminconfig', 'users', 'transactions', 'sessions',
- 'saml2sessions']:
+ 'saml2.sessions.db']:
cmd = ['/usr/bin/createdb', '-h', addr, '-p', port, d]
subprocess.check_call(cmd, env=env)