summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/backend.py5
-rw-r--r--ipalib/krb_utils.py71
-rw-r--r--ipalib/session.py126
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()