Implement password based session login

* Adjust URL's
  - rename /ipa/login -> /ipa/session/login_kerberos
  - add /ipa/session/login_password

* Adjust Kerberos protection on URL's in ipa.conf

* Bump VERSION in httpd ipa.conf to pick up session changes.

* Adjust login URL in ipa.js

* Add InvalidSessionPassword to errors.py

* Rename krblogin class to login_kerberos for consistency with
  new login_password class

* Implement login_password.kinit() method which invokes
  /usr/bin/kinit as a subprocess

* Add login_password class for WSGI dispatch, accepts POST
  application/x-www-form-urlencoded user & password
  parameters. We form the Kerberos principal from the server's
  realm.

* Add function  krb5_unparse_ccache()

* Refactor code to share common code

* Clean up use of ccache names, be consistent

* Replace read_krbccache_file(), store_krbccache_file(), delete_krbccache_file()
  with load_ccache_data(), bind_ipa_ccache(), release_ipa_ccache().
  bind_ipa_ccache() now sets environment KRB5CCNAME variable.
  release_ipa_ccache() now clears environment KRB5CCNAME variable.

* ccache names should now support any ccache storage scheme,
  not just FILE based ccaches

* Add utilies to return HTTP status from wsgi handlers,
  use constants for HTTP status code for consistency.
  Use utilies for returning from wsgi handlers rather than
  duplicated code.

* Add KerberosSession.finalize_kerberos_acquisition() method
  so different login handlers can share common code.

* add Requires: krb5-workstation to server (server now calls kinit)

* Fix test_rpcserver.py to use new dispatch inside route() method

https://fedorahosted.org/freeipa/ticket/2095
This commit is contained in:
John Dennis 2012-02-25 13:39:19 -05:00 committed by Rob Crittenden
parent 059a90702e
commit ee780df13c
8 changed files with 290 additions and 94 deletions

View File

@ -1,5 +1,5 @@
# #
# VERSION 3 - DO NOT REMOVE THIS LINE # VERSION 4 - DO NOT REMOVE THIS LINE
# #
# LoadModule auth_kerb_module modules/mod_auth_kerb.so # LoadModule auth_kerb_module modules/mod_auth_kerb.so
@ -60,7 +60,13 @@ KrbConstrainedDelegationLock ipa
</Location> </Location>
# Turn off Apache authentication for sessions # Turn off Apache authentication for sessions
<Location "/ipa/session"> <Location "/ipa/session/json">
Satisfy Any
Order Deny,Allow
Allow from all
</Location>
<Location "/ipa/session/login_password">
Satisfy Any Satisfy Any
Order Deny,Allow Order Deny,Allow
Allow from all Allow from all

View File

