Attached is a revised patch, it addresses the following concerns raised during review:

* The version in ipa.conf has been bumped.

* Rob reported duplicate session cookies being returned. As far as I can tell this was due to a Python bug where it reused the value of a default keyword parameter from a previous invocation rather than re-initializing it. Workaround is to change the default value from [] to the value to None and create an empty list if the arg is None.

* Rob reported two test failures, one for ERRNO (e.g. **1234**) not being present in the docstring for an error I added and the other was for a change in the wsgi dispatch route() method that showed up in test_rpcserver.py



--
John Dennis <[email protected]>

Looking to carve out IT costs?
www.redhat.com/carveoutcosts/
>From 0a3758276e75d540cecce72703a744fa37e26f14 Mon Sep 17 00:00:00 2001
From: John Dennis <[email protected]>
Date: Sat, 25 Feb 2012 13:39:19 -0500
Subject: [PATCH 64-1] Implement password based session login
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit

* 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 either
  GET or 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
---
 freeipa.spec.in                        |    1 +
 install/conf/ipa.conf                  |   10 +-
 install/ui/ipa.js                      |    2 +-
 ipalib/errors.py                       |    7 +
 ipalib/krb_utils.py                    |   13 +-
 ipalib/session.py                      |   79 +++++++----
 ipaserver/plugins/xmlserver.py         |    5 +-
 ipaserver/rpcserver.py                 |  248 +++++++++++++++++++++++++-------
 tests/test_ipaserver/test_rpcserver.py |    8 +-
 9 files changed, 279 insertions(+), 94 deletions(-)

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 47d0f28..0388453 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -95,6 +95,7 @@ Requires: nss
 Requires: nss-tools
 Requires: krb5-server >= 1.9.2-6
 Requires: krb5-pkinit-openssl
+Requires: krb5-workstation
 Requires: cyrus-sasl-gssapi%{?_isa}
 Requires: ntp
 Requires: httpd
diff --git a/install/conf/ipa.conf b/install/conf/ipa.conf
index cd806be..89c9849 100644
--- a/install/conf/ipa.conf
+++ b/install/conf/ipa.conf
@@ -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
 
@@ -60,7 +60,13 @@ KrbConstrainedDelegationLock ipa
 </Location>
 
 # 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
   Order Deny,Allow
   Allow from all
