summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2012-04-13 15:19:32 -0400
committerRob Crittenden <rcritten@redhat.com>2012-04-16 21:53:37 -0400
commitd05a5c646087877abe3819116cfc84ef9b053567 (patch)
tree5fd0220bd8f7af607ac1e7d42375cbf97459118c
parentdc0132addaf2a26daaf5f3b52dffdcb1502a9c03 (diff)
downloadfreeipa-d05a5c646087877abe3819116cfc84ef9b053567.tar.gz
freeipa-d05a5c646087877abe3819116cfc84ef9b053567.tar.xz
freeipa-d05a5c646087877abe3819116cfc84ef9b053567.zip
Return consistent expiration message for forms-based login
We need to inform users when a forms-based login fails due to the password needing to be reset. Currently there is no way to distinguish a reset case vs an incorrect password. This will bind the user using a simple LDAP bind over ldapi (by default) and if that is successful, check the expiration date against the current time. The UI portion of this that uses this message will come later. https://fedorahosted.org/freeipa/ticket/2608
-rw-r--r--ipaserver/rpcserver.py41
-rw-r--r--tests/test_ipaserver/test_rpcserver.py5
2 files changed, 42 insertions, 4 deletions
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
index 2fbd79f20..83c5c2d67 100644
--- a/ipaserver/rpcserver.py
+++ b/ipaserver/rpcserver.py
@@ -32,6 +32,8 @@ from ipalib.errors import PublicError, InternalError, CommandError, JSONError, C
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 ipalib.dn import DN
+from ipaserver.plugins.ldap2 import ldap2
from ipapython.compat import json
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
@@ -45,6 +47,7 @@ import string
import datetime
from decimal import Decimal
import urlparse
+import time
HTTP_STATUS_SUCCESS = '200 Success'
HTTP_STATUS_SERVER_ERROR = '500 Internal Server Error'
@@ -136,12 +139,14 @@ class HTTP_Status(plugable.Plugin):
output = _internal_error_template % dict(message=escape(message))
return [output]
- def unauthorized(self, environ, start_response, message):
+ def unauthorized(self, environ, start_response, message, reason):
"""
Return a 401 Unauthorized error.
"""
status = '401 Unauthorized'
response_headers = [('Content-Type', 'text/html; charset=utf-8')]
+ if reason:
+ response_headers.append(('X-IPA-Rejection-Reason', reason))
self.info('%s: %s', status, message)
@@ -935,10 +940,42 @@ class login_password(Backend, KerberosSession, HTTP_Status):
# Get the ccache we'll use and attempt to get credentials in it with user,password
ipa_ccache_name = get_ipa_ccache_name()
+ reason = 'invalid-password'
try:
self.kinit(user, self.api.env.realm, password, ipa_ccache_name)
except InvalidSessionPassword, e:
- return self.unauthorized(environ, start_response, str(e))
+ # Ok, now why is this bad. Is the password simply bad or is the
+ # password expired?
+ try:
+ dn = str(DN(('uid', user),
+ self.api.env.container_user,
+ self.api.env.basedn))
+ conn = ldap2(shared_instance=False,
+ ldap_uri=self.api.env.ldap_uri)
+ conn.connect(bind_dn=dn, bind_pw=password)
+
+ # password is ok, must be expired, lets double-check
+ (userdn, entry_attrs) = conn.get_entry(dn,
+ ['krbpasswordexpiration'])
+ if 'krbpasswordexpiration' in entry_attrs:
+ expiration = entry_attrs['krbpasswordexpiration'][0]
+ try:
+ exp = time.strptime(expiration, '%Y%m%d%H%M%SZ')
+ if exp <= time.gmtime():
+ reason = 'password-expired'
+ except ValueError, v:
+ self.error('Unable to convert %s to a time string'
+ % expiration)
+
+ except Exception:
+ # It doesn't really matter how we got here but the user's
+ # password is not accepted or the user is unknown.
+ pass
+ finally:
+ if conn.isconnected():
+ conn.destroy_connection()
+
+ return self.unauthorized(environ, start_response, str(e), reason)
return self.finalize_kerberos_acquisition('login_password', ipa_ccache_name, environ, start_response)
diff --git a/tests/test_ipaserver/test_rpcserver.py b/tests/test_ipaserver/test_rpcserver.py
index 96d4614a1..230eef241 100644
--- a/tests/test_ipaserver/test_rpcserver.py
+++ b/tests/test_ipaserver/test_rpcserver.py
@@ -102,11 +102,12 @@ def test_unauthorized_error():
s = StartResponse()
assert_equal(
- f.unauthorized(None, s, 'unauthorized'),
+ f.unauthorized(None, s, 'unauthorized', 'password-expired'),
[t % dict(message='unauthorized')]
)
assert s.status == '401 Unauthorized'
- assert s.headers == [('Content-Type', 'text/html; charset=utf-8')]
+ assert s.headers == [('Content-Type', 'text/html; charset=utf-8'),
+ ('X-IPA-Rejection-Reason', 'password-expired')]
def test_params_2_args_options():