@ -60,7 +60,7 @@ var IPA = function() {
// if current path matches live server path, use live data // if current path matches live server path, use live data
if (that.url && window.location.pathname.substring(0, that.url.length) === that.url) { if (that.url && window.location.pathname.substring(0, that.url.length) === that.url) {
that.json_url = params.url || '/ipa/session/json'; that.json_url = params.url || '/ipa/session/json';
that.login_url = params.url || '/ipa/login'; that.login_url = params.url || '/ipa/session/login_kerberos';
} else { // otherwise use fixtures } else { // otherwise use fixtures
that.json_path = params.url || "test/data"; that.json_path = params.url || "test/data";

View File

@ -612,6 +612,13 @@ class SessionError(AuthenticationError):
format= _('Session error') format= _('Session error')
class InvalidSessionPassword(SessionError):
"""
**1201** Raised when we cannot obtain a TGT for a principal.
"""
errno = 1201
format= _('Principal %(principal)s cannot be authenticated: %(message)s')
############################################################################## ##############################################################################
# 2000 - 2999: Authorization errors # 2000 - 2999: Authorization errors
class AuthorizationError(PublicError): class AuthorizationError(PublicError):

View File

@ -42,7 +42,7 @@ ccache_name_re = re.compile(r'^((\w+):)?(.+)')
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
def krb5_parse_ccache(name): def krb5_parse_ccache(ccache_name):
''' '''
Given a Kerberos ccache name parse it into it's scheme and Given a Kerberos ccache name parse it into it's scheme and
location components. Currently valid values for the scheme location components. Currently valid values for the scheme
@ -55,12 +55,12 @@ def krb5_parse_ccache(name):
does not exist it defaults to FILE. does not exist it defaults to FILE.
:parameters: :parameters:
name ccache_name
The name of the Kerberos ccache. The name of the Kerberos ccache.
:returns: :returns:
A two-tuple of (scheme, ccache) A two-tuple of (scheme, ccache)
''' '''
match = ccache_name_re.search(name) match = ccache_name_re.search(ccache_name)
if match: if match:
scheme = match.group(2) scheme = match.group(2)
location = match.group(3) location = match.group(3)
@ -71,7 +71,10 @@ def krb5_parse_ccache(name):
return scheme, location return scheme, location
else: else:
raise ValueError('Invalid ccache name = "%s"' % name) raise ValueError('Invalid ccache name = "%s"' % ccache_name)
def krb5_unparse_ccache(scheme, name):
return '%s:%s' % (scheme.upper(), name)
def krb5_format_principal_name(user, realm): def krb5_format_principal_name(user, realm):
''' '''
@ -388,5 +391,5 @@ class KRB5_CCache(object):
except KeyError: except KeyError:
pass pass
self.debug('"%s" ccache endtime=%s (%s)', self.ccache_str(), result, krb5_format_time(result)) self.debug('KRB5_CCache %s endtime=%s (%s)', self.ccache_str(), result, krb5_format_time(result))
return result return result

View File

@ -1197,34 +1197,71 @@ class MemcacheSessionManager(SessionManager):
krbccache_dir ='/var/run/ipa_memcached' krbccache_dir ='/var/run/ipa_memcached'
krbccache_prefix = 'krbcc_' krbccache_prefix = 'krbcc_'
def get_krbccache_pathname(): def _get_krbccache_pathname():
return os.path.join(krbccache_dir, '%s%s' % (krbccache_prefix, os.getpid())) return os.path.join(krbccache_dir, '%s%s' % (krbccache_prefix, os.getpid()))
def read_krbccache_file(krbccache_pathname): def get_ipa_ccache_name(scheme='FILE'):
root_logger.debug('reading krbccache data from "%s"', krbccache_pathname) if scheme == 'FILE':
src = open(krbccache_pathname) name = os.path.join(krbccache_dir, '%s%s' % (krbccache_prefix, os.getpid()))
else:
raise ValueError('ccache scheme "%s" unsupported', scheme)
ccache_name = krb5_unparse_ccache(scheme, name)
return ccache_name
def load_ccache_data(ccache_name):
scheme, name = krb5_parse_ccache(ccache_name)
if scheme == 'FILE':
root_logger.debug('reading ccache data from file "%s"', name)
src = open(name)
ccache_data = src.read() ccache_data = src.read()
src.close() src.close()
return ccache_data return ccache_data
else:
raise ValueError('ccache scheme "%s" unsupported (%s)', scheme, ccache_name)
def store_krbccache_file(ccache_data): def bind_ipa_ccache(ccache_data, scheme='FILE'):
krbccache_pathname = get_krbccache_pathname() if scheme == 'FILE':
root_logger.debug('storing krbccache data into "%s"', krbccache_pathname) name = _get_krbccache_pathname()
dst = open(krbccache_pathname, 'w') root_logger.debug('storing ccache data into file "%s"', name)
dst = open(name, 'w')
dst.write(ccache_data) dst.write(ccache_data)
dst.close() dst.close()
else:
raise ValueError('ccache scheme "%s" unsupported', scheme)
return krbccache_pathname ccache_name = krb5_unparse_ccache(scheme, name)
os.environ['KRB5CCNAME'] = ccache_name
return ccache_name
def delete_krbccache_file(krbccache_pathname=None): def release_ipa_ccache(ccache_name):
if krbccache_pathname is None: '''
krbccache_pathname = get_krbccache_pathname() Stop using the current request's ccache.
* Remove KRB5CCNAME from the enviroment
* Remove the ccache file from the file system
Note, we do not demand any of these elements exist, but if they
do we'll remove them.
'''
if os.environ.has_key('KRB5CCNAME'):
if ccache_name != os.environ['KRB5CCNAME']:
root_logger.error('release_ipa_ccache: ccache_name (%s) != KRB5CCNAME environment variable (%s)',
ccache_name, os.environ['KRB5CCNAME'])
del os.environ['KRB5CCNAME']
else:
root_logger.debug('release_ipa_ccache: KRB5CCNAME environment variable not set')
scheme, name = krb5_parse_ccache(ccache_name)
if scheme == 'FILE':
if os.path.exists(name):
try: try:
os.unlink(krbccache_pathname) os.unlink(name)
except Exception, e: except Exception, e:
root_logger.error('unable to delete session krbccache file "%s", %s', root_logger.error('unable to delete session ccache file "%s", %s', name, e)
krbccache_pathname, e) else:
raise ValueError('ccache scheme "%s" unsupported (%s)', scheme, ccache_name)
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------

View File

@ -25,9 +25,10 @@ Loads WSGI server plugins.
from ipalib import api from ipalib import api
if 'in_server' in api.env and api.env.in_server is True: if 'in_server' in api.env and api.env.in_server is True:
from ipaserver.rpcserver import wsgi_dispatch, xmlserver, jsonserver_kerb, jsonserver_session, krblogin from ipaserver.rpcserver import wsgi_dispatch, xmlserver, jsonserver_kerb, jsonserver_session, login_kerberos, login_password
api.register(wsgi_dispatch) api.register(wsgi_dispatch)
api.register(xmlserver) api.register(xmlserver)
api.register(jsonserver_kerb) api.register(jsonserver_kerb)
api.register(jsonserver_session) api.register(jsonserver_session)
api.register(krblogin) api.register(login_kerberos)
api.register(login_password)

View File

@ -27,14 +27,15 @@ from cgi import parse_qs
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
from xmlrpclib import Fault from xmlrpclib import Fault
from ipalib.backend import Executioner from ipalib.backend import Executioner
from ipalib.errors import PublicError, InternalError, CommandError, JSONError, ConversionError, CCacheError, RefererError from ipalib.errors import PublicError, InternalError, CommandError, JSONError, ConversionError, CCacheError, RefererError, InvalidSessionPassword
from ipalib.request import context, Connection, destroy_context from ipalib.request import context, Connection, destroy_context
from ipalib.rpc import xml_dumps, xml_loads from ipalib.rpc import xml_dumps, xml_loads
from ipalib.util import make_repr, parse_time_duration from ipalib.util import make_repr, parse_time_duration
from ipapython.compat import json 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_duration from ipalib.session import session_mgr, AuthManager, get_ipa_ccache_name, load_ccache_data, bind_ipa_ccache, release_ipa_ccache, fmt_time, default_max_session_duration
from ipalib.backend import Backend from ipalib.backend import Backend
from ipalib.krb_utils import krb5_parse_ccache, KRB5_CCache, krb_ticket_expiration_threshold from ipalib.krb_utils import krb5_parse_ccache, KRB5_CCache, krb_ticket_expiration_threshold, krb5_format_principal_name
from ipapython import ipautil
from wsgiref.util import shift_path_info from wsgiref.util import shift_path_info
from ipapython.version import VERSION from ipapython.version import VERSION
import base64 import base64
@ -42,6 +43,11 @@ import os
import string import string
import datetime import datetime
from decimal import Decimal from decimal import Decimal
import urlparse
HTTP_STATUS_SUCCESS = '200 Success'
HTTP_STATUS_SERVER_ERROR = '500 Internal Server Error'
_not_found_template = """<html> _not_found_template = """<html>
<head> <head>
<title>404 Not Found</title> <title>404 Not Found</title>
@ -54,19 +60,84 @@ The requested URL <strong>%(url)s</strong> was not found on this server.
</body> </body>
</html>""" </html>"""
_bad_request_template = """<html>
<head>
<title>400 Bad Request</title>
</head>
<body>
<h1>Bad Request</h1>
<p>
<strong>%(message)s</strong>
</p>
</body>
</html>"""
_internal_error_template = """<html>
<head>
<title>500 Internal Server Error</title>
</head>
<body>
<h1>Internal Server Error</h1>
<p>
<strong>%(message)s</strong>
</p>
</body>
</html>"""
_unauthorized_template = """<html>
<head>
<title>401 Unauthorized</title>
</head>
<body>
<h1>Invalid Authentication</h1>
<p>
<strong>%(message)s</strong>
</p>
</body>
</html>"""
def not_found(environ, start_response): def not_found(environ, start_response):
""" """
Return a 404 Not Found error. Return a 404 Not Found error.
""" """
status = '404 Not Found' status = '404 Not Found'
response_headers = [('Content-Type', 'text/html')] response_headers = [('Content-Type', 'text/html; charset=utf-8')]
start_response(status, response_headers) start_response(status, response_headers)
output = _not_found_template % dict( output = _not_found_template % dict(
url=escape(environ['SCRIPT_NAME'] + environ['PATH_INFO']) url=escape(environ['SCRIPT_NAME'] + environ['PATH_INFO'])
) )
return [output] return [output]
def bad_request(environ, start_response, message):
"""
Return a 400 Bad Request error.
"""
status = '400 Bad Request'
response_headers = [('Content-Type', 'text/html; charset=utf-8')]
start_response(status, response_headers)
output = _bad_request_template % dict(message=escape(message))
return [output]
def internal_error(environ, start_response, message):
"""
Return a 500 Internal Server Error.
"""
status = HTTP_STATUS_SERVER_ERROR
response_headers = [('Content-Type', 'text/html; charset=utf-8')]
start_response(status, response_headers)
output = _internal_error_template % dict(message=escape(message))
return [output]
def unauthorized(environ, start_response, message):
"""
Return a 401 Unauthorized error.
"""
status = '401 Unauthorized'
response_headers = [('Content-Type', 'text/html; charset=utf-8')]
start_response(status, response_headers)
output = _unauthorized_template % dict(message=escape(message))
return [output]
def read_input(environ): def read_input(environ):
""" """
Read the request body from environ['wsgi.input']. Read the request body from environ['wsgi.input'].
@ -267,14 +338,14 @@ class WSGIExecutioner(Executioner):
self.debug('WSGI WSGIExecutioner.__call__:') self.debug('WSGI WSGIExecutioner.__call__:')
try: try:
status = '200 OK' status = HTTP_STATUS_SUCCESS
response = self.wsgi_execute(environ) response = self.wsgi_execute(environ)
headers = [('Content-Type', self.content_type + '; charset=utf-8')] headers = [('Content-Type', self.content_type + '; charset=utf-8')]
except StandardError, e: except StandardError, e:
self.exception('WSGI %s.__call__():', self.name) self.exception('WSGI %s.__call__():', self.name)
status = '500 Internal Server Error' status = HTTP_STATUS_SERVER_ERROR
response = status response = status
headers = [('Content-Type', 'text/plain')] headers = [('Content-Type', 'text/plain; charset=utf-8')]
session_data = getattr(context, 'session_data', None) session_data = getattr(context, 'session_data', None)
if session_data is not None: if session_data is not None:
@ -316,17 +387,16 @@ class xmlserver(WSGIExecutioner):
''' '''
self.debug('WSGI xmlserver.__call__:') self.debug('WSGI xmlserver.__call__:')
ccache=environ.get('KRB5CCNAME') user_ccache=environ.get('KRB5CCNAME')
if ccache is None: if user_ccache is None:
return self.marshal(None, CCacheError()) return self.marshal(None, CCacheError())
self.create_context(ccache=ccache)
try: try:
self.create_context(ccache=environ.get('KRB5CCNAME')) self.create_context(ccache=user_ccache)
response = super(xmlserver, self).__call__(environ, start_response) response = super(xmlserver, self).__call__(environ, start_response)
except PublicError, e: except PublicError, e:
status = '200 OK' status = HTTP_STATUS_SUCCESS
response = status response = status
headers = [('Content-Type', 'text/plain')] headers = [('Content-Type', 'text/plain; charset=utf-8')]
start_response(status, headers) start_response(status, headers)
return self.marshal(None, e) return self.marshal(None, e)
finally: finally:
@ -619,6 +689,40 @@ class KerberosSession(object):
max_age=krb_expiration, max_age=krb_expiration,
duration_type=self.api.env.session_duration_type) duration_type=self.api.env.session_duration_type)
def finalize_kerberos_acquisition(self, who, ccache_name, environ, start_response, headers=None):
if headers is None:
headers = []
# Retrieve the session data (or newly create)
session_data = session_mgr.load_session_data(environ.get('HTTP_COOKIE'))
session_id = session_data['session_id']
self.debug('finalize_kerberos_acquisition: %s ccache_name="%s" session_id="%s"',
who, ccache_name, session_id)
# Copy the ccache file contents into the session data
session_data['ccache_data'] = load_ccache_data(ccache_name)
# Set when the session will expire
cc = KRB5_CCache(ccache_name)
endtime = cc.endtime(self.api.env.host, self.api.env.realm)
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)
# The request is finished with the ccache, destroy it.
release_ipa_ccache(ccache_name)
# Return success and set session cookie
session_cookie = session_mgr.generate_cookie('/ipa', session_id)
headers.append(('Set-Cookie', session_cookie))
start_response(HTTP_STATUS_SUCCESS, headers)
return ['']
class jsonserver_session(jsonserver, KerberosSession): class jsonserver_session(jsonserver, KerberosSession):
""" """
JSON RPC server protected with session auth. JSON RPC server protected with session auth.
@ -668,13 +772,14 @@ class jsonserver_session(jsonserver, KerberosSession):
self.debug('no ccache, need login') self.debug('no ccache, need login')
return self.need_login(start_response) return self.need_login(start_response)
krbccache_pathname = store_krbccache_file(ccache_data) ipa_ccache_name = bind_ipa_ccache(ccache_data)
# Redirect to login if Kerberos credentials are expired # Redirect to login if Kerberos credentials are expired
cc = KRB5_CCache(krbccache_pathname) cc = KRB5_CCache(ipa_ccache_name)
if not cc.valid(self.api.env.host, self.api.env.realm): if not cc.valid(self.api.env.host, self.api.env.realm):
self.debug('ccache expired, deleting session, need login') self.debug('ccache expired, deleting session, need login')
delete_krbccache_file(krbccache_pathname) # The request is finished with the ccache, destroy it.
release_ipa_ccache(ipa_ccache_name)
return self.need_login(start_response) return self.need_login(start_response)
# Update the session expiration based on the Kerberos expiration # Update the session expiration based on the Kerberos expiration
@ -684,7 +789,7 @@ class jsonserver_session(jsonserver, KerberosSession):
# Store the session data in the per-thread context # Store the session data in the per-thread context
setattr(context, 'session_data', session_data) setattr(context, 'session_data', session_data)
self.create_context(ccache=krbccache_pathname) self.create_context(ccache=ipa_ccache_name)
try: try:
response = super(jsonserver_session, self).__call__(environ, start_response) response = super(jsonserver_session, self).__call__(environ, start_response)
@ -701,10 +806,10 @@ class jsonserver_session(jsonserver, KerberosSession):
# data to invalidate the session credentials. # data to invalidate the session credentials.
if session_data.has_key('ccache_data'): if session_data.has_key('ccache_data'):
session_data['ccache_data'] = read_krbccache_file(krbccache_pathname) session_data['ccache_data'] = load_ccache_data(ipa_ccache_name)
# Delete the temporary ccache file we used # The request is finished with the ccache, destroy it.
delete_krbccache_file(krbccache_pathname) release_ipa_ccache(ipa_ccache_name)
# Store the session data. # Store the session data.
session_mgr.store_session_data(session_data) session_mgr.store_session_data(session_data)
destroy_context() destroy_context()
@ -724,10 +829,10 @@ class jsonserver_kerb(jsonserver):
self.debug('WSGI jsonserver_kerb.__call__:') self.debug('WSGI jsonserver_kerb.__call__:')
ccache=environ.get('KRB5CCNAME') user_ccache=environ.get('KRB5CCNAME')
if ccache is None: if user_ccache is None:
return self.marshal(None, CCacheError()) return self.marshal(None, CCacheError())
self.create_context(ccache=ccache) self.create_context(ccache=user_ccache)
try: try:
response = super(jsonserver_kerb, self).__call__(environ, start_response) response = super(jsonserver_kerb, self).__call__(environ, start_response)
@ -737,59 +842,96 @@ class jsonserver_kerb(jsonserver):
return response return response
class krblogin(Backend, KerberosSession): class login_kerberos(Backend, KerberosSession):
key = '/login' key = '/session/login_kerberos'
def __init__(self): def __init__(self):
super(krblogin, self).__init__() super(login_kerberos, self).__init__()
def _on_finalize(self): def _on_finalize(self):
super(krblogin, self)._on_finalize() super(login_kerberos, self)._on_finalize()
self.api.Backend.wsgi_dispatch.mount(self, self.key) self.api.Backend.wsgi_dispatch.mount(self, self.key)
self.kerb_session_on_finalize() self.kerb_session_on_finalize()
def __call__(self, environ, start_response): def __call__(self, environ, start_response):
headers = [] self.debug('WSGI login_kerberos.__call__:')
self.debug('WSGI krblogin.__call__:')
# Get the ccache created by mod_auth_kerb # Get the ccache created by mod_auth_kerb
ccache=environ.get('KRB5CCNAME') user_ccache_name=environ.get('KRB5CCNAME')
if ccache is None: if user_ccache_name is None:
status = '500 Internal Error' return internal_error(environ, start_response, 'KRB5CCNAME not defined')
response = 'KRB5CCNAME not defined'
start_response(status, headers)
return [response]
ccache_scheme, ccache_location = krb5_parse_ccache(ccache) return self.finalize_kerberos_acquisition('login_kerberos', user_ccache_name, environ, start_response)
assert ccache_scheme == 'FILE'
# Retrieve the session data (or newly create) class login_password(Backend, KerberosSession):
session_data = session_mgr.load_session_data(environ.get('HTTP_COOKIE'))
session_id = session_data['session_id']
# Copy the ccache file contents into the session data content_type = 'text/plain'
session_data['ccache_data'] = read_krbccache_file(ccache_location) key = '/session/login_password'
# Set when the session will expire def __init__(self):
cc = KRB5_CCache(ccache) super(login_password, self).__init__()
endtime = cc.endtime(self.api.env.host, self.api.env.realm)
self.update_session_expiration(session_data, endtime)
# Store the session data now that it's been updated with the ccache def _on_finalize(self):
session_mgr.store_session_data(session_data) super(login_password, self)._on_finalize()
self.api.Backend.wsgi_dispatch.mount(self, self.key)
self.kerb_session_on_finalize()
self.debug('krblogin: ccache="%s" session_id="%s" ccache="%s"', def __call__(self, environ, start_response):
ccache, session_id, ccache) self.debug('WSGI login_password.__call__:')
# Return success and set session cookie # Get the user and password parameters from the request
status = '200 Success' content_type = environ.get('CONTENT_TYPE', '').lower()
response = '' if content_type != 'application/x-www-form-urlencoded':
return bad_request(environ, start_response, "Content-Type must be application/x-www-form-urlencoded")
session_cookie = session_mgr.generate_cookie('/ipa', session_id) method = environ.get('REQUEST_METHOD', '').upper()
headers.append(('Set-Cookie', session_cookie)) if method == 'POST':
query_string = read_input(environ)
else:
return bad_request(environ, start_response, "HTTP request method must be POST")
start_response(status, headers) try:
return [response] query_dict = urlparse.parse_qs(query_string)
except Exception, e:
return bad_request(environ, start_response, "cannot parse query data")
user = query_dict.get('user', None)
if user is not None:
if len(user) == 1:
user = user[0]
else:
return bad_request(environ, start_response, "more than one user parameter")
else:
return bad_request(environ, start_response, "no user specified")
password = query_dict.get('password', None)
if password is not None:
if len(password) == 1:
password = password[0]
else:
return bad_request(environ, start_response, "more than one password parameter")
else:
return bad_request(environ, start_response, "no password specified")
# Get the ccache we'll use and attempt to get credentials in it with user,password
ipa_ccache_name = get_ipa_ccache_name()
try:
self.kinit(user, self.api.env.realm, password, ipa_ccache_name)
except InvalidSessionPassword, e:
return unauthorized(environ, start_response, str(e))
return self.finalize_kerberos_acquisition('login_password', ipa_ccache_name, environ, start_response)
def kinit(self, user, realm, password, ccache_name):
# Format the user as a kerberos principal
principal = krb5_format_principal_name(user, realm)
(stdout, stderr, returncode) = ipautil.run(['/usr/bin/kinit', principal],
env={'KRB5CCNAME':ccache_name},
stdin=password, raiseonerr=False)
self.debug('kinit: principal=%s returncode=%s, stderr="%s"',
principal, returncode, stderr)
if returncode != 0:
raise InvalidSessionPassword(principal=principal, message=unicode(stderr))

View File

@ -100,14 +100,14 @@ class test_session(object):
) )
inst = self.klass() inst = self.klass()
inst.mount(app1, 'foo') inst.mount(app1, '/foo/stuff')
inst.mount(app2, 'bar') inst.mount(app2, '/bar')
d = dict(SCRIPT_NAME='/ipa', PATH_INFO='/foo/stuff') d = dict(SCRIPT_NAME='/ipa', PATH_INFO='/foo/stuff')
assert inst.route(d, None) == ('from 1', ['/ipa/foo', '/stuff']) assert inst.route(d, None) == ('from 1', ['/ipa', '/foo/stuff'])
d = dict(SCRIPT_NAME='/ipa', PATH_INFO='/bar') d = dict(SCRIPT_NAME='/ipa', PATH_INFO='/bar')
assert inst.route(d, None) == ('from 2', ['/ipa/bar', '']) assert inst.route(d, None) == ('from 2', ['/ipa', '/bar'])
def test_mount(self): def test_mount(self):
def app1(environ, start_response): def app1(environ, start_response):