diff options
author | Rob Crittenden <rcritten@redhat.com> | 2012-02-14 09:41:25 -0500 |
---|---|---|
committer | Martin Kosek <mkosek@redhat.com> | 2012-03-02 16:28:54 +0100 |
commit | d5c9f7bcaa257571fa9f4092876864df86876fd3 (patch) | |
tree | 5a2b5f9bec91e590477a9d1dc867f140195dfb2d | |
parent | 1cc761353bad6d279cd7d14939f67baef784433f (diff) | |
download | freeipa-d5c9f7bcaa257571fa9f4092876864df86876fd3.tar.gz freeipa-d5c9f7bcaa257571fa9f4092876864df86876fd3.tar.xz freeipa-d5c9f7bcaa257571fa9f4092876864df86876fd3.zip |
Add status command to retrieve user lockout status
This information is not replicated so pull from all IPA masters
and display the status across all servers.
https://fedorahosted.org/freeipa/ticket/2162
-rw-r--r-- | API.txt | 10 | ||||
-rw-r--r-- | ipalib/plugins/user.py | 113 |
2 files changed, 122 insertions, 1 deletions
@@ -3218,6 +3218,16 @@ option: Str('version?', exclude='webui') output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('value', <type 'unicode'>, None) +command: user_status +args: 1,3,4 +arg: Str('uid', attribute=True, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', pattern_errmsg='may only include letters, numbers, _, -, . and $', primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('version?', exclude='webui') +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) +output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None)) +output: Output('count', <type 'int'>, None) +output: Output('truncated', <type 'bool'>, None) command: user_unlock args: 1,0,3 arg: Str('uid', attribute=True, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', pattern_errmsg='may only include letters, numbers, _, -, . and $', primary_key=True, query=True, required=True) diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py index 591132d3..64424e88 100644 --- a/ipalib/plugins/user.py +++ b/ipalib/plugins/user.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -from time import gmtime, strftime +from time import gmtime, strftime, strptime import copy import string @@ -27,9 +27,13 @@ from ipalib import Flag, Int, Password, Str, Bool, Bytes from ipalib.plugins.baseldap import * from ipalib.request import context from ipalib import _, ngettext +from ipalib import output from ipapython.ipautil import ipa_generate_password import posixpath from ipalib.util import validate_sshpubkey, output_sshpubkey +if api.env.in_server and api.env.context in ['lite', 'server']: + from ipaserver.plugins.ldap2 import ldap2 + import os __doc__ = _(""" Users @@ -79,6 +83,21 @@ user_output_params = ( ), ) +status_output_params = ( + Str('server', + label=_('Server'), + ), + Str('krbloginfailedcount', + label=_('Failed logins'), + ), + Str('krblastsuccessfulauth', + label=_('Last successful authentication'), + ), + Str('krblastfailedauth', + label=_('Last failed authentication'), + ), + ) + # characters to be used for generating random user passwords user_pwdchars = string.digits + string.ascii_letters + '_,.@+-=' @@ -681,3 +700,95 @@ class user_unlock(LDAPQuery): ) api.register(user_unlock) + +class user_status(LDAPQuery): + __doc__ = _(""" + Lockout status of a user account + + An account may become locked if the password is entered incorrectly too + many times within a specific time period as controlled by password + policy. A locked account is a temporary condition and may be unlocked by + an administrator. + + This connects to each IPA master and displays the lockout status on + each one.""") + + has_output = output.standard_list_of_entries + has_output_params = LDAPSearch.has_output_params + status_output_params + + def execute(self, *keys, **options): + ldap = self.obj.backend + dn = self.obj.get_dn(*keys, **options) + attr_list = ['krbloginfailedcount', 'krblastsuccessfulauth', 'krblastfailedauth'] + + masters = [] + # Get list of masters + try: + (masters, truncated) = ldap.find_entries( + None, ['*'], 'cn=masters,cn=ipa,cn=etc,%s' % api.env.basedn, + ldap.SCOPE_ONELEVEL + ) + except errors.NotFound: + # If this happens we have some pretty serious problems + self.error('No IPA masters found!') + pass + + entries = [] + count = 0 + for master in masters: + host = master[1]['cn'][0] + if host == api.env.host: + other_ldap = self.obj.backend + else: + other_ldap = ldap2(shared_instance=False, + ldap_uri='ldap://%s' % host, + base_dn=self.api.env.basedn) + try: + other_ldap.connect(ccache=os.environ['KRB5CCNAME']) + except Exception, e: + self.error("user_status: Connecting to %s failed with %s" % (host, str(e))) + newresult = dict() + newresult['dn'] = dn + newresult['server'] = _("%(host)s failed: %(error)s") % dict(host=host, error=str(e)) + entries.append(newresult) + count += 1 + continue + try: + entry = other_ldap.get_entry(dn, attr_list) + newresult = dict() + for attr in ['krblastsuccessfulauth', 'krblastfailedauth']: + newresult[attr] = entry[1].get(attr, [u'N/A']) + newresult['krbloginfailedcount'] = entry[1].get('krbloginfailedcount', u'0') + if not options.get('raw', False): + for attr in ['krblastsuccessfulauth', 'krblastfailedauth']: + try: + if newresult[attr][0] == u'N/A': + continue + newtime = time.strptime(newresult[attr][0], '%Y%m%d%H%M%SZ') + newresult[attr][0] = unicode(time.strftime('%Y-%m-%dT%H:%M:%SZ', newtime)) + except Exception, e: + self.debug("time conversion failed with %s" % str(e)) + pass + newresult['dn'] = dn + newresult['server'] = host + entries.append(newresult) + count += 1 + except errors.NotFound: + self.obj.handle_not_found(*keys) + except Exception, e: + self.error("user_status: Retrieving status for %s failed with %s" % (dn, str(e))) + newresult = dict() + newresult['dn'] = dn + newresult['server'] = _("%(host)s failed") % dict(host=host) + entries.append(newresult) + count += 1 + + if host != api.env.host: + other_ldap.destroy_connection() + + return dict(result=entries, + count=count, + truncated=False, + ) + +api.register(user_status) |