diff options
Diffstat (limited to 'ipalib')
-rw-r--r-- | ipalib/backend.py | 5 | ||||
-rw-r--r-- | ipalib/krb_utils.py | 71 | ||||
-rw-r--r-- | ipalib/session.py | 126 |
3 files changed, 193 insertions, 9 deletions
diff --git a/ipalib/backend.py b/ipalib/backend.py index 7ed378e8..0232fa53 100644 --- a/ipalib/backend.py +++ b/ipalib/backend.py @@ -23,6 +23,7 @@ Base classes for all backed-end plugins. import threading import plugable +import os from errors import PublicError, InternalError, CommandError from request import context, Connection, destroy_context @@ -106,6 +107,10 @@ class Executioner(Backend): """ client_ip: The IP address of the remote client. """ + + if ccache is not None: + os.environ["KRB5CCNAME"] = ccache + if self.env.in_server: self.Backend.ldap2.connect(ccache=ccache) else: diff --git a/ipalib/krb_utils.py b/ipalib/krb_utils.py index e04c70ae..21bca68a 100644 --- a/ipalib/krb_utils.py +++ b/ipalib/krb_utils.py @@ -158,7 +158,6 @@ class KRB5_CCache(object): self.ccache = None self.principal = None - self.debug('opening ccache file "%s"', ccache) try: self.context = krbV.default_context() self.scheme, self.name = krb5_parse_ccache(ccache) @@ -228,8 +227,6 @@ class KRB5_CCache(object): except krbV.Krb5Error, e: error_code = e.args[0] if error_code == KRB5_CC_NOTFOUND: - self.debug('"%s" credential not found in "%s" ccache', - krbV_principal.name, self.ccache_str()) #pylint: disable=E1103 raise KeyError('"%s" credential not found in "%s" ccache' % \ (krbV_principal.name, self.ccache_str())) #pylint: disable=E1103 raise e @@ -281,7 +278,7 @@ class KRB5_CCache(object): cred = self.get_credentials(krbV_principal) authtime, starttime, endtime, renew_till = cred[3] - self.debug('principal=%s, authtime=%s, starttime=%s, endtime=%s, renew_till=%s', + self.debug('get_credential_times: principal=%s, authtime=%s, starttime=%s, endtime=%s, renew_till=%s', krbV_principal.name, #pylint: disable=E1103 krb5_format_time(authtime), krb5_format_time(starttime), krb5_format_time(endtime), krb5_format_time(renew_till)) @@ -327,3 +324,69 @@ class KRB5_CCache(object): if endtime < now: return False return True + + def valid(self, host, realm): + ''' + Test to see if ldap service ticket or the TGT is valid. + + :parameters: + host + ldap server + realm + kerberos realm + :returns: + True if either the ldap service ticket or the TGT is valid, + False otherwise. + ''' + + try: + principal = krb5_format_service_principal_name('ldap', host, realm) + valid = self.credential_is_valid(principal) + if valid: + return True + except KeyError: + pass + + try: + principal = krb5_format_tgt_principal_name(realm) + valid = self.credential_is_valid(principal) + return valid + except KeyError: + return False + + def endtime(self, host, realm): + ''' + Returns the minimum endtime for tickets of interest (ldap service or TGT). + + :parameters: + host + ldap server + realm + kerberos realm + :returns: + UNIX timestamp value. + ''' + + result = 0 + try: + principal = krb5_format_service_principal_name('ldap', host, realm) + authtime, starttime, endtime, renew_till = self.get_credential_times(principal) + if result: + result = min(result, endtime) + else: + result = endtime + except KeyError: + pass + + try: + principal = krb5_format_tgt_principal_name(realm) + authtime, starttime, endtime, renew_till = self.get_credential_times(principal) + if result: + result = min(result, endtime) + else: + result = endtime + except KeyError: + pass + + self.debug('"%s" ccache endtime=%s', self.ccache_str(), krb5_format_time(result)) + return result diff --git a/ipalib/session.py b/ipalib/session.py index a5864398..1f5ee379 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() |