From e1e1db9c9fc71c88e847aba8a9a02bc383317c18 Mon Sep 17 00:00:00 2001 From: Pavel Zuna Date: Tue, 16 Jun 2009 13:44:37 +0200 Subject: Rename plugins2 files (remove '2' suffix'). --- ipalib/plugins/aci.py | 386 +++++++++++++++++++++ ipalib/plugins/aci2.py | 386 --------------------- ipalib/plugins/basegroup.py | 540 +++++++++++++++++++++++++++++ ipalib/plugins/basegroup2.py | 540 ----------------------------- ipalib/plugins/config.py | 183 ++++++++++ ipalib/plugins/config2.py | 183 ---------- ipalib/plugins/dns.py | 803 +++++++++++++++++++++++++++++++++++++++++++ ipalib/plugins/dns2.py | 803 ------------------------------------------- ipalib/plugins/group.py | 220 ++++++++++++ ipalib/plugins/group2.py | 220 ------------ ipalib/plugins/host.py | 375 ++++++++++++++++++++ ipalib/plugins/host2.py | 375 -------------------- ipalib/plugins/hostgroup.py | 240 +++++++++++++ ipalib/plugins/hostgroup2.py | 240 ------------- ipalib/plugins/netgroup.py | 372 ++++++++++++++++++++ ipalib/plugins/netgroup2.py | 372 -------------------- ipalib/plugins/passwd.py | 78 +++++ ipalib/plugins/passwd2.py | 78 ----- ipalib/plugins/pwpolicy.py | 148 ++++++++ ipalib/plugins/pwpolicy2.py | 148 -------- ipalib/plugins/rolegroup.py | 125 +++++++ ipalib/plugins/rolegroup2.py | 125 ------- ipalib/plugins/service.py | 362 +++++++++++++++++++ ipalib/plugins/service2.py | 362 ------------------- ipalib/plugins/taskgroup.py | 207 +++++++++++ ipalib/plugins/taskgroup2.py | 207 ----------- ipalib/plugins/user.py | 405 ++++++++++++++++++++++ ipalib/plugins/user2.py | 405 ---------------------- 28 files changed, 4444 insertions(+), 4444 deletions(-) create mode 100644 ipalib/plugins/aci.py delete mode 100644 ipalib/plugins/aci2.py create mode 100644 ipalib/plugins/basegroup.py delete mode 100644 ipalib/plugins/basegroup2.py create mode 100644 ipalib/plugins/config.py delete mode 100644 ipalib/plugins/config2.py create mode 100644 ipalib/plugins/dns.py delete mode 100644 ipalib/plugins/dns2.py create mode 100644 ipalib/plugins/group.py delete mode 100644 ipalib/plugins/group2.py create mode 100644 ipalib/plugins/host.py delete mode 100644 ipalib/plugins/host2.py create mode 100644 ipalib/plugins/hostgroup.py delete mode 100644 ipalib/plugins/hostgroup2.py create mode 100644 ipalib/plugins/netgroup.py delete mode 100644 ipalib/plugins/netgroup2.py create mode 100644 ipalib/plugins/passwd.py delete mode 100644 ipalib/plugins/passwd2.py create mode 100644 ipalib/plugins/pwpolicy.py delete mode 100644 ipalib/plugins/pwpolicy2.py create mode 100644 ipalib/plugins/rolegroup.py delete mode 100644 ipalib/plugins/rolegroup2.py create mode 100644 ipalib/plugins/service.py delete mode 100644 ipalib/plugins/service2.py create mode 100644 ipalib/plugins/taskgroup.py delete mode 100644 ipalib/plugins/taskgroup2.py create mode 100644 ipalib/plugins/user.py delete mode 100644 ipalib/plugins/user2.py (limited to 'ipalib/plugins') diff --git a/ipalib/plugins/aci.py b/ipalib/plugins/aci.py new file mode 100644 index 000000000..f728d2a4a --- /dev/null +++ b/ipalib/plugins/aci.py @@ -0,0 +1,386 @@ +# Authors: +# Rob Crittenden +# Pavel Zuna +# +# Copyright (C) 2009 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" +Directory Server Access Control Instructions (ACIs) +""" + +from ipalib import api, crud, errors +from ipalib import Object, Command +from ipalib import Flag, Int, List, Str, StrEnum +from ipalib.aci import ACI + +_type_map = { + 'user': 'ldap:///uid=*,%s,%s' % (api.env.container_user, api.env.basedn), + 'group': 'ldap:///cn=*,%s,%s' % (api.env.container_group, api.env.basedn), + 'host': 'ldap:///cn=*,%s,%s' % (api.env.container_host, api.env.basedn) +} + +_valid_permissions_values = [ + u'read', u'write', u'add', u'delete', u'selfwrite', u'all' +] + + +def _make_aci(current, aciname, kw): + try: + (dn, entry_attrs) = api.Command['taskgroup2_show'](kw['taskgroup']) + except errors.NotFound: + # The task group doesn't exist, let's be helpful and add it + tgkw = {'description': aciname} + (dn, entry_attrs) = api.Command['taskgroup2_create']( + kw['taskgroup'], **tgkw + ) + + a = ACI(current) + a.name = aciname + a.permissions = kw['permissions'] + a.set_bindrule('groupdn = "ldap:///%s"' % dn) + if 'attrs' in kw: + a.set_target_attr(kw['attrs']) + if 'memberof' in kw: + (dn, entry_attrs) = api.Command['group2_show'](kw['memberof']) + a.set_target_filter('memberOf=%s' % dn) + if 'filter' in kw: + a.set_target_filter(kw['filter']) + if 'type' in kw: + target = _type_map[kw['type']] + a.set_target(target) + if 'targetgroup' in kw: + # Purposely no try here so we'll raise a NotFound + (dn, entry_attrs) = api.Command['group2_show'](kw['targetgroup']) + target = 'ldap:///%s' % dn + a.set_target(target) + if 'subtree' in kw: + # See if the subtree is a full URI + target = kw['subtree'] + if not target.startswith('ldap:///'): + target = 'ldap:///%s' % target + a.set_target(target) + + return a + +def _convert_strings_to_acis(acistrs): + acis = [] + for a in acistrs: + try: + acis.append(ACI(a)) + except SyntaxError, e: + # FIXME: need to log syntax errors, ignore for now + pass + return acis + +def _find_aci_by_name(acis, aciname): + for a in acis: + if a.name.lower() == aciname.lower(): + return a + raise errors.NotFound('ACI with name "%s" not found' % aciname) + +def _normalize_permissions(permissions): + valid_permissions = [] + permissions = permissions.split(',') + for p in permissions: + p = p.strip().lower() + if p in _valid_permissions_values and p not in valid_permissions: + valid_permissions.append(p) + return ','.join(valid_permissions) + + +class aci2(Object): + """ + ACI object. + """ + takes_params = ( + Str('aciname', + cli_name='name', + doc='name', + primary_key=True, + ), + Str('taskgroup', + cli_name='taskgroup', + doc='taskgroup ACI grants access to', + ), + List('permissions', + cli_name='permissions', + doc='comma-separated list of permissions to grant' \ + '(read, write, add, delete, selfwrite, all)', + normalizer=_normalize_permissions, + ), + List('attrs?', + cli_name='attrs', + doc='comma-separated list of attributes', + ), + StrEnum('type?', + cli_name='type', + doc='type of IPA object (user, group, host)', + values=(u'user', u'group', u'host'), + ), + Str('memberof?', + cli_name='memberof', + doc='member of a group', + ), + Str('filter?', + cli_name='filter', + doc='legal LDAP filter (e.g. ou=Engineering)', + ), + Str('subtree?', + cli_name='subtree', + doc='subtree to apply ACI to', + ), + Str('targetgroup?', + cli_name='targetgroup', + doc='group to apply ACI to', + ), + ) + +api.register(aci2) + + +class aci2_create(crud.Create): + """ + Create new ACI. + """ + def execute(self, aciname, **kw): + """ + Execute the aci-create operation. + + Returns the entry as it will be created in LDAP. + + :param aciname: The name of the ACI being added. + :param kw: Keyword arguments for the other LDAP attributes. + """ + assert 'aciname' not in kw + ldap = self.api.Backend.ldap2 + + newaci = _make_aci(None, aciname, kw) + + (dn, entry_attrs) = ldap.get_entry(self.api.env.basedn, ['aci']) + + acis = _convert_strings_to_acis(entry_attrs.get('aci', [])) + for a in acis: + if a.isequal(newaci): + raise errors.DuplicateEntry() + + newaci_str = str(newaci) + entry_attrs['acis'].append(newaci_str) + + ldap.update_entry(dn, entry_attrs) + + return newaci_str + + def output_for_cli(self, textui, result, aciname, **options): + textui.print_name(self.name) + textui.print_plain(result) + textui.print_dashed('Created ACI "%s".' % aciname) + +api.register(aci2_create) + + +class aci2_delete(crud.Delete): + """ + Delete ACI. + """ + def execute(self, aciname, **kw): + """ + Execute the aci-delete operation. + + :param aciname: The name of the ACI being added. + :param kw: unused + """ + assert 'aciname' not in kw + ldap = self.api.Backend.ldap2 + + (dn, entry_attrs) = ldap.get_entry(self.api.env.basedn, ['aci']) + + acistrs = entry_attrs.get('aci', []) + acis = _convert_strings_to_acis(acistrs) + aci = _find_aci_by_name(acis, aciname) + for a in acistrs: + if aci.isequal(a): + acistrs.remove(a) + break + + entry_attrs['aci'] = acistrs + + ldap.update_entry(dn, entry_attrs) + + return True + + def output_for_cli(self, textui, result, aciname, **options): + """ + Output result of this command to command line interface. + """ + textui.print_name(self.name) + textui.print_plain('Deleted ACI "%s".' % aciname) + +api.register(aci2_delete) + + +class aci2_mod(crud.Update): + """ + Modify ACI. + """ + def execute(self, aciname, **kw): + ldap = self.api.Backend.ldap2 + + (dn, entry_attrs) = ldap.get_entry(self.api.env.basedn, ['aci']) + + acis = _convert_strings_to_acis(entry_attrs.get('aci', [])) + aci = _find_aci_by_name(acis, aciname) + + kw.setdefault('aciname', aci.name) + kw.setdefault('taskgroup', aci.bindrule['expression']) + kw.setdefault('permissions', aci.permissions) + kw.setdefault('attrs', aci.target['targetattr']['expression']) + if 'type' not in kw and 'targetgroup' not in kw and 'subtree' not in kw: + kw['subtree'] = aci.target['target']['expression'] + if 'memberof' not in kw and 'filter' not in kw: + kw['filter'] = aci.target['targetfilter']['expression'] + + self.api.Command['aci2_delete'](aciname) + + return self.api.Command['aci2_create'](aciname, **kw) + + def output_for_cli(self, textui, result, aciname, **options): + textui.print_name(self.name) + textui.print_plain(result) + textui.print_dashed('Modified ACI "%s".' % aciname) + +api.register(aci2_mod) + + +class aci2_find(crud.Search): + """ + Search for ACIs. + """ + def execute(self, term, **kw): + ldap = self.api.Backend.ldap2 + + (dn, entry_attrs) = ldap.get_entry(self.api.env.basedn, ['aci']) + + acis = _convert_strings_to_acis(entry_attrs.get('aci', [])) + results = [] + + if term: + term = term.lower() + for a in acis: + if a.name.lower().find(term) != -1 and a not in results: + results.append(a) + acis = list(results) + else: + results = list(acis) + + if 'aciname' in kw: + for a in acis: + if a.name != kw['aciname']: + results.remove(a) + acis = list(results) + + if 'attrs' in kw: + for a in acis: + alist1 = sorted( + [t.lower() for t in a.target['targetattr']['expression']] + ) + alist2 = sorted([t.lower() for t in kw['attrs']]) + if alist1 != alist2: + results.remove(a) + acis = list(results) + + if 'taskgroup' in kw: + try: + (dn, entry_attrs) = self.api.Command['taskgroup2_show']( + kw['taskgroup'] + ) + except errors.NotFound: + pass + else: + for a in acis: + if a.bindrule['expression'] != ('ldap:///%s' % dn): + results.remove(a) + acis = list(results) + + if 'permissions' in kw: + for a in acis: + alist1 = sorted(a.permissions) + alist2 = sorted(kw['permissions']) + if alist1 != alist2: + results.remove(a) + acis = list(results) + + if 'memberof' in kw: + try: + (dn, entry_attrs) = self.api.Command['group2_show']( + kw['memberof'] + ) + except errors.NotFound: + pass + else: + memberof_filter = '(memberOf=%s)' % dn + for a in acis: + if 'targetfilter' in a.target: + filter = a.target['targetfilter']['expression'] + if filter != memberof_filter: + results.remove(a) + else: + results.remove(a) + # uncomment next line if you add more search criteria + # acis = list(results) + + # TODO: searching by: type, filter, subtree + + return [str(aci) for aci in results] + + def output_for_cli(self, textui, result, term, **options): + textui.print_name(self.name) + for aci in result: + textui.print_plain(aci) + textui.print_plain('') + textui.print_count( + len(result), '%i ACI matched.', '%i ACIs matched.' + ) + +api.register(aci2_find) + + +class aci2_show(crud.Retrieve): + """ + Display ACI. + """ + def execute(self, aciname, **kw): + """ + Execute the aci-show operation. + + Returns the entry + + :param uid: The login name of the user to retrieve. + :param kw: unused + """ + ldap = self.api.Backend.ldap2 + + (dn, entry_attrs) = ldap.get_entry(self.api.env.basedn, ['aci']) + + acis = _convert_strings_to_acis(entry_attrs.get('aci', [])) + + return str(_find_aci_by_name(acis, aciname)) + + def output_for_cli(self, textui, result, aciname, **options): + textui.print_name(self.name) + textui.print_plain(result) + +api.register(aci2_show) + diff --git a/ipalib/plugins/aci2.py b/ipalib/plugins/aci2.py deleted file mode 100644 index f728d2a4a..000000000 --- a/ipalib/plugins/aci2.py +++ /dev/null @@ -1,386 +0,0 @@ -# Authors: -# Rob Crittenden -# Pavel Zuna -# -# Copyright (C) 2009 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -""" -Directory Server Access Control Instructions (ACIs) -""" - -from ipalib import api, crud, errors -from ipalib import Object, Command -from ipalib import Flag, Int, List, Str, StrEnum -from ipalib.aci import ACI - -_type_map = { - 'user': 'ldap:///uid=*,%s,%s' % (api.env.container_user, api.env.basedn), - 'group': 'ldap:///cn=*,%s,%s' % (api.env.container_group, api.env.basedn), - 'host': 'ldap:///cn=*,%s,%s' % (api.env.container_host, api.env.basedn) -} - -_valid_permissions_values = [ - u'read', u'write', u'add', u'delete', u'selfwrite', u'all' -] - - -def _make_aci(current, aciname, kw): - try: - (dn, entry_attrs) = api.Command['taskgroup2_show'](kw['taskgroup']) - except errors.NotFound: - # The task group doesn't exist, let's be helpful and add it - tgkw = {'description': aciname} - (dn, entry_attrs) = api.Command['taskgroup2_create']( - kw['taskgroup'], **tgkw - ) - - a = ACI(current) - a.name = aciname - a.permissions = kw['permissions'] - a.set_bindrule('groupdn = "ldap:///%s"' % dn) - if 'attrs' in kw: - a.set_target_attr(kw['attrs']) - if 'memberof' in kw: - (dn, entry_attrs) = api.Command['group2_show'](kw['memberof']) - a.set_target_filter('memberOf=%s' % dn) - if 'filter' in kw: - a.set_target_filter(kw['filter']) - if 'type' in kw: - target = _type_map[kw['type']] - a.set_target(target) - if 'targetgroup' in kw: - # Purposely no try here so we'll raise a NotFound - (dn, entry_attrs) = api.Command['group2_show'](kw['targetgroup']) - target = 'ldap:///%s' % dn - a.set_target(target) - if 'subtree' in kw: - # See if the subtree is a full URI - target = kw['subtree'] - if not target.startswith('ldap:///'): - target = 'ldap:///%s' % target - a.set_target(target) - - return a - -def _convert_strings_to_acis(acistrs): - acis = [] - for a in acistrs: - try: - acis.append(ACI(a)) - except SyntaxError, e: - # FIXME: need to log syntax errors, ignore for now - pass - return acis - -def _find_aci_by_name(acis, aciname): - for a in acis: - if a.name.lower() == aciname.lower(): - return a - raise errors.NotFound('ACI with name "%s" not found' % aciname) - -def _normalize_permissions(permissions): - valid_permissions = [] - permissions = permissions.split(',') - for p in permissions: - p = p.strip().lower() - if p in _valid_permissions_values and p not in valid_permissions: - valid_permissions.append(p) - return ','.join(valid_permissions) - - -class aci2(Object): - """ - ACI object. - """ - takes_params = ( - Str('aciname', - cli_name='name', - doc='name', - primary_key=True, - ), - Str('taskgroup', - cli_name='taskgroup', - doc='taskgroup ACI grants access to', - ), - List('permissions', - cli_name='permissions', - doc='comma-separated list of permissions to grant' \ - '(read, write, add, delete, selfwrite, all)', - normalizer=_normalize_permissions, - ), - List('attrs?', - cli_name='attrs', - doc='comma-separated list of attributes', - ), - StrEnum('type?', - cli_name='type', - doc='type of IPA object (user, group, host)', - values=(u'user', u'group', u'host'), - ), - Str('memberof?', - cli_name='memberof', - doc='member of a group', - ), - Str('filter?', - cli_name='filter', - doc='legal LDAP filter (e.g. ou=Engineering)', - ), - Str('subtree?', - cli_name='subtree', - doc='subtree to apply ACI to', - ), - Str('targetgroup?', - cli_name='targetgroup', - doc='group to apply ACI to', - ), - ) - -api.register(aci2) - - -class aci2_create(crud.Create): - """ - Create new ACI. - """ - def execute(self, aciname, **kw): - """ - Execute the aci-create operation. - - Returns the entry as it will be created in LDAP. - - :param aciname: The name of the ACI being added. - :param kw: Keyword arguments for the other LDAP attributes. - """ - assert 'aciname' not in kw - ldap = self.api.Backend.ldap2 - - newaci = _make_aci(None, aciname, kw) - - (dn, entry_attrs) = ldap.get_entry(self.api.env.basedn, ['aci']) - - acis = _convert_strings_to_acis(entry_attrs.get('aci', [])) - for a in acis: - if a.isequal(newaci): - raise errors.DuplicateEntry() - - newaci_str = str(newaci) - entry_attrs['acis'].append(newaci_str) - - ldap.update_entry(dn, entry_attrs) - - return newaci_str - - def output_for_cli(self, textui, result, aciname, **options): - textui.print_name(self.name) - textui.print_plain(result) - textui.print_dashed('Created ACI "%s".' % aciname) - -api.register(aci2_create) - - -class aci2_delete(crud.Delete): - """ - Delete ACI. - """ - def execute(self, aciname, **kw): - """ - Execute the aci-delete operation. - - :param aciname: The name of the ACI being added. - :param kw: unused - """ - assert 'aciname' not in kw - ldap = self.api.Backend.ldap2 - - (dn, entry_attrs) = ldap.get_entry(self.api.env.basedn, ['aci']) - - acistrs = entry_attrs.get('aci', []) - acis = _convert_strings_to_acis(acistrs) - aci = _find_aci_by_name(acis, aciname) - for a in acistrs: - if aci.isequal(a): - acistrs.remove(a) - break - - entry_attrs['aci'] = acistrs - - ldap.update_entry(dn, entry_attrs) - - return True - - def output_for_cli(self, textui, result, aciname, **options): - """ - Output result of this command to command line interface. - """ - textui.print_name(self.name) - textui.print_plain('Deleted ACI "%s".' % aciname) - -api.register(aci2_delete) - - -class aci2_mod(crud.Update): - """ - Modify ACI. - """ - def execute(self, aciname, **kw): - ldap = self.api.Backend.ldap2 - - (dn, entry_attrs) = ldap.get_entry(self.api.env.basedn, ['aci']) - - acis = _convert_strings_to_acis(entry_attrs.get('aci', [])) - aci = _find_aci_by_name(acis, aciname) - - kw.setdefault('aciname', aci.name) - kw.setdefault('taskgroup', aci.bindrule['expression']) - kw.setdefault('permissions', aci.permissions) - kw.setdefault('attrs', aci.target['targetattr']['expression']) - if 'type' not in kw and 'targetgroup' not in kw and 'subtree' not in kw: - kw['subtree'] = aci.target['target']['expression'] - if 'memberof' not in kw and 'filter' not in kw: - kw['filter'] = aci.target['targetfilter']['expression'] - - self.api.Command['aci2_delete'](aciname) - - return self.api.Command['aci2_create'](aciname, **kw) - - def output_for_cli(self, textui, result, aciname, **options): - textui.print_name(self.name) - textui.print_plain(result) - textui.print_dashed('Modified ACI "%s".' % aciname) - -api.register(aci2_mod) - - -class aci2_find(crud.Search): - """ - Search for ACIs. - """ - def execute(self, term, **kw): - ldap = self.api.Backend.ldap2 - - (dn, entry_attrs) = ldap.get_entry(self.api.env.basedn, ['aci']) - - acis = _convert_strings_to_acis(entry_attrs.get('aci', [])) - results = [] - - if term: - term = term.lower() - for a in acis: - if a.name.lower().find(term) != -1 and a not in results: - results.append(a) - acis = list(results) - else: - results = list(acis) - - if 'aciname' in kw: - for a in acis: - if a.name != kw['aciname']: - results.remove(a) - acis = list(results) - - if 'attrs' in kw: - for a in acis: - alist1 = sorted( - [t.lower() for t in a.target['targetattr']['expression']] - ) - alist2 = sorted([t.lower() for t in kw['attrs']]) - if alist1 != alist2: - results.remove(a) - acis = list(results) - - if 'taskgroup' in kw: - try: - (dn, entry_attrs) = self.api.Command['taskgroup2_show']( - kw['taskgroup'] - ) - except errors.NotFound: - pass - else: - for a in acis: - if a.bindrule['expression'] != ('ldap:///%s' % dn): - results.remove(a) - acis = list(results) - - if 'permissions' in kw: - for a in acis: - alist1 = sorted(a.permissions) - alist2 = sorted(kw['permissions']) - if alist1 != alist2: - results.remove(a) - acis = list(results) - - if 'memberof' in kw: - try: - (dn, entry_attrs) = self.api.Command['group2_show']( - kw['memberof'] - ) - except errors.NotFound: - pass - else: - memberof_filter = '(memberOf=%s)' % dn - for a in acis: - if 'targetfilter' in a.target: - filter = a.target['targetfilter']['expression'] - if filter != memberof_filter: - results.remove(a) - else: - results.remove(a) - # uncomment next line if you add more search criteria - # acis = list(results) - - # TODO: searching by: type, filter, subtree - - return [str(aci) for aci in results] - - def output_for_cli(self, textui, result, term, **options): - textui.print_name(self.name) - for aci in result: - textui.print_plain(aci) - textui.print_plain('') - textui.print_count( - len(result), '%i ACI matched.', '%i ACIs matched.' - ) - -api.register(aci2_find) - - -class aci2_show(crud.Retrieve): - """ - Display ACI. - """ - def execute(self, aciname, **kw): - """ - Execute the aci-show operation. - - Returns the entry - - :param uid: The login name of the user to retrieve. - :param kw: unused - """ - ldap = self.api.Backend.ldap2 - - (dn, entry_attrs) = ldap.get_entry(self.api.env.basedn, ['aci']) - - acis = _convert_strings_to_acis(entry_attrs.get('aci', [])) - - return str(_find_aci_by_name(acis, aciname)) - - def output_for_cli(self, textui, result, aciname, **options): - textui.print_name(self.name) - textui.print_plain(result) - -api.register(aci2_show) - diff --git a/ipalib/plugins/basegroup.py b/ipalib/plugins/basegroup.py new file mode 100644 index 000000000..42714bc74 --- /dev/null +++ b/ipalib/plugins/basegroup.py @@ -0,0 +1,540 @@ +# Authors: +# Rob Crittenden +# Pavel Zuna +# +# Copyright (C) 2009 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Base plugin for groups. +""" + +from ipalib import api, crud, errors +from ipalib import Command, Object +from ipalib import Flag, Int, List, Str + +_default_attributes = ['cn', 'description', 'member', 'memberof'] +_default_class = 'groupofnames' + + +def find_members(ldap, failed, members, attr, object_class, parent_dn=''): + """ + Search for a list of members to operate on. + + Returns a tuple of 2 lists: a list of DNs found, a list of errors + + :param ldap: The ldap connection + :param failed: The current list of failed entries + :param members: list of members to find DNs for + :param attr: The primary key attribute (cn, uid, etc) + :param object_class: type of entry we're looking for + :param parent_dn: base DN for the search + """ + found = [] + for m in members: + if not m: continue + try: + (member_dn, entry_attrs) = ldap.find_entry_by_attr( + attr, m, object_class, parent_dn + ) + found.append(member_dn) + except errors.NotFound: + failed.append(m) + + return (found, failed) + +def add_members(ldap, completed, members, add_failed, group_dn, memberattr): + """ + Add members to a group. + + Returns a tuple of the # completed and those that weren't added + + :param ldap: The ldap connection + :param completed: number of entries successfully added + :param members: list of member DNs to add + :param add_failed: members who failed to be added + :param dn: DN of group to add members to + :param membetattr: The attribute where members are stored + """ + for member_dn in members: + if not member_dn: + continue + try: + ldap.add_entry_to_group(member_dn, group_dn, memberattr) + completed += 1 + except: + add_failed.append(member_dn) + + return (completed, add_failed) + +def del_members(ldap, completed, members, rem_failed, group_dn, memberattr): + """ + Remove members from group. + + Returns a tuple of the # completed and those that weren't removed + + :param ldap: The ldap connection + :param completed: number of entries successfully removed + :param members: list of member DNs to remove + :param remove_failed: members who failed to be removed + :param dn: DN of group to remove members from + :param membetattr: The attribute where members are stored + """ + for member_dn in members: + if not member_dn: continue + try: + ldap.remove_entry_from_group(member_dn, group_dn, memberattr) + completed += 1 + except: + rem_failed.append(member_dn) + + return (completed, rem_failed) + + +class basegroup2(Object): + """ + Basic Group object. + """ + container = None + + takes_params = ( + Str('cn', + cli_name='name', + doc='group name', + primary_key=True, + normalizer=lambda value: value.lower(), + ), + Str('description', + cli_name='desc', + doc='A description of this group', + ), + ) + + def get_dn(self, ldap, cn): + """ + Construct group dn from cn. + """ + assert self.container + return ldap.make_dn_from_attr('cn', cn, self.container) + + +class basegroup2_create(crud.Create): + """ + Create new group. + """ + base_classes = ('top', _default_class) + + def execute(self, cn, **kw): + """ + Execute a group add operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + Returns the entry as it will be created in LDAP. + + :param cn: The name of the group being added. + :param kw: Keyword arguments for the other LDAP attributes. + """ + assert 'cn' not in kw + assert 'dn' not in kw + ldap = self.api.Backend.ldap2 + entry_attrs = self.args_options_2_entry(cn, **kw) + dn = self.obj.get_dn(ldap, cn) + + if kw.get('objectclass'): + entry_attrs['objectclass'] = kw['objectclass'] + else: + entry_attrs['objectclass'] = self.base_classes + + ldap.add_entry(dn, entry_attrs) + + return ldap.get_entry(dn, entry_attrs.keys()) + + def output_for_cli(self, textui, result, cn, **options): + """ + Output result of this command to command line interface. + """ + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_dashed('Created group "%s".' % cn) + + +class basegroup2_delete(crud.Delete): + """ + Delete group. + """ + filter_class = _default_class + container = None + + def execute(self, cn): + """ + Delete a group + + The memberOf plugin handles removing the group from any other + groups. + + :param cn: The name of the group being removed + :param kw: Unused + """ + assert self.container + ldap = self.api.Backend.ldap2 + + (dn, entry_attrs) = ldap.find_entry_by_attr( + 'cn', cn, self.filter_class, self.container + ) + + ldap.delete_entry(dn) + + return True + + def output_for_cli(self, textui, result, cn): + """ + Output result of this command to command line interface. + """ + textui.print_name(self.name) + textui.print_dashed('Deleted group "%s"' % cn) + + +class basegroup2_mod(crud.Update): + """ + Modify group. + """ + filter_class = _default_class + container = None + + def execute(self, cn, **kw): + """ + Execute the group-mod operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + Returns the entry + + :param cn: The name of the group to update. + :param kw: Keyword arguments for the other LDAP attributes. + """ + assert 'cn' not in kw + assert 'dn' not in kw + assert self.container + assert self.filter_class + ldap = self.api.Backend.ldap2 + + (dn, entry_attrs) = ldap.find_entry_by_attr( + 'cn', cn, self.filter_class, self.container_dn + ) + + entry_attrs = self.args_options_2_entry(cn, **kw) + if 'objectclass' in kw: + entry_attrs['objectclass'] = kw['objectclass'] + + ldap.update_entry(dn, entry_attrs) + + return ldap.get_entry(dn, entry_attrs.keys()) + + def output_for_cli(self, textui, result, cn, **options): + """ + Output result of this command to command line interface. + """ + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_dashed('Modified group "%s"' % cn) + + +class basegroup2_find(crud.Search): + """ + Search for groups. + """ + filter_class = _default_class + searchfields = [] + container = None + + takes_options = ( + Flag('all', + cli_name='all', + doc='Retrieve all attributes' + ), + ) + + def execute(self, term, **kw): + ldap = self.api.Backend.ldap2 + + search_kw = self.args_options_2_entry(**kw) + if self.filter_class: + search_kw['objectclass'] = self.filter_class + filter = ldap.make_filter(search_kw, rules=ldap.MATCH_ALL) + if term: + if not self.searchfields: + # Pull the list of searchable attributes out of the IPA config. + conf = ldap.get_ipa_config()[1] + search_fields = conf.get('ipagroupsearchfields')[0].split(',') + else: + search_fields = self.searchfields + + search_kw = {} + for s in search_fields: + search_kw[s] = term + term_filter = ldap.make_filter(search_kw, exact=False) + filter = ldap.combine_filters( + (filter, term_filter), ldap.MATCH_ALL + ) + + if kw['all']: + attrs_list = ['*'] + else: + attrs_list = _default_attributes + + parent_dn = self.container or '' + + try: + (entries, truncated) = ldap.find_entries( + filter, attrs_list, parent_dn + ) + except errors.NotFound: + (entries, truncated) = (tuple(), False) + + return (entries, truncated) + + def output_for_cli(self, textui, result, criteria, **options): + (entries, truncated) = result + + textui.print_name(self.name) + for (dn, entry_attrs) in entries: + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_plain('') + textui.print_count( + len(result), '%i group matched.', '%i groups matched.' + ) + if truncated: + textui.print_dashed('These results are truncated.', below=False) + textui.print_dashed( + 'Please refine your search and try again.', above=False + ) + + +class basegroup2_show(crud.Retrieve): + """ + Display group. + """ + filter_class = _default_class + default_attributes = _default_attributes + container = None + + takes_options = ( + Flag('all', + doc='Retrieve all attributes' + ), + ) + + def execute(self, cn, **kw): + """ + Execute the group-show operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + Returns the entry + + :param cn: The group name to retrieve. + :param kw: Not used. + """ + assert self.container + ldap = self.api.Backend.ldap2 + + (dn, entry_attrs) = ldap.find_entry_by_attr( + 'cn', cn, self.filter_class, self.container + ) + + if kw['all']: + attrs_list = ['*'] + else: + attrs_list = self.default_attributes + + return ldap.get_entry(dn, attrs_list) + + def output_for_cli(self, textui, result, *args, **options): + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + + +class basegroup2_add_member(Command): + """ + Add members to group. + """ + filter_class = _default_class + default_attributes = _default_attributes + container = None + + takes_args = ( + Str('cn', + cli_name='name', + doc='group name', + ), + ) + + takes_options = ( + List('users?', + cli_name='users', + doc='comma-separated list of users to add', + ), + List('groups?', + cli_name='groups', + doc='comma-separated list of user groups to add', + ), + ) + + def execute(self, cn, **kw): + """ + Execute the group-add-member operation. + + Returns a tuple containing the number of members added + and the updated entry. + + :param cn: The group name to add new members to. + :param kw: groups is a comma-separated list of groups to add + :param kw: users is a comma-separated list of users to add + """ + assert self.container + ldap = self.api.Backend.ldap2 + to_add = [] + add_failed = [] + completed = 0 + + (dn, entry_attrs) = ldap.find_entry_by_attrs( + 'cn', cn, self.filter_class, self.container + ) + + members = kw.get('groups', []) + (to_add, add_failed) = find_members( + ldap, add_failed, members, 'cn', 'ipausergroup', + self.api.env.container_group + ) + (completed, add_failed) = add_members( + ldap, completed, to_add, add_failed, dn, 'member' + ) + + members = kw.get('users', []) + (to_add, add_failed) = find_members( + ldap, add_failed, members, 'uid', 'posixaccount', + self.api.env.container_user + ) + (completed, add_failed) = add_members( + ldap, completed, to_add, add_failed, dn, 'member' + ) + + return (completed, ldap.get_entry(dn, self.default_attributes)) + + def output_for_cli(self, textui, result, *args, **options): + """ + Output result of this command to command line interface. + """ + (total, (dn, entry_attrs)) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_count(total, '%i member added.', '%i members added.') + + +class basegroup2_del_member(Command): + """ + Remove members from group. + """ + filter_class = _default_class + default_attributes = _default_attributes + container = None + + takes_args = ( + Str('cn', + cli_name='name', + doc='group name', + ), + ) + + takes_options = ( + List('users?', + cli_name='users', + doc='comma-separated list of users to remove', + ), + List('groups?', + cli_name='groups', + doc='comma-separated list of user groups to remove', + ), + ) + + def execute(self, cn, **kw): + """ + Execute the group-del-member operation. + + Returns a tuple containing the number of members removed + and the updated entry. + + :param cn: The group name to add new members to. + :param kw: groups is a comma-separated list of groups to remove + :param kw: users is a comma-separated list of users to remove + """ + assert self.container + ldap = self.api.Backend.ldap2 + to_remove = [] + remove_failed = [] + completed = 0 + + (dn, entry_attrs) = ldap.find_entry_by_attrs( + 'cn', cn, self.filter_class, self.container + ) + + members = kw.get('groups', []) + (to_remove, remove_failed) = find_members( + ldap, remove_failed, members, 'cn', 'ipausergroup', + self.api.env.container_group + ) + (completed, remove_failed) = del_members( + ldap, completed, to_remove, remove_failed, dn, 'member' + ) + + members = kw.get('users', []) + (to_remove, remove_failed) = find_members( + ldap, remove_failed, members, 'uid', 'posixaccount', + self.api.env.container_user + ) + (completed, remove_failed) = del_members( + ldap, completed, to_remove, remove_failed, dn, 'member' + ) + + return (completed, ldap.get_entry(dn, self.default_attributes)) + + def output_for_cli(self, textui, result, *args, **options): + """ + Output result of this command to command line interface. + """ + (total, (dn, entry_attrs)) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_count(total, '%i member removed.', '%i members removed.') + diff --git a/ipalib/plugins/basegroup2.py b/ipalib/plugins/basegroup2.py deleted file mode 100644 index 42714bc74..000000000 --- a/ipalib/plugins/basegroup2.py +++ /dev/null @@ -1,540 +0,0 @@ -# Authors: -# Rob Crittenden -# Pavel Zuna -# -# Copyright (C) 2009 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Base plugin for groups. -""" - -from ipalib import api, crud, errors -from ipalib import Command, Object -from ipalib import Flag, Int, List, Str - -_default_attributes = ['cn', 'description', 'member', 'memberof'] -_default_class = 'groupofnames' - - -def find_members(ldap, failed, members, attr, object_class, parent_dn=''): - """ - Search for a list of members to operate on. - - Returns a tuple of 2 lists: a list of DNs found, a list of errors - - :param ldap: The ldap connection - :param failed: The current list of failed entries - :param members: list of members to find DNs for - :param attr: The primary key attribute (cn, uid, etc) - :param object_class: type of entry we're looking for - :param parent_dn: base DN for the search - """ - found = [] - for m in members: - if not m: continue - try: - (member_dn, entry_attrs) = ldap.find_entry_by_attr( - attr, m, object_class, parent_dn - ) - found.append(member_dn) - except errors.NotFound: - failed.append(m) - - return (found, failed) - -def add_members(ldap, completed, members, add_failed, group_dn, memberattr): - """ - Add members to a group. - - Returns a tuple of the # completed and those that weren't added - - :param ldap: The ldap connection - :param completed: number of entries successfully added - :param members: list of member DNs to add - :param add_failed: members who failed to be added - :param dn: DN of group to add members to - :param membetattr: The attribute where members are stored - """ - for member_dn in members: - if not member_dn: - continue - try: - ldap.add_entry_to_group(member_dn, group_dn, memberattr) - completed += 1 - except: - add_failed.append(member_dn) - - return (completed, add_failed) - -def del_members(ldap, completed, members, rem_failed, group_dn, memberattr): - """ - Remove members from group. - - Returns a tuple of the # completed and those that weren't removed - - :param ldap: The ldap connection - :param completed: number of entries successfully removed - :param members: list of member DNs to remove - :param remove_failed: members who failed to be removed - :param dn: DN of group to remove members from - :param membetattr: The attribute where members are stored - """ - for member_dn in members: - if not member_dn: continue - try: - ldap.remove_entry_from_group(member_dn, group_dn, memberattr) - completed += 1 - except: - rem_failed.append(member_dn) - - return (completed, rem_failed) - - -class basegroup2(Object): - """ - Basic Group object. - """ - container = None - - takes_params = ( - Str('cn', - cli_name='name', - doc='group name', - primary_key=True, - normalizer=lambda value: value.lower(), - ), - Str('description', - cli_name='desc', - doc='A description of this group', - ), - ) - - def get_dn(self, ldap, cn): - """ - Construct group dn from cn. - """ - assert self.container - return ldap.make_dn_from_attr('cn', cn, self.container) - - -class basegroup2_create(crud.Create): - """ - Create new group. - """ - base_classes = ('top', _default_class) - - def execute(self, cn, **kw): - """ - Execute a group add operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - Returns the entry as it will be created in LDAP. - - :param cn: The name of the group being added. - :param kw: Keyword arguments for the other LDAP attributes. - """ - assert 'cn' not in kw - assert 'dn' not in kw - ldap = self.api.Backend.ldap2 - entry_attrs = self.args_options_2_entry(cn, **kw) - dn = self.obj.get_dn(ldap, cn) - - if kw.get('objectclass'): - entry_attrs['objectclass'] = kw['objectclass'] - else: - entry_attrs['objectclass'] = self.base_classes - - ldap.add_entry(dn, entry_attrs) - - return ldap.get_entry(dn, entry_attrs.keys()) - - def output_for_cli(self, textui, result, cn, **options): - """ - Output result of this command to command line interface. - """ - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_dashed('Created group "%s".' % cn) - - -class basegroup2_delete(crud.Delete): - """ - Delete group. - """ - filter_class = _default_class - container = None - - def execute(self, cn): - """ - Delete a group - - The memberOf plugin handles removing the group from any other - groups. - - :param cn: The name of the group being removed - :param kw: Unused - """ - assert self.container - ldap = self.api.Backend.ldap2 - - (dn, entry_attrs) = ldap.find_entry_by_attr( - 'cn', cn, self.filter_class, self.container - ) - - ldap.delete_entry(dn) - - return True - - def output_for_cli(self, textui, result, cn): - """ - Output result of this command to command line interface. - """ - textui.print_name(self.name) - textui.print_dashed('Deleted group "%s"' % cn) - - -class basegroup2_mod(crud.Update): - """ - Modify group. - """ - filter_class = _default_class - container = None - - def execute(self, cn, **kw): - """ - Execute the group-mod operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - Returns the entry - - :param cn: The name of the group to update. - :param kw: Keyword arguments for the other LDAP attributes. - """ - assert 'cn' not in kw - assert 'dn' not in kw - assert self.container - assert self.filter_class - ldap = self.api.Backend.ldap2 - - (dn, entry_attrs) = ldap.find_entry_by_attr( - 'cn', cn, self.filter_class, self.container_dn - ) - - entry_attrs = self.args_options_2_entry(cn, **kw) - if 'objectclass' in kw: - entry_attrs['objectclass'] = kw['objectclass'] - - ldap.update_entry(dn, entry_attrs) - - return ldap.get_entry(dn, entry_attrs.keys()) - - def output_for_cli(self, textui, result, cn, **options): - """ - Output result of this command to command line interface. - """ - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_dashed('Modified group "%s"' % cn) - - -class basegroup2_find(crud.Search): - """ - Search for groups. - """ - filter_class = _default_class - searchfields = [] - container = None - - takes_options = ( - Flag('all', - cli_name='all', - doc='Retrieve all attributes' - ), - ) - - def execute(self, term, **kw): - ldap = self.api.Backend.ldap2 - - search_kw = self.args_options_2_entry(**kw) - if self.filter_class: - search_kw['objectclass'] = self.filter_class - filter = ldap.make_filter(search_kw, rules=ldap.MATCH_ALL) - if term: - if not self.searchfields: - # Pull the list of searchable attributes out of the IPA config. - conf = ldap.get_ipa_config()[1] - search_fields = conf.get('ipagroupsearchfields')[0].split(',') - else: - search_fields = self.searchfields - - search_kw = {} - for s in search_fields: - search_kw[s] = term - term_filter = ldap.make_filter(search_kw, exact=False) - filter = ldap.combine_filters( - (filter, term_filter), ldap.MATCH_ALL - ) - - if kw['all']: - attrs_list = ['*'] - else: - attrs_list = _default_attributes - - parent_dn = self.container or '' - - try: - (entries, truncated) = ldap.find_entries( - filter, attrs_list, parent_dn - ) - except errors.NotFound: - (entries, truncated) = (tuple(), False) - - return (entries, truncated) - - def output_for_cli(self, textui, result, criteria, **options): - (entries, truncated) = result - - textui.print_name(self.name) - for (dn, entry_attrs) in entries: - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_plain('') - textui.print_count( - len(result), '%i group matched.', '%i groups matched.' - ) - if truncated: - textui.print_dashed('These results are truncated.', below=False) - textui.print_dashed( - 'Please refine your search and try again.', above=False - ) - - -class basegroup2_show(crud.Retrieve): - """ - Display group. - """ - filter_class = _default_class - default_attributes = _default_attributes - container = None - - takes_options = ( - Flag('all', - doc='Retrieve all attributes' - ), - ) - - def execute(self, cn, **kw): - """ - Execute the group-show operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - Returns the entry - - :param cn: The group name to retrieve. - :param kw: Not used. - """ - assert self.container - ldap = self.api.Backend.ldap2 - - (dn, entry_attrs) = ldap.find_entry_by_attr( - 'cn', cn, self.filter_class, self.container - ) - - if kw['all']: - attrs_list = ['*'] - else: - attrs_list = self.default_attributes - - return ldap.get_entry(dn, attrs_list) - - def output_for_cli(self, textui, result, *args, **options): - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - - -class basegroup2_add_member(Command): - """ - Add members to group. - """ - filter_class = _default_class - default_attributes = _default_attributes - container = None - - takes_args = ( - Str('cn', - cli_name='name', - doc='group name', - ), - ) - - takes_options = ( - List('users?', - cli_name='users', - doc='comma-separated list of users to add', - ), - List('groups?', - cli_name='groups', - doc='comma-separated list of user groups to add', - ), - ) - - def execute(self, cn, **kw): - """ - Execute the group-add-member operation. - - Returns a tuple containing the number of members added - and the updated entry. - - :param cn: The group name to add new members to. - :param kw: groups is a comma-separated list of groups to add - :param kw: users is a comma-separated list of users to add - """ - assert self.container - ldap = self.api.Backend.ldap2 - to_add = [] - add_failed = [] - completed = 0 - - (dn, entry_attrs) = ldap.find_entry_by_attrs( - 'cn', cn, self.filter_class, self.container - ) - - members = kw.get('groups', []) - (to_add, add_failed) = find_members( - ldap, add_failed, members, 'cn', 'ipausergroup', - self.api.env.container_group - ) - (completed, add_failed) = add_members( - ldap, completed, to_add, add_failed, dn, 'member' - ) - - members = kw.get('users', []) - (to_add, add_failed) = find_members( - ldap, add_failed, members, 'uid', 'posixaccount', - self.api.env.container_user - ) - (completed, add_failed) = add_members( - ldap, completed, to_add, add_failed, dn, 'member' - ) - - return (completed, ldap.get_entry(dn, self.default_attributes)) - - def output_for_cli(self, textui, result, *args, **options): - """ - Output result of this command to command line interface. - """ - (total, (dn, entry_attrs)) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_count(total, '%i member added.', '%i members added.') - - -class basegroup2_del_member(Command): - """ - Remove members from group. - """ - filter_class = _default_class - default_attributes = _default_attributes - container = None - - takes_args = ( - Str('cn', - cli_name='name', - doc='group name', - ), - ) - - takes_options = ( - List('users?', - cli_name='users', - doc='comma-separated list of users to remove', - ), - List('groups?', - cli_name='groups', - doc='comma-separated list of user groups to remove', - ), - ) - - def execute(self, cn, **kw): - """ - Execute the group-del-member operation. - - Returns a tuple containing the number of members removed - and the updated entry. - - :param cn: The group name to add new members to. - :param kw: groups is a comma-separated list of groups to remove - :param kw: users is a comma-separated list of users to remove - """ - assert self.container - ldap = self.api.Backend.ldap2 - to_remove = [] - remove_failed = [] - completed = 0 - - (dn, entry_attrs) = ldap.find_entry_by_attrs( - 'cn', cn, self.filter_class, self.container - ) - - members = kw.get('groups', []) - (to_remove, remove_failed) = find_members( - ldap, remove_failed, members, 'cn', 'ipausergroup', - self.api.env.container_group - ) - (completed, remove_failed) = del_members( - ldap, completed, to_remove, remove_failed, dn, 'member' - ) - - members = kw.get('users', []) - (to_remove, remove_failed) = find_members( - ldap, remove_failed, members, 'uid', 'posixaccount', - self.api.env.container_user - ) - (completed, remove_failed) = del_members( - ldap, completed, to_remove, remove_failed, dn, 'member' - ) - - return (completed, ldap.get_entry(dn, self.default_attributes)) - - def output_for_cli(self, textui, result, *args, **options): - """ - Output result of this command to command line interface. - """ - (total, (dn, entry_attrs)) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_count(total, '%i member removed.', '%i members removed.') - diff --git a/ipalib/plugins/config.py b/ipalib/plugins/config.py new file mode 100644 index 000000000..c97c57597 --- /dev/null +++ b/ipalib/plugins/config.py @@ -0,0 +1,183 @@ +# Authors: +# Rob Crittenden +# Pavel Zuna +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +IPA configuration. +""" + +from ipalib import api, errors +from ipalib import Command +from ipalib import Int, Str + +_search_options = { + 'ipaSearchTimeLimit': 'Time limit (in seconds)', + 'ipaSearchRecordsLimit': 'Record limit', + 'ipaUserSearchFields': 'User search fields', + 'ipaGroupSearchFields': 'Group search fields', +} + +_user_options = { + 'ipaMaxUsernameLength': 'Maximum name length', + 'ipaHomesRootDir': 'Root for home directories', + 'ipaDefaultLoginShell': 'Default shell', + 'ipaDefaultPrimaryGroup': 'Default group', + 'ipaDefaultEmailDomain': 'Default e-mail domain', +} + +_options = { + 'Search': _search_options, + 'User': _user_options, +} + + +class config2_mod(Command): + """ + Modify IPA configuration options. + """ + takes_options = ( + Int('ipamaxusernamelength?', + cli_name='maxusername', + doc='Max. Username length', + minvalue=1, + attribute=True, + ), + Str('ipahomesrootdir?', + cli_name='homedirectory', + doc='Default location of home directories', + attribute=True, + ), + Str('ipadefaultloginshell?', + cli_name='defaultshell', + doc='Default shell for new users', + attribute=True, + ), + Str('ipadefaultprimarygroup?', + cli_name='defaultgroup', + doc='Default group for new users', + attribute=True, + ), + Str('ipadefaultemaildomain?', + cli_name='emaildomain', + doc='Default e-mail domain new users', + attribute=True, + ), + Int('ipasearchtimelimit?', + cli_name='searchtimelimit', + doc='Max. amount of time (sec.) for a search (-1 is unlimited)', + minvalue=-1, + attribute=True, + ), + Int('ipasearchrecordslimit?', + cli_name='searchrecordslimit', + doc='Max. number of records to search (-1 is unlimited)', + minvalue=-1, + attribute=True, + ), + Str('ipausersearchfields?', + cli_name='usersearch', + doc='A comma-separated list of fields to search when searching for users', + attribute=True, + ), + Str('ipagroupsearchfields?', + cli_name='groupsearch', + doc='A comma-separated list of fields to search when searching for groups', + attribute=True, + ), + ) + + def execute(self, *args, **options): + """ + Execute the config-mod operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + Returns the entry + + :param args: This function takes no positional arguments + :param kw: Keyword arguments for the other LDAP attributes. + """ + assert 'dn' not in options + ldap = self.api.Backend.ldap2 + + (dn, entry_attrs) = ldap.get_ipa_config() + entry_attrs = self.args_options_2_entry(*args, **options) + + try: + ldap.update_entry(dn, entry_attrs) + except errors.EmptyModlist: + pass + + return ldap.get_entry(dn, entry_attrs.keys()) + + def output_for_cli(self, textui, result, *args, **options): + (dn, entry_attrs) = result + + for p in self.params: + textui.print_plain(p) + textui.print_name(self.name) + for (name, options) in _options.iteritems(): + textui.print_plain('%s options:' % name) + for (k, v) in options.iteritems(): + k = k.lower() + if k in entry_attrs: + textui.print_attribute(v, entry_attrs[k]) + textui.print_plain('') + textui.print_dashed('Modified IPA configuration options.') + +api.register(config2_mod) + + +class config2_show(Command): + """ + Display IPA configuration options. + """ + + def execute(self, *args, **options): + """ + Execute the config-show operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + Returns the entry + + :param args: Not used. + :param kw: Not used. + """ + ldap = self.api.Backend.ldap2 + return ldap.get_ipa_config() + + def output_for_cli(self, textui, result, *args, **options): + (dn, entry_attrs) = result + count = 0 + + textui.print_name(self.name) + for (name, options) in _options.iteritems(): + textui.print_plain('%s options:' % name) + for (k, v) in options.iteritems(): + if k in entry_attrs: + textui.print_attribute(v, entry_attrs[k]) + count += 1 + textui.print_plain('') + textui.print_count(count, '%d option', '%d options') + +api.register(config2_show) + diff --git a/ipalib/plugins/config2.py b/ipalib/plugins/config2.py deleted file mode 100644 index c97c57597..000000000 --- a/ipalib/plugins/config2.py +++ /dev/null @@ -1,183 +0,0 @@ -# Authors: -# Rob Crittenden -# Pavel Zuna -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -IPA configuration. -""" - -from ipalib import api, errors -from ipalib import Command -from ipalib import Int, Str - -_search_options = { - 'ipaSearchTimeLimit': 'Time limit (in seconds)', - 'ipaSearchRecordsLimit': 'Record limit', - 'ipaUserSearchFields': 'User search fields', - 'ipaGroupSearchFields': 'Group search fields', -} - -_user_options = { - 'ipaMaxUsernameLength': 'Maximum name length', - 'ipaHomesRootDir': 'Root for home directories', - 'ipaDefaultLoginShell': 'Default shell', - 'ipaDefaultPrimaryGroup': 'Default group', - 'ipaDefaultEmailDomain': 'Default e-mail domain', -} - -_options = { - 'Search': _search_options, - 'User': _user_options, -} - - -class config2_mod(Command): - """ - Modify IPA configuration options. - """ - takes_options = ( - Int('ipamaxusernamelength?', - cli_name='maxusername', - doc='Max. Username length', - minvalue=1, - attribute=True, - ), - Str('ipahomesrootdir?', - cli_name='homedirectory', - doc='Default location of home directories', - attribute=True, - ), - Str('ipadefaultloginshell?', - cli_name='defaultshell', - doc='Default shell for new users', - attribute=True, - ), - Str('ipadefaultprimarygroup?', - cli_name='defaultgroup', - doc='Default group for new users', - attribute=True, - ), - Str('ipadefaultemaildomain?', - cli_name='emaildomain', - doc='Default e-mail domain new users', - attribute=True, - ), - Int('ipasearchtimelimit?', - cli_name='searchtimelimit', - doc='Max. amount of time (sec.) for a search (-1 is unlimited)', - minvalue=-1, - attribute=True, - ), - Int('ipasearchrecordslimit?', - cli_name='searchrecordslimit', - doc='Max. number of records to search (-1 is unlimited)', - minvalue=-1, - attribute=True, - ), - Str('ipausersearchfields?', - cli_name='usersearch', - doc='A comma-separated list of fields to search when searching for users', - attribute=True, - ), - Str('ipagroupsearchfields?', - cli_name='groupsearch', - doc='A comma-separated list of fields to search when searching for groups', - attribute=True, - ), - ) - - def execute(self, *args, **options): - """ - Execute the config-mod operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - Returns the entry - - :param args: This function takes no positional arguments - :param kw: Keyword arguments for the other LDAP attributes. - """ - assert 'dn' not in options - ldap = self.api.Backend.ldap2 - - (dn, entry_attrs) = ldap.get_ipa_config() - entry_attrs = self.args_options_2_entry(*args, **options) - - try: - ldap.update_entry(dn, entry_attrs) - except errors.EmptyModlist: - pass - - return ldap.get_entry(dn, entry_attrs.keys()) - - def output_for_cli(self, textui, result, *args, **options): - (dn, entry_attrs) = result - - for p in self.params: - textui.print_plain(p) - textui.print_name(self.name) - for (name, options) in _options.iteritems(): - textui.print_plain('%s options:' % name) - for (k, v) in options.iteritems(): - k = k.lower() - if k in entry_attrs: - textui.print_attribute(v, entry_attrs[k]) - textui.print_plain('') - textui.print_dashed('Modified IPA configuration options.') - -api.register(config2_mod) - - -class config2_show(Command): - """ - Display IPA configuration options. - """ - - def execute(self, *args, **options): - """ - Execute the config-show operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - Returns the entry - - :param args: Not used. - :param kw: Not used. - """ - ldap = self.api.Backend.ldap2 - return ldap.get_ipa_config() - - def output_for_cli(self, textui, result, *args, **options): - (dn, entry_attrs) = result - count = 0 - - textui.print_name(self.name) - for (name, options) in _options.iteritems(): - textui.print_plain('%s options:' % name) - for (k, v) in options.iteritems(): - if k in entry_attrs: - textui.print_attribute(v, entry_attrs[k]) - count += 1 - textui.print_plain('') - textui.print_count(count, '%d option', '%d options') - -api.register(config2_show) - diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py new file mode 100644 index 000000000..7862e25ed --- /dev/null +++ b/ipalib/plugins/dns.py @@ -0,0 +1,803 @@ +# Authors: +# Pavel Zuna +# +# Copyright (C) 2009 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" +Domain Name System (DNS) Plugin + +Implements a set of commands useful for manipulating DNS records used by +the BIND LDAP plugin. + +EXAMPLES: + + Add new zone; + ipa dns2-create example.com nameserver.example.com admin@example.com + + Add second nameserver for example.com: + ipa dns2-add-rr example.com @ NS nameserver2.example.com + + Delete previously added nameserver from example.com: + ipa dns2-del-rr example.com @ NS nameserver2.example.com + + Add new A record for www.example.com: (random IP) + ipa dns2-add-rr example.com www A 80.142.15.2 + + Show zone example.com: + ipa dns2-show example.com + + Find zone with 'example' in it's domain name: + ipa dns2-find example + + Find records for resources with 'www' in their name in zone example.com: + ipa dns2-find-rr example.com www + + Find A records for resource www in zone example.com + ipa dns2-find-rr example.com --resource www --type A + + Show records for resource www in zone example.com + ipa dns2-show-rr example.com www + + Delete zone example.com with all resource records: + ipa dns2-delete example.com +""" + +# A few notes about the LDAP schema to make this plugin more understandable: +# - idnsRecord object is a HOSTNAME with one or more resource records +# - idnsZone object is a idnsRecord object with mandatory SOA record +# it basically makes the assumption that ZONE == DOMAINNAME + SOA record +# resource records can be stored in both idnsZone and idnsRecord objects + +import time + +from ipalib import api, crud, errors +from ipalib import Object, Command +from ipalib import Flag, Int, Str, StrEnum + +# parent DN +_zone_container_dn = api.env.container_dns + +# supported resource record types +_record_types = ( + u'A', u'AAAA', u'A6', u'AFSDB', u'CERT', u'CNAME', u'DNAME', + u'DS', u'HINFO', u'KEY', u'KX', u'LOC', u'MD', u'MINFO', u'MX', + u'NAPTR', u'NS', u'NSEC', u'NXT', u'PTR', u'RRSIG', u'SSHFP', + u'SRV', u'TXT', +) + +# supported DNS classes, IN = internet, rest is almost never used +_record_classes = (u'IN', u'CS', u'CH', u'HS') + +# attributes displayed by default for resource records +_record_default_attributes = ['%srecord' % r for r in _record_types] +_record_default_attributes.append('idnsname') + +# attributes displayed by default for zones +_zone_default_attributes = [ + 'idnsname', 'idnszoneactive', 'idnssoamname', 'idnssoarname', + 'idnssoaserial', 'idnssoarefresh', 'idnssoaretry', 'idnssoaexpire', + 'idnssoaminimum' +] + + +# build zone dn +def _get_zone_dn(ldap, idnsname): + rdn = ldap.make_rdn_from_attr('idnsname', idnsname) + return ldap.make_dn_from_rdn(rdn, _zone_container_dn) + +# build dn for entry with record +def _get_record_dn(ldap, zone, idnsname): + parent_dn = _get_zone_dn(ldap, zone) + if idnsname == '@' or idnsname == zone: + return parent_dn + rdn = ldap.make_rdn_from_attr('idnsname', idnsname) + return ldap.make_dn_from_rdn(rdn, parent_dn) + + +class dns2(Object): + """DNS zone/SOA record object.""" + + takes_params = ( + Str('idnsname', + cli_name='name', + doc='zone name (FQDN)', + normalizer=lambda value: value.lower(), + primary_key=True, + ), + Str('idnssoamname', + cli_name='name_server', + doc='authoritative name server', + ), + Str('idnssoarname', + cli_name='admin_email', + doc='administrator e-mail address', + default_from=lambda idnsname: 'root.%s' % idnsname, + normalizer=lambda value: value.replace('@', '.'), + ), + Int('idnssoaserial?', + cli_name='serial', + doc='SOA serial', + ), + Int('idnssoarefresh?', + cli_name='refresh', + doc='SOA refresh', + ), + Int('idnssoaretry?', + cli_name='retry', + doc='SOA retry', + ), + Int('idnssoaexpire?', + cli_name='expire', + doc='SOA expire', + ), + Int('idnssoaminimum?', + cli_name='minimum', + doc='SOA minimum', + ), + Int('dnsttl?', + cli_name='ttl', + doc='SOA time to live', + ), + StrEnum('dnsclass?', + cli_name='class', + doc='SOA class', + values=_record_classes, + ), + Flag('idnsallowdynupdate', + cli_name='allow_dynupdate', + doc='allow dynamic update?', + ), + ) + +api.register(dns2) + + +class dns2_create(crud.Create): + """ + Create new DNS zone/SOA record. + """ + + def execute(self, *args, **options): + ldap = self.Backend.ldap2 + idnsname = args[0] + + # build entry attributes + entry_attrs = self.args_options_2_entry(*args, **options) + + # build entry DN + dn = _get_zone_dn(ldap, idnsname) + + # fill in required attributes + entry_attrs['objectclass'] = ['top', 'idnsrecord', 'idnszone'] + entry_attrs['idnszoneactive'] = True + + # fill default values, build SOA serial from current date + soa_serial = int('%s01' % time.strftime('%Y%d%m')) + entry_attrs.setdefault('idnssoaserial', soa_serial) + entry_attrs.setdefault('idnssoarefresh', 3600) + entry_attrs.setdefault('idnssoaretry', 900) + entry_attrs.setdefault('idnssoaexpire', 1209600) + entry_attrs.setdefault('idnssoaminimum', 3600) + + # create zone entry + ldap.add_entry(dn, entry_attrs) + + # get zone entry with created attributes for output + return ldap.get_entry(dn, entry_attrs.keys()) + + def output_for_cli(self, textui, result, *args, **options): + (dn, entry_attrs) = result + idnsname = args[0] + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_dashed('Created DNS zone "%s".' % idnsname) + +api.register(dns2_create) + + +class dns2_delete(crud.Delete): + """ + Delete existing DNS zone/SOA record. + """ + + def execute(self, *args, **options): + ldap = self.api.Backend.ldap2 + idnsname = args[0] + + # build zone entry DN + dn = _get_zone_dn(ldap, idnsname) + # just check if zone exists for now + ldap.get_entry(dn, ['']) + + # retrieve all subentries of zone - records + try: + (entries, truncated) = ldap.find_entries( + None, [''], dn, ldap.SCOPE_ONELEVEL + ) + except errors.NotFound: + (entries, truncated) = (tuple(), False) + + # kill'em all, records first + for e in entries: + ldap.delete_entry(e[0]) + ldap.delete_entry(dn) + + # return something positive + return True + + def output_for_cli(self, textui, result, *args, **options): + textui.print_name(self.name) + textui.print_dashed('Deleted DNS zone "%s".' % args[0]) + +api.register(dns2_delete) + + +class dns2_mod(crud.Update): + """ + Modify DNS zone/SOA record. + """ + + def execute(self, *args, **options): + ldap = self.api.Backend.ldap2 + idnsname = args[0] + + # build entry attributes, don't include idnsname! + entry_attrs = self.args_options_2_entry(*tuple(), **options) + + # build entry DN + dn = _get_zone_dn(ldap, idnsname) + + # update zone entry + ldap.update_entry(dn, entry_attrs) + + # get zone entry with modified + default attributes for output + return ldap.get_entry( + dn, (entry_attrs.keys() + _zone_default_attributes) + ) + + def output_for_cli(self, textui, result, *args, **options): + (dn, entry_attrs) = result + idnsname = args[0] + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_dashed('Modified DNS zone "%s".' % idnsname) + +api.register(dns2_mod) + + +class dns2_find(crud.Search): + """ + Search for DNS zones/SOA records. + """ + + takes_options = ( + Flag('all', + doc='retrieve all attributes', + ), + ) + + def execute(self, term, **options): + ldap = self.api.Backend.ldap2 + + # build search filter + filter = ldap.make_filter_from_attr('idnsname', term, exact=False) + + # select attributes we want to retrieve + if options['all']: + attrs_list = ['*'] + else: + attrs_list = _zone_default_attributes + + # get matching entries + try: + (entries, truncated) = ldap.find_entries( + filter, attrs_list, _zone_container_dn, ldap.SCOPE_ONELEVEL + ) + except errors.NotFound: + (entries, truncated) = (tuple(), False) + + return entries + + def output_for_cli(self, textui, result, term, **options): + (entries, truncated) = result + + textui.print_name(self.name) + for (dn, entry_attrs) in entries: + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_plain('') + textui.print_count( + len(result), '%i DNS zone matched.', '%i DNS zones matched.' + ) + if truncated: + textui.print_dashed('These results are truncated.', below=False) + textui.print_dashed( + 'Please refine your search and try again.', above=False + ) + +api.register(dns2_find) + + +class dns2_show(crud.Retrieve): + """ + Display DNS zone/SOA record. + """ + + takes_options = ( + Flag('all', + doc='retrieve all attributes', + ), + ) + + def execute(self, idnsname, **options): + ldap = self.api.Backend.ldap2 + + # build entry DN + dn = _get_zone_dn(ldap, idnsname) + + # select attributes we want to retrieve + if options['all']: + attrs_list = ['*'] + else: + attrs_list = _zone_default_attributes + + return ldap.get_entry(dn, attrs_list) + + def output_for_cli(self, textui, result, *args, **options): + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + +api.register(dns2_show) + + +class dns2_enable(Command): + """ + Activate DNS zone. + """ + + takes_args = ( + Str('zone', + cli_name='zone', + doc='zone name', + normalizer=lambda value: value.lower(), + ), + ) + + def execute(self, zone): + ldap = self.api.Backend.ldap2 + + # build entry DN + dn = _get_zone_dn(ldap, zone) + + # activate! + try: + ldap.update_entry(dn, {'idnszoneactive': True}) + except errors.EmptyModlist: + pass + + # return something positive + return True + + def output_for_cli(self, textui, result, zone): + textui.print_name(self.name) + textui.print_dashed('Activated DNS zone "%s".' % zone) + +api.register(dns2_enable) + + +class dns2_disable(Command): + """ + Deactivate DNS zone. + """ + + takes_args = ( + Str('zone', + cli_name='zone', + doc='zone name', + normalizer=lambda value: value.lower(), + ), + ) + + def execute(self, zone): + ldap = self.api.Backend.ldap2 + + # build entry DN + dn = _get_zone_dn(ldap, zone) + + # deactivate! + try: + ldap.update_entry(dn, {'idnszoneactive': False}) + except errors.EmptyModlist: + pass + + # return something positive + return True + + def output_for_cli(self, textui, result, zone): + textui.print_name(self.name) + textui.print_dashed('Deactivated DNS zone "%s".' % zone) + +api.register(dns2_disable) + + +class dns2_add_rr(Command): + """ + Add new DNS resource record. + """ + + takes_args = ( + Str('zone', + cli_name='zone', + doc='zone name', + normalizer=lambda value: value.lower(), + ), + Str('idnsname', + cli_name='resource', + doc='resource name', + default_from=lambda zone: zone.lower(), + attribute=True, + ), + StrEnum('type', + cli_name='type', + doc='record type', + values=_record_types, + ), + Str('data', + cli_name='data', + doc='type-specific data', + ), + ) + + takes_options = ( + Int('dnsttl?', + cli_name='ttl', + doc='time to live', + attribute=True, + ), + StrEnum('dnsclass?', + cli_name='class', + doc='class', + values=_record_classes, + attribute=True, + ), + ) + + def execute(self, zone, idnsname, type, data, **options): + ldap = self.api.Backend.ldap2 + attr = '%srecord' % type + + # build entry DN + dn = _get_record_dn(ldap, zone, idnsname) + + # get resource entry where to store the new record + try: + (dn, entry_attrs) = ldap.get_entry(dn, [attr]) + except errors.NotFound: + if idnsname != '@' and idnsname != zone: + # resource entry doesn't exist, check if zone exists + zone_dn = _get_zone_dn(ldap, zone) + ldap.get_entry(zone_dn, ['']) + # it does, create new resource entry + + # build entry attributes + entry_attrs = self.args_options_2_entry( + (idnsname, ), **options + ) + + # fill in required attributes + entry_attrs['objectclass'] = ['top', 'idnsrecord'] + + # fill in the record + entry_attrs[attr] = data + + # create the entry + ldap.add_entry(dn, entry_attrs) + + # get entry with created attributes for output + return ldap.get_entry(dn, entry_attrs.keys()) + + # zone doesn't exist + raise + # resource entry already exists, create a modlist for the new record + + # convert entry_attrs keys to lowercase + #entry_attrs = dict( + # (k.lower(), v) for (k, v) in entry_attrs.iteritems() + #) + + # get new value for record attribute + attr_value = entry_attrs.get(attr, []) + attr_value.append(data) + + ldap.update_entry(dn, {attr: attr_value}) + # get entry with updated attribute for output + return ldap.get_entry(dn, ['idnsname', attr]) + + def output_for_cli(self, textui, result, zone, idnsname, type, data, + **options): + (dn, entry_attrs) = result + output = '"%s %s %s" to zone "%s"' % ( + idnsname, type, data, zone, + ) + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_dashed('Added DNS resource record %s.' % output) + +api.register(dns2_add_rr) + + +class dns2_del_rr(Command): + """ + Delete DNS resource record. + """ + + takes_args = ( + Str('zone', + cli_name='zone', + doc='zone name', + normalizer=lambda value: value.lower(), + ), + Str('idnsname', + cli_name='resource', + doc='resource name', + default_from=lambda zone: zone.lower(), + attribute=True, + ), + StrEnum('type', + cli_name='type', + doc='record type', + values=_record_types, + ), + Str('data', + cli_name='data', + doc='type-specific data', + ), + ) + + def execute(self, zone, idnsname, type, data): + ldap = self.api.Backend.ldap2 + attr = '%srecord' % type + + # build entry DN + dn = _get_record_dn(ldap, zone, idnsname) + + # get resource entry with the record we're trying to delete + (dn, entry_attrs) = ldap.get_entry(dn) + + # convert entry_attrs keys to lowercase + entry_attrs = dict( + (k.lower(), v) for (k, v) in entry_attrs.iteritems() + ) + + # get new value for record attribute + attr_value = entry_attrs.get(attr.lower(), []) + try: + attr_value.remove(data) + except ValueError: + raise errors.NotFound(reason=u'resource record not found') + + # check if it's worth to keep this entry in LDAP + if 'idnszone' not in entry_attrs['objectclass']: + # get a list of all meaningful record attributes + record_attrs = [] + for (k, v) in entry_attrs.iteritems(): + if k.endswith('record') and v: + record_attrs.append(k) + # check if the list is empty + if not record_attrs: + # it's not + ldap.delete_entry(dn) + return True + + ldap.update_entry(dn, {attr: attr_value}) + # get entry with updated attribute for output + return ldap.get_entry(dn, ['idnsname', attr]) + + def output_for_cli(self, textui, result, zone, idnsname, type, data): + output = '"%s %s %s" from zone "%s"' % ( + idnsname, type, data, zone, + ) + + textui.print_name(self.name) + if not isinstance(result, bool): + (dn, entry_attrs) = result + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_dashed('Deleted DNS resource record %s' % output) + +api.register(dns2_del_rr) + + +class dns2_find_rr(Command): + """ + Search for DNS resource records. + """ + + takes_args = ( + Str('zone', + cli_name='zone', + doc='zone name', + normalizer=lambda value: value.lower(), + ), + Str('criteria?', + cli_name='criteria', + doc='search criteria', + ), + ) + + takes_options = ( + Str('idnsname?', + cli_name='resource', + doc='resource name', + default_from=lambda zone: zone.lower(), + ), + StrEnum('type?', + cli_name='type', + doc='record type', + values=_record_types, + ), + Str('data?', + cli_name='data', + doc='type-specific data', + ), + Flag('all', + doc='retrieve all attributes', + ), + ) + + def execute(self, zone, term, **options): + ldap = self.api.Backend.ldap2 + if 'type' in options: + attr = '%srecord' % options['type'] + else: + attr = None + + # build base dn for search + base_dn = _get_zone_dn(ldap, zone) + + # build search keywords + search_kw = {} + if 'data' in options: + if attr is not None: + # user is looking for a certain record type + search_kw[attr] = options['data'] + else: + # search in all record types + for a in _record_default_attributes: + search_kw[a] = term + if 'idnsname' in options: + idnsname = options['idnsname'] + if idnsname == '@': + search_kw['idnsname'] = zone + else: + search_kw['idnsname'] = idnsname + + # build search filter + filter = ldap.make_filter(search_kw, rules=ldap.MATCH_ALL) + if term: + search_kw = {} + for a in _record_default_attributes: + search_kw[a] = term + term_filter = ldap.make_filter(search_kw, exact=False) + filter = ldap.combine_filters((filter, term_filter), ldap.MATCH_ALL) + self.log.info(filter) + + # select attributes we want to retrieve + if options['all']: + attrs_list = ['*'] + elif attr is not None: + attrs_list = [attr] + else: + attrs_list = _record_default_attributes + + # get matching entries + try: + (entries, truncated) = ldap.find_entries( + filter, attrs_list, base_dn + ) + except errors.NotFound: + (entries, truncated) = (tuple(), False) + + # if the user is looking for a certain record type, don't display + # entries that do not contain it + if attr is not None: + related_entries = [] + for e in entries: + entry_attrs = e[1] + if attr in entry_attrs: + related_entries.append(e) + entries = related_entries + + return (entries, truncated) + + def output_for_cli(self, textui, result, zone, term, **options): + (entries, truncated) = result + + textui.print_name(self.name) + for (dn, entry_attrs) in entries: + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_plain('') + textui.print_count( + len(result), '%i DNS resource record matched.', + '%i DNS resource records matched.' + ) + if truncated: + textui.print_dashed('These results are truncated.', below=False) + textui.print_dashed( + 'Please refine your search and try again.', above=False + ) + +api.register(dns2_find_rr) + + +class dns2_show_rr(Command): + """ + Show existing DNS resource records. + """ + + takes_args = ( + Str('zone', + cli_name='zone', + doc='zone name', + normalizer=lambda value: value.lower(), + ), + Str('idnsname', + cli_name='resource', + doc='resource name', + normalizer=lambda value: value.lower(), + ), + ) + + takes_options = ( + Flag('all', + doc='retrieve all attributes', + ), + ) + + def execute(self, zone, idnsname, **options): + # shows all records associated with resource + ldap = self.api.Backend.ldap2 + + # build entry DN + dn = _get_record_dn(ldap, zone, idnsname) + + # select attributes we want to retrieve + if options['all']: + attrs_list = ['*'] + else: + attrs_list = _record_default_attributes + + return ldap.get_entry(dn, attrs_list) + + def output_for_cli(self, textui, result, zone, idnsname, **options): + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + +api.register(dns2_show_rr) + diff --git a/ipalib/plugins/dns2.py b/ipalib/plugins/dns2.py deleted file mode 100644 index 7862e25ed..000000000 --- a/ipalib/plugins/dns2.py +++ /dev/null @@ -1,803 +0,0 @@ -# Authors: -# Pavel Zuna -# -# Copyright (C) 2009 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -""" -Domain Name System (DNS) Plugin - -Implements a set of commands useful for manipulating DNS records used by -the BIND LDAP plugin. - -EXAMPLES: - - Add new zone; - ipa dns2-create example.com nameserver.example.com admin@example.com - - Add second nameserver for example.com: - ipa dns2-add-rr example.com @ NS nameserver2.example.com - - Delete previously added nameserver from example.com: - ipa dns2-del-rr example.com @ NS nameserver2.example.com - - Add new A record for www.example.com: (random IP) - ipa dns2-add-rr example.com www A 80.142.15.2 - - Show zone example.com: - ipa dns2-show example.com - - Find zone with 'example' in it's domain name: - ipa dns2-find example - - Find records for resources with 'www' in their name in zone example.com: - ipa dns2-find-rr example.com www - - Find A records for resource www in zone example.com - ipa dns2-find-rr example.com --resource www --type A - - Show records for resource www in zone example.com - ipa dns2-show-rr example.com www - - Delete zone example.com with all resource records: - ipa dns2-delete example.com -""" - -# A few notes about the LDAP schema to make this plugin more understandable: -# - idnsRecord object is a HOSTNAME with one or more resource records -# - idnsZone object is a idnsRecord object with mandatory SOA record -# it basically makes the assumption that ZONE == DOMAINNAME + SOA record -# resource records can be stored in both idnsZone and idnsRecord objects - -import time - -from ipalib import api, crud, errors -from ipalib import Object, Command -from ipalib import Flag, Int, Str, StrEnum - -# parent DN -_zone_container_dn = api.env.container_dns - -# supported resource record types -_record_types = ( - u'A', u'AAAA', u'A6', u'AFSDB', u'CERT', u'CNAME', u'DNAME', - u'DS', u'HINFO', u'KEY', u'KX', u'LOC', u'MD', u'MINFO', u'MX', - u'NAPTR', u'NS', u'NSEC', u'NXT', u'PTR', u'RRSIG', u'SSHFP', - u'SRV', u'TXT', -) - -# supported DNS classes, IN = internet, rest is almost never used -_record_classes = (u'IN', u'CS', u'CH', u'HS') - -# attributes displayed by default for resource records -_record_default_attributes = ['%srecord' % r for r in _record_types] -_record_default_attributes.append('idnsname') - -# attributes displayed by default for zones -_zone_default_attributes = [ - 'idnsname', 'idnszoneactive', 'idnssoamname', 'idnssoarname', - 'idnssoaserial', 'idnssoarefresh', 'idnssoaretry', 'idnssoaexpire', - 'idnssoaminimum' -] - - -# build zone dn -def _get_zone_dn(ldap, idnsname): - rdn = ldap.make_rdn_from_attr('idnsname', idnsname) - return ldap.make_dn_from_rdn(rdn, _zone_container_dn) - -# build dn for entry with record -def _get_record_dn(ldap, zone, idnsname): - parent_dn = _get_zone_dn(ldap, zone) - if idnsname == '@' or idnsname == zone: - return parent_dn - rdn = ldap.make_rdn_from_attr('idnsname', idnsname) - return ldap.make_dn_from_rdn(rdn, parent_dn) - - -class dns2(Object): - """DNS zone/SOA record object.""" - - takes_params = ( - Str('idnsname', - cli_name='name', - doc='zone name (FQDN)', - normalizer=lambda value: value.lower(), - primary_key=True, - ), - Str('idnssoamname', - cli_name='name_server', - doc='authoritative name server', - ), - Str('idnssoarname', - cli_name='admin_email', - doc='administrator e-mail address', - default_from=lambda idnsname: 'root.%s' % idnsname, - normalizer=lambda value: value.replace('@', '.'), - ), - Int('idnssoaserial?', - cli_name='serial', - doc='SOA serial', - ), - Int('idnssoarefresh?', - cli_name='refresh', - doc='SOA refresh', - ), - Int('idnssoaretry?', - cli_name='retry', - doc='SOA retry', - ), - Int('idnssoaexpire?', - cli_name='expire', - doc='SOA expire', - ), - Int('idnssoaminimum?', - cli_name='minimum', - doc='SOA minimum', - ), - Int('dnsttl?', - cli_name='ttl', - doc='SOA time to live', - ), - StrEnum('dnsclass?', - cli_name='class', - doc='SOA class', - values=_record_classes, - ), - Flag('idnsallowdynupdate', - cli_name='allow_dynupdate', - doc='allow dynamic update?', - ), - ) - -api.register(dns2) - - -class dns2_create(crud.Create): - """ - Create new DNS zone/SOA record. - """ - - def execute(self, *args, **options): - ldap = self.Backend.ldap2 - idnsname = args[0] - - # build entry attributes - entry_attrs = self.args_options_2_entry(*args, **options) - - # build entry DN - dn = _get_zone_dn(ldap, idnsname) - - # fill in required attributes - entry_attrs['objectclass'] = ['top', 'idnsrecord', 'idnszone'] - entry_attrs['idnszoneactive'] = True - - # fill default values, build SOA serial from current date - soa_serial = int('%s01' % time.strftime('%Y%d%m')) - entry_attrs.setdefault('idnssoaserial', soa_serial) - entry_attrs.setdefault('idnssoarefresh', 3600) - entry_attrs.setdefault('idnssoaretry', 900) - entry_attrs.setdefault('idnssoaexpire', 1209600) - entry_attrs.setdefault('idnssoaminimum', 3600) - - # create zone entry - ldap.add_entry(dn, entry_attrs) - - # get zone entry with created attributes for output - return ldap.get_entry(dn, entry_attrs.keys()) - - def output_for_cli(self, textui, result, *args, **options): - (dn, entry_attrs) = result - idnsname = args[0] - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_dashed('Created DNS zone "%s".' % idnsname) - -api.register(dns2_create) - - -class dns2_delete(crud.Delete): - """ - Delete existing DNS zone/SOA record. - """ - - def execute(self, *args, **options): - ldap = self.api.Backend.ldap2 - idnsname = args[0] - - # build zone entry DN - dn = _get_zone_dn(ldap, idnsname) - # just check if zone exists for now - ldap.get_entry(dn, ['']) - - # retrieve all subentries of zone - records - try: - (entries, truncated) = ldap.find_entries( - None, [''], dn, ldap.SCOPE_ONELEVEL - ) - except errors.NotFound: - (entries, truncated) = (tuple(), False) - - # kill'em all, records first - for e in entries: - ldap.delete_entry(e[0]) - ldap.delete_entry(dn) - - # return something positive - return True - - def output_for_cli(self, textui, result, *args, **options): - textui.print_name(self.name) - textui.print_dashed('Deleted DNS zone "%s".' % args[0]) - -api.register(dns2_delete) - - -class dns2_mod(crud.Update): - """ - Modify DNS zone/SOA record. - """ - - def execute(self, *args, **options): - ldap = self.api.Backend.ldap2 - idnsname = args[0] - - # build entry attributes, don't include idnsname! - entry_attrs = self.args_options_2_entry(*tuple(), **options) - - # build entry DN - dn = _get_zone_dn(ldap, idnsname) - - # update zone entry - ldap.update_entry(dn, entry_attrs) - - # get zone entry with modified + default attributes for output - return ldap.get_entry( - dn, (entry_attrs.keys() + _zone_default_attributes) - ) - - def output_for_cli(self, textui, result, *args, **options): - (dn, entry_attrs) = result - idnsname = args[0] - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_dashed('Modified DNS zone "%s".' % idnsname) - -api.register(dns2_mod) - - -class dns2_find(crud.Search): - """ - Search for DNS zones/SOA records. - """ - - takes_options = ( - Flag('all', - doc='retrieve all attributes', - ), - ) - - def execute(self, term, **options): - ldap = self.api.Backend.ldap2 - - # build search filter - filter = ldap.make_filter_from_attr('idnsname', term, exact=False) - - # select attributes we want to retrieve - if options['all']: - attrs_list = ['*'] - else: - attrs_list = _zone_default_attributes - - # get matching entries - try: - (entries, truncated) = ldap.find_entries( - filter, attrs_list, _zone_container_dn, ldap.SCOPE_ONELEVEL - ) - except errors.NotFound: - (entries, truncated) = (tuple(), False) - - return entries - - def output_for_cli(self, textui, result, term, **options): - (entries, truncated) = result - - textui.print_name(self.name) - for (dn, entry_attrs) in entries: - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_plain('') - textui.print_count( - len(result), '%i DNS zone matched.', '%i DNS zones matched.' - ) - if truncated: - textui.print_dashed('These results are truncated.', below=False) - textui.print_dashed( - 'Please refine your search and try again.', above=False - ) - -api.register(dns2_find) - - -class dns2_show(crud.Retrieve): - """ - Display DNS zone/SOA record. - """ - - takes_options = ( - Flag('all', - doc='retrieve all attributes', - ), - ) - - def execute(self, idnsname, **options): - ldap = self.api.Backend.ldap2 - - # build entry DN - dn = _get_zone_dn(ldap, idnsname) - - # select attributes we want to retrieve - if options['all']: - attrs_list = ['*'] - else: - attrs_list = _zone_default_attributes - - return ldap.get_entry(dn, attrs_list) - - def output_for_cli(self, textui, result, *args, **options): - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - -api.register(dns2_show) - - -class dns2_enable(Command): - """ - Activate DNS zone. - """ - - takes_args = ( - Str('zone', - cli_name='zone', - doc='zone name', - normalizer=lambda value: value.lower(), - ), - ) - - def execute(self, zone): - ldap = self.api.Backend.ldap2 - - # build entry DN - dn = _get_zone_dn(ldap, zone) - - # activate! - try: - ldap.update_entry(dn, {'idnszoneactive': True}) - except errors.EmptyModlist: - pass - - # return something positive - return True - - def output_for_cli(self, textui, result, zone): - textui.print_name(self.name) - textui.print_dashed('Activated DNS zone "%s".' % zone) - -api.register(dns2_enable) - - -class dns2_disable(Command): - """ - Deactivate DNS zone. - """ - - takes_args = ( - Str('zone', - cli_name='zone', - doc='zone name', - normalizer=lambda value: value.lower(), - ), - ) - - def execute(self, zone): - ldap = self.api.Backend.ldap2 - - # build entry DN - dn = _get_zone_dn(ldap, zone) - - # deactivate! - try: - ldap.update_entry(dn, {'idnszoneactive': False}) - except errors.EmptyModlist: - pass - - # return something positive - return True - - def output_for_cli(self, textui, result, zone): - textui.print_name(self.name) - textui.print_dashed('Deactivated DNS zone "%s".' % zone) - -api.register(dns2_disable) - - -class dns2_add_rr(Command): - """ - Add new DNS resource record. - """ - - takes_args = ( - Str('zone', - cli_name='zone', - doc='zone name', - normalizer=lambda value: value.lower(), - ), - Str('idnsname', - cli_name='resource', - doc='resource name', - default_from=lambda zone: zone.lower(), - attribute=True, - ), - StrEnum('type', - cli_name='type', - doc='record type', - values=_record_types, - ), - Str('data', - cli_name='data', - doc='type-specific data', - ), - ) - - takes_options = ( - Int('dnsttl?', - cli_name='ttl', - doc='time to live', - attribute=True, - ), - StrEnum('dnsclass?', - cli_name='class', - doc='class', - values=_record_classes, - attribute=True, - ), - ) - - def execute(self, zone, idnsname, type, data, **options): - ldap = self.api.Backend.ldap2 - attr = '%srecord' % type - - # build entry DN - dn = _get_record_dn(ldap, zone, idnsname) - - # get resource entry where to store the new record - try: - (dn, entry_attrs) = ldap.get_entry(dn, [attr]) - except errors.NotFound: - if idnsname != '@' and idnsname != zone: - # resource entry doesn't exist, check if zone exists - zone_dn = _get_zone_dn(ldap, zone) - ldap.get_entry(zone_dn, ['']) - # it does, create new resource entry - - # build entry attributes - entry_attrs = self.args_options_2_entry( - (idnsname, ), **options - ) - - # fill in required attributes - entry_attrs['objectclass'] = ['top', 'idnsrecord'] - - # fill in the record - entry_attrs[attr] = data - - # create the entry - ldap.add_entry(dn, entry_attrs) - - # get entry with created attributes for output - return ldap.get_entry(dn, entry_attrs.keys()) - - # zone doesn't exist - raise - # resource entry already exists, create a modlist for the new record - - # convert entry_attrs keys to lowercase - #entry_attrs = dict( - # (k.lower(), v) for (k, v) in entry_attrs.iteritems() - #) - - # get new value for record attribute - attr_value = entry_attrs.get(attr, []) - attr_value.append(data) - - ldap.update_entry(dn, {attr: attr_value}) - # get entry with updated attribute for output - return ldap.get_entry(dn, ['idnsname', attr]) - - def output_for_cli(self, textui, result, zone, idnsname, type, data, - **options): - (dn, entry_attrs) = result - output = '"%s %s %s" to zone "%s"' % ( - idnsname, type, data, zone, - ) - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_dashed('Added DNS resource record %s.' % output) - -api.register(dns2_add_rr) - - -class dns2_del_rr(Command): - """ - Delete DNS resource record. - """ - - takes_args = ( - Str('zone', - cli_name='zone', - doc='zone name', - normalizer=lambda value: value.lower(), - ), - Str('idnsname', - cli_name='resource', - doc='resource name', - default_from=lambda zone: zone.lower(), - attribute=True, - ), - StrEnum('type', - cli_name='type', - doc='record type', - values=_record_types, - ), - Str('data', - cli_name='data', - doc='type-specific data', - ), - ) - - def execute(self, zone, idnsname, type, data): - ldap = self.api.Backend.ldap2 - attr = '%srecord' % type - - # build entry DN - dn = _get_record_dn(ldap, zone, idnsname) - - # get resource entry with the record we're trying to delete - (dn, entry_attrs) = ldap.get_entry(dn) - - # convert entry_attrs keys to lowercase - entry_attrs = dict( - (k.lower(), v) for (k, v) in entry_attrs.iteritems() - ) - - # get new value for record attribute - attr_value = entry_attrs.get(attr.lower(), []) - try: - attr_value.remove(data) - except ValueError: - raise errors.NotFound(reason=u'resource record not found') - - # check if it's worth to keep this entry in LDAP - if 'idnszone' not in entry_attrs['objectclass']: - # get a list of all meaningful record attributes - record_attrs = [] - for (k, v) in entry_attrs.iteritems(): - if k.endswith('record') and v: - record_attrs.append(k) - # check if the list is empty - if not record_attrs: - # it's not - ldap.delete_entry(dn) - return True - - ldap.update_entry(dn, {attr: attr_value}) - # get entry with updated attribute for output - return ldap.get_entry(dn, ['idnsname', attr]) - - def output_for_cli(self, textui, result, zone, idnsname, type, data): - output = '"%s %s %s" from zone "%s"' % ( - idnsname, type, data, zone, - ) - - textui.print_name(self.name) - if not isinstance(result, bool): - (dn, entry_attrs) = result - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_dashed('Deleted DNS resource record %s' % output) - -api.register(dns2_del_rr) - - -class dns2_find_rr(Command): - """ - Search for DNS resource records. - """ - - takes_args = ( - Str('zone', - cli_name='zone', - doc='zone name', - normalizer=lambda value: value.lower(), - ), - Str('criteria?', - cli_name='criteria', - doc='search criteria', - ), - ) - - takes_options = ( - Str('idnsname?', - cli_name='resource', - doc='resource name', - default_from=lambda zone: zone.lower(), - ), - StrEnum('type?', - cli_name='type', - doc='record type', - values=_record_types, - ), - Str('data?', - cli_name='data', - doc='type-specific data', - ), - Flag('all', - doc='retrieve all attributes', - ), - ) - - def execute(self, zone, term, **options): - ldap = self.api.Backend.ldap2 - if 'type' in options: - attr = '%srecord' % options['type'] - else: - attr = None - - # build base dn for search - base_dn = _get_zone_dn(ldap, zone) - - # build search keywords - search_kw = {} - if 'data' in options: - if attr is not None: - # user is looking for a certain record type - search_kw[attr] = options['data'] - else: - # search in all record types - for a in _record_default_attributes: - search_kw[a] = term - if 'idnsname' in options: - idnsname = options['idnsname'] - if idnsname == '@': - search_kw['idnsname'] = zone - else: - search_kw['idnsname'] = idnsname - - # build search filter - filter = ldap.make_filter(search_kw, rules=ldap.MATCH_ALL) - if term: - search_kw = {} - for a in _record_default_attributes: - search_kw[a] = term - term_filter = ldap.make_filter(search_kw, exact=False) - filter = ldap.combine_filters((filter, term_filter), ldap.MATCH_ALL) - self.log.info(filter) - - # select attributes we want to retrieve - if options['all']: - attrs_list = ['*'] - elif attr is not None: - attrs_list = [attr] - else: - attrs_list = _record_default_attributes - - # get matching entries - try: - (entries, truncated) = ldap.find_entries( - filter, attrs_list, base_dn - ) - except errors.NotFound: - (entries, truncated) = (tuple(), False) - - # if the user is looking for a certain record type, don't display - # entries that do not contain it - if attr is not None: - related_entries = [] - for e in entries: - entry_attrs = e[1] - if attr in entry_attrs: - related_entries.append(e) - entries = related_entries - - return (entries, truncated) - - def output_for_cli(self, textui, result, zone, term, **options): - (entries, truncated) = result - - textui.print_name(self.name) - for (dn, entry_attrs) in entries: - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_plain('') - textui.print_count( - len(result), '%i DNS resource record matched.', - '%i DNS resource records matched.' - ) - if truncated: - textui.print_dashed('These results are truncated.', below=False) - textui.print_dashed( - 'Please refine your search and try again.', above=False - ) - -api.register(dns2_find_rr) - - -class dns2_show_rr(Command): - """ - Show existing DNS resource records. - """ - - takes_args = ( - Str('zone', - cli_name='zone', - doc='zone name', - normalizer=lambda value: value.lower(), - ), - Str('idnsname', - cli_name='resource', - doc='resource name', - normalizer=lambda value: value.lower(), - ), - ) - - takes_options = ( - Flag('all', - doc='retrieve all attributes', - ), - ) - - def execute(self, zone, idnsname, **options): - # shows all records associated with resource - ldap = self.api.Backend.ldap2 - - # build entry DN - dn = _get_record_dn(ldap, zone, idnsname) - - # select attributes we want to retrieve - if options['all']: - attrs_list = ['*'] - else: - attrs_list = _record_default_attributes - - return ldap.get_entry(dn, attrs_list) - - def output_for_cli(self, textui, result, zone, idnsname, **options): - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - -api.register(dns2_show_rr) - diff --git a/ipalib/plugins/group.py b/ipalib/plugins/group.py new file mode 100644 index 000000000..7666188ee --- /dev/null +++ b/ipalib/plugins/group.py @@ -0,0 +1,220 @@ +# Authors: +# Rob Crittenden +# Pavel Zuna +# +# Copyright (C) 2009 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Groups of users. +""" + +from ipalib import api +from ipalib.plugins.basegroup2 import * + +_container_dn = api.env.container_group +_default_attributes = ['cn', 'description', 'gidnumber', 'member', 'memberof'] +_default_class = 'ipausergroup' + + +class group2(basegroup2): + """ + Group object. + """ + container = _container_dn + + takes_params = basegroup2.takes_params + ( + Int('gidnumber?', + cli_name='gid', + doc='GID (use this option to set it manually)', + ), + ) + +api.register(group2) + + +class group2_create(basegroup2_create): + """ + Create new group. + """ + takes_options = ( + Flag('posix', + cli_name='posix', + doc='create as posix group?', + ), + ) + + def execute(self, cn, **kw): + """ + Execute the group-add operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + Returns the entry as it will be created in LDAP. + + No need to explicitly set gidNumber. The dna_plugin will do this + for us if the value isn't provided by the caller. + + :param cn: The name of the group being added. + :param kw: Keyword arguments for the other LDAP attributes. + """ + assert 'cn' not in kw + assert 'dn' not in kw + ldap = self.api.Backend.ldap2 + + config = ldap.get_ipa_config()[1] + + kw['objectclass'] = config.get('ipagroupobjectclasses') + if kw['posix'] or 'gidnumber' in kw: + kw['objectclass'].append('posixgroup') + + return super(group2_create, self).execute(cn, **kw) + +api.register(group2_create) + + +class group2_delete(basegroup2_delete): + """ + Delete group. + """ + container = _container_dn + filter_class = _default_class + + def execute(self, cn, **kw): + """ + Delete a group + + The memberOf plugin handles removing the group from any other + groups. + + :param cn: The name of the group being removed + :param kw: Unused + """ + ldap = self.api.Backend.ldap2 + dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) + + # Don't allow the default user group to be removed + try: + config = ldap.get_ipa_config()[1] + def_group_cn = config.get('ipadefaultprimarygroup') + def_group_dn = get_dn_by_attr( + ldap, 'cn', def_group_cn, self.filter_class, self.container + ) + if dn == def_group_dn: + raise errors.DefaultGroup() + except errors.NotFound: + pass + + return super(group2_delete, self).execute(cn, **kw) + +api.register(group2_delete) + + +class group2_mod(basegroup2_mod): + """ + Modify group. + """ + container = _container_dn + filter_class = _default_class + + takes_options = ( + Flag('posix', + cli_name='posix', + doc='change to posix group', + ), + ) + def execute(self, cn, **kw): + """ + Execute the group-mod operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + Returns the entry + + :param cn: The name of the group to update. + :param kw: Keyword arguments for the other LDAP attributes. + """ + assert 'cn' not in kw + assert 'dn' not in kw + ldap = self.api.Backend.ldap2 + + if kw['posix'] or 'gidnumber' in kw: + dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) + (dn, entry_attrs) = ldap.get_entry(dn, ['objectclass']) + if 'posixgroup' in entry_attrs['objectclass']: + if kw['posix'] in entry_attrs['objectclass']: + raise errors.AlreadyPosixGroup() + else: + entry_attrs['objectclass'].append('posixgroup') + kw['objectclass'] = entry_attrs['objectclass'] + + return super(group2_mod, self).execute(cn, **kw) + +api.register(group2_mod) + + +class group2_find(basegroup2_find): + """ + Search for groups. + """ + default_attributes = _default_attributes + container = _container_dn + filter_class = _default_class + + def execute(self, cn, **kw): + return super(group2_find, self).execute(cn, **kw) + +api.register(group2_find) + + +class group2_show(basegroup2_show): + """ + Display group. + """ + default_attributes = _default_attributes + container = _container_dn + + def execute(self, cn, **kw): + return super(group2_show, self).execute(cn, **kw) + +api.register(group2_show) + + +class group2_add_member(basegroup2_add_member): + """ + Add members to group. + """ + container = _container_dn + + def execute(self, cn, **kw): + return super(group2_add_member, self).execute(cn, **kw) + +api.register(group2_add_member) + + +class group2_del_member(basegroup2_del_member): + """ + Remove members from group. + """ + container = _container_dn + + def execute(self, cn, **kw): + return super(group2_del_member, self).execute(cn, **kw) + +api.register(group2_del_member) + diff --git a/ipalib/plugins/group2.py b/ipalib/plugins/group2.py deleted file mode 100644 index 7666188ee..000000000 --- a/ipalib/plugins/group2.py +++ /dev/null @@ -1,220 +0,0 @@ -# Authors: -# Rob Crittenden -# Pavel Zuna -# -# Copyright (C) 2009 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Groups of users. -""" - -from ipalib import api -from ipalib.plugins.basegroup2 import * - -_container_dn = api.env.container_group -_default_attributes = ['cn', 'description', 'gidnumber', 'member', 'memberof'] -_default_class = 'ipausergroup' - - -class group2(basegroup2): - """ - Group object. - """ - container = _container_dn - - takes_params = basegroup2.takes_params + ( - Int('gidnumber?', - cli_name='gid', - doc='GID (use this option to set it manually)', - ), - ) - -api.register(group2) - - -class group2_create(basegroup2_create): - """ - Create new group. - """ - takes_options = ( - Flag('posix', - cli_name='posix', - doc='create as posix group?', - ), - ) - - def execute(self, cn, **kw): - """ - Execute the group-add operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - Returns the entry as it will be created in LDAP. - - No need to explicitly set gidNumber. The dna_plugin will do this - for us if the value isn't provided by the caller. - - :param cn: The name of the group being added. - :param kw: Keyword arguments for the other LDAP attributes. - """ - assert 'cn' not in kw - assert 'dn' not in kw - ldap = self.api.Backend.ldap2 - - config = ldap.get_ipa_config()[1] - - kw['objectclass'] = config.get('ipagroupobjectclasses') - if kw['posix'] or 'gidnumber' in kw: - kw['objectclass'].append('posixgroup') - - return super(group2_create, self).execute(cn, **kw) - -api.register(group2_create) - - -class group2_delete(basegroup2_delete): - """ - Delete group. - """ - container = _container_dn - filter_class = _default_class - - def execute(self, cn, **kw): - """ - Delete a group - - The memberOf plugin handles removing the group from any other - groups. - - :param cn: The name of the group being removed - :param kw: Unused - """ - ldap = self.api.Backend.ldap2 - dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) - - # Don't allow the default user group to be removed - try: - config = ldap.get_ipa_config()[1] - def_group_cn = config.get('ipadefaultprimarygroup') - def_group_dn = get_dn_by_attr( - ldap, 'cn', def_group_cn, self.filter_class, self.container - ) - if dn == def_group_dn: - raise errors.DefaultGroup() - except errors.NotFound: - pass - - return super(group2_delete, self).execute(cn, **kw) - -api.register(group2_delete) - - -class group2_mod(basegroup2_mod): - """ - Modify group. - """ - container = _container_dn - filter_class = _default_class - - takes_options = ( - Flag('posix', - cli_name='posix', - doc='change to posix group', - ), - ) - def execute(self, cn, **kw): - """ - Execute the group-mod operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - Returns the entry - - :param cn: The name of the group to update. - :param kw: Keyword arguments for the other LDAP attributes. - """ - assert 'cn' not in kw - assert 'dn' not in kw - ldap = self.api.Backend.ldap2 - - if kw['posix'] or 'gidnumber' in kw: - dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) - (dn, entry_attrs) = ldap.get_entry(dn, ['objectclass']) - if 'posixgroup' in entry_attrs['objectclass']: - if kw['posix'] in entry_attrs['objectclass']: - raise errors.AlreadyPosixGroup() - else: - entry_attrs['objectclass'].append('posixgroup') - kw['objectclass'] = entry_attrs['objectclass'] - - return super(group2_mod, self).execute(cn, **kw) - -api.register(group2_mod) - - -class group2_find(basegroup2_find): - """ - Search for groups. - """ - default_attributes = _default_attributes - container = _container_dn - filter_class = _default_class - - def execute(self, cn, **kw): - return super(group2_find, self).execute(cn, **kw) - -api.register(group2_find) - - -class group2_show(basegroup2_show): - """ - Display group. - """ - default_attributes = _default_attributes - container = _container_dn - - def execute(self, cn, **kw): - return super(group2_show, self).execute(cn, **kw) - -api.register(group2_show) - - -class group2_add_member(basegroup2_add_member): - """ - Add members to group. - """ - container = _container_dn - - def execute(self, cn, **kw): - return super(group2_add_member, self).execute(cn, **kw) - -api.register(group2_add_member) - - -class group2_del_member(basegroup2_del_member): - """ - Remove members from group. - """ - container = _container_dn - - def execute(self, cn, **kw): - return super(group2_del_member, self).execute(cn, **kw) - -api.register(group2_del_member) - diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py new file mode 100644 index 000000000..1b2b74901 --- /dev/null +++ b/ipalib/plugins/host.py @@ -0,0 +1,375 @@ +# Authors: +# Rob Crittenden +# Pavel Zuna +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" +Hosts/Machines (Identity) +""" + +import platform +import os +import sys + +from ipalib import api, crud, errors, util +from ipalib import Object +from ipalib import Str, Flag + +_container_dn = api.env.container_host +_default_attributes = [ + 'fqdn', 'description', 'localityname', 'nshostlocation', + 'nshardwareplatform', 'nsosversion' +] + + +def get_host(ldap, hostname): + """ + Try to get the hostname as fully-qualified first, then fall back to + just a host name search. + """ + if hostname.endswith('.'): + hostname = hostname[:-1] + try: + dn = ldap.find_entry_by_attr('fqdn', hostname, 'ipaHost')[0] + except errors.NotFound: + dn = ldap.find_entry_by_attr('serverhostname', hostname, 'ipaHost')[0] + return dn + +def validate_host(ugettext, fqdn): + """ + Require at least one dot in the hostname (to support localhost.localdomain) + """ + if fqdn.index('.') == -1: + return 'Fully-qualified hostname required' + return None + +def determine_os(): + """ + Return OS name (e.g. redhat 10 Cambridge). + """ + (sysname, nodename, release, version, machine) = os.uname() + if sys.platform == 'linux2': + # something like 'fedora 9 Sulpher' + return unicode(' '.join(platform.dist())) + else: + # on Solaris this will be: 'SunOS 5.10' + return unicode(sysname + ' ' + release) + +def determine_platform(): + """ + Return platform name (e.g. i686). + """ + (sysname, nodename, release, version, machine) = os.uname() + return unicode(machine) + + +class host2(Object): + """ + Host object. + """ + takes_params = ( + # FIXME: All Object params get cloned with query=True in the new + # CRUD base classes, so there's no validation going on + Str('fqdn', validate_host, + cli_name='hostname', + doc='Hostname', + primary_key=True, + normalizer=lambda value: value.lower(), + ), + Str('description?', + doc='Description of the host', + ), + Str('localityname?', + cli_name='locality', + doc='Locality of the host (Baltimore, MD)', + ), + Str('nshostlocation?', + cli_name='location', + doc='Location of the host (e.g. Lab 2)', + ), + Str('nshardwareplatform?', + cli_name='platform', + doc='Hardware platform of the host (e.g. Lenovo T61)', + default=determine_platform(), + autofill=True, + ), + Str('nsosversion?', + cli_name='os', + doc='Operating System and version of the host (e.g. Fedora 9)', + default=determine_os(), + autofill=True, + ), + Str('userpassword?', + cli_name='password', + doc='Password used in bulk enrollment', + ), + ) + +api.register(host2) + + +class host2_create(crud.Create): + """ + Create new host. + """ + def execute(self, hostname, **kw): + """ + Execute the host-add operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + If password is set then this is considered a 'bulk' host so we + do not create a kerberos service principal. + + Returns the entry as it will be created in LDAP. + + :param hostname: The name of the host being added. + :param kw: Keyword arguments for the other LDAP attributes. + """ + assert 'fqdn' not in kw + assert 'cn' not in kw + assert 'dn' not in kw + assert 'krbprincipalname' not in kw + ldap = self.api.Backend.ldap2 + + entry_attrs = self.args_options_2_entry(hostname, **kw) + entry_attrs['cn'] = hostname + entry_attrs['serverhostname'] = hostname.split('.', 1)[0] + + dn = ldap.make_dn(entry_attrs, 'fqdn', _container_dn) + + # FIXME: do a DNS lookup to ensure host exists + + current = util.get_current_principal() + if not current: + raise errors.NotFound(reason='Unable to determine current user') + entry_attrs['enrolledby'] = ldap.find_entry_by_attr( + 'krbprincipalname', current, 'posixAccount' + )[0] + + # FIXME: add this attribute to cn=ipaconfig + # config = ldap.get_ipa_config()[1] + # kw['objectclass'] = config.get('ipahostobjectclasses') + entry_attrs['objectclass'] = ['nshost', 'ipahost', 'pkiuser'] + + if 'userpassword' not in entry_attrs: + entry_attrs['krbprincipalname'] = 'host/%s@%s' % ( + hostname, self.api.env.realm + ) + if 'krbprincipalaux' not in entry_attrs['objectclass']: + entry_attrs['objectclass'].append('krbprincipalaux') + entry_attrs['objectclass'].append('krbprincipal') + elif 'krbprincipalaux' in entry_attrs['objectclass']: + entry_attrs['objectclass'].remove('krbprincipalaux') + + ldap.add_entry(dn, entry_attrs) + + return ldap.get_entry(dn, entry_attrs.keys()) + + def output_for_cli(self, textui, result, hostname, **options): + """ + Output result of this command to command line interface. + """ + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_dashed('Created host "%s".' % hostname) + +api.register(host2_create) + + +class host2_delete(crud.Delete): + """ + Delete host. + """ + def execute(self, hostname, **kw): + """ + Delete a host. + + hostname is the name of the host to delete + + :param hostname: The name of the host being removed. + :param kw: Not used. + """ + ldap = self.api.Backend.ldap2 + dn = get_host(ldap, hostname) + + # Remove all service records for this host + services = api.Command['service2_find'](hostname) + for (dn, entry_attrs) in services: + principal = entry_attrs['krbprincipalname'] + api.Command['service2_delete'](principal) + + ldap.delete_entry(dn) + + return True + + def output_for_cli(self, textui, result, hostname, **options): + """ + Output result of this command to command line interface. + """ + textui.print_name(self.name) + textui.print_dashed('Deleted host "%s".' % hostname) + +api.register(host2_delete) + + +class host2_mod(crud.Update): + """ + Modify host. + """ + def execute(self, hostname, **kw): + """ + Execute the host-mod operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + Returns the entry + + :param hostname: The name of the host to retrieve. + :param kw: Keyword arguments for the other LDAP attributes. + """ + assert 'fqdn' not in kw + assert 'dn' not in kw + ldap = self.api.Backend.ldap2 + dn = get_host(ldap, hostname) + + entry_attrs = self.args_options_2_entry(**kw) + + try: + ldap.update_entry(dn, entry_attrs) + except errors.EmptyModlist: + pass + + return ldap.get_entry(dn, entry_attrs.keys()) + + def output_for_cli(self, textui, result, hostname, **options): + """ + Output result of this command to command line interface. + """ + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_dashed('Modified host "%s".' % hostname) + +api.register(host2_mod) + + +class host2_find(crud.Search): + """ + Search for hosts. + """ + + takes_options = ( + Flag('all', + doc='Retrieve all attributes' + ), + ) + + def execute(self, term, **kw): + ldap = self.api.Backend.ldap2 + + search_kw = self.args_options_2_entry(**kw) + search_kw['objectclass'] = 'ipaHost' + filter = ldap.make_filter(search_kw, rules=ldap.MATCH_ALL) + + search_kw = {} + for a in _default_attributes: + search_kw[a] = term + term_filter = ldap.make_filter(search_kw, exact=False) + + if kw['all']: + attrs_list = ['*'] + else: + attrs_list = _default_attributes + + try: + (entries, truncated) = ldap.find_entries( + filter, attrs_list, _container_dn + ) + except errors.NotFound: + (entries, truncated) = (tuple(), False) + + return entries + + def output_for_cli(self, textui, result, term, **options): + (entries, truncated) = result + + textui.print_name(self.name) + for (dn, entry_attrs) in entries: + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_plain('') + textui.print_count( + len(result), '%i host matched.', '%i hosts matched.' + ) + if truncated: + textui.print_dashed('These results are truncated.', below=False) + textui.print_dashed( + 'Please refine your search and try again.', above=False + ) + +api.register(host2_find) + + +class host2_show(crud.Retrieve): + """ + Display host. + """ + takes_options = ( + Flag('all', + doc='Retrieve all attributes' + ), + ) + + def execute(self, hostname, **kw): + """ + Execute the host-show operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + Returns the entry + + :param hostname: The login name of the host to retrieve. + :param kw: "all" set to True = return all attributes + """ + ldap = self.api.Backend.ldap2 + dn = get_host(ldap, hostname) + + if kw['all']: + attrs_list = ['*'] + else: + attrs_list = _default_attributes + + return ldap.get_entry(dn, attrs_list) + + def output_for_cli(self, textui, result, *args, **options): + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + +api.register(host2_show) + diff --git a/ipalib/plugins/host2.py b/ipalib/plugins/host2.py deleted file mode 100644 index 1b2b74901..000000000 --- a/ipalib/plugins/host2.py +++ /dev/null @@ -1,375 +0,0 @@ -# Authors: -# Rob Crittenden -# Pavel Zuna -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -""" -Hosts/Machines (Identity) -""" - -import platform -import os -import sys - -from ipalib import api, crud, errors, util -from ipalib import Object -from ipalib import Str, Flag - -_container_dn = api.env.container_host -_default_attributes = [ - 'fqdn', 'description', 'localityname', 'nshostlocation', - 'nshardwareplatform', 'nsosversion' -] - - -def get_host(ldap, hostname): - """ - Try to get the hostname as fully-qualified first, then fall back to - just a host name search. - """ - if hostname.endswith('.'): - hostname = hostname[:-1] - try: - dn = ldap.find_entry_by_attr('fqdn', hostname, 'ipaHost')[0] - except errors.NotFound: - dn = ldap.find_entry_by_attr('serverhostname', hostname, 'ipaHost')[0] - return dn - -def validate_host(ugettext, fqdn): - """ - Require at least one dot in the hostname (to support localhost.localdomain) - """ - if fqdn.index('.') == -1: - return 'Fully-qualified hostname required' - return None - -def determine_os(): - """ - Return OS name (e.g. redhat 10 Cambridge). - """ - (sysname, nodename, release, version, machine) = os.uname() - if sys.platform == 'linux2': - # something like 'fedora 9 Sulpher' - return unicode(' '.join(platform.dist())) - else: - # on Solaris this will be: 'SunOS 5.10' - return unicode(sysname + ' ' + release) - -def determine_platform(): - """ - Return platform name (e.g. i686). - """ - (sysname, nodename, release, version, machine) = os.uname() - return unicode(machine) - - -class host2(Object): - """ - Host object. - """ - takes_params = ( - # FIXME: All Object params get cloned with query=True in the new - # CRUD base classes, so there's no validation going on - Str('fqdn', validate_host, - cli_name='hostname', - doc='Hostname', - primary_key=True, - normalizer=lambda value: value.lower(), - ), - Str('description?', - doc='Description of the host', - ), - Str('localityname?', - cli_name='locality', - doc='Locality of the host (Baltimore, MD)', - ), - Str('nshostlocation?', - cli_name='location', - doc='Location of the host (e.g. Lab 2)', - ), - Str('nshardwareplatform?', - cli_name='platform', - doc='Hardware platform of the host (e.g. Lenovo T61)', - default=determine_platform(), - autofill=True, - ), - Str('nsosversion?', - cli_name='os', - doc='Operating System and version of the host (e.g. Fedora 9)', - default=determine_os(), - autofill=True, - ), - Str('userpassword?', - cli_name='password', - doc='Password used in bulk enrollment', - ), - ) - -api.register(host2) - - -class host2_create(crud.Create): - """ - Create new host. - """ - def execute(self, hostname, **kw): - """ - Execute the host-add operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - If password is set then this is considered a 'bulk' host so we - do not create a kerberos service principal. - - Returns the entry as it will be created in LDAP. - - :param hostname: The name of the host being added. - :param kw: Keyword arguments for the other LDAP attributes. - """ - assert 'fqdn' not in kw - assert 'cn' not in kw - assert 'dn' not in kw - assert 'krbprincipalname' not in kw - ldap = self.api.Backend.ldap2 - - entry_attrs = self.args_options_2_entry(hostname, **kw) - entry_attrs['cn'] = hostname - entry_attrs['serverhostname'] = hostname.split('.', 1)[0] - - dn = ldap.make_dn(entry_attrs, 'fqdn', _container_dn) - - # FIXME: do a DNS lookup to ensure host exists - - current = util.get_current_principal() - if not current: - raise errors.NotFound(reason='Unable to determine current user') - entry_attrs['enrolledby'] = ldap.find_entry_by_attr( - 'krbprincipalname', current, 'posixAccount' - )[0] - - # FIXME: add this attribute to cn=ipaconfig - # config = ldap.get_ipa_config()[1] - # kw['objectclass'] = config.get('ipahostobjectclasses') - entry_attrs['objectclass'] = ['nshost', 'ipahost', 'pkiuser'] - - if 'userpassword' not in entry_attrs: - entry_attrs['krbprincipalname'] = 'host/%s@%s' % ( - hostname, self.api.env.realm - ) - if 'krbprincipalaux' not in entry_attrs['objectclass']: - entry_attrs['objectclass'].append('krbprincipalaux') - entry_attrs['objectclass'].append('krbprincipal') - elif 'krbprincipalaux' in entry_attrs['objectclass']: - entry_attrs['objectclass'].remove('krbprincipalaux') - - ldap.add_entry(dn, entry_attrs) - - return ldap.get_entry(dn, entry_attrs.keys()) - - def output_for_cli(self, textui, result, hostname, **options): - """ - Output result of this command to command line interface. - """ - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_dashed('Created host "%s".' % hostname) - -api.register(host2_create) - - -class host2_delete(crud.Delete): - """ - Delete host. - """ - def execute(self, hostname, **kw): - """ - Delete a host. - - hostname is the name of the host to delete - - :param hostname: The name of the host being removed. - :param kw: Not used. - """ - ldap = self.api.Backend.ldap2 - dn = get_host(ldap, hostname) - - # Remove all service records for this host - services = api.Command['service2_find'](hostname) - for (dn, entry_attrs) in services: - principal = entry_attrs['krbprincipalname'] - api.Command['service2_delete'](principal) - - ldap.delete_entry(dn) - - return True - - def output_for_cli(self, textui, result, hostname, **options): - """ - Output result of this command to command line interface. - """ - textui.print_name(self.name) - textui.print_dashed('Deleted host "%s".' % hostname) - -api.register(host2_delete) - - -class host2_mod(crud.Update): - """ - Modify host. - """ - def execute(self, hostname, **kw): - """ - Execute the host-mod operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - Returns the entry - - :param hostname: The name of the host to retrieve. - :param kw: Keyword arguments for the other LDAP attributes. - """ - assert 'fqdn' not in kw - assert 'dn' not in kw - ldap = self.api.Backend.ldap2 - dn = get_host(ldap, hostname) - - entry_attrs = self.args_options_2_entry(**kw) - - try: - ldap.update_entry(dn, entry_attrs) - except errors.EmptyModlist: - pass - - return ldap.get_entry(dn, entry_attrs.keys()) - - def output_for_cli(self, textui, result, hostname, **options): - """ - Output result of this command to command line interface. - """ - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_dashed('Modified host "%s".' % hostname) - -api.register(host2_mod) - - -class host2_find(crud.Search): - """ - Search for hosts. - """ - - takes_options = ( - Flag('all', - doc='Retrieve all attributes' - ), - ) - - def execute(self, term, **kw): - ldap = self.api.Backend.ldap2 - - search_kw = self.args_options_2_entry(**kw) - search_kw['objectclass'] = 'ipaHost' - filter = ldap.make_filter(search_kw, rules=ldap.MATCH_ALL) - - search_kw = {} - for a in _default_attributes: - search_kw[a] = term - term_filter = ldap.make_filter(search_kw, exact=False) - - if kw['all']: - attrs_list = ['*'] - else: - attrs_list = _default_attributes - - try: - (entries, truncated) = ldap.find_entries( - filter, attrs_list, _container_dn - ) - except errors.NotFound: - (entries, truncated) = (tuple(), False) - - return entries - - def output_for_cli(self, textui, result, term, **options): - (entries, truncated) = result - - textui.print_name(self.name) - for (dn, entry_attrs) in entries: - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_plain('') - textui.print_count( - len(result), '%i host matched.', '%i hosts matched.' - ) - if truncated: - textui.print_dashed('These results are truncated.', below=False) - textui.print_dashed( - 'Please refine your search and try again.', above=False - ) - -api.register(host2_find) - - -class host2_show(crud.Retrieve): - """ - Display host. - """ - takes_options = ( - Flag('all', - doc='Retrieve all attributes' - ), - ) - - def execute(self, hostname, **kw): - """ - Execute the host-show operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - Returns the entry - - :param hostname: The login name of the host to retrieve. - :param kw: "all" set to True = return all attributes - """ - ldap = self.api.Backend.ldap2 - dn = get_host(ldap, hostname) - - if kw['all']: - attrs_list = ['*'] - else: - attrs_list = _default_attributes - - return ldap.get_entry(dn, attrs_list) - - def output_for_cli(self, textui, result, *args, **options): - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - -api.register(host2_show) - diff --git a/ipalib/plugins/hostgroup.py b/ipalib/plugins/hostgroup.py new file mode 100644 index 000000000..5a1aa8128 --- /dev/null +++ b/ipalib/plugins/hostgroup.py @@ -0,0 +1,240 @@ +# Authors: +# Rob Crittenden +# +# Copyright (C) 2009 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Groups of hosts. +""" + +from ipalib import api +from ipalib.plugins.basegroup2 import * + +_container_dn = api.env.container_hostgroup +_default_attributes = ['cn', 'description', 'member', 'memberof'] +_default_class = 'ipahostgroup' + + +class hostgroup2(basegroup2): + """ + Hostgroup object. + """ + container = _container_dn + +api.register(hostgroup2) + + +class hostgroup2_create(basegroup2_create): + """ + Create a new hostgroup. + """ + base_classes = basegroup2_create.base_classes + (_default_class, ) + + def execute(self, cn, **kw): + return super(hostgroup2_create, self).execute(cn, **kw) + +api.register(hostgroup2_create) + + +class hostgroup2_delete(basegroup2_delete): + """ + Delete an existing hostgroup. + """ + container = _container_dn + + def execute(self, cn, **kw): + return super(hostgroup2_delete, self).execute(cn, **kw) + +api.register(hostgroup2_delete) + + +class hostgroup2_mod(basegroup2_mod): + """ + Edit an existing hostgroup. + """ + container = _container_dn + + def execute(self, cn, **kw): + return super(hostgroup2_mod, self).execute(cn, **kw) + +api.register(hostgroup2_mod) + + +class hostgroup2_find(basegroup2_find): + """ + Search the groups. + """ + container = _container_dn + + def execute(self, cn, **kw): + return super(hostgroup2_find, self).execute(cn, **kw) + +api.register(hostgroup2_find) + + +class hostgroup2_show(basegroup2_show): + """ + Examine an existing hostgroup. + """ + container = _container_dn + + def execute(self, cn, **kw): + return super(hostgroup2_show, self).execute(cn, **kw) + +api.register(hostgroup2_show) + + +class hostgroup2_add_member(basegroup2_add_member): + """ + Add members to hostgroup. + """ + container = _container_dn + + takes_options = ( + List('groups?', + cli_name='groups', + doc='comma-separated list of user groups to add' + ), + List('hosts?', + cli_name='hosts', + doc='comma-separated list of hosts to add' + ), + List('hostgroups?', + cli_name='hostgroups', + doc='comma-separated list of hostgroups to add' + ), + ) + + def execute(self, cn, **kw): + """ + Execute the group-add-member operation. + + Returns a tuple containing the number of members added + and the updated entry. + + :param cn: The group name to add new members to. + :param kw: groups is a comma-separated list of groups to add + :parem kw: users is a comma-separated list of users to add + """ + assert self.container + ldap = self.api.Backend.ldap2 + dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) + to_add = [] + add_failed = [] + completed = 0 + + members = kw.get('groups', []) + (to_add, add_failed) = find_members( + ldap, add_failed, members, 'cn', 'ipausergroup', + self.api.env.container_group + ) + (completed, add_failed) = add_members( + ldap, completed, to_add, add_failed, dn, 'member' + ) + + members = kw.get('hosts', []) + (to_add, add_failed) = find_members( + ldap, add_failed, members, 'cn', 'ipahost', + self.api.env.container_host + ) + (completed, add_failed) = add_members( + ldap, completed, to_add, add_failed, dn, 'member' + ) + + members = kw.get('hostgroups', []) + (to_add, add_failed) = find_members( + ldap, add_failed, members, 'cn', 'ipahostgroup', + self.api.env.container_hostgroup + ) + (completed, add_failed) = add_members( + ldap, completed, to_add, add_failed, dn, 'member' + ) + + return (completed, ldap.get_entry(dn, self.default_attributes)) + +api.register(hostgroup2_add_member) + + +class hostgroup2_del_member(basegroup2_del_member): + """ + Remove members from hostgroup. + """ + container = _container_dn + + takes_options = ( + List('groups?', + cli_name='groups', + doc='comma-separated list of user groups to add' + ), + List('hosts?', + cli_name='hosts', + doc='comma-separated list of hosts to add' + ), + List('hostgroups?', + cli_name='hostgroups', + doc='comma-separated list of hostgroups to add' + ), + ) + + def execute(self, cn, **kw): + """ + Execute the group-del-member operation. + + Returns a tuple containing the number of members removed + and the updated entry. + + :param cn: The group name to remove new members from. + :parem kw: users is a comma-separated list of users to remove + """ + assert self.container + ldap = self.api.Backend.ldap2 + dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) + to_remove = [] + remove_failed = [] + completed = 0 + + members = kw.get('groups', []) + (to_remove, remove_failed) = find_members( + ldap, remove_failed, members, 'cn', 'ipausergroup', + self.api.env.container_group + ) + (completed, remove_failed) = del_members( + ldap, completed, to_remove, remove_failed, dn, 'member' + ) + + members = kw.get('hosts', []) + (to_remove, remove_failed) = find_members( + ldap, remove_failed, members, 'cn', 'ipahost', + self.api.env.container_host + ) + (completed, remove_failed) = del_members( + ldap, completed, to_remove, remove_failed, dn, 'member' + ) + + members = kw.get('hostgroups', []) + (to_remove, remove_failed) = find_members( + ldap, remove_failed, members, 'cn', 'ipahostgroup', + self.api.env.container_hostgroup + ) + (completed, remove_failed) = del_members( + ldap, completed, to_remove, remove_failed, dn, 'member' + ) + + return (completed, ldap.get_entry(dn, _default_attributes)) + +api.register(hostgroup2_del_member) + diff --git a/ipalib/plugins/hostgroup2.py b/ipalib/plugins/hostgroup2.py deleted file mode 100644 index 5a1aa8128..000000000 --- a/ipalib/plugins/hostgroup2.py +++ /dev/null @@ -1,240 +0,0 @@ -# Authors: -# Rob Crittenden -# -# Copyright (C) 2009 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Groups of hosts. -""" - -from ipalib import api -from ipalib.plugins.basegroup2 import * - -_container_dn = api.env.container_hostgroup -_default_attributes = ['cn', 'description', 'member', 'memberof'] -_default_class = 'ipahostgroup' - - -class hostgroup2(basegroup2): - """ - Hostgroup object. - """ - container = _container_dn - -api.register(hostgroup2) - - -class hostgroup2_create(basegroup2_create): - """ - Create a new hostgroup. - """ - base_classes = basegroup2_create.base_classes + (_default_class, ) - - def execute(self, cn, **kw): - return super(hostgroup2_create, self).execute(cn, **kw) - -api.register(hostgroup2_create) - - -class hostgroup2_delete(basegroup2_delete): - """ - Delete an existing hostgroup. - """ - container = _container_dn - - def execute(self, cn, **kw): - return super(hostgroup2_delete, self).execute(cn, **kw) - -api.register(hostgroup2_delete) - - -class hostgroup2_mod(basegroup2_mod): - """ - Edit an existing hostgroup. - """ - container = _container_dn - - def execute(self, cn, **kw): - return super(hostgroup2_mod, self).execute(cn, **kw) - -api.register(hostgroup2_mod) - - -class hostgroup2_find(basegroup2_find): - """ - Search the groups. - """ - container = _container_dn - - def execute(self, cn, **kw): - return super(hostgroup2_find, self).execute(cn, **kw) - -api.register(hostgroup2_find) - - -class hostgroup2_show(basegroup2_show): - """ - Examine an existing hostgroup. - """ - container = _container_dn - - def execute(self, cn, **kw): - return super(hostgroup2_show, self).execute(cn, **kw) - -api.register(hostgroup2_show) - - -class hostgroup2_add_member(basegroup2_add_member): - """ - Add members to hostgroup. - """ - container = _container_dn - - takes_options = ( - List('groups?', - cli_name='groups', - doc='comma-separated list of user groups to add' - ), - List('hosts?', - cli_name='hosts', - doc='comma-separated list of hosts to add' - ), - List('hostgroups?', - cli_name='hostgroups', - doc='comma-separated list of hostgroups to add' - ), - ) - - def execute(self, cn, **kw): - """ - Execute the group-add-member operation. - - Returns a tuple containing the number of members added - and the updated entry. - - :param cn: The group name to add new members to. - :param kw: groups is a comma-separated list of groups to add - :parem kw: users is a comma-separated list of users to add - """ - assert self.container - ldap = self.api.Backend.ldap2 - dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) - to_add = [] - add_failed = [] - completed = 0 - - members = kw.get('groups', []) - (to_add, add_failed) = find_members( - ldap, add_failed, members, 'cn', 'ipausergroup', - self.api.env.container_group - ) - (completed, add_failed) = add_members( - ldap, completed, to_add, add_failed, dn, 'member' - ) - - members = kw.get('hosts', []) - (to_add, add_failed) = find_members( - ldap, add_failed, members, 'cn', 'ipahost', - self.api.env.container_host - ) - (completed, add_failed) = add_members( - ldap, completed, to_add, add_failed, dn, 'member' - ) - - members = kw.get('hostgroups', []) - (to_add, add_failed) = find_members( - ldap, add_failed, members, 'cn', 'ipahostgroup', - self.api.env.container_hostgroup - ) - (completed, add_failed) = add_members( - ldap, completed, to_add, add_failed, dn, 'member' - ) - - return (completed, ldap.get_entry(dn, self.default_attributes)) - -api.register(hostgroup2_add_member) - - -class hostgroup2_del_member(basegroup2_del_member): - """ - Remove members from hostgroup. - """ - container = _container_dn - - takes_options = ( - List('groups?', - cli_name='groups', - doc='comma-separated list of user groups to add' - ), - List('hosts?', - cli_name='hosts', - doc='comma-separated list of hosts to add' - ), - List('hostgroups?', - cli_name='hostgroups', - doc='comma-separated list of hostgroups to add' - ), - ) - - def execute(self, cn, **kw): - """ - Execute the group-del-member operation. - - Returns a tuple containing the number of members removed - and the updated entry. - - :param cn: The group name to remove new members from. - :parem kw: users is a comma-separated list of users to remove - """ - assert self.container - ldap = self.api.Backend.ldap2 - dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) - to_remove = [] - remove_failed = [] - completed = 0 - - members = kw.get('groups', []) - (to_remove, remove_failed) = find_members( - ldap, remove_failed, members, 'cn', 'ipausergroup', - self.api.env.container_group - ) - (completed, remove_failed) = del_members( - ldap, completed, to_remove, remove_failed, dn, 'member' - ) - - members = kw.get('hosts', []) - (to_remove, remove_failed) = find_members( - ldap, remove_failed, members, 'cn', 'ipahost', - self.api.env.container_host - ) - (completed, remove_failed) = del_members( - ldap, completed, to_remove, remove_failed, dn, 'member' - ) - - members = kw.get('hostgroups', []) - (to_remove, remove_failed) = find_members( - ldap, remove_failed, members, 'cn', 'ipahostgroup', - self.api.env.container_hostgroup - ) - (completed, remove_failed) = del_members( - ldap, completed, to_remove, remove_failed, dn, 'member' - ) - - return (completed, ldap.get_entry(dn, _default_attributes)) - -api.register(hostgroup2_del_member) - diff --git a/ipalib/plugins/netgroup.py b/ipalib/plugins/netgroup.py new file mode 100644 index 000000000..a967791d3 --- /dev/null +++ b/ipalib/plugins/netgroup.py @@ -0,0 +1,372 @@ +# Authors: +# Rob Crittenden +# +# Copyright (C) 2009 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Netgroups. +""" + +from ipalib import api +from ipalib.plugins.basegroup2 import * +from ipalib import uuid + +_container_dn = 'cn=ng,cn=alt' +_default_attributes = [ + 'cn', 'description', 'member', 'memberUser', 'memberhost','externalhost' +] +_default_class = 'ipanisnetgroup' + + +class netgroup2(basegroup2): + """ + Netgroup object. + """ + container = _container_dn + +api.register(netgroup2) + + +class netgroup2_create(basegroup2_create): + """ + Create new netgroup. + """ + takes_options = ( + Str('nisdomainname?', + cli_name='nisdomain', + doc='NIS domain name', + ), + ) + + def execute(self, cn, **kw): + """ + Execute the netgroup-add operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + Returns the entry as it will be created in LDAP. + + :param cn: The name of the netgroup + :param kw: Keyword arguments for the other LDAP attributes. + """ + assert 'cn' not in kw + assert 'dn' not in kw + ldap = self.api.Backend.ldap2 + + entry_attrs = self.args_options_2_entry(cn, **kw) + entry_attrs['ipauniqueid'] = str(uuid.uuid1()) + entry_attrs['objectclass'] = ['top', 'ipaassociation', _default_class] + entry_attrs.setdefault('nisdomainname', self.api.env.domain) + + dn = ldap.make_dn(entry_attrs, 'ipauniqueid', _container_dn) + + ldap.add_entry(dn, entry_attrs) + + return ldap.get_entry(dn, _default_attributes) + +api.register(netgroup2_create) + + +class netgroup2_delete(basegroup2_delete): + """ + Delete netgroup. + """ + container = _container_dn + filter_class = _default_class + + def execute(self, cn, **kw): + return super(netgroup2_delete, self).execute(cn, **kw) + +api.register(netgroup2_delete) + + +class netgroup2_mod(basegroup2_mod): + """ + Edit an existing netgroup. + """ + container = _container_dn + filter_class = _default_class + + def execute(self, cn, **kw): + return super(netgroup2_mod, self).execute(cn, **kw) + +api.register(netgroup2_mod) + + +class netgroup2_find(basegroup2_find): + """ + Search the groups. + """ + container = _container_dn + filter_class = _default_class + + def execute(self, cn, **kw): + return super(netgroup2_find, self).execute(cn, **kw) + +api.register(netgroup2_find) + + +class netgroup2_show(basegroup2_show): + """ + Display netgroup. + """ + filter_class = _default_class + default_attributes = _default_attributes + container = _container_dn + + def execute(self, cn, **kw): + return super(netgroup2_show, self).execute(cn, **kw) + +api.register(netgroup2_show) + + +class netgroup2_add_member(basegroup2_add_member): + """ + Add members to netgroup. + """ + default_attributes = _default_attributes + container = _container_dn + filter_class = _default_class + + takes_options = basegroup2_add_member.takes_options + ( + List('hosts?', + cli_name='hosts', + doc='comma-separated list of hosts to add' + ), + List('hostgroups?', + cli_name='hostgroups', + doc='comma-separated list of host groups to add' + ), + List('netgroups?', + cli_name='netgroups', + doc='comma-separated list of netgroups to add' + ), + ) + + def _add_external(self, ldap, completed, members, group_dn): + add_failed = [] + entry_attrs = ldap.get_entry(group_dn, ['externalhost']) + external_hosts = entry_attrs.get('externalhost', []) + + for m in members: + m = m.lower() + if m not in external_hosts: + external_hosts.append(m) + completed += 1 + else: + add_failed.append(m) + + try: + ldap.update_entry(group_dn, **{'externalhost': external_hosts}) + except errors.EmptyModlist: + pass + + return (completed, add_failed) + + def execute(self, cn, **kw): + """ + Execute the group-add-member operation. + + Returns a tuple containing the number of members added + and the updated entry. + + :param cn: The group name to add new members to. + :param kw: groups is a comma-separated list of groups to add + :param kw: users is a comma-separated list of users to add + :param kw: hostgroups is a comma-separated list of hostgroups to add + """ + assert self.container + ldap = self.api.Backend.ldap2 + dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) + to_add = [] + add_failed = [] + completed = 0 + + members = kw.get('groups', []) + (to_add, add_failed) = find_members( + ldap, add_failed, members, 'cn', 'ipausergroup', + self.api.env.container_group + ) + (completed, add_failed) = add_members( + ldap, completed, to_add, add_failed, dn, 'member' + ) + + members = kw.get('users', []) + (to_add, add_failed) = find_members( + ldap, add_failed, members, 'uid', 'posixaccount', + self.api.env.container_user + ) + (completed, add_failed) = add_members( + ldap, completed, to_add, add_failed, dn, 'member' + ) + + members = kw.get('hosts', []) + (to_add, add_failed) = find_members( + ldap, add_failed, members, 'cn', 'ipahost', + self.api.env.container_host + ) + + # If a host is not found we'll consider it an externalHost. It will + # be up to the user to handle typos + if add_failed: + (completed, add_failed) = self._add_external(ldap, completed, add_failed, dn) + + (completed, add_failed) = add_members( + ldap, completed, to_add, add_failed, dn, 'member' + ) + + members = kw.get('hostgroups', []) + (to_add, add_failed) = find_members( + ldap, add_failed, members, 'cn', 'ipahostgroup', + self.api.env.container_hostgroup + ) + (completed, add_failed) = add_members( + ldap, completed, to_add, add_failed, dn, 'member' + ) + + members = kw.get('netgroups', []) + (to_add, add_failed) = find_members( + ldap, add_failed, members, 'cn', _default_class, + _container_dn + ) + (completed, add_failed) = add_members( + ldap, completed, to_add, add_failed, dn, 'member' + ) + + return (completed, ldap.get_entry(dn, _default_attributes)) + +api.register(netgroup2_add_member) + + +class netgroup2_del_member(basegroup2_del_member): + """ + Remove a member from a netgroup. + """ + default_attributes = _default_attributes + container = _container_dn + filter_class = _default_class + + takes_options = basegroup2_del_member.takes_options + ( + List('hosts?', + cli_name='hosts', + doc='comma-separated list of hosts to remove' + ), + List('hostgroups?', + cli_name='hostgroups', + doc='comma-separated list of host groups to remove' + ), + List('netgroups?', + cli_name='netgroups', + doc='comma-separated list of netgroups to remove' + ), + ) + + def _del_external(self, ldap, completed, members, group_dn): + rem_failed = [] + entry_attrs = ldap.get_entry(group_dn, ['externalhost']) + external_hosts = entry_attrs.get('externalhost', []) + + for m in members: + m = m.lower() + if m in external_hosts: + external_hosts.remove(m) + completed += 1 + else: + rem_failed.append(m) + + try: + ldap.update_entry(group_dn, **{'externalhost': external_hosts}) + except errors.EmptyModlist: + pass + + return (completed, rem_failed) + + def execute(self, cn, **kw): + """ + Execute the group-remove-member operation. + + Returns a tuple containing the number of members removed + and the updated entry. + + :param cn: The group name to remove new members to. + :param kw: groups is a comma-separated list of groups to remove + :param kw: users is a comma-separated list of users to remove + :param kw: hostgroups is a comma-separated list of hostgroups to remove + """ + assert self.container + ldap = self.api.Backend.ldap2 + dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) + to_rem = [] + rem_failed = [] + completed = 0 + + members = kw.get('groups', []) + (to_rem, rem_failed) = find_members( + ldap, rem_failed, members, 'cn', 'ipausergroup', + self.api.env.container_group + ) + (completed, rem_failed) = del_members( + ldap, completed, to_rem, rem_failed, dn, 'member' + ) + + members = kw.get('users', []) + (to_rem, rem_failed) = find_members( + ldap, rem_failed, members, 'uid', 'posixaccount', + self.api.env.container_user + ) + (completed, rem_failed) = del_members( + ldap, completed, to_rem, rem_failed, dn, 'member' + ) + + members = kw.get('hosts', []) + (to_rem, rem_failed) = find_members( + ldap, rem_failed, members, 'cn', 'ipahost', + self.api.env.container_host + ) + + # If a host is not found we'll consider it an externalHost. It will + # be up to the user to handle typos + if rem_failed: + (completed, rem_failed) = self._del_external(ldap, completed, rem_failed, dn) + + (completed, rem_failed) = del_members( + ldap, completed, to_rem, rem_failed, dn, 'member' + ) + + members = kw.get('hostgroups', []) + (to_rem, rem_failed) = find_members( + ldap, rem_failed, members, 'cn', 'ipahostgroup', + self.api.env.container_hostgroup + ) + (completed, rem_failed) = del_members( + ldap, completed, to_rem, rem_failed, dn, 'member' + ) + + members = kw.get('netgroups', []) + (to_rem, rem_failed) = find_members( + ldap, rem_failed, members, 'cn', _default_class, + _container_dn + ) + (completed, rem_failed) = del_members( + ldap, completed, to_rem, rem_failed, dn, 'member' + ) + + return (completed, ldap.get_entry(dn, _default_attributes)) + +api.register(netgroup2_del_member) + diff --git a/ipalib/plugins/netgroup2.py b/ipalib/plugins/netgroup2.py deleted file mode 100644 index a967791d3..000000000 --- a/ipalib/plugins/netgroup2.py +++ /dev/null @@ -1,372 +0,0 @@ -# Authors: -# Rob Crittenden -# -# Copyright (C) 2009 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Netgroups. -""" - -from ipalib import api -from ipalib.plugins.basegroup2 import * -from ipalib import uuid - -_container_dn = 'cn=ng,cn=alt' -_default_attributes = [ - 'cn', 'description', 'member', 'memberUser', 'memberhost','externalhost' -] -_default_class = 'ipanisnetgroup' - - -class netgroup2(basegroup2): - """ - Netgroup object. - """ - container = _container_dn - -api.register(netgroup2) - - -class netgroup2_create(basegroup2_create): - """ - Create new netgroup. - """ - takes_options = ( - Str('nisdomainname?', - cli_name='nisdomain', - doc='NIS domain name', - ), - ) - - def execute(self, cn, **kw): - """ - Execute the netgroup-add operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - Returns the entry as it will be created in LDAP. - - :param cn: The name of the netgroup - :param kw: Keyword arguments for the other LDAP attributes. - """ - assert 'cn' not in kw - assert 'dn' not in kw - ldap = self.api.Backend.ldap2 - - entry_attrs = self.args_options_2_entry(cn, **kw) - entry_attrs['ipauniqueid'] = str(uuid.uuid1()) - entry_attrs['objectclass'] = ['top', 'ipaassociation', _default_class] - entry_attrs.setdefault('nisdomainname', self.api.env.domain) - - dn = ldap.make_dn(entry_attrs, 'ipauniqueid', _container_dn) - - ldap.add_entry(dn, entry_attrs) - - return ldap.get_entry(dn, _default_attributes) - -api.register(netgroup2_create) - - -class netgroup2_delete(basegroup2_delete): - """ - Delete netgroup. - """ - container = _container_dn - filter_class = _default_class - - def execute(self, cn, **kw): - return super(netgroup2_delete, self).execute(cn, **kw) - -api.register(netgroup2_delete) - - -class netgroup2_mod(basegroup2_mod): - """ - Edit an existing netgroup. - """ - container = _container_dn - filter_class = _default_class - - def execute(self, cn, **kw): - return super(netgroup2_mod, self).execute(cn, **kw) - -api.register(netgroup2_mod) - - -class netgroup2_find(basegroup2_find): - """ - Search the groups. - """ - container = _container_dn - filter_class = _default_class - - def execute(self, cn, **kw): - return super(netgroup2_find, self).execute(cn, **kw) - -api.register(netgroup2_find) - - -class netgroup2_show(basegroup2_show): - """ - Display netgroup. - """ - filter_class = _default_class - default_attributes = _default_attributes - container = _container_dn - - def execute(self, cn, **kw): - return super(netgroup2_show, self).execute(cn, **kw) - -api.register(netgroup2_show) - - -class netgroup2_add_member(basegroup2_add_member): - """ - Add members to netgroup. - """ - default_attributes = _default_attributes - container = _container_dn - filter_class = _default_class - - takes_options = basegroup2_add_member.takes_options + ( - List('hosts?', - cli_name='hosts', - doc='comma-separated list of hosts to add' - ), - List('hostgroups?', - cli_name='hostgroups', - doc='comma-separated list of host groups to add' - ), - List('netgroups?', - cli_name='netgroups', - doc='comma-separated list of netgroups to add' - ), - ) - - def _add_external(self, ldap, completed, members, group_dn): - add_failed = [] - entry_attrs = ldap.get_entry(group_dn, ['externalhost']) - external_hosts = entry_attrs.get('externalhost', []) - - for m in members: - m = m.lower() - if m not in external_hosts: - external_hosts.append(m) - completed += 1 - else: - add_failed.append(m) - - try: - ldap.update_entry(group_dn, **{'externalhost': external_hosts}) - except errors.EmptyModlist: - pass - - return (completed, add_failed) - - def execute(self, cn, **kw): - """ - Execute the group-add-member operation. - - Returns a tuple containing the number of members added - and the updated entry. - - :param cn: The group name to add new members to. - :param kw: groups is a comma-separated list of groups to add - :param kw: users is a comma-separated list of users to add - :param kw: hostgroups is a comma-separated list of hostgroups to add - """ - assert self.container - ldap = self.api.Backend.ldap2 - dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) - to_add = [] - add_failed = [] - completed = 0 - - members = kw.get('groups', []) - (to_add, add_failed) = find_members( - ldap, add_failed, members, 'cn', 'ipausergroup', - self.api.env.container_group - ) - (completed, add_failed) = add_members( - ldap, completed, to_add, add_failed, dn, 'member' - ) - - members = kw.get('users', []) - (to_add, add_failed) = find_members( - ldap, add_failed, members, 'uid', 'posixaccount', - self.api.env.container_user - ) - (completed, add_failed) = add_members( - ldap, completed, to_add, add_failed, dn, 'member' - ) - - members = kw.get('hosts', []) - (to_add, add_failed) = find_members( - ldap, add_failed, members, 'cn', 'ipahost', - self.api.env.container_host - ) - - # If a host is not found we'll consider it an externalHost. It will - # be up to the user to handle typos - if add_failed: - (completed, add_failed) = self._add_external(ldap, completed, add_failed, dn) - - (completed, add_failed) = add_members( - ldap, completed, to_add, add_failed, dn, 'member' - ) - - members = kw.get('hostgroups', []) - (to_add, add_failed) = find_members( - ldap, add_failed, members, 'cn', 'ipahostgroup', - self.api.env.container_hostgroup - ) - (completed, add_failed) = add_members( - ldap, completed, to_add, add_failed, dn, 'member' - ) - - members = kw.get('netgroups', []) - (to_add, add_failed) = find_members( - ldap, add_failed, members, 'cn', _default_class, - _container_dn - ) - (completed, add_failed) = add_members( - ldap, completed, to_add, add_failed, dn, 'member' - ) - - return (completed, ldap.get_entry(dn, _default_attributes)) - -api.register(netgroup2_add_member) - - -class netgroup2_del_member(basegroup2_del_member): - """ - Remove a member from a netgroup. - """ - default_attributes = _default_attributes - container = _container_dn - filter_class = _default_class - - takes_options = basegroup2_del_member.takes_options + ( - List('hosts?', - cli_name='hosts', - doc='comma-separated list of hosts to remove' - ), - List('hostgroups?', - cli_name='hostgroups', - doc='comma-separated list of host groups to remove' - ), - List('netgroups?', - cli_name='netgroups', - doc='comma-separated list of netgroups to remove' - ), - ) - - def _del_external(self, ldap, completed, members, group_dn): - rem_failed = [] - entry_attrs = ldap.get_entry(group_dn, ['externalhost']) - external_hosts = entry_attrs.get('externalhost', []) - - for m in members: - m = m.lower() - if m in external_hosts: - external_hosts.remove(m) - completed += 1 - else: - rem_failed.append(m) - - try: - ldap.update_entry(group_dn, **{'externalhost': external_hosts}) - except errors.EmptyModlist: - pass - - return (completed, rem_failed) - - def execute(self, cn, **kw): - """ - Execute the group-remove-member operation. - - Returns a tuple containing the number of members removed - and the updated entry. - - :param cn: The group name to remove new members to. - :param kw: groups is a comma-separated list of groups to remove - :param kw: users is a comma-separated list of users to remove - :param kw: hostgroups is a comma-separated list of hostgroups to remove - """ - assert self.container - ldap = self.api.Backend.ldap2 - dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) - to_rem = [] - rem_failed = [] - completed = 0 - - members = kw.get('groups', []) - (to_rem, rem_failed) = find_members( - ldap, rem_failed, members, 'cn', 'ipausergroup', - self.api.env.container_group - ) - (completed, rem_failed) = del_members( - ldap, completed, to_rem, rem_failed, dn, 'member' - ) - - members = kw.get('users', []) - (to_rem, rem_failed) = find_members( - ldap, rem_failed, members, 'uid', 'posixaccount', - self.api.env.container_user - ) - (completed, rem_failed) = del_members( - ldap, completed, to_rem, rem_failed, dn, 'member' - ) - - members = kw.get('hosts', []) - (to_rem, rem_failed) = find_members( - ldap, rem_failed, members, 'cn', 'ipahost', - self.api.env.container_host - ) - - # If a host is not found we'll consider it an externalHost. It will - # be up to the user to handle typos - if rem_failed: - (completed, rem_failed) = self._del_external(ldap, completed, rem_failed, dn) - - (completed, rem_failed) = del_members( - ldap, completed, to_rem, rem_failed, dn, 'member' - ) - - members = kw.get('hostgroups', []) - (to_rem, rem_failed) = find_members( - ldap, rem_failed, members, 'cn', 'ipahostgroup', - self.api.env.container_hostgroup - ) - (completed, rem_failed) = del_members( - ldap, completed, to_rem, rem_failed, dn, 'member' - ) - - members = kw.get('netgroups', []) - (to_rem, rem_failed) = find_members( - ldap, rem_failed, members, 'cn', _default_class, - _container_dn - ) - (completed, rem_failed) = del_members( - ldap, completed, to_rem, rem_failed, dn, 'member' - ) - - return (completed, ldap.get_entry(dn, _default_attributes)) - -api.register(netgroup2_del_member) - diff --git a/ipalib/plugins/passwd.py b/ipalib/plugins/passwd.py new file mode 100644 index 000000000..07e4cc301 --- /dev/null +++ b/ipalib/plugins/passwd.py @@ -0,0 +1,78 @@ +# Authors: +# Rob Crittenden +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" +Password changes +""" + +from ipalib import api, errors, util +from ipalib import Command +from ipalib import Str, Password + + +class passwd2(Command): + """ + Change user password. + """ + takes_args = ( + Str('principal', + cli_name='user', + doc='username', + primary_key=True, + autofill=True, + create_default=lambda **kw: util.get_current_principal(), + ), + Password('password'), + ) + + def execute(self, principal, password): + """ + Execute the passwd operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + Returns the entry + + :param principal: The login name or principal of the user + :param password: the new password + """ + ldap = self.api.Backend.ldap2 + + if principal.find('@') != -1: + principal_parts = principal.split('@') + if len(principal_parts) > 2: + raise errors.MalformedUserPrincipal(principal=principal) + else: + principal = '%s@%s' % (principal, self.api.env.realm) + + dn = ldap.find_entry_by_attr( + 'krbprincipalname', principal, 'posixaccount' + ) + + ldap.modify_password(dn, password) + + return True + + def output_for_cli(self, textui, result, principal, password): + assert password is None + textui.print_name(self.name) + textui.print_dashed('Changed password for "%s."' % principal) + +api.register(passwd2) + diff --git a/ipalib/plugins/passwd2.py b/ipalib/plugins/passwd2.py deleted file mode 100644 index 07e4cc301..000000000 --- a/ipalib/plugins/passwd2.py +++ /dev/null @@ -1,78 +0,0 @@ -# Authors: -# Rob Crittenden -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -""" -Password changes -""" - -from ipalib import api, errors, util -from ipalib import Command -from ipalib import Str, Password - - -class passwd2(Command): - """ - Change user password. - """ - takes_args = ( - Str('principal', - cli_name='user', - doc='username', - primary_key=True, - autofill=True, - create_default=lambda **kw: util.get_current_principal(), - ), - Password('password'), - ) - - def execute(self, principal, password): - """ - Execute the passwd operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - Returns the entry - - :param principal: The login name or principal of the user - :param password: the new password - """ - ldap = self.api.Backend.ldap2 - - if principal.find('@') != -1: - principal_parts = principal.split('@') - if len(principal_parts) > 2: - raise errors.MalformedUserPrincipal(principal=principal) - else: - principal = '%s@%s' % (principal, self.api.env.realm) - - dn = ldap.find_entry_by_attr( - 'krbprincipalname', principal, 'posixaccount' - ) - - ldap.modify_password(dn, password) - - return True - - def output_for_cli(self, textui, result, principal, password): - assert password is None - textui.print_name(self.name) - textui.print_dashed('Changed password for "%s."' % principal) - -api.register(passwd2) - diff --git a/ipalib/plugins/pwpolicy.py b/ipalib/plugins/pwpolicy.py new file mode 100644 index 000000000..9e2417611 --- /dev/null +++ b/ipalib/plugins/pwpolicy.py @@ -0,0 +1,148 @@ +# Authors: +# Rob Crittenden +# Pavel Zuna +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Password policy +""" + +from ipalib import api, errors +from ipalib import Command +from ipalib import Int + +_fields = { + 'krbminpwdlife': 'Minimum lifetime (in hours)', + 'krbmaxpwdlife': 'Maximum lifetime (in days)', + 'krbpwdmindiffchars': 'Minimum number of characters classes', + 'krbpwdminlength': 'Minimum length', + 'krbpwdhistorylength': 'History size', +} + +def _convert_time_for_output(entry_attrs): + if 'krbmaxpwdlife' in entry_attrs: + entry_attrs['krbmaxpwdlife'][0] = str( + int(entry_attrs['krbmaxpwdlife'][0]) / 86400 + ) + if 'krbminpwdlife' in entry_attrs: + entry_attrs['krbminpwdlife'][0] = str( + int(entry_attrs['krbminpwdlife'][0]) / 3600 + ) + + +class pwpolicy2_mod(Command): + """ + Modify password policy. + """ + takes_options = ( + Int('krbmaxpwdlife?', + cli_name='maxlife', + doc='Max. Password Lifetime (days)', + minvalue=0, + attribute=True, + ), + Int('krbminpwdlife?', + cli_name='minlife', + doc='Min. Password Lifetime (hours)', + minvalue=0, + attribute=True, + ), + Int('krbpwdhistorylength?', + cli_name='history', + doc='Password History Size', + minvalue=0, + attribute=True, + ), + Int('krbpwdmindiffchars?', + cli_name='minclasses', + doc='Min. Number of Character Classes', + minvalue=0, + attribute=True, + ), + Int('krbpwdminlength?', + cli_name='minlength', + doc='Min. Length of Password', + minvalue=0, + attribute=True, + ), + ) + + def execute(self, *args, **options): + assert 'dn' not in options + ldap = self.api.Backend.ldap2 + + entry_attrs = self.args_options_2_entry(*args, **options) + dn = self.api.env.container_accounts + + # Convert hours and days to seconds + if 'krbmaxpwdlife' in entry_attrs: + entry_attrs['krbmaxpwdlife'] = entry_attrs['krbmaxpwdlife'] * 86400 + del entry_attrs['krbmaxpwdlife'] + if 'krbminpwdlife' in entry_attrs: + entry_attrs['krbminpwdlife'] = entry_attrs['krbminpwdlife'] * 3600 + del entry_attrs['krbminpwdlife'] + + try: + ldap.update_entry(dn, entry_attrs) + except errors.EmptyModlist: + pass + + (dn, entry_attrs) = ldap.get_entry(dn, entry_attrs.keys()) + + _convert_time_for_output(entry_attrs) + + return (dn, entry_attrs) + + def output_for_cli(self, textui, result, *args, **options): + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_plain('Password policy:') + for (k, v) in _fields.iteritems(): + if k in entry_attrs: + textui.print_attribute(v, entry_attrs[k]) + textui.print_dashed('Modified password policy.') + +api.register(pwpolicy2_mod) + + +class pwpolicy2_show(Command): + """ + Display password policy. + """ + def execute(self, *args, **options): + ldap = self.api.Backend.ldap2 + + dn = self.api.env.container_accounts + (dn, entry_attrs) = ldap.get_entry(dn) + + _convert_time_for_output(entry_attrs) + + return (dn, entry_attrs) + + def output_for_cli(self, textui, result, *args, **options): + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_plain('Password policy:') + for (k, v) in _fields.iteritems(): + if k in entry_attrs: + textui.print_attribute(v, entry_attrs[k]) + +api.register(pwpolicy2_show) + diff --git a/ipalib/plugins/pwpolicy2.py b/ipalib/plugins/pwpolicy2.py deleted file mode 100644 index 9e2417611..000000000 --- a/ipalib/plugins/pwpolicy2.py +++ /dev/null @@ -1,148 +0,0 @@ -# Authors: -# Rob Crittenden -# Pavel Zuna -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Password policy -""" - -from ipalib import api, errors -from ipalib import Command -from ipalib import Int - -_fields = { - 'krbminpwdlife': 'Minimum lifetime (in hours)', - 'krbmaxpwdlife': 'Maximum lifetime (in days)', - 'krbpwdmindiffchars': 'Minimum number of characters classes', - 'krbpwdminlength': 'Minimum length', - 'krbpwdhistorylength': 'History size', -} - -def _convert_time_for_output(entry_attrs): - if 'krbmaxpwdlife' in entry_attrs: - entry_attrs['krbmaxpwdlife'][0] = str( - int(entry_attrs['krbmaxpwdlife'][0]) / 86400 - ) - if 'krbminpwdlife' in entry_attrs: - entry_attrs['krbminpwdlife'][0] = str( - int(entry_attrs['krbminpwdlife'][0]) / 3600 - ) - - -class pwpolicy2_mod(Command): - """ - Modify password policy. - """ - takes_options = ( - Int('krbmaxpwdlife?', - cli_name='maxlife', - doc='Max. Password Lifetime (days)', - minvalue=0, - attribute=True, - ), - Int('krbminpwdlife?', - cli_name='minlife', - doc='Min. Password Lifetime (hours)', - minvalue=0, - attribute=True, - ), - Int('krbpwdhistorylength?', - cli_name='history', - doc='Password History Size', - minvalue=0, - attribute=True, - ), - Int('krbpwdmindiffchars?', - cli_name='minclasses', - doc='Min. Number of Character Classes', - minvalue=0, - attribute=True, - ), - Int('krbpwdminlength?', - cli_name='minlength', - doc='Min. Length of Password', - minvalue=0, - attribute=True, - ), - ) - - def execute(self, *args, **options): - assert 'dn' not in options - ldap = self.api.Backend.ldap2 - - entry_attrs = self.args_options_2_entry(*args, **options) - dn = self.api.env.container_accounts - - # Convert hours and days to seconds - if 'krbmaxpwdlife' in entry_attrs: - entry_attrs['krbmaxpwdlife'] = entry_attrs['krbmaxpwdlife'] * 86400 - del entry_attrs['krbmaxpwdlife'] - if 'krbminpwdlife' in entry_attrs: - entry_attrs['krbminpwdlife'] = entry_attrs['krbminpwdlife'] * 3600 - del entry_attrs['krbminpwdlife'] - - try: - ldap.update_entry(dn, entry_attrs) - except errors.EmptyModlist: - pass - - (dn, entry_attrs) = ldap.get_entry(dn, entry_attrs.keys()) - - _convert_time_for_output(entry_attrs) - - return (dn, entry_attrs) - - def output_for_cli(self, textui, result, *args, **options): - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_plain('Password policy:') - for (k, v) in _fields.iteritems(): - if k in entry_attrs: - textui.print_attribute(v, entry_attrs[k]) - textui.print_dashed('Modified password policy.') - -api.register(pwpolicy2_mod) - - -class pwpolicy2_show(Command): - """ - Display password policy. - """ - def execute(self, *args, **options): - ldap = self.api.Backend.ldap2 - - dn = self.api.env.container_accounts - (dn, entry_attrs) = ldap.get_entry(dn) - - _convert_time_for_output(entry_attrs) - - return (dn, entry_attrs) - - def output_for_cli(self, textui, result, *args, **options): - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_plain('Password policy:') - for (k, v) in _fields.iteritems(): - if k in entry_attrs: - textui.print_attribute(v, entry_attrs[k]) - -api.register(pwpolicy2_show) - diff --git a/ipalib/plugins/rolegroup.py b/ipalib/plugins/rolegroup.py new file mode 100644 index 000000000..4a8e5aff7 --- /dev/null +++ b/ipalib/plugins/rolegroup.py @@ -0,0 +1,125 @@ +# Authors: +# Rob Crittenden +# Pavel Zuna +# +# Copyright (C) 2009 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Groups of roles +""" + +from ipalib import api +from ipalib.plugins.basegroup2 import * + +_container_dn = api.env.container_rolegroup +_default_attributes = ['cn', 'description', 'member', 'memberOf'] +_default_class = 'nestedGroup' + + +class rolegroup2(basegroup2): + """ + Rolegroup object. + """ + container = _container_dn + +api.register(rolegroup2) + + +class rolegroup2_create(basegroup2_create): + """ + Create new rolegroup. + """ + base_classes = basegroup2_create.base_classes + (_default_class, ) + + def execute(self, cn, **kw): + return super(rolegroup2_create, self).execute(cn, **kw) + +api.register(rolegroup2_create) + + +class rolegroup2_delete(basegroup2_delete): + """ + Delete rolegroup. + """ + container = _container_dn + + def execute(self, cn, **kw): + return super(rolegroup2_delete, self).execute(cn, **kw) + +api.register(rolegroup2_delete) + + +class rolegroup2_mod(basegroup2_mod): + """ + Edit rolegroup. + """ + container = _container_dn + + def execute(self, cn, **kw): + return super(rolegroup2_mod, self).execute(cn, **kw) + +api.register(rolegroup2_mod) + + +class rolegroup2_find(basegroup2_find): + """ + Search for rolegroups. + """ + container = _container_dn + + def execute(self, cn, **kw): + return super(rolegroup2_find, self).execute(cn, **kw) + +api.register(rolegroup2_find) + + +class rolegroup2_show(basegroup2_show): + """ + Display rolegroup. + """ + default_attributes = _default_attributes + container = _container_dn + + def execute(self, cn, **kw): + return super(rolegroup2_show, self).execute(cn, **kw) + +api.register(rolegroup2_show) + + +class rolegroup2_add_member(basegroup2_add_member): + """ + Add member to rolegroup. + """ + container = _container_dn + + def execute(self, cn, **kw): + return super(rolegroup2_add_member, self).execute(cn, **kw) + +api.register(rolegroup2_add_member) + + +class rolegroup2_del_member(basegroup2_del_member): + """ + Remove member from rolegroup. + """ + container = _container_dn + + def execute(self, cn, **kw): + return super(rolegroup2_del_member, self).execute(cn, **kw) + +api.register(rolegroup2_del_member) + diff --git a/ipalib/plugins/rolegroup2.py b/ipalib/plugins/rolegroup2.py deleted file mode 100644 index 4a8e5aff7..000000000 --- a/ipalib/plugins/rolegroup2.py +++ /dev/null @@ -1,125 +0,0 @@ -# Authors: -# Rob Crittenden -# Pavel Zuna -# -# Copyright (C) 2009 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Groups of roles -""" - -from ipalib import api -from ipalib.plugins.basegroup2 import * - -_container_dn = api.env.container_rolegroup -_default_attributes = ['cn', 'description', 'member', 'memberOf'] -_default_class = 'nestedGroup' - - -class rolegroup2(basegroup2): - """ - Rolegroup object. - """ - container = _container_dn - -api.register(rolegroup2) - - -class rolegroup2_create(basegroup2_create): - """ - Create new rolegroup. - """ - base_classes = basegroup2_create.base_classes + (_default_class, ) - - def execute(self, cn, **kw): - return super(rolegroup2_create, self).execute(cn, **kw) - -api.register(rolegroup2_create) - - -class rolegroup2_delete(basegroup2_delete): - """ - Delete rolegroup. - """ - container = _container_dn - - def execute(self, cn, **kw): - return super(rolegroup2_delete, self).execute(cn, **kw) - -api.register(rolegroup2_delete) - - -class rolegroup2_mod(basegroup2_mod): - """ - Edit rolegroup. - """ - container = _container_dn - - def execute(self, cn, **kw): - return super(rolegroup2_mod, self).execute(cn, **kw) - -api.register(rolegroup2_mod) - - -class rolegroup2_find(basegroup2_find): - """ - Search for rolegroups. - """ - container = _container_dn - - def execute(self, cn, **kw): - return super(rolegroup2_find, self).execute(cn, **kw) - -api.register(rolegroup2_find) - - -class rolegroup2_show(basegroup2_show): - """ - Display rolegroup. - """ - default_attributes = _default_attributes - container = _container_dn - - def execute(self, cn, **kw): - return super(rolegroup2_show, self).execute(cn, **kw) - -api.register(rolegroup2_show) - - -class rolegroup2_add_member(basegroup2_add_member): - """ - Add member to rolegroup. - """ - container = _container_dn - - def execute(self, cn, **kw): - return super(rolegroup2_add_member, self).execute(cn, **kw) - -api.register(rolegroup2_add_member) - - -class rolegroup2_del_member(basegroup2_del_member): - """ - Remove member from rolegroup. - """ - container = _container_dn - - def execute(self, cn, **kw): - return super(rolegroup2_del_member, self).execute(cn, **kw) - -api.register(rolegroup2_del_member) - diff --git a/ipalib/plugins/service.py b/ipalib/plugins/service.py new file mode 100644 index 000000000..2722203a2 --- /dev/null +++ b/ipalib/plugins/service.py @@ -0,0 +1,362 @@ +# Authors: +# Jason Gerard DeRose +# Rob Crittenden +# Pavel Zuna +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" +Services (Identity) +""" +import base64 + +from OpenSSL import crypto + +from ipalib import api, crud, errors +from ipalib import Object +from ipalib import Str, Flag, Bytes + +_container_dn = api.env.container_service +_default_attributes = ['krbprincipalname', 'usercertificate'] + + +def split_principal(principal): + service = hostname = realm = None + + # Break down the principal into its component parts, which may or + # may not include the realm. + sp = principal.split('/') + if len(sp) != 2: + raise errors.MalformedServicePrincipal(reason='missing service') + + service = sp[0] + sr = sp[1].split('@') + if len(sr) > 2: + raise errors.MalformedServicePrincipal( + reason='unable to determine realm' + ) + + hostname = sr[0].lower() + if len(sr) == 2: + realm = sr[1].upper() + # At some point we'll support multiple realms + if realm != api.env.realm: + raise errors.RealmMismatch() + else: + realm = api.env.realm + + # Note that realm may be None. + return (service, hostname, realm) + +def validate_principal(ugettext, principal): + (service, hostname, principal) = split_principal(principal) + return None + +def normalize_principal(principal): + # The principal is already validated when it gets here + (service, hostname, realm) = split_principal(principal) + # Put the principal back together again + principal = '%s/%s@%s' % (service, hostname, realm) + return unicode(principal) + +def validate_certificate(ugettext, cert): + """ + For now just verify that it is properly base64-encoded. + """ + try: + base64.b64decode(cert) + except Exception, e: + raise errors.Base64DecodeError(reason=str(e)) + + +class service2(Object): + """ + Service object. + """ + takes_params = ( + Str('krbprincipalname', validate_principal, + cli_name='principal', + doc='Service principal', + primary_key=True, + normalizer=lambda value: normalize_principal(value), + ), + Bytes('usercertificate?', validate_certificate, + cli_name='certificate', + doc='Base-64 encoded server certificate', + ), + ) + +api.register(service2) + + +class service2_create(crud.Create): + """ + Add new service. + """ + takes_options = ( + Flag('force', + doc='Force principal name even if not in DNS', + ), + ) + def execute(self, principal, **kw): + """ + Execute the service-add operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + Returns the entry as it will be created in LDAP. + + :param principal: The service to be added in the form: service/hostname + :param kw: Keyword arguments for the other LDAP attributes. + """ + assert 'krbprincipalname' not in kw + ldap = self.api.Backend.ldap2 + # FIXME: should be in a normalizer. Need to fix normalizers to work + # on non-unicode data + if kw.get('usercertificate'): + kw['usercertificate'] = base64.b64decode(kw['usercertificate']) + + (service, hostname, realm) = split_principal(principal) + + if service.lower() == 'host' and not kw['force']: + raise errors.HostService() + + # FIXME: once DNS client is done + # if not kw['force']: + # fqdn = hostname + '.' + # rs = dnsclient.query(fqdn, dnsclient.DNS_C_IN, dnsclient.DNS_T_A) + # if len(rs) == 0: + # self.log.debug( + # 'IPA: DNS A record lookup failed for '%s'" % hostname + # ) + # raise ipaerror.gen_exception(ipaerror.INPUT_NOT_DNS_A_RECORD) + # else: + # self.log.debug( + # 'IPA: found %d records for '%s'" % (len(rs), hostname) + # ) + + entry_attrs = self.args_options_2_entry(principal, **kw) + entry_attrs['objectclass'] = [ + 'krbprincipal', 'krbprincipalaux', 'krbticketpolicyaux', + 'ipaservice', 'pkiuser' + ] + dn = ldap.make_dn(entry_attrs, 'krbprincipalname', _container_dn) + + ldap.add_entry(dn, entry_attrs) + + return ldap.get_entry(dn, entry_attrs.keys()) + + def output_for_cli(self, textui, result, principal, **options): + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_dashed('Created service "%s".' % principal) + +api.register(service2_create) + + +class service2_delete(crud.Delete): + """ + Delete an existing service. + """ + def execute(self, principal, **kw): + """ + Delete a service principal. + + principal is the krbprincipalname of the entry to delete. + + This should be called with much care. + + :param principal: The service to be added in the form: service/hostname + :param kw: not used + """ + ldap = self.api.Backend.ldap2 + + (dn, entry_attrs) = ldap.find_entry_by_attr( + 'krbprincipalname', principal, 'ipaservice' + ) + + if 'usercerfificate' in entry_attrs: + cert = entry_attrs['usercertificate'] + x509 = crypto.load_certificate(crypto.FILETYPE_ASN1, cert) + serial = str(x509.get_serial_number()) + api.Command['cert_revoke'](unicode(serial), revocation_reason=5) + + ldap.delete_entry(dn) + + return True + + def output_for_cli(self, textui, result, principal, **options): + textui.print_name(self.name) + textui.print_dashed('Deleted service "%s".' % principal) + +api.register(service2_delete) + + +class service2_mod(crud.Update): + """ + Modify service. + """ + def execute(self, principal, **kw): + ldap = self.api.Backend.ldap + # FIXME, should be in a normalizer. Need to fix normalizers to work + # on non-unicode data. + if kw.get('usercertificate'): + kw['usercertificate'] = base64.b64decode(kw['usercertificate']) + + dn = ldap.make_dn(entry_attrs, 'krbprincipalname', _container_dn) + + (dn, old_entry_attrs) = ldap.get_entry(dn) + if 'usercertificate' in old_entry_attrs and 'usercerficate' in kw: + # FIXME, what to do here? Do we revoke the old cert? + raise errors.GenericError(format='entry already has a certificate') + + entry_attrs = self.args_options_to_entry(principal, **kw) + + try: + ldap.update_entry(dn, entry_attrs) + except errors.EmptyModlist: + pass + + return ldap.get_entry(dn, entry_attrs.keys()) + + def output_to_cli(self, textui, result, principal, **options): + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_dashed('Modified service "%s".' % principal) + +api.register(service2_mod) + + +class service2_find(crud.Search): + """ + Search for services. + """ + takes_options = ( + Flag('all', + doc='Retrieve all attributes' + ), + ) + + def execute(self, term, **kw): + ldap = self.api.Backend.ldap2 + + # lisp style! + custom_filter = '(&(objectclass=ipaService)' \ + '(!(objectClass=posixAccount))' \ + '(!(|(krbprincipalname=kadmin/*)' \ + '(krbprincipalname=K/M@*)' \ + '(krbprincipalname=krbtgt/*))' \ + ')' \ + ')' + + search_kw = self.args_options_2_entry(**kw) + search_kw['objectclass'] = 'krbprincipal' + filter = ldap.make_filter(search_kw, rules=ldap.MATCH_ALL) + + search_kw = {} + for a in _default_attributes: + search_kw[a] = term + term_filter = ldap.make_filter(search_kw, exact=False) + + filter = ldap.combine_filters( + (custom_filter, filter, term_filter), rules=ldap.MATCH_ALL + ) + + if kw['all']: + attrs_list = ['*'] + else: + attrs_list = _default_attributes + + try: + (entries, truncated) = ldap.find_entries( + filter, attrs_list, _container_dn + ) + except errors.NotFound: + (entries, truncated) = (tuple(), False) + + return entries + + def output_for_cli(self, textui, result, principal, **options): + (entries, truncated) = result + + textui.print_name(self.name) + for (dn, entry_attrs) in entries: + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_plain('') + textui.print_count( + len(result), '%i service matched.', '%i services matched.' + ) + if truncated: + textui.print_dashed('These results are truncated.', below=False) + textui.print_dashed( + 'Please refine your search and try again.', above=False + ) + +api.register(service2_find) + + +class service2_show(crud.Retrieve): + """ + Display service. + """ + takes_options = ( + Flag('all', + doc='Retrieve all attributes' + ), + ) + + def execute(self, principal, **kw): + """ + Execute the service-show operation. + + The dn should not be passed as a keyword argument as it is constructed + by this method. + + Returns the entry + + :param principal: The service principal to retrieve + :param kw: Not used. + """ + ldap = self.api.Backend.ldap2 + + dn = ldap.make_dn_from_attr( + 'krbprincipalname', principal, _container_dn + ) + + if kw['all']: + attrs_list = ['*'] + else: + attrs_list = _default_attributes + + return ldap.get_entry(dn, attrs_list) + + def output_for_cli(self, textui, result, principal, **options): + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + +api.register(service2_show) + diff --git a/ipalib/plugins/service2.py b/ipalib/plugins/service2.py deleted file mode 100644 index 2722203a2..000000000 --- a/ipalib/plugins/service2.py +++ /dev/null @@ -1,362 +0,0 @@ -# Authors: -# Jason Gerard DeRose -# Rob Crittenden -# Pavel Zuna -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -""" -Services (Identity) -""" -import base64 - -from OpenSSL import crypto - -from ipalib import api, crud, errors -from ipalib import Object -from ipalib import Str, Flag, Bytes - -_container_dn = api.env.container_service -_default_attributes = ['krbprincipalname', 'usercertificate'] - - -def split_principal(principal): - service = hostname = realm = None - - # Break down the principal into its component parts, which may or - # may not include the realm. - sp = principal.split('/') - if len(sp) != 2: - raise errors.MalformedServicePrincipal(reason='missing service') - - service = sp[0] - sr = sp[1].split('@') - if len(sr) > 2: - raise errors.MalformedServicePrincipal( - reason='unable to determine realm' - ) - - hostname = sr[0].lower() - if len(sr) == 2: - realm = sr[1].upper() - # At some point we'll support multiple realms - if realm != api.env.realm: - raise errors.RealmMismatch() - else: - realm = api.env.realm - - # Note that realm may be None. - return (service, hostname, realm) - -def validate_principal(ugettext, principal): - (service, hostname, principal) = split_principal(principal) - return None - -def normalize_principal(principal): - # The principal is already validated when it gets here - (service, hostname, realm) = split_principal(principal) - # Put the principal back together again - principal = '%s/%s@%s' % (service, hostname, realm) - return unicode(principal) - -def validate_certificate(ugettext, cert): - """ - For now just verify that it is properly base64-encoded. - """ - try: - base64.b64decode(cert) - except Exception, e: - raise errors.Base64DecodeError(reason=str(e)) - - -class service2(Object): - """ - Service object. - """ - takes_params = ( - Str('krbprincipalname', validate_principal, - cli_name='principal', - doc='Service principal', - primary_key=True, - normalizer=lambda value: normalize_principal(value), - ), - Bytes('usercertificate?', validate_certificate, - cli_name='certificate', - doc='Base-64 encoded server certificate', - ), - ) - -api.register(service2) - - -class service2_create(crud.Create): - """ - Add new service. - """ - takes_options = ( - Flag('force', - doc='Force principal name even if not in DNS', - ), - ) - def execute(self, principal, **kw): - """ - Execute the service-add operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - Returns the entry as it will be created in LDAP. - - :param principal: The service to be added in the form: service/hostname - :param kw: Keyword arguments for the other LDAP attributes. - """ - assert 'krbprincipalname' not in kw - ldap = self.api.Backend.ldap2 - # FIXME: should be in a normalizer. Need to fix normalizers to work - # on non-unicode data - if kw.get('usercertificate'): - kw['usercertificate'] = base64.b64decode(kw['usercertificate']) - - (service, hostname, realm) = split_principal(principal) - - if service.lower() == 'host' and not kw['force']: - raise errors.HostService() - - # FIXME: once DNS client is done - # if not kw['force']: - # fqdn = hostname + '.' - # rs = dnsclient.query(fqdn, dnsclient.DNS_C_IN, dnsclient.DNS_T_A) - # if len(rs) == 0: - # self.log.debug( - # 'IPA: DNS A record lookup failed for '%s'" % hostname - # ) - # raise ipaerror.gen_exception(ipaerror.INPUT_NOT_DNS_A_RECORD) - # else: - # self.log.debug( - # 'IPA: found %d records for '%s'" % (len(rs), hostname) - # ) - - entry_attrs = self.args_options_2_entry(principal, **kw) - entry_attrs['objectclass'] = [ - 'krbprincipal', 'krbprincipalaux', 'krbticketpolicyaux', - 'ipaservice', 'pkiuser' - ] - dn = ldap.make_dn(entry_attrs, 'krbprincipalname', _container_dn) - - ldap.add_entry(dn, entry_attrs) - - return ldap.get_entry(dn, entry_attrs.keys()) - - def output_for_cli(self, textui, result, principal, **options): - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_dashed('Created service "%s".' % principal) - -api.register(service2_create) - - -class service2_delete(crud.Delete): - """ - Delete an existing service. - """ - def execute(self, principal, **kw): - """ - Delete a service principal. - - principal is the krbprincipalname of the entry to delete. - - This should be called with much care. - - :param principal: The service to be added in the form: service/hostname - :param kw: not used - """ - ldap = self.api.Backend.ldap2 - - (dn, entry_attrs) = ldap.find_entry_by_attr( - 'krbprincipalname', principal, 'ipaservice' - ) - - if 'usercerfificate' in entry_attrs: - cert = entry_attrs['usercertificate'] - x509 = crypto.load_certificate(crypto.FILETYPE_ASN1, cert) - serial = str(x509.get_serial_number()) - api.Command['cert_revoke'](unicode(serial), revocation_reason=5) - - ldap.delete_entry(dn) - - return True - - def output_for_cli(self, textui, result, principal, **options): - textui.print_name(self.name) - textui.print_dashed('Deleted service "%s".' % principal) - -api.register(service2_delete) - - -class service2_mod(crud.Update): - """ - Modify service. - """ - def execute(self, principal, **kw): - ldap = self.api.Backend.ldap - # FIXME, should be in a normalizer. Need to fix normalizers to work - # on non-unicode data. - if kw.get('usercertificate'): - kw['usercertificate'] = base64.b64decode(kw['usercertificate']) - - dn = ldap.make_dn(entry_attrs, 'krbprincipalname', _container_dn) - - (dn, old_entry_attrs) = ldap.get_entry(dn) - if 'usercertificate' in old_entry_attrs and 'usercerficate' in kw: - # FIXME, what to do here? Do we revoke the old cert? - raise errors.GenericError(format='entry already has a certificate') - - entry_attrs = self.args_options_to_entry(principal, **kw) - - try: - ldap.update_entry(dn, entry_attrs) - except errors.EmptyModlist: - pass - - return ldap.get_entry(dn, entry_attrs.keys()) - - def output_to_cli(self, textui, result, principal, **options): - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_dashed('Modified service "%s".' % principal) - -api.register(service2_mod) - - -class service2_find(crud.Search): - """ - Search for services. - """ - takes_options = ( - Flag('all', - doc='Retrieve all attributes' - ), - ) - - def execute(self, term, **kw): - ldap = self.api.Backend.ldap2 - - # lisp style! - custom_filter = '(&(objectclass=ipaService)' \ - '(!(objectClass=posixAccount))' \ - '(!(|(krbprincipalname=kadmin/*)' \ - '(krbprincipalname=K/M@*)' \ - '(krbprincipalname=krbtgt/*))' \ - ')' \ - ')' - - search_kw = self.args_options_2_entry(**kw) - search_kw['objectclass'] = 'krbprincipal' - filter = ldap.make_filter(search_kw, rules=ldap.MATCH_ALL) - - search_kw = {} - for a in _default_attributes: - search_kw[a] = term - term_filter = ldap.make_filter(search_kw, exact=False) - - filter = ldap.combine_filters( - (custom_filter, filter, term_filter), rules=ldap.MATCH_ALL - ) - - if kw['all']: - attrs_list = ['*'] - else: - attrs_list = _default_attributes - - try: - (entries, truncated) = ldap.find_entries( - filter, attrs_list, _container_dn - ) - except errors.NotFound: - (entries, truncated) = (tuple(), False) - - return entries - - def output_for_cli(self, textui, result, principal, **options): - (entries, truncated) = result - - textui.print_name(self.name) - for (dn, entry_attrs) in entries: - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_plain('') - textui.print_count( - len(result), '%i service matched.', '%i services matched.' - ) - if truncated: - textui.print_dashed('These results are truncated.', below=False) - textui.print_dashed( - 'Please refine your search and try again.', above=False - ) - -api.register(service2_find) - - -class service2_show(crud.Retrieve): - """ - Display service. - """ - takes_options = ( - Flag('all', - doc='Retrieve all attributes' - ), - ) - - def execute(self, principal, **kw): - """ - Execute the service-show operation. - - The dn should not be passed as a keyword argument as it is constructed - by this method. - - Returns the entry - - :param principal: The service principal to retrieve - :param kw: Not used. - """ - ldap = self.api.Backend.ldap2 - - dn = ldap.make_dn_from_attr( - 'krbprincipalname', principal, _container_dn - ) - - if kw['all']: - attrs_list = ['*'] - else: - attrs_list = _default_attributes - - return ldap.get_entry(dn, attrs_list) - - def output_for_cli(self, textui, result, principal, **options): - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - -api.register(service2_show) - diff --git a/ipalib/plugins/taskgroup.py b/ipalib/plugins/taskgroup.py new file mode 100644 index 000000000..9825adcb6 --- /dev/null +++ b/ipalib/plugins/taskgroup.py @@ -0,0 +1,207 @@ +# Authors: +# Rob Crittenden +# Pavel Zuna +# +# Copyright (C) 2009 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Taskgroups +""" + +from ipalib import api +from ipalib.plugins.basegroup2 import * + +_container_dn = api.env.container_taskgroup +_default_attributes = ['cn', 'description', 'member', 'memberOf'] + +class taskgroup2(basegroup2): + """ + Taskgroup object. + """ + container = _container_dn + +api.register(taskgroup2) + + +class taskgroup2_create(basegroup2_create): + """ + Create new taskgroup. + """ + +api.register(taskgroup2_create) + + +class taskgroup2_delete(basegroup2_delete): + """ + Delete taskgroup. + """ + container = _container_dn + +api.register(taskgroup2_delete) + + +class taskgroup2_mod(basegroup2_mod): + """ + Edit taskgroup. + """ + container = _container_dn + +api.register(taskgroup2_mod) + + +class taskgroup2_find(basegroup2_find): + """ + Search for taskgroups. + """ + container = _container_dn + +api.register(taskgroup2_find) + + +class taskgroup2_show(basegroup2_show): + """ + Display taskgroup. + """ + default_attributes = _default_attributes + container = _container_dn + +api.register(taskgroup2_show) + + +class taskgroup2_add_member(basegroup2_add_member): + """ + Add member to taskgroup. + """ + container = _container_dn + takes_options = basegroup2_add_member.takes_options + ( + List('rolegroups?', + cli_name='rolegroups', + doc='comma-separated list of role groups to add' + ), + ) + + def execute(self, cn, **kw): + """ + Execute the group-add-member operation. + + Returns the updated group entry + + :param cn: The group name to add new members to. + :param kw: groups is a comma-separated list of groups to add + :param kw: users is a comma-separated list of users to add + :param kw: rolegroups is a comma-separated list of rolegroups to add + """ + assert self.container + ldap = self.api.Backend.ldap2 + dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) + to_add = [] + add_failed = [] + completed = 0 + + members = kw.get('groups', []) + (to_add, add_failed) = find_members( + ldap, add_failed, members, 'cn', 'ipaUserGroup', + self.api.env.container_group + ) + (completed, add_failed) = add_members( + ldap, completed, to_add, add_failed, dn, 'member' + ) + + members = kw.get('hosts', []) + (to_add, add_failed) = find_members( + ldap, add_failed, members, 'cn', 'ipaHost', + self.api.env.container_host + ) + (completed, add_failed) = add_members( + ldap, completed, to_add, add_failed, dn, 'member' + ) + + members = kw.get('rolegroups', []) + (to_add, add_failed) = find_members( + ldap, add_failed, members, 'cn', self.filter_class, + self.api.env.container_rolegroups + ) + (completed, add_failed) = add_members( + ldap, completed, to_add, add_failed, dn, 'member' + ) + + return (completed, ldap.get_entry(dn, _default_attributes)) + +api.register(taskgroup2_add_member) + + +class taskgroup2_del_member(basegroup2_del_member): + """ + Remove member from taskgroup. + """ + container = _container_dn + takes_options = basegroup2_del_member.takes_options + ( + List('rolegroups?', + cli_name='rolegroups', + doc='comma-separated list of role groups to remove' + ), + ) + + def execute(self, cn, **kw): + """ + Execute the group-remove-member operation. + + Returns the updated group entry + + :param cn: The group name to remove new members from. + :param kw: groups is a comma-separated list of groups to remove + :param kw: users is a comma-separated list of users to remove + :param kw: rolegroups is a comma-separated list of rolegroups to remove + """ + assert self.container + ldap = self.api.Backend.ldap2 + dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) + to_remove = [] + remove_failed = [] + completed = 0 + + members = kw.get('groups', []) + (to_remove, remove_failed) = find_members( + ldap, remove_failed, members, 'cn', 'ipaUserGroup', + self.api.env.container_group + ) + (completed, remove_failed) = del_members( + ldap, completed, to_remove, remove_failed, dn, 'member' + ) + + members = kw.get('hosts', []) + (to_remove, remove_failed) = find_members( + ldap, remove_failed, members, 'cn', 'ipaHost', + self.api.env.container_host + ) + (completed, remove_failed) = del_members( + ldap, completed, to_remove, remove_failed, dn, 'member' + ) + + members = kw.get('rolegroups', []) + (to_remove, remove_failed) = find_members( + ldap, remove_failed, members, 'cn', self.filter_class, + self.api.env.container_rolegroups + ) + (completed, remove_failed) = del_members( + ldap, completed, to_remove, remove_failed, dn, 'member' + ) + + return (completed, ldap.get_entry(dn, _default_attributes)) + +api.register(taskgroup2_del_member) + diff --git a/ipalib/plugins/taskgroup2.py b/ipalib/plugins/taskgroup2.py deleted file mode 100644 index 9825adcb6..000000000 --- a/ipalib/plugins/taskgroup2.py +++ /dev/null @@ -1,207 +0,0 @@ -# Authors: -# Rob Crittenden -# Pavel Zuna -# -# Copyright (C) 2009 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Taskgroups -""" - -from ipalib import api -from ipalib.plugins.basegroup2 import * - -_container_dn = api.env.container_taskgroup -_default_attributes = ['cn', 'description', 'member', 'memberOf'] - -class taskgroup2(basegroup2): - """ - Taskgroup object. - """ - container = _container_dn - -api.register(taskgroup2) - - -class taskgroup2_create(basegroup2_create): - """ - Create new taskgroup. - """ - -api.register(taskgroup2_create) - - -class taskgroup2_delete(basegroup2_delete): - """ - Delete taskgroup. - """ - container = _container_dn - -api.register(taskgroup2_delete) - - -class taskgroup2_mod(basegroup2_mod): - """ - Edit taskgroup. - """ - container = _container_dn - -api.register(taskgroup2_mod) - - -class taskgroup2_find(basegroup2_find): - """ - Search for taskgroups. - """ - container = _container_dn - -api.register(taskgroup2_find) - - -class taskgroup2_show(basegroup2_show): - """ - Display taskgroup. - """ - default_attributes = _default_attributes - container = _container_dn - -api.register(taskgroup2_show) - - -class taskgroup2_add_member(basegroup2_add_member): - """ - Add member to taskgroup. - """ - container = _container_dn - takes_options = basegroup2_add_member.takes_options + ( - List('rolegroups?', - cli_name='rolegroups', - doc='comma-separated list of role groups to add' - ), - ) - - def execute(self, cn, **kw): - """ - Execute the group-add-member operation. - - Returns the updated group entry - - :param cn: The group name to add new members to. - :param kw: groups is a comma-separated list of groups to add - :param kw: users is a comma-separated list of users to add - :param kw: rolegroups is a comma-separated list of rolegroups to add - """ - assert self.container - ldap = self.api.Backend.ldap2 - dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) - to_add = [] - add_failed = [] - completed = 0 - - members = kw.get('groups', []) - (to_add, add_failed) = find_members( - ldap, add_failed, members, 'cn', 'ipaUserGroup', - self.api.env.container_group - ) - (completed, add_failed) = add_members( - ldap, completed, to_add, add_failed, dn, 'member' - ) - - members = kw.get('hosts', []) - (to_add, add_failed) = find_members( - ldap, add_failed, members, 'cn', 'ipaHost', - self.api.env.container_host - ) - (completed, add_failed) = add_members( - ldap, completed, to_add, add_failed, dn, 'member' - ) - - members = kw.get('rolegroups', []) - (to_add, add_failed) = find_members( - ldap, add_failed, members, 'cn', self.filter_class, - self.api.env.container_rolegroups - ) - (completed, add_failed) = add_members( - ldap, completed, to_add, add_failed, dn, 'member' - ) - - return (completed, ldap.get_entry(dn, _default_attributes)) - -api.register(taskgroup2_add_member) - - -class taskgroup2_del_member(basegroup2_del_member): - """ - Remove member from taskgroup. - """ - container = _container_dn - takes_options = basegroup2_del_member.takes_options + ( - List('rolegroups?', - cli_name='rolegroups', - doc='comma-separated list of role groups to remove' - ), - ) - - def execute(self, cn, **kw): - """ - Execute the group-remove-member operation. - - Returns the updated group entry - - :param cn: The group name to remove new members from. - :param kw: groups is a comma-separated list of groups to remove - :param kw: users is a comma-separated list of users to remove - :param kw: rolegroups is a comma-separated list of rolegroups to remove - """ - assert self.container - ldap = self.api.Backend.ldap2 - dn = get_dn_by_attr(ldap, 'cn', cn, self.filter_class, self.container) - to_remove = [] - remove_failed = [] - completed = 0 - - members = kw.get('groups', []) - (to_remove, remove_failed) = find_members( - ldap, remove_failed, members, 'cn', 'ipaUserGroup', - self.api.env.container_group - ) - (completed, remove_failed) = del_members( - ldap, completed, to_remove, remove_failed, dn, 'member' - ) - - members = kw.get('hosts', []) - (to_remove, remove_failed) = find_members( - ldap, remove_failed, members, 'cn', 'ipaHost', - self.api.env.container_host - ) - (completed, remove_failed) = del_members( - ldap, completed, to_remove, remove_failed, dn, 'member' - ) - - members = kw.get('rolegroups', []) - (to_remove, remove_failed) = find_members( - ldap, remove_failed, members, 'cn', self.filter_class, - self.api.env.container_rolegroups - ) - (completed, remove_failed) = del_members( - ldap, completed, to_remove, remove_failed, dn, 'member' - ) - - return (completed, ldap.get_entry(dn, _default_attributes)) - -api.register(taskgroup2_del_member) - diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py new file mode 100644 index 000000000..dda09d2cc --- /dev/null +++ b/ipalib/plugins/user.py @@ -0,0 +1,405 @@ +# Authors: +# Jason Gerard DeRose +# Pavel Zuna +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" +Users (Identity) +""" + +from ipalib import api, crud, errors +from ipalib import Command, Object +from ipalib import Flag, Int, Password, Str + +# parent DN +_container_dn = api.env.container_user + +# attributes displayed by default +_default_attributes = [ + 'uid', 'givenname', 'sn', 'homedirectory', 'loginshell' +] + + +class user2(Object): + """ + User object. + """ + + takes_params = ( + Str('givenname', + cli_name='first', + doc='first name', + ), + Str('sn', + cli_name='last', + doc='last name', + ), + Str('uid', + cli_name='user', + doc='login name', + primary_key=True, + default_from=lambda givenname, sn: givenname[0] + sn, + normalizer=lambda value: value.lower(), + ), + Str('gecos?', + doc='GECOS field', + default_from=lambda uid: uid, + ), + Str('homedirectory?', + cli_name='homedir', + doc='home directory', + default_from=lambda uid: '/home/%s' % uid, + ), + Str('loginshell?', + cli_name='shell', + default=u'/bin/sh', + doc='login shell', + ), + Str('krbprincipalname?', + cli_name='principal', + doc='Kerberos principal name', + default_from=lambda uid: '%s@%s' % (uid, api.env.realm), + ), + Str('mail?', + cli_name='email', + doc='e-mail address', + ), + Password('userpassword?', + cli_name='password', + doc='password', + ), + Int('uidnumber?', + cli_name='uid', + doc='UID (use this option to set it manually)', + ), + Str('street?', + cli_name='street', + doc='street address', + ), + ) + +api.register(user2) + + +class user2_create(crud.Create): + """ + Create new user. + """ + + def execute(self, *args, **options): + ldap = self.api.Backend.ldap2 + uid = args[0] + + # build entry attributes + entry_attrs = self.args_options_2_entry(*args, **options) + + # build entry DN + dn = ldap.make_dn(entry_attrs, 'uid', _container_dn) + + # get configuration entry attributes + config = ldap.get_ipa_config()[1] + + # fill in required attributes + entry_attrs['objectclass'] = config.get('ipauserobjectclasses') + + # fill default values + # uidNumber gets filled automatically by the DS dna_plugin + entry_attrs.setdefault('loginshell', config.get('ipadefaultloginshell')) + entry_attrs.setdefault('gecos', uid) + entry_attrs.setdefault( + 'krbprincipalname', '%s@%s' % (uid, self.api.env.realm) + ) + # hack so we can request separate first and last name in CLI + entry_attrs.setdefault( + 'cn', '%s %s' % (entry_attrs['givenname'], entry_attrs['sn']) + ) + if 'homedirectory' not in entry_attrs: + # get home's root directory from config + homes_root = config.get('ipahomesrootdir', '/home')[0] + # build user's home directory based on his uid + home_dir = '%s/%s' % (homes_root, uid) + home_dir = home_dir.replace('//', '/').rstrip('/') + entry_attrs['homedirectory'] = home_dir + + # we're adding new users to a default group, get it's DN and gidNumber + # get default group name from config + def_primary_group = config.get('ipadefaultprimarygroup') + # build the group's DN + group_parent_dn = self.api.env.container_group + group_rdn = ldap.make_rdn_from_attr('cn', def_primary_group) + group_dn = ldap.make_dn_from_rdn(group_rdn, group_parent_dn) + # try to retrieve the group's gidNumber + try: + (group_dn, group_attrs) = ldap.get_entry(group_dn, ['gidnumber']) + except errors.NotFound: + error_msg = 'Default group for new users not found.' + raise errors.NotFound(reason=error_msg) + # fill default group's gidNumber + entry_attrs['gidnumber'] = group_attrs['gidnumber'] + + # create user entry + ldap.add_entry(dn, entry_attrs) + + # add user to default group + ldap.add_entry_to_group(dn, group_dn) + + # get user entry with created attributes for output + return ldap.get_entry(dn, entry_attrs.keys()) + + def output_for_cli(self, textui, result, *args, **options): + (dn, entry_attrs) = result + uid = args[0] + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_dashed('Created user "%s".' % uid) + +api.register(user2_create) + + +class user2_delete(crud.Delete): + """ + Delete user. + """ + + def execute(self, uid): + ldap = self.api.Backend.ldap2 + + if uid == 'admin': + # FIXME: add a specific exception for this? + raise errors.ExecutionError('Cannot delete user "admin".') + + # build entry DN + rdn = ldap.make_rdn_from_attr('uid', uid) + dn = ldap.make_dn_from_rdn(rdn, _container_dn) + + # delete user entry + ldap.delete_entry(dn) + + # blog about it + self.log.info('IPA: %s "%s"' % (self.name, uid)) + + # return something positive + return True + + def output_for_cli(self, textui, result, uid): + textui.print_name(self.name) + textui.print_dashed('Deleted user "%s".' % uid) + +api.register(user2_delete) + + +class user2_mod(crud.Update): + """ + Modify user. + """ + + def execute(self, uid, **options): + ldap = self.api.Backend.ldap2 + + # build entry attributes, don't include uid! + entry_attrs = self.args_options_2_entry(*tuple(), **options) + + # build entry DN + rdn = ldap.make_rdn_from_attr('uid', uid) + dn = ldap.make_dn_from_rdn(rdn, _container_dn) + + # update user entry + ldap.update_entry(dn, entry_attrs) + + # get user entry with modified + default attributes for output + return ldap.get_entry(dn, (entry_attrs.keys() + _default_attributes)) + + def output_for_cli(self, textui, result, uid, **options): + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_dashed('Modified user "%s".' % uid) + +api.register(user2_mod) + + +class user2_find(crud.Search): + """ + Search for users. + """ + + takes_options = ( + Flag('all', + doc='retrieve all attributes?', + ), + ) + + def execute(self, term, **options): + ldap = self.api.Backend.ldap2 + + # get list of search fields from config + config = ldap.get_ipa_config()[1] + search_fields = config.get('ipausersearchfields')[0].split(',') + + # look for term in all search fields + search_kw = self.args_options_2_entry(**options) + if term: + for f in search_fields: + search_kw[f] = '%s' % term + # build search filter + filter = ldap.make_filter(search_kw, exact=False) + + # select attributes we want to retrieve + if options['all']: + attrs_list = ['*'] + else: + attrs_list = _default_attributes + + # get matching entries + try: + (entries, truncated) = ldap.find_entries( + filter, attrs_list, _container_dn, ldap.SCOPE_ONELEVEL + ) + except errors.NotFound: + (entries, truncated) = (tuple(), False) + + return (entries, truncated) + + def output_for_cli(self, textui, result, term, **options): + (entries, truncated) = result + + textui.print_name(self.name) + for (dn, entry_attrs) in entries: + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + textui.print_plain('') + textui.print_count( + len(result), '%i user matched.', '%i users matched.' + ) + if truncated: + textui.print_dashed('These results are truncated.', below=False) + textui.print_dashed( + 'Please refine your search and try again.', above=False + ) + +api.register(user2_find) + + +class user2_show(crud.Retrieve): + """ + Display user. + """ + + takes_options = ( + Flag('all', + doc='retrieve all attributes?', + ), + ) + + def execute(self, uid, **options): + ldap = self.api.Backend.ldap2 + + # build entry DN + rdn = ldap.make_rdn_from_attr('uid', uid) + dn = ldap.make_dn_from_rdn(rdn, _container_dn) + + # select attributes we want to retrieve + if options['all']: + attrs_list = ['*'] + else: + attrs_list = _default_attributes + + return ldap.get_entry(dn, attrs_list) + + def output_for_cli(self, textui, result, uid, **options): + (dn, entry_attrs) = result + + textui.print_name(self.name) + textui.print_attribute('dn', dn) + textui.print_entry(entry_attrs) + +api.register(user2_show) + + +class user2_lock(Command): + """ + Lock user account. + """ + + takes_args = ( + Str('uid', + cli_name='user', + doc='login name', + ), + ) + + def execute(self, uid): + ldap = self.api.Backend.ldap2 + + # build entry DN + rdn = ldap.make_rdn_from_attr('uid', uid) + dn = ldap.make_dn_from_rdn(rdn, _container_dn) + + # lock! + try: + ldap.deactivate_entry(dn) + except errors.AlreadyInactive: + pass + + # return something positive + return True + + def output_for_cli(self, textui, result, uid): + textui.print_name(self.name) + textui.print_dashed('Locked user "%s".' % uid) + +api.register(user2_lock) + + +class user2_unlock(Command): + """ + Unlock user account. + """ + + takes_args = ( + Str('uid', + cli_name='user', + doc='login name', + ), + ) + + def execute(self, uid): + ldap = self.api.Backend.ldap2 + + # build entry DN + rdn = ldap.make_rdn_from_attr('uid', uid) + dn = ldap.make_dn_from_rdn(rdn, _container_dn) + + # unlock! + try: + ldap.activate_entry(dn) + except errors.AlreadyActive: + pass + + # return something positive + return True + + def output_for_cli(self, textui, result, uid): + textui.print_name(self.name) + textui.print_dashed('Unlocked user "%s".' % uid) + +api.register(user2_unlock) + diff --git a/ipalib/plugins/user2.py b/ipalib/plugins/user2.py deleted file mode 100644 index dda09d2cc..000000000 --- a/ipalib/plugins/user2.py +++ /dev/null @@ -1,405 +0,0 @@ -# Authors: -# Jason Gerard DeRose -# Pavel Zuna -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -""" -Users (Identity) -""" - -from ipalib import api, crud, errors -from ipalib import Command, Object -from ipalib import Flag, Int, Password, Str - -# parent DN -_container_dn = api.env.container_user - -# attributes displayed by default -_default_attributes = [ - 'uid', 'givenname', 'sn', 'homedirectory', 'loginshell' -] - - -class user2(Object): - """ - User object. - """ - - takes_params = ( - Str('givenname', - cli_name='first', - doc='first name', - ), - Str('sn', - cli_name='last', - doc='last name', - ), - Str('uid', - cli_name='user', - doc='login name', - primary_key=True, - default_from=lambda givenname, sn: givenname[0] + sn, - normalizer=lambda value: value.lower(), - ), - Str('gecos?', - doc='GECOS field', - default_from=lambda uid: uid, - ), - Str('homedirectory?', - cli_name='homedir', - doc='home directory', - default_from=lambda uid: '/home/%s' % uid, - ), - Str('loginshell?', - cli_name='shell', - default=u'/bin/sh', - doc='login shell', - ), - Str('krbprincipalname?', - cli_name='principal', - doc='Kerberos principal name', - default_from=lambda uid: '%s@%s' % (uid, api.env.realm), - ), - Str('mail?', - cli_name='email', - doc='e-mail address', - ), - Password('userpassword?', - cli_name='password', - doc='password', - ), - Int('uidnumber?', - cli_name='uid', - doc='UID (use this option to set it manually)', - ), - Str('street?', - cli_name='street', - doc='street address', - ), - ) - -api.register(user2) - - -class user2_create(crud.Create): - """ - Create new user. - """ - - def execute(self, *args, **options): - ldap = self.api.Backend.ldap2 - uid = args[0] - - # build entry attributes - entry_attrs = self.args_options_2_entry(*args, **options) - - # build entry DN - dn = ldap.make_dn(entry_attrs, 'uid', _container_dn) - - # get configuration entry attributes - config = ldap.get_ipa_config()[1] - - # fill in required attributes - entry_attrs['objectclass'] = config.get('ipauserobjectclasses') - - # fill default values - # uidNumber gets filled automatically by the DS dna_plugin - entry_attrs.setdefault('loginshell', config.get('ipadefaultloginshell')) - entry_attrs.setdefault('gecos', uid) - entry_attrs.setdefault( - 'krbprincipalname', '%s@%s' % (uid, self.api.env.realm) - ) - # hack so we can request separate first and last name in CLI - entry_attrs.setdefault( - 'cn', '%s %s' % (entry_attrs['givenname'], entry_attrs['sn']) - ) - if 'homedirectory' not in entry_attrs: - # get home's root directory from config - homes_root = config.get('ipahomesrootdir', '/home')[0] - # build user's home directory based on his uid - home_dir = '%s/%s' % (homes_root, uid) - home_dir = home_dir.replace('//', '/').rstrip('/') - entry_attrs['homedirectory'] = home_dir - - # we're adding new users to a default group, get it's DN and gidNumber - # get default group name from config - def_primary_group = config.get('ipadefaultprimarygroup') - # build the group's DN - group_parent_dn = self.api.env.container_group - group_rdn = ldap.make_rdn_from_attr('cn', def_primary_group) - group_dn = ldap.make_dn_from_rdn(group_rdn, group_parent_dn) - # try to retrieve the group's gidNumber - try: - (group_dn, group_attrs) = ldap.get_entry(group_dn, ['gidnumber']) - except errors.NotFound: - error_msg = 'Default group for new users not found.' - raise errors.NotFound(reason=error_msg) - # fill default group's gidNumber - entry_attrs['gidnumber'] = group_attrs['gidnumber'] - - # create user entry - ldap.add_entry(dn, entry_attrs) - - # add user to default group - ldap.add_entry_to_group(dn, group_dn) - - # get user entry with created attributes for output - return ldap.get_entry(dn, entry_attrs.keys()) - - def output_for_cli(self, textui, result, *args, **options): - (dn, entry_attrs) = result - uid = args[0] - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_dashed('Created user "%s".' % uid) - -api.register(user2_create) - - -class user2_delete(crud.Delete): - """ - Delete user. - """ - - def execute(self, uid): - ldap = self.api.Backend.ldap2 - - if uid == 'admin': - # FIXME: add a specific exception for this? - raise errors.ExecutionError('Cannot delete user "admin".') - - # build entry DN - rdn = ldap.make_rdn_from_attr('uid', uid) - dn = ldap.make_dn_from_rdn(rdn, _container_dn) - - # delete user entry - ldap.delete_entry(dn) - - # blog about it - self.log.info('IPA: %s "%s"' % (self.name, uid)) - - # return something positive - return True - - def output_for_cli(self, textui, result, uid): - textui.print_name(self.name) - textui.print_dashed('Deleted user "%s".' % uid) - -api.register(user2_delete) - - -class user2_mod(crud.Update): - """ - Modify user. - """ - - def execute(self, uid, **options): - ldap = self.api.Backend.ldap2 - - # build entry attributes, don't include uid! - entry_attrs = self.args_options_2_entry(*tuple(), **options) - - # build entry DN - rdn = ldap.make_rdn_from_attr('uid', uid) - dn = ldap.make_dn_from_rdn(rdn, _container_dn) - - # update user entry - ldap.update_entry(dn, entry_attrs) - - # get user entry with modified + default attributes for output - return ldap.get_entry(dn, (entry_attrs.keys() + _default_attributes)) - - def output_for_cli(self, textui, result, uid, **options): - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_dashed('Modified user "%s".' % uid) - -api.register(user2_mod) - - -class user2_find(crud.Search): - """ - Search for users. - """ - - takes_options = ( - Flag('all', - doc='retrieve all attributes?', - ), - ) - - def execute(self, term, **options): - ldap = self.api.Backend.ldap2 - - # get list of search fields from config - config = ldap.get_ipa_config()[1] - search_fields = config.get('ipausersearchfields')[0].split(',') - - # look for term in all search fields - search_kw = self.args_options_2_entry(**options) - if term: - for f in search_fields: - search_kw[f] = '%s' % term - # build search filter - filter = ldap.make_filter(search_kw, exact=False) - - # select attributes we want to retrieve - if options['all']: - attrs_list = ['*'] - else: - attrs_list = _default_attributes - - # get matching entries - try: - (entries, truncated) = ldap.find_entries( - filter, attrs_list, _container_dn, ldap.SCOPE_ONELEVEL - ) - except errors.NotFound: - (entries, truncated) = (tuple(), False) - - return (entries, truncated) - - def output_for_cli(self, textui, result, term, **options): - (entries, truncated) = result - - textui.print_name(self.name) - for (dn, entry_attrs) in entries: - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - textui.print_plain('') - textui.print_count( - len(result), '%i user matched.', '%i users matched.' - ) - if truncated: - textui.print_dashed('These results are truncated.', below=False) - textui.print_dashed( - 'Please refine your search and try again.', above=False - ) - -api.register(user2_find) - - -class user2_show(crud.Retrieve): - """ - Display user. - """ - - takes_options = ( - Flag('all', - doc='retrieve all attributes?', - ), - ) - - def execute(self, uid, **options): - ldap = self.api.Backend.ldap2 - - # build entry DN - rdn = ldap.make_rdn_from_attr('uid', uid) - dn = ldap.make_dn_from_rdn(rdn, _container_dn) - - # select attributes we want to retrieve - if options['all']: - attrs_list = ['*'] - else: - attrs_list = _default_attributes - - return ldap.get_entry(dn, attrs_list) - - def output_for_cli(self, textui, result, uid, **options): - (dn, entry_attrs) = result - - textui.print_name(self.name) - textui.print_attribute('dn', dn) - textui.print_entry(entry_attrs) - -api.register(user2_show) - - -class user2_lock(Command): - """ - Lock user account. - """ - - takes_args = ( - Str('uid', - cli_name='user', - doc='login name', - ), - ) - - def execute(self, uid): - ldap = self.api.Backend.ldap2 - - # build entry DN - rdn = ldap.make_rdn_from_attr('uid', uid) - dn = ldap.make_dn_from_rdn(rdn, _container_dn) - - # lock! - try: - ldap.deactivate_entry(dn) - except errors.AlreadyInactive: - pass - - # return something positive - return True - - def output_for_cli(self, textui, result, uid): - textui.print_name(self.name) - textui.print_dashed('Locked user "%s".' % uid) - -api.register(user2_lock) - - -class user2_unlock(Command): - """ - Unlock user account. - """ - - takes_args = ( - Str('uid', - cli_name='user', - doc='login name', - ), - ) - - def execute(self, uid): - ldap = self.api.Backend.ldap2 - - # build entry DN - rdn = ldap.make_rdn_from_attr('uid', uid) - dn = ldap.make_dn_from_rdn(rdn, _container_dn) - - # unlock! - try: - ldap.activate_entry(dn) - except errors.AlreadyActive: - pass - - # return something positive - return True - - def output_for_cli(self, textui, result, uid): - textui.print_name(self.name) - textui.print_dashed('Unlocked user "%s".' % uid) - -api.register(user2_unlock) - -- cgit