summaryrefslogtreecommitdiffstats
path: root/ipalib/session.py
diff options
context:
space:
mode:
authorJohn Dennis <jdennis@redhat.com>2012-02-15 10:26:42 -0500
committerRob Crittenden <rcritten@redhat.com>2012-02-27 05:54:29 -0500
commit9753fd423059e8d5725ead9a90a7cf1b9e0b9b85 (patch)
treec5a0a933b1357164f2dc4c0d4d22c9cac05dd1eb /ipalib/session.py
parent7d7322de2eb0de61ea917d03662452d3efa4c834 (diff)
downloadfreeipa-9753fd423059e8d5725ead9a90a7cf1b9e0b9b85.tar.gz
freeipa-9753fd423059e8d5725ead9a90a7cf1b9e0b9b85.tar.xz
freeipa-9753fd423059e8d5725ead9a90a7cf1b9e0b9b85.zip
Tweak the session auth to reflect developer consensus.
* Increase the session ID from 48 random bits to 128. * Implement the sesison_logout RPC command. It permits the UI to send a command that destroys the users credentials in the current session. * Restores the original web URL's and their authentication protections. Adds a new URL for sessions /ipa/session/json. Restores the original Kerberos auth which was for /ipa and everything below. New /ipa/session/json URL is treated as an exception and turns all authenticaion off. Similar to how /ipa/ui is handled. * Refactor the RPC handlers in rpcserver.py such that there is one handler per URL, specifically one handler per RPC and AuthMechanism combination. * Reworked how the URL names are used to map a URL to a handler. Previously it only permitted one level in the URL path hierarchy. We now dispatch on more that one URL path component. * Renames the api.Backend.session object to wsgi_dispatch. The use of the name session was historical and is now confusing since we've implemented sessions in a different location than the api.Backend.session object, which is really a WSGI dispatcher, hence the new name wsgi_dispatch. * Bullet-proof the setting of the KRB5CCNAME environment variable. ldap2.connect already sets it via the create_context() call but just in case that's not called or not called early enough (we now have other things besides ldap which need the ccache) we explicitly set it early as soon as we know it. * Rework how we test for credential validity and expiration. The previous code did not work with s4u2proxy because it assumed the existance of a TGT. Now we first try ldap credentials and if we can't find those fallback to the TGT. This logic was moved to the KRB5_CCache object, it's an imperfect location for it but it's the only location that makes sense at the moment given some of the current code limitations. The new methods are KRB5_CCache.valid() and KRB5_CCache.endtime(). * Add two new classes to session.py AuthManager and SessionAuthManager. Their purpose is to emit authication events to interested listeners. At the moment the logout event is the only event, but the framework should support other events as they arise. * Add BuildRequires python-memcached to freeipa.spec.in * Removed the marshaled_dispatch method, it was cruft, no longer referenced. https://fedorahosted.org/freeipa/ticket/2362
Diffstat (limited to 'ipalib/session.py')
-rw-r--r--ipalib/session.py126
1 files changed, 121 insertions, 5 deletions
diff --git a/ipalib/session.py b/ipalib/session.py
index a58643983..1f5ee379d 100644
--- a/ipalib/session.py
+++ b/ipalib/session.py
@@ -25,6 +25,8 @@ import re
import time
from text import _
from ipapython.ipa_log_manager import *
+from ipalib import api, errors
+from ipalib import Command
from ipalib.krb_utils import *
__doc__ = '''
@@ -632,6 +634,82 @@ def fmt_time(timestamp):
#-------------------------------------------------------------------------------
+class AuthManager(object):
+ '''
+ This class is an abstract base class and is meant to be subclassed
+ to provide actual functionality. The purpose is to encapsulate all
+ the callbacks one might need to manage authenticaion. Different
+ authentication mechanisms will instantiate a subclass of this and
+ register it with the SessionAuthManger. When an authentication
+ event occurs the matching method will be called for each
+ registered class. This allows the SessionAuthManager to notify
+ interested parties.
+ '''
+
+ def __init__(self, name):
+ log_mgr.get_logger(self, True)
+ self.name = name
+
+
+ def logout(self, session_data):
+ '''
+ Called when a user requests to be logged out of their session.
+
+ :parameters:
+ session_data
+ The current session data
+ :returns:
+ None
+ '''
+ self.debug('AuthManager.logout.%s:', self.name)
+
+class SessionAuthManager(object):
+ '''
+ '''
+
+ def __init__(self):
+ '''
+ '''
+ log_mgr.get_logger(self, True)
+ self.auth_managers = {}
+
+ def register(self, name, auth_mgr):
+ self.debug('SessionAuthManager.register: name=%s', name)
+
+ existing_mgr = self.auth_managers.get(name)
+ if existing_mgr is not None:
+ raise KeyError('cannot register auth manager named "%s" one already exists, name="%s" object=%s',
+ name, existing_mgr.name, repr(existing_mgr))
+
+ if not isinstance(auth_mgr, AuthManager):
+ raise TypeError('auth_mgr must be an instance of AuthManager, not %s',
+ auth_mgr.__class__.__name__)
+
+ self.auth_managers[name] = auth_mgr
+
+
+ def unregister(self, name):
+ self.debug('SessionAuthManager.unregister: name=%s', name)
+
+ if not self.auth_managers.has_key(name):
+ raise KeyError('cannot unregister auth manager named "%s", does not exist',
+ name)
+ del self.auth_managers[name]
+
+
+ def logout(self, session_data):
+ '''
+ '''
+ self.debug('SessionAuthManager.logout:')
+
+ for auth_mgr in self.auth_managers.values():
+ try:
+ auth_mgr.logout(session_data)
+ except Exception, e:
+ self.error('%s auth_mgr logout failed: %s', auth_mgr.name, e)
+
+#-------------------------------------------------------------------------------
+
class SessionManager(object):
'''
@@ -649,8 +727,9 @@ class SessionManager(object):
log_mgr.get_logger(self, True)
self.generated_session_ids = set()
+ self.auth_mgr = SessionAuthManager()
- def generate_session_id(self, n_bits=48):
+ def generate_session_id(self, n_bits=128):
'''
Return a random string to be used as a session id.
@@ -790,8 +869,7 @@ class MemcacheSessionManager(SessionManager):
n_retries = 0
while n_retries < max_retries:
session_id = super(MemcacheSessionManager, self).new_session_id(max_retries)
- session_key = self.session_key(session_id)
- session_data = self.mc.get(session_key)
+ session_data = self.get_session_data(session_id)
if session_data is None:
break
n_retries += 1
@@ -843,6 +921,21 @@ class MemcacheSessionManager(SessionManager):
'''
return 'ipa.session.%s' % (session_id)
+ def get_session_data(self, session_id):
+ '''
+ Given a session id retrieve the session data associated with it.
+ If no session data exists for the session id return None.
+
+ :parameters:
+ session_id
+ The session id whose session data is desired.
+ :returns:
+ Session data if found, None otherwise.
+ '''
+ session_key = self.session_key(session_id)
+ session_data = self.mc.get(session_key)
+ return session_data
+
def get_session_id_from_http_cookie(self, cookie_header):
'''
Parse an HTTP cookie header and search for our session
@@ -904,8 +997,7 @@ class MemcacheSessionManager(SessionManager):
self.store_session_data(session_data)
return session_data
else:
- session_key = self.session_key(session_id)
- session_data = self.mc.get(session_key)
+ session_data = self.get_session_data(session_id)
if session_data is None:
self.debug('no session data in cache with id=%s, generating empty session data', session_id)
session_data = self.new_session_data(session_id)
@@ -1094,5 +1186,29 @@ def delete_krbccache_file(krbccache_pathname=None):
#-------------------------------------------------------------------------------
+from ipalib.request import context
+
+class session_logout(Command):
+ '''
+ RPC command used to log the current user out of their session.
+ '''
+
+ def execute(self, *args, **options):
+ session_data = getattr(context, 'session_data', None)
+ if session_data is None:
+ self.debug('session logout command: no session_data found')
+ else:
+ session_id = session_data.get('session_id')
+ self.debug('session logout command: session_id=%s', session_id)
+
+ # Notifiy registered listeners
+ session_mgr.auth_mgr.logout(session_data)
+
+ return dict(result=None)
+
+api.register(session_logout)
+
+#-------------------------------------------------------------------------------
+
session_mgr = MemcacheSessionManager()