summaryrefslogtreecommitdiffstats
path: root/ipaserver
diff options
context:
space:
mode:
authorJohn Dennis <jdennis@redhat.com>2012-02-19 10:02:38 -0500
committerRob Crittenden <rcritten@redhat.com>2012-02-27 05:55:45 -0500
commitdc8cca83f70bc4710ebf552c650874c88c577361 (patch)
tree06b20e67e6b78ae6e9754dd1b3c2f474bd342dda /ipaserver
parent49289bca5370b4b74f9bf4e5fd052d298a2a94d1 (diff)
downloadfreeipa.git-dc8cca83f70bc4710ebf552c650874c88c577361.tar.gz
freeipa.git-dc8cca83f70bc4710ebf552c650874c88c577361.tar.xz
freeipa.git-dc8cca83f70bc4710ebf552c650874c88c577361.zip
Implement session activity timeout
Previously sessions expired after session_auth_duration had elapsed commencing from the start of the session. We new support a "rolling" expiration where the expiration is advanced by session_auth_duration everytime the session is accessed, this is equivalent to a inactivity timeout. The expiration is still constrained by the credential expiration in all cases. The session expiration behavior is configurable based on the session_auth_duration_type. * Reduced the default session_auth_duration from 1 hour to 20 minutes. * Replaced the sesssion write_timestamp with the access_timestamp and update the access_timestamp whenever the session data is created, retrieved, or written. * Modify set_session_expiration_time to handle both an inactivity timeout and a fixed duration. * Introduce KerberosSession as a mixin class to share session duration functionality with all classes manipulating session data with Kerberos auth. This is both the non-RPC login class and the RPC classes. * Update make-lint to handle new classes. * Added session_auth_duration_type config item. * Updated default.conf.5 man page for new session_auth_duration_type item. * Removed these unused config items: mount_xmlserver, mount_jsonserver, webui_assets_dir https://fedorahosted.org/freeipa/ticket/2392
Diffstat (limited to 'ipaserver')
-rw-r--r--ipaserver/rpcserver.py96
1 files changed, 71 insertions, 25 deletions
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
index d0bb605f..0b8aa408 100644
--- a/ipaserver/rpcserver.py
+++ b/ipaserver/rpcserver.py
@@ -32,7 +32,7 @@ from ipalib.request import context, Connection, destroy_context
from ipalib.rpc import xml_dumps, xml_loads
from ipalib.util import make_repr, parse_time_duration
from ipapython.compat import json
-from ipalib.session import session_mgr, AuthManager, read_krbccache_file, store_krbccache_file, delete_krbccache_file, fmt_time, default_max_session_lifetime
+from ipalib.session import session_mgr, AuthManager, read_krbccache_file, store_krbccache_file, delete_krbccache_file, fmt_time, default_max_session_duration
from ipalib.backend import Backend
from ipalib.krb_utils import krb5_parse_ccache, KRB5_CCache, krb_ticket_expiration_threshold
from wsgiref.util import shift_path_info
@@ -566,7 +566,60 @@ class AuthManagerKerb(AuthManager):
self.error('AuthManager.logout.%s: session_data does not contain ccache_data', self.name)
-class jsonserver_session(jsonserver):
+class KerberosSession(object):
+ '''
+ Functionally shared by all RPC handlers using both sessions and
+ Kerberos. This class must be implemented as a mixin class rather
+ than the more obvious technique of subclassing because the classes
+ needing this do not share a common base class.
+ '''
+
+ def kerb_session_on_finalize(self):
+ '''
+ Initialize values from the Env configuration.
+
+ Why do it this way and not simply reference
+ api.env.session_auth_duration? Because that config item cannot
+ be used directly, it must be parsed and converted to an
+ integer. It would be inefficient to reparse it on every
+ request. So we parse it once and store the result in the class
+ instance.
+ '''
+ # Set the session expiration time
+ try:
+ seconds = parse_time_duration(self.api.env.session_auth_duration)
+ self.session_auth_duration = int(seconds)
+ self.debug("session_auth_duration: %s", datetime.timedelta(seconds=self.session_auth_duration))
+ except Exception, e:
+ self.session_auth_duration = default_max_session_duration
+ self.error('unable to parse session_auth_duration, defaulting to %d: %s',
+ self.session_auth_duration, e)
+
+ def update_session_expiration(self, session_data, krb_endtime):
+ '''
+ Each time a session is created or accessed we need to update
+ it's expiration time. The expiration time is set inside the
+ session_data.
+
+ :parameters:
+ session_data
+ The session data whose expiration is being updatded.
+ krb_endtime
+ The UNIX timestamp for when the Kerberos credentials expire.
+ :returns:
+ None
+ '''
+
+ # Account for clock skew and/or give us some time leeway
+ krb_expiration = krb_endtime - krb_ticket_expiration_threshold
+
+ # Set the session expiration time
+ session_mgr.set_session_expiration_time(session_data,
+ duration=self.session_auth_duration,
+ max_age=krb_expiration,
+ duration_type=self.api.env.session_duration_type)
+
+class jsonserver_session(jsonserver, KerberosSession):
"""
JSON RPC server protected with session auth.
"""
@@ -578,6 +631,10 @@ class jsonserver_session(jsonserver):
auth_mgr = AuthManagerKerb(self.__class__.__name__)
session_mgr.auth_mgr.register(auth_mgr.name, auth_mgr)
+ def _on_finalize(self):
+ super(jsonserver_session, self)._on_finalize()
+ self.kerb_session_on_finalize()
+
def need_login(self, start_response):
status = '401 Unauthorized'
headers = []
@@ -598,10 +655,10 @@ class jsonserver_session(jsonserver):
session_data = session_mgr.load_session_data(environ.get('HTTP_COOKIE'))
session_id = session_data['session_id']
- self.debug('jsonserver_session.__call__: session_id=%s start_timestamp=%s write_timestamp=%s expiration_timestamp=%s',
+ self.debug('jsonserver_session.__call__: session_id=%s start_timestamp=%s access_timestamp=%s expiration_timestamp=%s',
session_id,
fmt_time(session_data['session_start_timestamp']),
- fmt_time(session_data['session_write_timestamp']),
+ fmt_time(session_data['session_access_timestamp']),
fmt_time(session_data['session_expiration_timestamp']))
ccache_data = session_data.get('ccache_data')
@@ -620,6 +677,10 @@ class jsonserver_session(jsonserver):
delete_krbccache_file(krbccache_pathname)
return self.need_login(start_response)
+ # Update the session expiration based on the Kerberos expiration
+ endtime = cc.endtime(self.api.env.host, self.api.env.realm)
+ self.update_session_expiration(session_data, endtime)
+
# Store the session data in the per-thread context
setattr(context, 'session_data', session_data)
@@ -676,7 +737,7 @@ class jsonserver_kerb(jsonserver):
return response
-class krblogin(Backend):
+class krblogin(Backend, KerberosSession):
key = '/login'
def __init__(self):
@@ -685,17 +746,7 @@ class krblogin(Backend):
def _on_finalize(self):
super(krblogin, self)._on_finalize()
self.api.Backend.wsgi_dispatch.mount(self, self.key)
-
- # Set the session expiration time
- try:
- seconds = parse_time_duration(self.api.env.session_auth_duration)
- self.session_auth_duration = int(seconds)
- self.debug("session_auth_duration: %s", datetime.timedelta(seconds=self.session_auth_duration))
- except Exception, e:
- self.session_auth_duration = default_max_session_lifetime
- self.error('unable to parse session_auth_duration, defaulting to %d: %s',
- self.session_auth_duration, e)
-
+ self.kerb_session_on_finalize()
def __call__(self, environ, start_response):
headers = []
@@ -720,17 +771,10 @@ class krblogin(Backend):
# Copy the ccache file contents into the session data
session_data['ccache_data'] = read_krbccache_file(ccache_location)
- # Compute when the session will expire
+ # Set when the session will expire
cc = KRB5_CCache(ccache)
endtime = cc.endtime(self.api.env.host, self.api.env.realm)
-
- # Account for clock skew and/or give us some time leeway
- krb_expiration = endtime - krb_ticket_expiration_threshold
-
- # Set the session expiration time
- session_mgr.set_session_expiration_time(session_data,
- lifetime=self.session_auth_duration,
- max_age=krb_expiration)
+ self.update_session_expiration(session_data, endtime)
# Store the session data now that it's been updated with the ccache
session_mgr.store_session_data(session_data)
@@ -747,3 +791,5 @@ class krblogin(Backend):
start_response(status, headers)
return [response]
+
+