diff --git a/install/ui/ipa.js b/install/ui/ipa.js
index a599f6a..433d7fe 100644
--- a/install/ui/ipa.js
+++ b/install/ui/ipa.js
@@ -60,7 +60,7 @@ var IPA = function() {
         // if current path matches live server path, use live data
         if (that.url && window.location.pathname.substring(0, that.url.length) === that.url) {
             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
             that.json_path = params.url || "test/data";
diff --git a/ipalib/errors.py b/ipalib/errors.py
index bdc4a5e..df4ab41 100644
--- a/ipalib/errors.py
+++ b/ipalib/errors.py
@@ -612,6 +612,13 @@ class SessionError(AuthenticationError):
     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
 class AuthorizationError(PublicError):
diff --git a/ipalib/krb_utils.py b/ipalib/krb_utils.py
index 7e68bf6..a8f7751 100644
--- a/ipalib/krb_utils.py
+++ b/ipalib/krb_utils.py
@@ -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
     location components. Currently valid values for the scheme
@@ -55,12 +55,12 @@ def krb5_parse_ccache(name):
     does not exist it defaults to FILE.
 
     :parameters:
-      name
+      ccache_name
         The name of the Kerberos ccache.
     :returns:
       A two-tuple of (scheme, ccache)
     '''
-    match = ccache_name_re.search(name)
+    match = ccache_name_re.search(ccache_name)
     if match:
         scheme = match.group(2)
         location = match.group(3)
@@ -71,7 +71,10 @@ def krb5_parse_ccache(name):
 
         return scheme, location
     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):
     '''
@@ -388,5 +391,5 @@ class KRB5_CCache(object):
         except KeyError:
             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
diff --git a/ipalib/session.py b/ipalib/session.py
index 5c71a92..c742363 100644
--- a/ipalib/session.py
+++ b/ipalib/session.py
@@ -1197,34 +1197,61 @@ class MemcacheSessionManager(SessionManager):
 krbccache_dir ='/var/run/ipa_memcached'
 krbccache_prefix = 'krbcc_'
 
-def get_krbccache_pathname():
+def _get_krbccache_pathname():
     return os.path.join(krbccache_dir, '%s%s' % (krbccache_prefix, os.getpid()))
 
-def read_krbccache_file(krbccache_pathname):
-    root_logger.debug('reading krbccache data from "%s"', krbccache_pathname)
-    src = open(krbccache_pathname)
-    ccache_data = src.read()
-    src.close()
-    return ccache_data
-
-def store_krbccache_file(ccache_data):
-    krbccache_pathname = get_krbccache_pathname()
-    root_logger.debug('storing krbccache data into "%s"', krbccache_pathname)
-    dst = open(krbccache_pathname, 'w')
-    dst.write(ccache_data)
-    dst.close()
-
-    return krbccache_pathname
-
-def delete_krbccache_file(krbccache_pathname=None):
-    if krbccache_pathname is None:
-        krbccache_pathname = get_krbccache_pathname()
-
-    try:
-        os.unlink(krbccache_pathname)
-    except Exception, e:
-        root_logger.error('unable to delete session krbccache file "%s", %s',
-                          krbccache_pathname, e)
+def get_ipa_ccache_name(scheme='FILE'):
+    if scheme == 'FILE':
+        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()
+        src.close()
+        return ccache_data
+    else:
+        raise ValueError('ccache scheme "%s" unsupported (%s)', scheme, ccache_name)
+
+def bind_ipa_ccache(ccache_data, scheme='FILE'):
+    if scheme == 'FILE':
+        name = _get_krbccache_pathname()
+        root_logger.debug('storing ccache data into file "%s"', name)
+        dst = open(name, 'w')
+        dst.write(ccache_data)
+        dst.close()
+    else:
+        raise ValueError('ccache scheme "%s" unsupported', scheme)
+
+    ccache_name = krb5_unparse_ccache(scheme, name)
+    os.environ['KRB5CCNAME'] = ccache_name
+    return ccache_name
+
+def release_ipa_ccache(ccache_name):
+    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.error('release_ipa_ccache: KRB5CCNAME environment variable not set')
+
+    scheme, name = krb5_parse_ccache(ccache_name)
+    if scheme == 'FILE':
+        try:
+            os.unlink(name)
+        except Exception, e:
+            root_logger.error('unable to delete session ccache file "%s", %s', name, e)
+    else:
+        raise ValueError('ccache scheme "%s" unsupported (%s)', scheme, ccache_name)
 
 
 #-------------------------------------------------------------------------------
diff --git a/ipaserver/plugins/xmlserver.py b/ipaserver/plugins/xmlserver.py
index d2a28ec..4ae9149 100644
--- a/ipaserver/plugins/xmlserver.py
+++ b/ipaserver/plugins/xmlserver.py
@@ -25,9 +25,10 @@ Loads WSGI server plugins.
 from ipalib import api
 
 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(xmlserver)
     api.register(jsonserver_kerb)
     api.register(jsonserver_session)
-    api.register(krblogin)
+    api.register(login_kerberos)
+    api.register(login_password)
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
index 0b8aa40..5e27a66 100644
--- a/ipaserver/rpcserver.py
+++ b/ipaserver/rpcserver.py
@@ -27,14 +27,15 @@ from cgi import parse_qs
 from xml.sax.saxutils import escape
 from xmlrpclib import Fault
 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.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_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.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 ipapython.version import VERSION
 import base64
@@ -42,6 +43,11 @@ import os
 import string
 import datetime
 from decimal import Decimal
+import urlparse
+
+HTTP_STATUS_SUCCESS = '200 Success'
+HTTP_STATUS_SERVER_ERROR = '500 Internal Server Error'
+
 _not_found_template = """<html>
 <head>
 <title>404 Not Found</title>
@@ -54,6 +60,41 @@ The requested URL <strong>%(url)s</strong> was not found on this server.
 </body>
 </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>"""
+
+_invalid_auth_template = """<html>
+<head>
+<title>403 Forbidden</title>
+</head>
+<body>
+<h1>Invalid Authentication</h1>
+<p>
+<strong>%(message)s</strong>
+</p>
+</body>
+</html>"""
 
 def not_found(environ, start_response):
     """
@@ -67,6 +108,36 @@ def not_found(environ, start_response):
     )
     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')]
+    start_response(status, response_headers)
+    output = _bad_request_template % dict(message=escape(message))
+    return [output]
+
+def internal_error(environ, start_response, message):
+    """
+    Return a 400 Bad Request error.
+    """
+    status = HTTP_STATUS_SERVER_ERROR
+    response_headers = [('Content-Type', 'text/html')]
+    start_response(status, response_headers)
+    output = _internal_error_template % dict(message=escape(message))
+    return [output]
+
+def invalid_auth(environ, start_response, message):
+    """
+    Return a 403 Forbidden error.
+    """
+    status = '403 Forbidden'
+    response_headers = [('Content-Type', 'text/html')]
+    start_response(status, response_headers)
+    output = _invalid_auth_template % dict(message=escape(message))
+    return [output]
+
 def read_input(environ):
     """
     Read the request body from environ['wsgi.input'].
@@ -267,12 +338,12 @@ class WSGIExecutioner(Executioner):
 
         self.debug('WSGI WSGIExecutioner.__call__:')
         try:
-            status = '200 OK'
+            status = HTTP_STATUS_SUCCESS
             response = self.wsgi_execute(environ)
             headers = [('Content-Type', self.content_type + '; charset=utf-8')]
         except StandardError, e:
             self.exception('WSGI %s.__call__():', self.name)
-            status = '500 Internal Server Error'
+            status = HTTP_STATUS_SERVER_ERROR
             response = status
             headers = [('Content-Type', 'text/plain')]
 
@@ -316,15 +387,14 @@ class xmlserver(WSGIExecutioner):
         '''
 
         self.debug('WSGI xmlserver.__call__:')
-        ccache=environ.get('KRB5CCNAME')
-        if ccache is None:
+        user_ccache=environ.get('KRB5CCNAME')
+        if user_ccache is None:
             return self.marshal(None, CCacheError())
-        self.create_context(ccache=ccache)
         try:
-            self.create_context(ccache=environ.get('KRB5CCNAME'))
+            self.create_context(ccache=user_ccache)
             response = super(xmlserver, self).__call__(environ, start_response)
         except PublicError, e:
-            status = '200 OK'
+            status = HTTP_STATUS_SUCCESS
             response = status
             headers = [('Content-Type', 'text/plain')]
             start_response(status, headers)
@@ -619,6 +689,37 @@ class KerberosSession(object):
                                                 max_age=krb_expiration,
                                                 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)
+
+        # 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):
     """
     JSON RPC server protected with session auth.
@@ -668,13 +769,13 @@ class jsonserver_session(jsonserver, KerberosSession):
             self.debug('no ccache, need login')
             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
-        cc = KRB5_CCache(krbccache_pathname)
+        cc = KRB5_CCache(ipa_ccache_name)
         if not cc.valid(self.api.env.host, self.api.env.realm):
             self.debug('ccache expired, deleting session, need login')
-            delete_krbccache_file(krbccache_pathname)
+            release_ipa_ccache(ipa_ccache_name)
             return self.need_login(start_response)
 
         # Update the session expiration based on the Kerberos expiration
@@ -684,7 +785,7 @@ class jsonserver_session(jsonserver, KerberosSession):
         # Store the session data in the per-thread context
         setattr(context, 'session_data', session_data)
 
-        self.create_context(ccache=krbccache_pathname)
+        self.create_context(ccache=ipa_ccache_name)
 
         try:
             response = super(jsonserver_session, self).__call__(environ, start_response)
@@ -701,10 +802,10 @@ class jsonserver_session(jsonserver, KerberosSession):
             # data to invalidate the session credentials.
 
             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
-            delete_krbccache_file(krbccache_pathname)
+            release_ipa_ccache(ipa_ccache_name)
             # Store the session data.
             session_mgr.store_session_data(session_data)
             destroy_context()
@@ -724,10 +825,10 @@ class jsonserver_kerb(jsonserver):
 
         self.debug('WSGI jsonserver_kerb.__call__:')
 
-        ccache=environ.get('KRB5CCNAME')
-        if ccache is None:
+        user_ccache=environ.get('KRB5CCNAME')
+        if user_ccache is None:
             return self.marshal(None, CCacheError())
-        self.create_context(ccache=ccache)
+        self.create_context(ccache=user_ccache)
 
         try:
             response = super(jsonserver_kerb, self).__call__(environ, start_response)
@@ -737,59 +838,98 @@ class jsonserver_kerb(jsonserver):
         return response
 
 
-class krblogin(Backend, KerberosSession):
-    key = '/login'
+class login_kerberos(Backend, KerberosSession):
+    key = '/session/login_kerberos'
 
     def __init__(self):
-        super(krblogin, self).__init__()
+        super(login_kerberos, self).__init__()
 
     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.kerb_session_on_finalize()
 
     def __call__(self, environ, start_response):
-        headers = []
-
-        self.debug('WSGI krblogin.__call__:')
+        self.debug('WSGI login_kerberos.__call__:')
 
         # Get the ccache created by mod_auth_kerb
-        ccache=environ.get('KRB5CCNAME')
-        if ccache is None:
-            status = '500 Internal Error'
-            response = 'KRB5CCNAME not defined'
-            start_response(status, headers)
-            return [response]
+        user_ccache_name=environ.get('KRB5CCNAME')
+        if user_ccache_name is None:
+            return internal_error(environ, start_response, 'KRB5CCNAME not defined')
 
-        ccache_scheme, ccache_location = krb5_parse_ccache(ccache)
-        assert ccache_scheme == 'FILE'
+        return self.finalize_kerberos_acquisition('login_kerberos', user_ccache_name, environ, start_response)
 
-        # Retrieve the session data (or newly create)
-        session_data = session_mgr.load_session_data(environ.get('HTTP_COOKIE'))
-        session_id = session_data['session_id']
+class login_password(Backend, KerberosSession):
 
-        # Copy the ccache file contents into the session data
-        session_data['ccache_data'] = read_krbccache_file(ccache_location)
+    content_type = 'text/plain'
+    key = '/session/login_password'
 
-        # Set when the session will expire
-        cc = KRB5_CCache(ccache)
-        endtime = cc.endtime(self.api.env.host, self.api.env.realm)
-        self.update_session_expiration(session_data, endtime)
+    def __init__(self):
+        super(login_password, self).__init__()
 
-        # Store the session data now that it's been updated with the ccache
-        session_mgr.store_session_data(session_data)
+    def _on_finalize(self):
+        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"',
-                   ccache, session_id, ccache)
+    def __call__(self, environ, start_response):
+        self.debug('WSGI login_password.__call__:')
+
+        # Get the user and password parameters from the request
+        content_type = environ.get('CONTENT_TYPE', '').lower()
+        if content_type != 'application/x-www-form-urlencoded':
+            return bad_request(environ, start_response, "Content-Type must be application/x-www-form-urlencoded")
+
+        method = environ.get('REQUEST_METHOD', '').upper()
+        if method == 'POST':
+            query_string = read_input(environ)
+        elif method == 'GET':
+            query_string = environ.get('QUERY_STRING', '')
+        else:
+            return bad_request(environ, start_response, "method must be GET or POST")
 
-        # Return success and set session cookie
-        status = '200 Success'
-        response = ''
+        try:
+            query_dict = urlparse.parse_qs(query_string)
+        except Exception, e:
+            return bad_request(environ, start_response, "cannot parse query data")
 
-        session_cookie = session_mgr.generate_cookie('/ipa', session_id)
-        headers.append(('Set-Cookie', session_cookie))
+        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")
 
-        start_response(status, headers)
-        return [response]
+        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 invalid_auth(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))
 
diff --git a/tests/test_ipaserver/test_rpcserver.py b/tests/test_ipaserver/test_rpcserver.py
index e712078..15ca9dc 100644
--- a/tests/test_ipaserver/test_rpcserver.py
+++ b/tests/test_ipaserver/test_rpcserver.py
@@ -100,14 +100,14 @@ class test_session(object):
             )
 
         inst = self.klass()
-        inst.mount(app1, 'foo')
-        inst.mount(app2, 'bar')
+        inst.mount(app1, '/foo/stuff')
+        inst.mount(app2, '/bar')
 
         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')
-        assert inst.route(d, None) == ('from 2', ['/ipa/bar', ''])
+        assert inst.route(d, None) == ('from 2', ['/ipa', '/bar'])
 
     def test_mount(self):
         def app1(environ, start_response):
-- 
1.7.7.6

_______________________________________________
Freeipa-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to