Beispiel #1
0
def convert_nsaccountlock(entry_attrs):
    if 'nsaccountlock' not in entry_attrs:
        entry_attrs['nsaccountlock'] = False
    else:
        nsaccountlock = Bool('temp')
        entry_attrs['nsaccountlock'] = nsaccountlock.convert(
            entry_attrs['nsaccountlock'][0])
Beispiel #2
0
class userstatus(LDAPObject):
    parent_object = 'user'

    takes_params = (
        Bool(
            'preserved?',
            label=_('Preserved user'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'server',
            label=_('Server'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'krbloginfailedcount',
            label=_('Failed logins'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'krblastsuccessfulauth',
            label=_('Last successful authentication'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'krblastfailedauth',
            label=_('Last failed authentication'),
            flags={'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'now',
            label=_('Time now'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
    )
Beispiel #3
0
class user(LDAPObject):
    """
    User object.
    """
    container_dn = api.env.container_user
    object_name = _('user')
    object_name_plural = _('users')
    object_class = ['posixaccount']
    object_class_config = 'ipauserobjectclasses'
    possible_objectclasses = ['meporiginentry']
    disallow_object_classes = ['krbticketpolicyaux']
    search_attributes_config = 'ipausersearchfields'
    default_attributes = [
        'uid', 'givenname', 'sn', 'homedirectory', 'loginshell',
        'uidnumber', 'gidnumber', 'mail', 'ou',
        'telephonenumber', 'title', 'memberof', 'nsaccountlock',
        'memberofindirect',
    ]
    search_display_attributes = [
        'uid', 'givenname', 'sn', 'homedirectory', 'loginshell',
        'mail', 'telephonenumber', 'title', 'nsaccountlock',
        'uidnumber', 'gidnumber', 'sshpubkeyfp',
    ]
    uuid_attribute = 'ipauniqueid'
    attribute_members = {
        'memberof': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
        'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
    }
    rdn_is_primary_key = True
    bindable = True
    password_attributes = [('userpassword', 'has_password'),
                           ('krbprincipalkey', 'has_keytab')]

    label = _('Users')
    label_singular = _('User')

    takes_params = (
        Str('uid',
            pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$',
            pattern_errmsg='may only include letters, numbers, _, -, . and $',
            maxlength=255,
            cli_name='login',
            label=_('User login'),
            primary_key=True,
            default_from=lambda givenname, sn: givenname[0] + sn,
            normalizer=lambda value: value.lower(),
        ),
        Str('givenname',
            cli_name='first',
            label=_('First name'),
        ),
        Str('sn',
            cli_name='last',
            label=_('Last name'),
        ),
        Str('cn',
            label=_('Full name'),
            default_from=lambda givenname, sn: '%s %s' % (givenname, sn),
            autofill=True,
        ),
        Str('displayname?',
            label=_('Display name'),
            default_from=lambda givenname, sn: '%s %s' % (givenname, sn),
            autofill=True,
        ),
        Str('initials?',
            label=_('Initials'),
            default_from=lambda givenname, sn: '%c%c' % (givenname[0], sn[0]),
            autofill=True,
        ),
        Str('homedirectory?',
            cli_name='homedir',
            label=_('Home directory'),
        ),
        Str('gecos?',
            label=_('GECOS field'),
            default_from=lambda givenname, sn: '%s %s' % (givenname, sn),
            autofill=True,
        ),
        Str('loginshell?',
            cli_name='shell',
            label=_('Login shell'),
        ),
        Str('krbprincipalname?', validate_principal,
            cli_name='principal',
            label=_('Kerberos principal'),
            default_from=lambda uid: '%s@%s' % (uid.lower(), api.env.realm),
            autofill=True,
            flags=['no_update'],
            normalizer=lambda value: normalize_principal(value),
        ),
        Str('mail*',
            cli_name='email',
            label=_('Email address'),
        ),
        Password('userpassword?',
            cli_name='password',
            label=_('Password'),
            doc=_('Prompt to set the user password'),
            # FIXME: This is temporary till bug is fixed causing updates to
            # bomb out via the webUI.
            exclude='webui',
        ),
        Flag('random?',
            doc=_('Generate a random user password'),
            flags=('no_search', 'virtual_attribute'),
            default=False,
        ),
        Str('randompassword?',
            label=_('Random password'),
            flags=('no_create', 'no_update', 'no_search', 'virtual_attribute'),
        ),
        Int('uidnumber',
            cli_name='uid',
            label=_('UID'),
            doc=_('User ID Number (system will assign one if not provided)'),
            autofill=True,
            default=DNA_MAGIC,
            minvalue=1,
        ),
        Int('gidnumber',
            label=_('GID'),
            doc=_('Group ID Number'),
            minvalue=1,
            default=DNA_MAGIC,
            autofill=True,
        ),
        Str('street?',
            cli_name='street',
            label=_('Street address'),
        ),
        Str('l?',
            cli_name='city',
            label=_('City'),
        ),
        Str('st?',
            cli_name='state',
            label=_('State/Province'),
        ),
        Str('postalcode?',
            label=_('ZIP'),
        ),
        Str('telephonenumber*',
            cli_name='phone',
            label=_('Telephone Number')
        ),
        Str('mobile*',
            label=_('Mobile Telephone Number')
        ),
        Str('pager*',
            label=_('Pager Number')
        ),
        Str('facsimiletelephonenumber*',
            cli_name='fax',
            label=_('Fax Number'),
        ),
        Str('ou?',
            cli_name='orgunit',
            label=_('Org. Unit'),
        ),
        Str('title?',
            label=_('Job Title'),
        ),
        Str('manager?',
            label=_('Manager'),
        ),
        Str('carlicense?',
            label=_('Car License'),
        ),
        Bool('nsaccountlock?',
            label=_('Account disabled'),
            flags=['no_option'],
        ),
        Str('ipasshpubkey*', validate_sshpubkey,
            cli_name='sshpubkey',
            label=_('SSH public key'),
            normalizer=normalize_sshpubkey,
            csv=True,
            flags=['no_search'],
        ),
    )

    def _normalize_and_validate_email(self, email, config=None):
        if not config:
            config = self.backend.get_ipa_config()[1]

        # check if default email domain should be added
        defaultdomain = config.get('ipadefaultemaildomain', [None])[0]
        if email:
            norm_email = []
            if not isinstance(email, (list, tuple)):
                email = [email]
            for m in email:
                if isinstance(m, basestring):
                    if '@' not in m and defaultdomain:
                        m = m + u'@' + defaultdomain
                    if not Email(m):
                        raise errors.ValidationError(name='email', error=_('invalid e-mail format: %(email)s') % dict(email=m))
                    norm_email.append(m)
                else:
                    if not Email(m):
                        raise errors.ValidationError(name='email', error=_('invalid e-mail format: %(email)s') % dict(email=m))
                    norm_email.append(m)
            return norm_email

        return email

    def _normalize_manager(self, manager):
        """
        Given a userid verify the user's existence and return the dn.
        """
        if not manager:
            return None

        if not isinstance(manager, list):
            manager = [manager]
        try:
            container_dn = DN(self.container_dn, api.env.basedn)
            for m in xrange(len(manager)):
                if isinstance(manager[m], DN) and manager[m].endswith(container_dn):
                    continue
                (dn, entry_attrs) = self.backend.find_entry_by_attr(
                        self.primary_key.name, manager[m], self.object_class, [''],
                        self.container_dn
                    )
                manager[m] = dn
        except errors.NotFound:
            raise errors.NotFound(reason=_('manager %(manager)s not found') % dict(manager=manager[m]))

        return manager

    def _convert_manager(self, entry_attrs, **options):
        """
        Convert a manager dn into a userid
        """
        if options.get('raw', False):
             return

        if 'manager' in entry_attrs:
            for m in xrange(len(entry_attrs['manager'])):
                entry_attrs['manager'][m] = self.get_primary_key_from_dn(entry_attrs['manager'][m])
class deskprofilerule(LDAPObject):
    """
    Desktop profile object.
    """
    container_dn = None
    object_name = _('FleetCommander Desktop Profile Rule Map')
    object_name_plural = _('FleetCommander Desktop Profile Rule Maps')
    object_class = ['ipaassociation', 'ipadeskprofilerule']
    permission_filter_objectclasses = ['ipadeskprofilerule']
    default_attributes = [
        'cn',
        'ipaenabledflag',
        'ipadeskprofiletarget'
        'description',
        'usercategory',
        'hostcategory',
        'memberuser',
        'memberhost',
        'ipadeskprofilepriority',
        'seealso',
    ]
    uuid_attribute = 'ipauniqueid'
    rdn_is_primary_key = True
    attribute_members = {
        'memberuser': ['user', 'group'],
        'memberhost': ['host', 'hostgroup'],
    }
    managed_permissions = {
        'System: Read FleetCommander Desktop Profile Rule Map': {
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'cn',
                'description',
                'hostcategory',
                'ipaenabledflag',
                'ipauniqueid',
                'memberhost',
                'memberuser',
                'seealso',
                'usercategory',
                'objectclass',
                'member',
                'ipadeskprofilepriority',
                'ipadeskprofiletarget',
            },
        },
        'System: Add FleetCommander Desktop Profile Rule Map': {
            'ipapermbindruletype': 'permission',
            'ipapermright': {'add'},
            'default_privileges':
            {'FleetCommander Desktop Profile Administrators'},
        },
        'System: Modify FleetCommander Desktop Profile Rule Map': {
            'ipapermbindruletype': 'permission',
            'ipapermright': {'write'},
            'ipapermdefaultattr': {
                'cn',
                'ipaenabledflag',
                'memberhost',
                'memberuser',
                'seealso',
                'ipadeskprofilepriority',
                'ipadeskprofiletarget',
            },
            'default_privileges':
            {'FleetCommander Desktop Profile Administrators'},
        },
        'System: Remove FleetCommander Desktop Profile Rule Map': {
            'ipapermbindruletype': 'permission',
            'ipapermright': {'delete'},
            'default_privileges':
            {'FleetCommander Desktop Profile Administrators'},
        },
    }

    label = _('FleetCommander Desktop Profile Rule Map')
    label_singular = _('FleetCommander Desktop Profile Rule Map')

    takes_params = (
        Str(
            'cn',
            cli_name='name',
            label=_('Rule name'),
            primary_key=True,
        ),
        Str(
            'ipadeskprofiletarget',
            cli_name='profile',
            label=_('Desktop profile'),
            doc=_('Desktop profile associated with the rule'),
        ),
        Int(
            'ipadeskprofilepriority',
            cli_name='prio',
            label=_('Desktop profile priority'),
            minvalue=1,
            maxvalue=100000,
            doc=_('Priority for desktop profile associated with the rule'),
        ),
        Str(
            'seealso?',
            cli_name='hbacrule',
            label=_('HBAC Rule'),
            doc=_('HBAC Rule that defines the users, groups and hostgroups'),
        ),
        StrEnum(
            'usercategory?',
            cli_name='usercat',
            label=_('User category'),
            doc=_('User category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'hostcategory?',
            cli_name='hostcat',
            label=_('Host category'),
            doc=_('Host category the rule applies to'),
            values=(u'all', ),
        ),
        Str(
            'description?',
            cli_name='desc',
            label=_('Description'),
        ),
        Bool(
            'ipaenabledflag?',
            label=_('Enabled'),
            flags=['no_option'],
        ),
        Str(
            'memberuser_user?',
            label=_('Users'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberuser_group?',
            label=_('User Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_host?',
            label=_('Hosts'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_hostgroup?',
            label=_('Host Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
    )

    # Inject constants into the api.env before it is locked down
    def _on_finalize(self):
        self.env._merge(**dict(PLUGIN_CONFIG))
        self.container_dn = self.env.container_deskprofilerule
        super(deskprofilerule, self)._on_finalize()

    def _normalize_seealso(self, seealso):
        """
        Given a HBAC rule name verify its existence and return the dn.
        """
        if not seealso:
            return None

        try:
            dn = DN(seealso)
            return str(dn)
        except ValueError:
            try:
                entry_attrs = self.backend.find_entry_by_attr(
                    self.api.Object['hbacrule'].primary_key.name, seealso,
                    self.api.Object['hbacrule'].object_class, [''],
                    DN(self.api.Object['hbacrule'].container_dn,
                       api.env.basedn))
                seealso = entry_attrs.dn
            except errors.NotFound:
                raise errors.NotFound(
                    reason=_('HBAC rule %(rule)s not found') %
                    dict(rule=seealso))

        return seealso

    def _convert_seealso(self, ldap, entry_attrs, **options):
        """
        Convert an HBAC rule dn into a name
        """
        if options.get('raw', False):
            return

        if 'seealso' in entry_attrs:
            hbac_attrs = ldap.get_entry(entry_attrs['seealso'][0], ['cn'])
            entry_attrs['seealso'] = hbac_attrs['cn'][0]

    def _normalize_profile(self, profile):
        """
        Given a Desktop Profile name verify its existence and return the dn.
        """
        if not profile:
            return None

        try:
            dn = DN(profile)
            return str(dn)
        except ValueError:
            try:
                entry_attrs = self.backend.find_entry_by_attr(
                    self.api.Object['deskprofile'].primary_key.name, profile,
                    self.api.Object['deskprofile'].object_class, [''],
                    DN(self.api.Object['deskprofile'].container_dn,
                       api.env.basedn))
                return entry_attrs.dn
            except errors.NotFound:
                raise errors.NotFound(
                    reason=_('Desktop profile %(rule)s not found') %
                    dict(rule=profile))

    def _convert_profile(self, ldap, entry_attrs, **options):
        """
        Convert an Desktop Profile dn into a name
        """
        if options.get('raw', False):
            return

        if 'ipadeskprofiletarget' in entry_attrs:
            profile_attrs = ldap.get_entry(
                entry_attrs['ipadeskprofiletarget'][0], ['cn'])
            entry_attrs['ipadeskprofiletarget'] = profile_attrs['cn'][0]
Beispiel #5
0
class hbacrule(LDAPObject):
    """
    HBAC object.
    """
    container_dn = api.env.container_hbac
    object_name = _('HBAC rule')
    object_name_plural = _('HBAC rules')
    object_class = ['ipaassociation', 'ipahbacrule']
    permission_filter_objectclasses = ['ipahbacrule']
    default_attributes = [
        'cn',
        'ipaenabledflag',
        'description',
        'usercategory',
        'hostcategory',
        'servicecategory',
        'ipaenabledflag',
        'memberuser',
        'sourcehost',
        'memberhost',
        'memberservice',
        'externalhost',
    ]
    uuid_attribute = 'ipauniqueid'
    rdn_attribute = 'ipauniqueid'
    allow_rename = True
    attribute_members = {
        'memberuser': ['user', 'group'],
        'memberhost': ['host', 'hostgroup'],
        'sourcehost': ['host', 'hostgroup'],
        'memberservice': ['hbacsvc', 'hbacsvcgroup'],
    }
    managed_permissions = {
        'System: Read HBAC Rules': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'accessruletype',
                'accesstime',
                'cn',
                'description',
                'externalhost',
                'hostcategory',
                'ipaenabledflag',
                'ipauniqueid',
                'memberhost',
                'memberservice',
                'memberuser',
                'servicecategory',
                'sourcehost',
                'sourcehostcategory',
                'usercategory',
                'objectclass',
                'member',
            },
        },
        'System: Add HBAC Rule': {
            'ipapermright': {'add'},
            'replaces': [
                '(target = "ldap:///ipauniqueid=*,cn=hbac,$SUFFIX")(version 3.0;acl "permission:Add HBAC rule";allow (add) groupdn = "ldap:///cn=Add HBAC rule,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'HBAC Administrator'},
        },
        'System: Delete HBAC Rule': {
            'ipapermright': {'delete'},
            'replaces': [
                '(target = "ldap:///ipauniqueid=*,cn=hbac,$SUFFIX")(version 3.0;acl "permission:Delete HBAC rule";allow (delete) groupdn = "ldap:///cn=Delete HBAC rule,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'HBAC Administrator'},
        },
        'System: Manage HBAC Rule Membership': {
            'ipapermright': {'write'},
            'ipapermdefaultattr':
            {'externalhost', 'memberhost', 'memberservice', 'memberuser'},
            'replaces': [
                '(targetattr = "memberuser || externalhost || memberservice || memberhost")(target = "ldap:///ipauniqueid=*,cn=hbac,$SUFFIX")(version 3.0;acl "permission:Manage HBAC rule membership";allow (write) groupdn = "ldap:///cn=Manage HBAC rule membership,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'HBAC Administrator'},
        },
        'System: Modify HBAC Rule': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {
                'accessruletype', 'accesstime', 'cn', 'description',
                'hostcategory', 'ipaenabledflag', 'servicecategory',
                'sourcehost', 'sourcehostcategory', 'usercategory'
            },
            'replaces': [
                '(targetattr = "servicecategory || sourcehostcategory || cn || description || ipaenabledflag || accesstime || usercategory || hostcategory || accessruletype || sourcehost")(target = "ldap:///ipauniqueid=*,cn=hbac,$SUFFIX")(version 3.0;acl "permission:Modify HBAC rule";allow (write) groupdn = "ldap:///cn=Modify HBAC rule,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'HBAC Administrator'},
        },
    }

    label = _('HBAC Rules')
    label_singular = _('HBAC Rule')

    takes_params = (
        Str(
            'cn',
            cli_name='name',
            label=_('Rule name'),
            primary_key=True,
        ),
        StrEnum(
            'accessruletype',
            validate_type,
            cli_name='type',
            doc=_('Rule type (allow)'),
            label=_('Rule type'),
            values=(u'allow', u'deny'),
            default=u'allow',
            autofill=True,
            exclude='webui',
            flags=['no_option', 'no_output'],
        ),
        # FIXME: {user,host,service}categories should expand in the future
        StrEnum(
            'usercategory?',
            cli_name='usercat',
            label=_('User category'),
            doc=_('User category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'hostcategory?',
            cli_name='hostcat',
            label=_('Host category'),
            doc=_('Host category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'sourcehostcategory?',
            deprecated=True,
            cli_name='srchostcat',
            label=_('Source host category'),
            doc=_('Source host category the rule applies to'),
            values=(u'all', ),
            flags={'no_option'},
        ),
        StrEnum(
            'servicecategory?',
            cli_name='servicecat',
            label=_('Service category'),
            doc=_('Service category the rule applies to'),
            values=(u'all', ),
        ),
        #        AccessTime('accesstime?',
        #            cli_name='time',
        #            label=_('Access time'),
        #        ),
        Str(
            'description?',
            cli_name='desc',
            label=_('Description'),
        ),
        Bool(
            'ipaenabledflag?',
            label=_('Enabled'),
            flags=['no_option'],
        ),
        Str(
            'memberuser_user?',
            label=_('Users'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberuser_group?',
            label=_('User Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_host?',
            label=_('Hosts'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_hostgroup?',
            label=_('Host Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'sourcehost_host?',
            deprecated=True,
            label=_('Source Hosts'),
            flags=['no_create', 'no_update', 'no_search', 'no_option'],
        ),
        Str(
            'sourcehost_hostgroup?',
            deprecated=True,
            label=_('Source Host Groups'),
            flags=['no_create', 'no_update', 'no_search', 'no_option'],
        ),
        Str(
            'memberservice_hbacsvc?',
            label=_('Services'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberservice_hbacsvcgroup?',
            label=_('Service Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        external_host_param,
    )
Beispiel #6
0
class fasagreement(LDAPObject):
    """User Agreement object for FAS"""

    container_dn = DN(("cn", "fasagreements"))
    object_name = _("Agreement")
    object_name_plural = _("Agreements")
    object_class = ["ipaassociation", "fasagreement"]
    permission_filter_objectclasses = ["fasagreement"]
    default_attributes = [
        "cn",
        "description",
        "ipaenabledflag",
        "member",
        "memberuser",
    ]
    uuid_attribute = "ipauniqueid"
    attribute_members = {
        "memberuser": ["user"],
        "member": ["group"],
    }
    allow_rename = True
    managed_permissions = {
        "System: Read FAS Agreements": {
            "replaces_global_anonymous_aci": True,
            "ipapermbindruletype": "all",
            "ipapermright": {"read", "search", "compare"},
            "ipapermdefaultattr": {
                "objectclass",
                "cn",
                "description",
                "ipauniqueid",
                "ipaenabledflag",
                "member",
                "memberuser",
            },
        },
        "System: Add FAS Agreement": {
            "ipapermright": {"add"},
            "default_privileges": {"FAS Agreement Administrators"},
        },
        "System: Delete FAS Agreement": {
            "ipapermright": {"delete"},
            "default_privileges": {"FAS Agreement Administrators"},
        },
        "System: Manage FAS Agreement user membership": {
            "ipapermright": {"write"},
            "ipapermdefaultattr": {"memberUser"},
            "default_privileges": {"FAS Agreement Administrators"},
        },
        "System: Modify FAS Agreement": {
            "ipapermright": {"write"},
            "ipapermdefaultattr": {
                "cn",
                "description",
                "ipaenabledflag",
                "member",
            },
            "default_privileges": {"FAS Agreement Administrators"},
        },
    }

    label = _("User Agreements")
    label_singular = _("User Agreement")

    takes_params = (
        Str(
            "cn",
            cli_name="name",
            label=_("Agreement name"),
            primary_key=True,
        ),
        Str(
            "description?",
            cli_name="desc",
            label=_("Agreement Description"),
        ),
        Bool(
            "ipaenabledflag?",
            label=_("Enabled"),
            flags=["no_option"],
        ),
    )
Beispiel #7
0
class config(LDAPObject):
    """
    IPA configuration object
    """
    object_name = _('configuration options')
    default_attributes = [
        'ipamaxusernamelength', 'ipahomesrootdir', 'ipadefaultloginshell',
        'ipadefaultprimarygroup', 'ipadefaultemaildomain', 'ipasearchtimelimit',
        'ipasearchrecordslimit', 'ipausersearchfields', 'ipagroupsearchfields',
        'ipamigrationenabled', 'ipacertificatesubjectbase',
        'ipapwdexpadvnotify', 'ipaselinuxusermaporder',
        'ipaselinuxusermapdefault', 'ipaconfigstring', 'ipakrbauthzdata',
        'ipauserauthtype', 'ipadomainresolutionorder'
    ]
    container_dn = DN(('cn', 'ipaconfig'), ('cn', 'etc'))
    permission_filter_objectclasses = ['ipaguiconfig']
    managed_permissions = {
        'System: Read Global Configuration': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'cn', 'objectclass',
                'ipacertificatesubjectbase', 'ipaconfigstring',
                'ipadefaultemaildomain', 'ipadefaultloginshell',
                'ipadefaultprimarygroup', 'ipadomainresolutionorder',
                'ipagroupobjectclasses',
                'ipagroupsearchfields', 'ipahomesrootdir',
                'ipakrbauthzdata', 'ipamaxusernamelength',
                'ipamigrationenabled', 'ipapwdexpadvnotify',
                'ipaselinuxusermapdefault', 'ipaselinuxusermaporder',
                'ipasearchrecordslimit', 'ipasearchtimelimit',
                'ipauserauthtype', 'ipauserobjectclasses',
                'ipausersearchfields', 'ipacustomfields',
            },
        },
    }

    label = _('Configuration')
    label_singular = _('Configuration')

    takes_params = (
        Int('ipamaxusernamelength',
            cli_name='maxusername',
            label=_('Maximum username length'),
            minvalue=1,
            maxvalue=255,
        ),
        IA5Str('ipahomesrootdir',
            cli_name='homedirectory',
            label=_('Home directory base'),
            doc=_('Default location of home directories'),
        ),
        Str('ipadefaultloginshell',
            cli_name='defaultshell',
            label=_('Default shell'),
            doc=_('Default shell for new users'),
        ),
        Str('ipadefaultprimarygroup',
            cli_name='defaultgroup',
            label=_('Default users group'),
            doc=_('Default group for new users'),
        ),
        Str('ipadefaultemaildomain?',
            cli_name='emaildomain',
            label=_('Default e-mail domain'),
            doc=_('Default e-mail domain'),
        ),
        Int('ipasearchtimelimit',
            cli_name='searchtimelimit',
            label=_('Search time limit'),
            doc=_('Maximum amount of time (seconds) for a search (-1 or 0 is unlimited)'),
            minvalue=-1,
        ),
        Int('ipasearchrecordslimit',
            cli_name='searchrecordslimit',
            label=_('Search size limit'),
            doc=_('Maximum number of records to search (-1 or 0 is unlimited)'),
            minvalue=-1,
        ),
        IA5Str('ipausersearchfields',
            cli_name='usersearch',
            label=_('User search fields'),
            doc=_('A comma-separated list of fields to search in when searching for users'),
        ),
        IA5Str('ipagroupsearchfields',
            cli_name='groupsearch',
            label=_('Group search fields'),
            doc=_('A comma-separated list of fields to search in when searching for groups'),
        ),
        Bool('ipamigrationenabled',
            cli_name='enable_migration',
            label=_('Enable migration mode'),
            doc=_('Enable migration mode'),
        ),
        DNParam('ipacertificatesubjectbase',
            cli_name='subject',
            label=_('Certificate Subject base'),
            doc=_('Base for certificate subjects (OU=Test,O=Example)'),
            flags=['no_update'],
        ),
        Str('ipagroupobjectclasses+',
            cli_name='groupobjectclasses',
            label=_('Default group objectclasses'),
            doc=_('Default group objectclasses (comma-separated list)'),
        ),
        Str('ipauserobjectclasses+',
            cli_name='userobjectclasses',
            label=_('Default user objectclasses'),
            doc=_('Default user objectclasses (comma-separated list)'),
        ),
        Int('ipapwdexpadvnotify',
            cli_name='pwdexpnotify',
            label=_('Password Expiration Notification (days)'),
            doc=_('Number of days\'s notice of impending password expiration'),
            minvalue=0,
        ),
        StrEnum('ipaconfigstring*',
            cli_name='ipaconfigstring',
            label=_('Password plugin features'),
            doc=_('Extra hashes to generate in password plug-in'),
            values=(u'AllowNThash',
                    u'KDC:Disable Last Success', u'KDC:Disable Lockout',
                    u'KDC:Disable Default Preauth for SPNs'),
        ),
        Str('ipaselinuxusermaporder',
            label=_('SELinux user map order'),
            doc=_('Order in increasing priority of SELinux users, delimited by $'),
        ),
        Str('ipaselinuxusermapdefault?',
            label=_('Default SELinux user'),
            doc=_('Default SELinux user when no match is found in SELinux map rule'),
        ),
        StrEnum('ipakrbauthzdata*',
            cli_name='pac_type',
            label=_('Default PAC types'),
            doc=_('Default types of PAC supported for services'),
            values=(u'MS-PAC', u'PAD', u'nfs:NONE'),
        ),
        StrEnum('ipauserauthtype*',
            cli_name='user_auth_type',
            label=_('Default user authentication types'),
            doc=_('Default types of supported user authentication'),
            values=(u'password', u'radius', u'otp', u'disabled'),
        ),
        Str(
            'ipa_master_server*',
            label=_('IPA masters'),
            doc=_('List of all IPA masters'),
            flags={'virtual_attribute', 'no_create', 'no_update'}
        ),
        Str(
            'ca_server_server*',
            label=_('IPA CA servers'),
            doc=_('IPA servers configured as certificate authority'),
            flags={'virtual_attribute', 'no_create', 'no_update'}
        ),
        Str(
            'ntp_server_server*',
            label=_('IPA NTP servers'),
            doc=_('IPA servers with enabled NTP'),
            flags={'virtual_attribute', 'no_create', 'no_update'}
        ),
        Str(
            'ca_renewal_master_server?',
            label=_('IPA CA renewal master'),
            doc=_('Renewal master for IPA certificate authority'),
            flags={'virtual_attribute', 'no_create'}
        ),
        Str(
            'pkinit_server_server*',
            label=_('IPA master capable of PKINIT'),
            doc=_('IPA master which can process PKINIT requests'),
            flags={'virtual_attribute', 'no_create', 'no_update'}
        ),
        Str(
            'ipadomainresolutionorder?',
            cli_name='domain_resolution_order',
            label=_('Domain resolution order'),
            doc=_('colon-separated list of domains used for short name'
                  ' qualification')
        )
    )

    def get_dn(self, *keys, **kwargs):
        return DN(('cn', 'ipaconfig'), ('cn', 'etc'), api.env.basedn)

    def update_entry_with_role_config(self, role_name, entry_attrs):
        backend = self.api.Backend.serverroles

        try:
            role_config = backend.config_retrieve(role_name)
        except errors.EmptyResult:
            # No role config means current user identity
            # has no rights to see it, return with no action
            return

        for key, value in role_config.items():
            try:
                entry_attrs.update({key: value})
            except errors.EmptyResult:
                # An update that doesn't change an entry is fine here
                # Just ignore and move to the next key pair
                pass


    def show_servroles_attributes(self, entry_attrs, *roles, **options):
        if options.get('raw', False):
            return

        for role in roles:
            self.update_entry_with_role_config(role, entry_attrs)

    def gather_trusted_domains(self):
        """
        Aggregate all trusted domains into a dict keyed by domain names with
        values corresponding to domain status (enabled/disabled)
        """
        command = self.api.Command
        try:
            ad_forests = command.trust_find(sizelimit=0)['result']
        except errors.NotFound:
            return {}

        trusted_domains = {}
        for forest_name in [a['cn'][0] for a in ad_forests]:
            forest_domains = command.trustdomain_find(
                forest_name, sizelimit=0)['result']

            trusted_domains.update(
                {
                    dom['cn'][0]: dom['domain_enabled'][0]
                    for dom in forest_domains if 'domain_enabled' in dom
                }
            )

        return trusted_domains

    def _validate_single_domain(self, attr_name, domain, known_domains):
        """
        Validate a single domain from domain resolution order

        :param attr_name: name of attribute that holds domain resolution order
        :param domain: domain name
        :param known_domains: dict of domains known to IPA keyed by domain name
            and valued by boolean value corresponding to domain status
            (enabled/disabled)

        :raises: ValidationError if the domain name is empty, syntactically
            invalid or corresponds to a disable domain
                 NotFound if a syntactically correct domain name unknown to IPA
                 is supplied (not IPA domain and not any of trusted domains)
        """
        if not domain:
            raise errors.ValidationError(
                name=attr_name,
                error=_("Empty domain is not allowed")
            )

        try:
            validate_domain_name(domain)
        except ValueError as e:
            raise errors.ValidationError(
                name=attr_name,
                error=_("Invalid domain name '%(domain)s': %(e)s")
                % dict(domain=domain, e=e))

        if domain not in known_domains:
            raise errors.NotFound(
                reason=_("Server has no information about domain '%(domain)s'")
                % dict(domain=domain)
            )

        if not known_domains[domain]:
            raise errors.ValidationError(
                name=attr_name,
                error=_("Disabled domain '%(domain)s' is not allowed")
                % dict(domain=domain)
            )

    def validate_domain_resolution_order(self, entry_attrs):
        """
        Validate domain resolution order, e.g. split by the delimiter (colon)
        and check each domain name for non-emptiness, syntactic correctness,
        and status (enabled/disabled).

        supplying empty order (':') bypasses validations and allows to specify
        empty attribute value.
        """
        attr_name = 'ipadomainresolutionorder'
        if attr_name not in entry_attrs:
            return

        domain_resolution_order = entry_attrs[attr_name]

        # setting up an empty string means that the previous configuration has
        # to be cleaned up/removed. So, do nothing and let it pass
        if not domain_resolution_order:
            return

        # empty resolution order is signalized by single separator, do nothing
        # and let it pass
        if domain_resolution_order == DOMAIN_RESOLUTION_ORDER_SEPARATOR:
            return

        submitted_domains = domain_resolution_order.split(
                DOMAIN_RESOLUTION_ORDER_SEPARATOR)

        known_domains = self.gather_trusted_domains()

        # add FreeIPA domain to the list of domains. This one is always enabled
        known_domains.update({self.api.env.domain: True})

        for domain in submitted_domains:
            self._validate_single_domain(attr_name, domain, known_domains)
Beispiel #8
0
class stageuser_add(baseuser_add):
    __doc__ = _('Add a new stage user.')

    msg_summary = _('Added stage user "%(value)s"')

    has_output_params = baseuser_add.has_output_params + stageuser_output_params

    takes_options = LDAPCreate.takes_options + (Bool(
        'from_delete?',
        deprecated=True,
        doc=_('Create Stage user in from a delete user'),
        cli_name='from_delete',
        flags={'no_option'},
    ), )

    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys,
                     **options):
        assert isinstance(dn, DN)

        # then givenname and sn are required attributes
        if 'givenname' not in entry_attrs:
            raise errors.RequirementError(name='givenname',
                                          error=_('givenname is required'))

        if 'sn' not in entry_attrs:
            raise errors.RequirementError(name='sn', error=_('sn is required'))

        # we don't want an user private group to be created for this user
        # add NO_UPG_MAGIC description attribute to let the DS plugin know
        entry_attrs.setdefault('description', [])
        entry_attrs['description'].append(NO_UPG_MAGIC)

        # uidNumber/gidNumber
        entry_attrs.setdefault('uidnumber', baseldap.DNA_MAGIC)
        entry_attrs.setdefault('gidnumber', baseldap.DNA_MAGIC)

        if not client_has_capability(options['version'],
                                     'optional_uid_params'):
            # https://fedorahosted.org/freeipa/ticket/2886
            # Old clients say 999 (OLD_DNA_MAGIC) when they really mean
            # "assign a value dynamically".
            OLD_DNA_MAGIC = 999
            if entry_attrs.get('uidnumber') == OLD_DNA_MAGIC:
                entry_attrs['uidnumber'] = baseldap.DNA_MAGIC
            if entry_attrs.get('gidnumber') == OLD_DNA_MAGIC:
                entry_attrs['gidnumber'] = baseldap.DNA_MAGIC

        # Check the lenght of the RDN (uid) value
        config = ldap.get_ipa_config()
        if 'ipamaxusernamelength' in config:
            if len(keys[-1]) > int(config.get('ipamaxusernamelength')[0]):
                raise errors.ValidationError(
                    name=self.obj.primary_key.cli_name,
                    error=_('can be at most %(len)d characters') %
                    dict(len=int(config.get('ipamaxusernamelength')[0])))
        default_shell = config.get('ipadefaultloginshell',
                                   [platformconstants.DEFAULT_SHELL])[0]
        entry_attrs.setdefault('loginshell', default_shell)
        # hack so we can request separate first and last name in CLI
        full_name = '%s %s' % (entry_attrs['givenname'], entry_attrs['sn'])
        entry_attrs.setdefault('cn', full_name)

        # Homedirectory
        # (order is : option, placeholder (TBD), CLI default value (here in config))
        if 'homedirectory' not in entry_attrs:
            # get home's root directory from config
            homes_root = config.get('ipahomesrootdir', [paths.HOME_DIR])[0]
            # build user's home directory based on his uid
            entry_attrs['homedirectory'] = posixpath.join(homes_root, keys[-1])

        # Kerberos principal
        entry_attrs.setdefault('krbprincipalname',
                               '%s@%s' % (entry_attrs['uid'], api.env.realm))

        # If requested, generate a userpassword
        if 'userpassword' not in entry_attrs and options.get('random'):
            entry_attrs['userpassword'] = ipa_generate_password(
                entropy_bits=TMP_PWD_ENTROPY_BITS)
            # save the password so it can be displayed in post_callback
            setattr(context, 'randompassword', entry_attrs['userpassword'])

        # Check the email or create it
        if 'mail' in entry_attrs:
            entry_attrs['mail'] = self.obj.normalize_and_validate_email(
                entry_attrs['mail'], config)
        else:
            # No e-mail passed in. If we have a default e-mail domain set
            # then we'll add it automatically.
            defaultdomain = config.get('ipadefaultemaildomain', [None])[0]
            if defaultdomain:
                entry_attrs['mail'] = self.obj.normalize_and_validate_email(
                    keys[-1], config)

        # If the manager is defined, check it is a ACTIVE user to validate it
        if 'manager' in entry_attrs:
            entry_attrs['manager'] = self.obj.normalize_manager(
                entry_attrs['manager'], self.obj.active_container_dn)

        if ('objectclass' in entry_attrs and 'userclass' in entry_attrs
                and 'ipauser' not in entry_attrs['objectclass']):
            entry_attrs['objectclass'].append('ipauser')

        if 'ipatokenradiusconfiglink' in entry_attrs:
            cl = entry_attrs['ipatokenradiusconfiglink']
            if cl:
                if 'objectclass' not in entry_attrs:
                    _entry = ldap.get_entry(dn, ['objectclass'])
                    entry_attrs['objectclass'] = _entry['objectclass']

                if 'ipatokenradiusproxyuser' not in entry_attrs['objectclass']:
                    entry_attrs['objectclass'].append(
                        'ipatokenradiusproxyuser')

                answer = self.api.Object['radiusproxy'].get_dn_if_exists(cl)
                entry_attrs['ipatokenradiusconfiglink'] = answer

        self.pre_common_callback(ldap, dn, entry_attrs, attrs_list, *keys,
                                 **options)

        return dn

    def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
        assert isinstance(dn, DN)

        # Fetch the entry again to update memberof, mep data, etc updated
        # at the end of the transaction.
        newentry = ldap.get_entry(dn, ['*'])
        entry_attrs.update(newentry)

        if options.get('random', False):
            try:
                entry_attrs['randompassword'] = unicode(
                    getattr(context, 'randompassword'))
            except AttributeError:
                # if both randompassword and userpassword options were used
                pass

        self.post_common_callback(ldap, dn, entry_attrs, *keys, **options)
        return dn
Beispiel #9
0
    ),
    Str('ipaallowedtoperform_write_keys_hostgroup',
        label=_('Host Groups allowed to create keytab'),
    ),
    Str('ipaallowedtoperform_read_keys',
        label=_('Failed allowed to retrieve keytab'),
    ),
    Str('ipaallowedtoperform_write_keys',
        label=_('Failed allowed to create keytab'),
    ),
)

ticket_flags_params = (
    Bool('ipakrbrequirespreauth?',
        cli_name='requires_pre_auth',
        label=_('Requires pre-authentication'),
        doc=_('Pre-authentication is required for the service'),
        flags=['virtual_attribute', 'no_search'],
    ),
    Bool('ipakrbokasdelegate?',
        cli_name='ok_as_delegate',
        label=_('Trusted for delegation'),
        doc=_('Client credentials may be delegated to the service'),
        flags=['virtual_attribute', 'no_search'],
    ),
    Bool('ipakrboktoauthasdelegate?',
        cli_name='ok_to_auth_as_delegate',
        label=_('Trusted to authenticate as user'),
        doc=_('The service is allowed to authenticate on behalf of a client'),
        flags=['virtual_attribute', 'no_search'],
    ),
)
Beispiel #10
0
class aii_find(Command):  # class name has to be lowercase
    """aii_find command"""

    takes_options = (
        Bool(
            'detail',
            default=False,
            required=False,
            autofill=True,
            doc='Show details',
        ),
        Bool(
            'all',
            default=False,
            required=False,
            autofill=True,
            doc='Use --all option (implies detail)',
        ),
        Bool(
            'raw',
            default=False,
            required=False,
            autofill=True,
            doc='Use --all --raw  option (implies detail)',
        ),
        Str(
            'hostname',
            default=None,
            required=False,
            autofill=True,
            doc='Check this host (ignores hostregex)',
        ),
        Str(
            'hostregex',
            default=None,
            required=False,
            autofill=True,
            doc='Show host(s) matching this regex (might be slow)',
        ),
    )

    def run(self, **options):
        """
        Show all hosts with no keytab, filtered with hostregex

        Implemented as frontend command (ie no forward/execute)
        """
        opts = {}
        opts['raw'] = options.get('raw', False)
        opts['all'] = options.get('all', False) or opts['raw']
        self.log.debug('Options all %s raw %s' % (opts['all'], opts['raw']))

        reg = None
        if 'hostname' in options:
            opts['fqdn'] = options['hostname']
            self.log.debug('Set hostname %s' % opts['fqdn'])
        elif 'hostregex' in options:
            reg = re.compile(r'' + options.get('hostregex'))
            self.log.debug('Using regexp pattern %s' % reg.pattern)

        found = self.Command.host_find(**opts)

        res = {'fqdns': [], 'details': {}}
        detail = options.get('detail',
                             False) or opts['all']  # already deals with raw
        if 'result' in found and len(found['result']):
            for host in found['result']:
                fqdns = host.pop('fqdn')  # this is a tuple!
                self.log.debug('host fqdns found %s ' % (fqdns))
                for fqdn in fqdns:
                    if (reg is not None) and (not reg.search(fqdn)):
                        continue
                    res['fqdns'].append(fqdn)
                    if detail:
                        res['details'][fqdn] = host
        else:
            self.log.debug('No results from host_find')

        # sort the hostnames before returning them
        res['fqdns'].sort()

        return dict(result=res)

    def output_for_cli(self, textui, result, **options):
        detail = options.get('detail', False) or options.get(
            'all', False) or options.get('raw', False)

        fqdns = result['result']['fqdns']
        if detail:
            # print per host details
            for fqdn in fqdns:
                textui.print_plain("Hostname %s" % (fqdn))
                details = result['result']['details'][fqdn].items()
                details.sort(key=lambda x: x[0])
                textui.print_keyval(details)
        else:
            # print list of hostnames
            textui.print_plain(" ".join(fqdns))
Beispiel #11
0
class aii(Command):  # class name has to be lowercase
    """aii command"""
    takes_args = ('shorthostname', 'domain')

    takes_options = (
        Bool(
            'install',
            default=False,
            required=False,
            autofill=True,
            doc='Prepare for installation, returns random OTP',
        ),
        Bool(
            'disable',
            default=False,
            required=False,
            autofill=True,
            doc='Disable the host in IPA',
        ),
        Str('ip',
            default=None,
            required=False,
            autofill=True,
            doc=
            'Set the IP (implies DNS configuration; don\'t use it if DNS is not enabled/wanted)'
            ),
    )

    def __init__(self, *args, **kwargs):
        """Customise the __init__ a bit"""
        super(aii, self).__init__(*args, **kwargs)
        self.fqdns = None

    def find_fqdns(self):
        """Update list of hosts"""
        res = self.Command.host_find()
        self.fqdns = {}
        for host in res['result']:
            for fqdn in host['fqdn']:
                self.fqdns[fqdn] = host
        self.log.debug('Found fqdns %s' % self.fqdns)
        self.log.debug('Found fqdns %s' % ', '.join(self.fqdns.keys()))
        return self.fqdns

    def host_in_ipa(self, hostname, force_fqdn=False):
        """Check if hostname is known in IPA"""
        if self.fqdns is None or force_fqdn:
            self.find_fqdns()
        res = hostname in self.fqdns
        if res:
            host = self.fqdns[hostname]
            self.log.debug(
                'host %s found in IPA (has_password %s; has_keytab %s)' %
                (hostname, host['has_password'], host['has_keytab']))
        else:
            self.log.debug('host %s  NOT found in IPA' % (hostname))
        return res

    def disable_host(self, hostname):
        """Disable the host (removes keytab)"""
        res = {}
        if self.host_in_ipa(hostname):
            try:
                disable = api.Command.host_disable(hostname)
                res['disable'] = disable['result']
                self.log.debug('host_disable on %s OK.' % hostname)
            except AlreadyInactive:
                self.log.debug('Host %s already inactive.' % hostname)
        else:
            self.log.debug('No need to disable unknown host %s.' % s)

        self.log.info('Host %s disabled.' % hostname)
        return res

    def aii_install(self, hostname):
        """Take action to allow proper installation"""
        res = {}
        do_add = True
        if self.host_in_ipa(hostname):
            host = self.fqdns[hostname]
            if host['has_keytab']:
                self.log.error(
                    'Can\'t install host %s, already in IPA (disable first?)' %
                    hostname)
                raise AlreadyActive
            else:
                self.log.debug('Host %s in IPA, but no keytab' % hostname)
                do_add = False

        if do_add:
            self.log.debug('host_add %s' % hostname)
            added = api.Command.host_add(hostname)
            res['add'] = added['result']

        # modify to set random password
        self.log.debug('host_mod %s random password' % hostname)
        # do not print/log res, it contains a password
        modified = api.Command.host_mod(hostname, random=True)
        res['modify'] = modified['result']

        return res

    def run(self, shorthostname, domain, **options):
        """
        Implemented as frontend command (ie no forward/execute)
        """
        hostname = unicode("%s.%s" % (shorthostname, domain))
        self.log.debug('AII called with hostname %s (options %s)' %
                       (hostname, options))

        ip = options.get('ip', None)

        res = {}
        # first try to disable (e.g. in case --install=1 --disable=1 is passed)
        if options.get('disable', False):
            self.log.debug('Going to disable')
            res.update(self.disable_host(hostname))

        # check for install
        if options.get('install', False):
            self.log.debug('Going to install')
            if ip is not None:
                self.log.debug('Adding ip %s for hostname %s' % (ip, hostname))
                add_records_for_host(shorthostname, domain, [ip])
            # do not print/log res, it contains a password
            res.update(self.aii_install(hostname))

        # always return like this
        return dict(result=res)

    def output_for_cli(self, textui, result, shorthostname, domain, **options):
        if options.get('install', False) and 'modify' in result[
                'result'] and 'randompassword' in result['result']['modify']:
            # use pop to remove it (eg in case we use it for logging)
            textui.print_plain(
                'randompassword = %s' %
                (result['result']['modify'].pop('randompassword')))
        textui.print_plain('%s.%s = %r (options %s)' %
                           (shorthostname, domain, result, options))
Beispiel #12
0
class pwpolicy(LDAPObject):
    """
    Password Policy object
    """
    container_dn = DN(('cn', api.env.realm), ('cn', 'kerberos'))
    object_name = _('password policy')
    object_name_plural = _('password policies')
    object_class = ['top', 'nscontainer', 'krbpwdpolicy', 'ipapwdpolicy']
    permission_filter_objectclasses = ['krbpwdpolicy', 'ipapwdpolicy']
    default_attributes = [
        'cn',
        'cospriority',
        'krbmaxpwdlife',
        'krbminpwdlife',
        'krbpwdhistorylength',
        'krbpwdmindiffchars',
        'krbpwdminlength',
        'krbpwdmaxfailure',
        'krbpwdfailurecountinterval',
        'krbpwdlockoutduration',
        'ipapwdmaxrepeat',
        'ipapwdmaxsequence',
        'ipapwddictcheck',
        'ipapwdusercheck',
    ]
    managed_permissions = {
        'System: Read Group Password Policy': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'permission',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'cn',
                'cospriority',
                'krbmaxpwdlife',
                'krbminpwdlife',
                'krbpwdfailurecountinterval',
                'krbpwdhistorylength',
                'krbpwdlockoutduration',
                'krbpwdmaxfailure',
                'krbpwdmindiffchars',
                'krbpwdminlength',
                'objectclass',
                'ipapwdmaxrepeat',
                'ipapwdmaxsequence',
                'ipapwddictcheck',
                'ipapwdusercheck',
            },
            'default_privileges': {
                'Password Policy Readers',
                'Password Policy Administrator',
            },
        },
        'System: Add Group Password Policy': {
            'ipapermright': {'add'},
            'replaces': [
                '(target = "ldap:///cn=*,cn=$REALM,cn=kerberos,$SUFFIX")(version 3.0;acl "permission:Add Group Password Policy";allow (add) groupdn = "ldap:///cn=Add Group Password Policy,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'Password Policy Administrator'},
        },
        'System: Delete Group Password Policy': {
            'ipapermright': {'delete'},
            'replaces': [
                '(target = "ldap:///cn=*,cn=$REALM,cn=kerberos,$SUFFIX")(version 3.0;acl "permission:Delete Group Password Policy";allow (delete) groupdn = "ldap:///cn=Delete Group Password Policy,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'Password Policy Administrator'},
        },
        'System: Modify Group Password Policy': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {
                'krbmaxpwdlife',
                'krbminpwdlife',
                'krbpwdfailurecountinterval',
                'krbpwdhistorylength',
                'krbpwdlockoutduration',
                'krbpwdmaxfailure',
                'krbpwdmindiffchars',
                'krbpwdminlength',
                'ipapwdmaxrepeat',
                'ipapwdmaxsequence',
                'ipapwddictcheck',
                'ipapwdusercheck',
            },
            'replaces': [
                '(targetattr = "krbmaxpwdlife || krbminpwdlife || krbpwdhistorylength || krbpwdmindiffchars || krbpwdminlength || krbpwdmaxfailure || krbpwdfailurecountinterval || krbpwdlockoutduration")(target = "ldap:///cn=*,cn=$REALM,cn=kerberos,$SUFFIX")(version 3.0;acl "permission:Modify Group Password Policy";allow (write) groupdn = "ldap:///cn=Modify Group Password Policy,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'Password Policy Administrator'},
        },
    }

    label = _('Password Policies')
    label_singular = _('Password Policy')

    takes_params = (
        Str(
            'cn?',
            cli_name='group',
            label=_('Group'),
            doc=_('Manage password policy for specific group'),
            primary_key=True,
        ),
        Int(
            'krbmaxpwdlife?',
            cli_name='maxlife',
            label=_('Max lifetime (days)'),
            doc=_('Maximum password lifetime (in days)'),
            minvalue=0,
            maxvalue=20000,  # a little over 54 years
        ),
        Int(
            'krbminpwdlife?',
            cli_name='minlife',
            label=_('Min lifetime (hours)'),
            doc=_('Minimum password lifetime (in hours)'),
            minvalue=0,
        ),
        Int(
            'krbpwdhistorylength?',
            cli_name='history',
            label=_('History size'),
            doc=_('Password history size'),
            minvalue=0,
        ),
        Int(
            'krbpwdmindiffchars?',
            cli_name='minclasses',
            label=_('Character classes'),
            doc=_('Minimum number of character classes'),
            minvalue=0,
            maxvalue=5,
        ),
        Int(
            'krbpwdminlength?',
            cli_name='minlength',
            label=_('Min length'),
            doc=_('Minimum length of password'),
            minvalue=0,
        ),
        Int(
            'cospriority',
            cli_name='priority',
            label=_('Priority'),
            doc=_(
                'Priority of the policy (higher number means lower priority'),
            minvalue=0,
            flags=('virtual_attribute', ),
        ),
        Int(
            'krbpwdmaxfailure?',
            cli_name='maxfail',
            label=_('Max failures'),
            doc=_('Consecutive failures before lockout'),
            minvalue=0,
        ),
        Int(
            'krbpwdfailurecountinterval?',
            cli_name='failinterval',
            label=_('Failure reset interval'),
            doc=_('Period after which failure count will be reset (seconds)'),
            minvalue=0,
        ),
        Int(
            'krbpwdlockoutduration?',
            cli_name='lockouttime',
            label=_('Lockout duration'),
            doc=_('Period for which lockout is enforced (seconds)'),
            minvalue=0,
        ),
        Int(
            'ipapwdmaxrepeat?',
            cli_name='maxrepeat',
            label=_('Max repeat'),
            doc=_('Maximum number of same consecutive characters'),
            minvalue=0,
            maxvalue=256,
            default=0,
        ),
        Int(
            'ipapwdmaxsequence?',
            cli_name='maxsequence',
            label=_('Max sequence'),
            doc=_('The max. length of monotonic character sequences (abcd)'),
            minvalue=0,
            maxvalue=256,
            default=0,
        ),
        Bool(
            'ipapwddictcheck?',
            cli_name='dictcheck',
            label=_('Dictionary check'),
            doc=_('Check if the password is a dictionary word'),
            default=False,
        ),
        Bool(
            'ipapwdusercheck?',
            cli_name='usercheck',
            label=_('User check'),
            doc=_('Check if the password contains the username'),
            default=False,
        ),
    )

    def get_dn(self, *keys, **options):
        if keys[-1] is not None:
            return self.backend.make_dn_from_attr(
                self.primary_key.name, keys[-1],
                DN(self.container_dn, api.env.basedn))
        return global_policy_dn

    def convert_time_for_output(self, entry_attrs, **options):
        # Convert seconds to hours and days for displaying to user
        if not options.get('raw', False):
            if 'krbmaxpwdlife' in entry_attrs:
                entry_attrs['krbmaxpwdlife'][0] = unicode(
                    int(entry_attrs['krbmaxpwdlife'][0]) // 86400)
            if 'krbminpwdlife' in entry_attrs:
                entry_attrs['krbminpwdlife'][0] = unicode(
                    int(entry_attrs['krbminpwdlife'][0]) // 3600)

    def convert_time_on_input(self, entry_attrs):
        # Convert hours and days to seconds for writing to LDAP
        if 'krbmaxpwdlife' in entry_attrs and entry_attrs['krbmaxpwdlife']:
            entry_attrs['krbmaxpwdlife'] = entry_attrs['krbmaxpwdlife'] * 86400
        if 'krbminpwdlife' in entry_attrs and entry_attrs['krbminpwdlife']:
            entry_attrs['krbminpwdlife'] = entry_attrs['krbminpwdlife'] * 3600

    def validate_minlength(self, ldap, entry_attrs, add=False, *keys):
        """
        If any of the libpwquality options are used then the minimum
        length must be >= 6 which is the built-in default of libpwquality.
        Allowing a lower value to be set will result in a failed policy
        check and a generic error message.
        """
        def get_val(entry, attr):
            """Get a single value from a list or a string"""
            val = entry.get(attr, 0)
            if isinstance(val, list):
                val = val[0]
            return val

        def has_pwquality_set(entry):
            for attr in [
                    'ipapwdmaxrepeat', 'ipapwdmaxsequence', 'ipapwddictcheck',
                    'ipapwdusercheck'
            ]:
                val = get_val(entry, attr)
                if val not in ('FALSE', '0', 0, None):
                    return True
            return False

        has_pwquality_value = False
        if not add:
            if len(keys) > 0:
                existing_entry = self.api.Command.pwpolicy_show(
                    keys[-1],
                    all=True,
                )['result']
            else:
                existing_entry = self.api.Command.pwpolicy_show(
                    all=True, )['result']
            existing_entry.update(entry_attrs)
            min_length = int(get_val(existing_entry, 'krbpwdminlength'))

            has_pwquality_value = has_pwquality_set(existing_entry)
        else:
            min_length = int(get_val(entry_attrs, 'krbpwdminlength'))
            has_pwquality_value = has_pwquality_set(entry_attrs)

        if min_length and min_length < 6 and has_pwquality_value:
            raise errors.ValidationError(
                name='minlength',
                error=_('Minimum length must be >= 6 if maxrepeat, '
                        'maxsequence, dictcheck or usercheck are defined'))

    def validate_lifetime(self, entry_attrs, add=False, *keys):
        """
        Ensure that the maximum lifetime is greater than the minimum.
        If there is no minimum lifetime set then don't return an error.
        """
        maxlife = entry_attrs.get('krbmaxpwdlife', None)
        minlife = entry_attrs.get('krbminpwdlife', None)
        existing_entry = {}
        if not add:  # then read existing entry
            existing_entry = self.api.Command.pwpolicy_show(
                keys[-1],
                all=True,
            )['result']
            if minlife is None and 'krbminpwdlife' in existing_entry:
                minlife = int(existing_entry['krbminpwdlife'][0]) * 3600
            if maxlife is None and 'krbmaxpwdlife' in existing_entry:
                maxlife = int(existing_entry['krbmaxpwdlife'][0]) * 86400

        if maxlife not in (None, 0) and minlife is not None:
            if minlife > maxlife:
                raise errors.ValidationError(
                    name='maxlife',
                    error=_("Maximum password life must be equal to "
                            "or greater than the minimum."),
                )

    def add_cospriority(self, entry, pwpolicy_name, rights=True):
        if pwpolicy_name and pwpolicy_name != global_policy_name:
            cos_entry = self.api.Command.cosentry_show(pwpolicy_name,
                                                       rights=rights,
                                                       all=rights)['result']
            if cos_entry.get('cospriority') is not None:
                entry['cospriority'] = cos_entry['cospriority']
                if rights:
                    entry['attributelevelrights']['cospriority'] = \
                        cos_entry['attributelevelrights']['cospriority']
Beispiel #13
0
class hbacrule(LDAPObject):
    """
    HBAC object.
    """
    container_dn = api.env.container_hbac
    object_name = _('HBAC rule')
    object_name_plural = _('HBAC rules')
    object_class = ['ipaassociation', 'ipahbacrule']
    default_attributes = [
        'cn',
        'ipaenabledflag',
        'description',
        'usercategory',
        'hostcategory',
        'sourcehostcategory',
        'servicecategory',
        'ipaenabledflag',
        'memberuser',
        'sourcehost',
        'memberhost',
        'memberservice',
        'memberhostgroup',
        'externalhost',
    ]
    uuid_attribute = 'ipauniqueid'
    rdn_attribute = 'ipauniqueid'
    attribute_members = {
        'memberuser': ['user', 'group'],
        'memberhost': ['host', 'hostgroup'],
        'sourcehost': ['host', 'hostgroup'],
        'memberservice': ['hbacsvc', 'hbacsvcgroup'],
    }

    label = _('HBAC Rules')
    label_singular = _('HBAC Rule')

    takes_params = (
        Str(
            'cn',
            cli_name='name',
            label=_('Rule name'),
            primary_key=True,
        ),
        StrEnum(
            'accessruletype',
            validate_type,
            cli_name='type',
            doc=_('Rule type (allow)'),
            label=_('Rule type'),
            values=(u'allow', u'deny'),
            default=u'allow',
            autofill=True,
            exclude='webui',
            flags=['no_option', 'no_output'],
        ),
        # FIXME: {user,host,service}categories should expand in the future
        StrEnum(
            'usercategory?',
            cli_name='usercat',
            label=_('User category'),
            doc=_('User category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'hostcategory?',
            cli_name='hostcat',
            label=_('Host category'),
            doc=_('Host category the rule applies to'),
            values=(u'all', ),
        ),
        DeprecatedParam('sourcehostcategory?'),
        StrEnum(
            'servicecategory?',
            cli_name='servicecat',
            label=_('Service category'),
            doc=_('Service category the rule applies to'),
            values=(u'all', ),
        ),
        #        AccessTime('accesstime?',
        #            cli_name='time',
        #            label=_('Access time'),
        #        ),
        Str(
            'description?',
            cli_name='desc',
            label=_('Description'),
        ),
        Bool(
            'ipaenabledflag?',
            label=_('Enabled'),
            flags=['no_option'],
        ),
        Str(
            'memberuser_user?',
            label=_('Users'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberuser_group?',
            label=_('User Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_host?',
            label=_('Hosts'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_hostgroup?',
            label=_('Host Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        DeprecatedParam('sourcehost_host?'),
        DeprecatedParam('sourcehost_hostgroup?'),
        Str(
            'memberservice_hbacsvc?',
            label=_('Services'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberservice_hbacsvcgroup?',
            label=_('Service Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        external_host_param,
    )
Beispiel #14
0
class certprofile(LDAPObject):
    """
    Certificate Profile object.
    """
    container_dn = api.env.container_certprofile
    object_name = _('Certificate Profile')
    object_name_plural = _('Certificate Profiles')
    object_class = ['ipacertprofile']
    default_attributes = ['cn', 'description', 'ipacertprofilestoreissued']
    search_attributes = ['cn', 'description', 'ipacertprofilestoreissued']
    label = _('Certificate Profiles')
    label_singular = _('Certificate Profile')

    takes_params = (
        Str(
            'cn',
            validate_profile_id,
            primary_key=True,
            cli_name='id',
            label=_('Profile ID'),
            doc=_('Profile ID for referring to this profile'),
        ),
        Str(
            'config',
            label=_('Profile configuration'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
        Str(
            'description',
            required=True,
            cli_name='desc',
            label=_('Profile description'),
            doc=_('Brief description of this profile'),
        ),
        Bool(
            'ipacertprofilestoreissued',
            default=True,
            cli_name='store',
            label=_('Store issued certificates'),
            doc=_('Whether to store certs issued using this profile'),
        ),
    )

    permission_filter_objectclasses = ['ipacertprofile']
    managed_permissions = {
        'System: Read Certificate Profiles': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'cn',
                'description',
                'ipacertprofilestoreissued',
                'objectclass',
            },
        },
        'System: Import Certificate Profile': {
            'ipapermright': {'add'},
            'replaces': [
                '(target = "ldap:///cn=*,cn=certprofiles,cn=ca,$SUFFIX")(version 3.0;acl "permission:Import Certificate Profile";allow (add) groupdn = "ldap:///cn=Import Certificate Profile,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'CA Administrator'},
        },
        'System: Delete Certificate Profile': {
            'ipapermright': {'delete'},
            'replaces': [
                '(target = "ldap:///cn=*,cn=certprofiles,cn=ca,$SUFFIX")(version 3.0;acl "permission:Delete Certificate Profile";allow (delete) groupdn = "ldap:///cn=Delete Certificate Profile,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'CA Administrator'},
        },
        'System: Modify Certificate Profile': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {
                'cn',
                'description',
                'ipacertprofilestoreissued',
            },
            'replaces': [
                '(targetattr = "cn || description || ipacertprofilestoreissued")(target = "ldap:///cn=*,cn=certprofiles,cn=ca,$SUFFIX")(version 3.0;acl "permission:Modify Certificate Profile";allow (write) groupdn = "ldap:///cn=Modify Certificate Profile,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'CA Administrator'},
        },
    }
Beispiel #15
0
class sudorule(LDAPObject):
    """
    Sudo Rule object.
    """
    container_dn = api.env.container_sudorule
    object_name = _('sudo rule')
    object_name_plural = _('sudo rules')
    object_class = ['ipaassociation', 'ipasudorule']
    permission_filter_objectclasses = ['ipasudorule']
    default_attributes = [
        'cn', 'ipaenabledflag', 'externaluser', 'description', 'usercategory',
        'hostcategory', 'cmdcategory', 'memberuser', 'memberhost',
        'memberallowcmd', 'memberdenycmd', 'ipasudoopt', 'ipasudorunas',
        'ipasudorunasgroup', 'ipasudorunasusercategory',
        'ipasudorunasgroupcategory', 'sudoorder', 'hostmask', 'externalhost',
        'ipasudorunasextusergroup', 'ipasudorunasextgroup',
        'ipasudorunasextuser'
    ]
    uuid_attribute = 'ipauniqueid'
    rdn_attribute = 'ipauniqueid'
    allow_rename = True
    attribute_members = {
        'memberuser': ['user', 'group'],
        'memberhost': ['host', 'hostgroup'],
        'memberallowcmd': ['sudocmd', 'sudocmdgroup'],
        'memberdenycmd': ['sudocmd', 'sudocmdgroup'],
        'ipasudorunas': ['user', 'group'],
        'ipasudorunasgroup': ['group'],
    }
    managed_permissions = {
        'System: Read Sudo Rules': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'cmdcategory',
                'cn',
                'description',
                'externalhost',
                'externaluser',
                'hostcategory',
                'hostmask',
                'ipaenabledflag',
                'ipasudoopt',
                'ipasudorunas',
                'ipasudorunasextgroup',
                'ipasudorunasextuser',
                'ipasudorunasextusergroup',
                'ipasudorunasgroup',
                'ipasudorunasgroupcategory',
                'ipasudorunasusercategory',
                'ipauniqueid',
                'memberallowcmd',
                'memberdenycmd',
                'memberhost',
                'memberuser',
                'sudonotafter',
                'sudonotbefore',
                'sudoorder',
                'usercategory',
                'objectclass',
                'member',
            },
        },
        'System: Read Sudoers compat tree': {
            'non_object': True,
            'ipapermlocation': api.env.basedn,
            'ipapermtarget': DN('ou=sudoers', api.env.basedn),
            'ipapermbindruletype': 'anonymous',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'objectclass',
                'cn',
                'ou',
                'sudouser',
                'sudohost',
                'sudocommand',
                'sudorunas',
                'sudorunasuser',
                'sudorunasgroup',
                'sudooption',
                'sudonotbefore',
                'sudonotafter',
                'sudoorder',
                'description',
            },
        },
        'System: Add Sudo rule': {
            'ipapermright': {'add'},
            'replaces': [
                '(target = "ldap:///ipauniqueid=*,cn=sudorules,cn=sudo,$SUFFIX")(version 3.0;acl "permission:Add Sudo rule";allow (add) groupdn = "ldap:///cn=Add Sudo rule,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'Sudo Administrator'},
        },
        'System: Delete Sudo rule': {
            'ipapermright': {'delete'},
            'replaces': [
                '(target = "ldap:///ipauniqueid=*,cn=sudorules,cn=sudo,$SUFFIX")(version 3.0;acl "permission:Delete Sudo rule";allow (delete) groupdn = "ldap:///cn=Delete Sudo rule,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'Sudo Administrator'},
        },
        'System: Modify Sudo rule': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {
                'description',
                'ipaenabledflag',
                'usercategory',
                'hostcategory',
                'cmdcategory',
                'ipasudorunasusercategory',
                'ipasudorunasgroupcategory',
                'externaluser',
                'ipasudorunasextusergroup',
                'ipasudorunasextuser',
                'ipasudorunasextgroup',
                'memberdenycmd',
                'memberallowcmd',
                'memberuser',
                'memberhost',
                'externalhost',
                'sudonotafter',
                'hostmask',
                'sudoorder',
                'sudonotbefore',
                'ipasudorunas',
                'externalhost',
                'ipasudorunasgroup',
                'ipasudoopt',
                'memberhost',
            },
            'replaces': [
                '(targetattr = "description || ipaenabledflag || usercategory || hostcategory || cmdcategory || ipasudorunasusercategory || ipasudorunasgroupcategory || externaluser || ipasudorunasextuser || ipasudorunasextgroup || memberdenycmd || memberallowcmd || memberuser")(target = "ldap:///ipauniqueid=*,cn=sudorules,cn=sudo,$SUFFIX")(version 3.0;acl "permission:Modify Sudo rule";allow (write) groupdn = "ldap:///cn=Modify Sudo rule,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'Sudo Administrator'},
        },
    }

    label = _('Sudo Rules')
    label_singular = _('Sudo Rule')

    takes_params = (
        Str(
            'cn',
            cli_name='sudorule_name',
            label=_('Rule name'),
            primary_key=True,
        ),
        Str(
            'description?',
            cli_name='desc',
            label=_('Description'),
        ),
        Bool(
            'ipaenabledflag?',
            label=_('Enabled'),
            flags=['no_option'],
        ),
        StrEnum(
            'usercategory?',
            cli_name='usercat',
            label=_('User category'),
            doc=_('User category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'hostcategory?',
            cli_name='hostcat',
            label=_('Host category'),
            doc=_('Host category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'cmdcategory?',
            cli_name='cmdcat',
            label=_('Command category'),
            doc=_('Command category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'ipasudorunasusercategory?',
            cli_name='runasusercat',
            label=_('RunAs User category'),
            doc=_('RunAs User category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'ipasudorunasgroupcategory?',
            cli_name='runasgroupcat',
            label=_('RunAs Group category'),
            doc=_('RunAs Group category the rule applies to'),
            values=(u'all', ),
        ),
        Int(
            'sudoorder?',
            cli_name='order',
            label=_('Sudo order'),
            doc=_('integer to order the Sudo rules'),
            default=0,
            minvalue=0,
        ),
        Str(
            'memberuser_user?',
            label=_('Users'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberuser_group?',
            label=_('User Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'externaluser?',
            validate_externaluser,
            cli_name='externaluser',
            label=_('External User'),
            doc=_('External User the rule applies to (sudorule-find only)'),
        ),
        Str(
            'memberhost_host?',
            label=_('Hosts'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_hostgroup?',
            label=_('Host Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'hostmask',
            validate_hostmask,
            normalizer=lambda x: unicode(netaddr.IPNetwork(x).cidr),
            label=_('Host Masks'),
            flags=['no_create', 'no_update', 'no_search'],
            multivalue=True,
        ),
        external_host_param,
        Str(
            'memberallowcmd_sudocmd?',
            label=_('Sudo Allow Commands'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberdenycmd_sudocmd?',
            label=_('Sudo Deny Commands'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberallowcmd_sudocmdgroup?',
            label=_('Sudo Allow Command Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberdenycmd_sudocmdgroup?',
            label=_('Sudo Deny Command Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'ipasudorunas_user?',
            label=_('RunAs Users'),
            doc=_('Run as a user'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'ipasudorunas_group?',
            label=_('Groups of RunAs Users'),
            doc=_('Run as any user within a specified group'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'ipasudorunasextuser?',
            validate_runasextuser,
            cli_name='runasexternaluser',
            label=_('RunAs External User'),
            doc=_(
                'External User the commands can run as (sudorule-find only)'),
        ),
        Str(
            'ipasudorunasextusergroup?',
            cli_name='runasexternalusergroup',
            label=_('External Groups of RunAs Users'),
            doc=_('External Groups of users that the command can run as'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'ipasudorunasgroup_group?',
            label=_('RunAs Groups'),
            doc=_('Run with the gid of a specified POSIX group'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'ipasudorunasextgroup?',
            validate_runasextgroup,
            cli_name='runasexternalgroup',
            label=_('RunAs External Group'),
            doc=_(
                'External Group the commands can run as (sudorule-find only)'),
        ),
        Str(
            'ipasudoopt*',
            label=_('Sudo Option'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
    )

    order_not_unique_msg = _(
        'order must be a unique value (%(order)d already used by %(rule)s)')

    def check_order_uniqueness(self, *keys, **options):
        if options.get('sudoorder') is not None:
            entries = self.methods.find(
                sudoorder=options['sudoorder'])['result']

            if len(entries) > 0:
                rule_name = entries[0]['cn'][0]
                raise errors.ValidationError(name='order',
                                             error=self.order_not_unique_msg %
                                             {
                                                 'order': options['sudoorder'],
                                                 'rule': rule_name,
                                             })
Beispiel #16
0
            else:
                entry_attrs['primarymail'] = entry_attrs['mail'][0]

    if 'primarymail' in entry_attrs:
        entry_attrs['mail'] = [entry_attrs['primarymail']]


user.takes_params += (Str('primarymail?',
                          cli_name='primary_mail',
                          label=_('Primary mail address')),
                      Str('alias*', cli_name='alias', label=_('Mail aliases')),
                      Str('sendalias*',
                          cli_name='send_alias',
                          label=_('Allowed sender aliases')),
                      Bool('canreceiveexternally?',
                           cli_name='can_receive_externally',
                           label='Can receive external mails'),
                      Bool('cansendexternally?',
                           cli_name='can_send_externally',
                           label='Can send mails to external locations'),
                      Int('mailboxquota?',
                          cli_name='mailbox_quota',
                          label='Mailbox quota [MB]'),
                      Str('mailboxtransport?',
                          cli_name='mailbox_transport',
                          label=_('Mailbox transport string')))

user.default_attributes = user.default_attributes + [
    'alias', 'sendalias', 'mailboxquota', 'mailboxtransport'
]
Beispiel #17
0
class caacl(LDAPObject):
    """
    CA ACL object.
    """
    container_dn = api.env.container_caacl
    object_name = _('CA ACL')
    object_name_plural = _('CA ACLs')
    object_class = ['ipaassociation', 'ipacaacl']
    permission_filter_objectclasses = ['ipacaacl']
    default_attributes = [
        'cn', 'description', 'ipaenabledflag',
        'ipacacategory', 'ipamemberca',
        'ipacertprofilecategory', 'ipamembercertprofile',
        'usercategory', 'memberuser',
        'hostcategory', 'memberhost',
        'servicecategory', 'memberservice',
    ]
    uuid_attribute = 'ipauniqueid'
    rdn_attribute = 'ipauniqueid'
    attribute_members = {
        'memberuser': ['user', 'group'],
        'memberhost': ['host', 'hostgroup'],
        'memberservice': ['service'],
        'ipamemberca': ['ca'],
        'ipamembercertprofile': ['certprofile'],
    }
    managed_permissions = {
        'System: Read CA ACLs': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'cn', 'description', 'ipaenabledflag',
                'ipacacategory', 'ipamemberca',
                'ipacertprofilecategory', 'ipamembercertprofile',
                'usercategory', 'memberuser',
                'hostcategory', 'memberhost',
                'servicecategory', 'memberservice',
                'ipauniqueid',
                'objectclass', 'member',
            },
        },
        'System: Add CA ACL': {
            'ipapermright': {'add'},
            'replaces': [
                '(target = "ldap:///ipauniqueid=*,cn=caacls,cn=ca,$SUFFIX")(version 3.0;acl "permission:Add CA ACL";allow (add) groupdn = "ldap:///cn=Add CA ACL,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'CA Administrator'},
        },
        'System: Delete CA ACL': {
            'ipapermright': {'delete'},
            'replaces': [
                '(target = "ldap:///ipauniqueid=*,cn=caacls,cn=ca,$SUFFIX")(version 3.0;acl "permission:Delete CA ACL";allow (delete) groupdn = "ldap:///cn=Delete CA ACL,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'CA Administrator'},
        },
        'System: Manage CA ACL Membership': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {
                'ipacacategory', 'ipamemberca',
                'ipacertprofilecategory', 'ipamembercertprofile',
                'usercategory', 'memberuser',
                'hostcategory', 'memberhost',
                'servicecategory', 'memberservice'
            },
            'replaces': [
                '(targetattr = "ipamemberca || ipamembercertprofile || memberuser || memberservice || memberhost || ipacacategory || ipacertprofilecategory || usercategory || hostcategory || servicecategory")(target = "ldap:///ipauniqueid=*,cn=caacls,cn=ca,$SUFFIX")(version 3.0;acl "permission:Manage CA ACL membership";allow (write) groupdn = "ldap:///cn=Manage CA ACL membership,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'CA Administrator'},
        },
        'System: Modify CA ACL': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {
                'cn', 'description', 'ipaenabledflag',
            },
            'replaces': [
                '(targetattr = "cn || description || ipaenabledflag")(target = "ldap:///ipauniqueid=*,cn=caacls,cn=ca,$SUFFIX")(version 3.0;acl "permission:Modify CA ACL";allow (write) groupdn = "ldap:///cn=Modify CA ACL,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'CA Administrator'},
        },
    }

    label = _('CA ACLs')
    label_singular = _('CA ACL')

    takes_params = (
        Str('cn',
            cli_name='name',
            label=_('ACL name'),
            primary_key=True,
        ),
        Str('description?',
            cli_name='desc',
            label=_('Description'),
        ),
        Bool('ipaenabledflag?',
             label=_('Enabled'),
             flags=['no_option'],
        ),
        StrEnum('ipacacategory?',
            cli_name='cacat',
            label=_('CA category'),
            doc=_('CA category the ACL applies to'),
            values=(u'all', ),
        ),
        StrEnum('ipacertprofilecategory?',
            cli_name='profilecat',
            label=_('Profile category'),
            doc=_('Profile category the ACL applies to'),
            values=(u'all', ),
        ),
        StrEnum('usercategory?',
            cli_name='usercat',
            label=_('User category'),
            doc=_('User category the ACL applies to'),
            values=(u'all', ),
        ),
        StrEnum('hostcategory?',
            cli_name='hostcat',
            label=_('Host category'),
            doc=_('Host category the ACL applies to'),
            values=(u'all', ),
        ),
        StrEnum('servicecategory?',
            cli_name='servicecat',
            label=_('Service category'),
            doc=_('Service category the ACL applies to'),
            values=(u'all', ),
        ),
        Str('ipamemberca_ca?',
            label=_('CAs'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str('ipamembercertprofile_certprofile?',
            label=_('Profiles'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str('memberuser_user?',
            label=_('Users'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str('memberuser_group?',
            label=_('User Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str('memberhost_host?',
            label=_('Hosts'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str('memberhost_hostgroup?',
            label=_('Host Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str('memberservice_service?',
            label=_('Services'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
    )
Beispiel #18
0
class migrate_ds(Command):
    __doc__ = _('Migrate users and groups from DS to IPA.')

    migrate_objects = {
        # OBJECT_NAME: (search_filter, pre_callback, post_callback)
        #
        # OBJECT_NAME - is the name of an LDAPObject subclass
        # search_filter - is the filter to retrieve objects from DS
        # pre_callback - is called for each object just after it was
        #                retrieved from DS and before being added to IPA
        # post_callback - is called for each object after it was added to IPA
        # exc_callback - is called when adding entry to IPA raises an exception
        #
        # {pre, post}_callback parameters:
        #  ldap - ldap2 instance connected to IPA
        #  pkey - primary key value of the object (uid for users, etc.)
        #  dn - dn of the object as it (will be/is) stored in IPA
        #  entry_attrs - attributes of the object
        #  failed - a list of so-far failed objects
        #  config - IPA config entry attributes
        #  ctx - object context, used to pass data between callbacks
        #
        # If pre_callback return value evaluates to False, migration
        # of the current object is aborted.
        'user': {
            'filter_template': '(&(|%s)(uid=*))',
            'oc_option': 'userobjectclass',
            'oc_blocklist_option': 'userignoreobjectclass',
            'attr_blocklist_option': 'userignoreattribute',
            'pre_callback': _pre_migrate_user,
            'post_callback': _post_migrate_user,
            'exc_callback': None
        },
        'group': {
            'filter_template': '(&(|%s)(cn=*))',
            'oc_option': 'groupobjectclass',
            'oc_blocklist_option': 'groupignoreobjectclass',
            'attr_blocklist_option': 'groupignoreattribute',
            'pre_callback': _pre_migrate_group,
            'post_callback': None,
            'exc_callback': _group_exc_callback,
        },
    }
    migrate_order = ('user', 'group')

    takes_args = (
        Str(
            'ldapuri',
            validate_ldapuri,
            cli_name='ldap_uri',
            label=_('LDAP URI'),
            doc=_('LDAP URI of DS server to migrate from'),
        ),
        Password(
            'bindpw',
            cli_name='password',
            label=_('Password'),
            confirm=False,
            doc=_('bind password'),
        ),
    )

    takes_options = (
        DNParam('binddn?',
            cli_name='bind_dn',
            label=_('Bind DN'),
            default=DN(('cn', 'directory manager')),
            autofill=True,
        ),
        DNParam('usercontainer',
            cli_name='user_container',
            label=_('User container'),
            doc=_('DN of container for users in DS relative to base DN'),
            default=DN(('ou', 'people')),
            autofill=True,
        ),
        DNParam('groupcontainer',
            cli_name='group_container',
            label=_('Group container'),
            doc=_('DN of container for groups in DS relative to base DN'),
            default=DN(('ou', 'groups')),
            autofill=True,
        ),
        Str('userobjectclass+',
            cli_name='user_objectclass',
            label=_('User object class'),
            doc=_('Objectclasses used to search for user entries in DS'),
            default=(u'person',),
            autofill=True,
        ),
        Str('groupobjectclass+',
            cli_name='group_objectclass',
            label=_('Group object class'),
            doc=_('Objectclasses used to search for group entries in DS'),
            default=(u'groupOfUniqueNames', u'groupOfNames'),
            autofill=True,
        ),
        Str('userignoreobjectclass*',
            cli_name='user_ignore_objectclass',
            label=_('Ignore user object class'),
            doc=_('Objectclasses to be ignored for user entries in DS'),
            default=tuple(),
            autofill=True,
        ),
        Str('userignoreattribute*',
            cli_name='user_ignore_attribute',
            label=_('Ignore user attribute'),
            doc=_('Attributes to be ignored for user entries in DS'),
            default=tuple(),
            autofill=True,
        ),
        Str('groupignoreobjectclass*',
            cli_name='group_ignore_objectclass',
            label=_('Ignore group object class'),
            doc=_('Objectclasses to be ignored for group entries in DS'),
            default=tuple(),
            autofill=True,
        ),
        Str('groupignoreattribute*',
            cli_name='group_ignore_attribute',
            label=_('Ignore group attribute'),
            doc=_('Attributes to be ignored for group entries in DS'),
            default=tuple(),
            autofill=True,
        ),
        Flag('groupoverwritegid',
            cli_name='group_overwrite_gid',
            label=_('Overwrite GID'),
            doc=_('When migrating a group already existing in IPA domain overwrite the '\
                  'group GID and report as success'),
        ),
        StrEnum('schema?',
            cli_name='schema',
            label=_('LDAP schema'),
            doc=_('The schema used on the LDAP server. Supported values are RFC2307 and RFC2307bis. The default is RFC2307bis'),
            values=_supported_schemas,
            default=_supported_schemas[0],
            autofill=True,
        ),
        Flag('continue?',
            label=_('Continue'),
            doc=_('Continuous operation mode. Errors are reported but the process continues'),
            default=False,
        ),
        DNParam('basedn?',
            cli_name='base_dn',
            label=_('Base DN'),
            doc=_('Base DN on remote LDAP server'),
        ),
        Flag('compat?',
            cli_name='with_compat',
            label=_('Ignore compat plugin'),
            doc=_('Allows migration despite the usage of compat plugin'),
            default=False,
        ),
        Str('cacertfile?',
            cli_name='ca_cert_file',
            label=_('CA certificate'),
            doc=_('Load CA certificate of LDAP server from FILE'),
            default=None,
            noextrawhitespace=False,
            ),
        Bool('use_def_group?',
             cli_name='use_default_group',
             label=_('Add to default group'),
             doc=_('Add migrated users without a group to a default group '
                   '(default: true)'),
             default=True,
             autofill=True,
             ),
        StrEnum('scope',
                cli_name='scope',
                label=_('Search scope'),
                doc=_('LDAP search scope for users and groups: base, '
                      'onelevel, or subtree. Defaults to onelevel'),
                values=sorted(_supported_scopes),
                default=_default_scope,
                autofill=True,
                ),
    )

    has_output = (
        output.Output(
            'result',
            type=dict,
            doc=_('Lists of objects migrated; categorized by type.'),
        ),
        output.Output(
            'failed',
            type=dict,
            doc=
            _('Lists of objects that could not be migrated; categorized by type.'
              ),
        ),
        output.Output(
            'enabled',
            type=bool,
            doc=_('False if migration mode was disabled.'),
        ),
        output.Output(
            'compat',
            type=bool,
            doc=
            _('False if migration fails because the compatibility plug-in is enabled.'
              ),
        ),
    )

    exclude_doc = _('%s to exclude from migration')

    truncated_err_msg = _('''\
search results for objects to be migrated
have been truncated by the server;
migration process might be incomplete\n''')

    def get_options(self):
        """
        Call get_options of the baseclass and add "exclude" options
        for each type of object being migrated.
        """
        for option in super(migrate_ds, self).get_options():
            yield option
        for ldap_obj_name in self.migrate_objects:
            ldap_obj = self.api.Object[ldap_obj_name]
            name = 'exclude_%ss' % to_cli(ldap_obj_name)
            doc = self.exclude_doc % ldap_obj.object_name_plural
            yield Str('%s*' % name,
                      cli_name=name,
                      doc=doc,
                      default=tuple(),
                      autofill=True)

    def normalize_options(self, options):
        """
        Convert all "exclude" option values to lower-case.

        Also, empty List parameters are converted to None, but the migration
        plugin doesn't like that - convert back to empty lists.
        """
        names = [
            'userobjectclass', 'groupobjectclass', 'userignoreobjectclass',
            'userignoreattribute', 'groupignoreobjectclass',
            'groupignoreattribute'
        ]
        names.extend('exclude_%ss' % to_cli(n) for n in self.migrate_objects)
        for name in names:
            if options[name]:
                options[name] = tuple(v.lower() for v in options[name])
            else:
                options[name] = tuple()

    def _get_search_bases(self, options, ds_base_dn, migrate_order):
        search_bases = dict()
        for ldap_obj_name in migrate_order:
            container = options.get('%scontainer' % to_cli(ldap_obj_name))
            if container:
                # Don't append base dn if user already appended it in the container dn
                if container.endswith(ds_base_dn):
                    search_base = container
                else:
                    search_base = DN(container, ds_base_dn)
            else:
                search_base = ds_base_dn
            search_bases[ldap_obj_name] = search_base
        return search_bases

    def migrate(self, ldap, config, ds_ldap, ds_base_dn, options):
        """
        Migrate objects from DS to LDAP.
        """
        assert isinstance(ds_base_dn, DN)
        migrated = {}  # {'OBJ': ['PKEY1', 'PKEY2', ...], ...}
        failed = {}  # {'OBJ': {'PKEY1': 'Failed 'cos blabla', ...}, ...}
        search_bases = self._get_search_bases(options, ds_base_dn,
                                              self.migrate_order)
        migration_start = datetime.datetime.now()

        scope = _supported_scopes[options.get('scope')]

        for ldap_obj_name in self.migrate_order:
            ldap_obj = self.api.Object[ldap_obj_name]

            template = self.migrate_objects[ldap_obj_name]['filter_template']
            oc_list = options[to_cli(
                self.migrate_objects[ldap_obj_name]['oc_option'])]
            search_filter = construct_filter(template, oc_list)

            exclude = options['exclude_%ss' % to_cli(ldap_obj_name)]
            context = dict(ds_ldap=ds_ldap)

            migrated[ldap_obj_name] = []
            failed[ldap_obj_name] = {}

            try:
                entries, truncated = ds_ldap.find_entries(
                    search_filter, ['*'],
                    search_bases[ldap_obj_name],
                    scope,
                    time_limit=0,
                    size_limit=-1)
            except errors.NotFound:
                if not options.get('continue', False):
                    raise errors.NotFound(
                        reason=
                        _('%(container)s LDAP search did not return any result '
                          '(search base: %(search_base)s, '
                          'objectclass: %(objectclass)s)') % {
                              'container': ldap_obj_name,
                              'search_base': search_bases[ldap_obj_name],
                              'objectclass': ', '.join(oc_list)
                          })
                else:
                    truncated = False
                    entries = []
            if truncated:
                logger.error('%s: %s', ldap_obj.name, self.truncated_err_msg)

            blocklists = {}
            for blocklist in ('oc_blocklist', 'attr_blocklist'):
                blocklist_option = (
                    self.migrate_objects[ldap_obj_name][blocklist + '_option'])
                if blocklist_option is not None:
                    blocklists[blocklist] = options.get(
                        blocklist_option, tuple())
                else:
                    blocklists[blocklist] = tuple()

            # get default primary group for new users
            if 'def_group_dn' not in context and options.get('use_def_group'):
                def_group = config.get('ipadefaultprimarygroup')
                context['def_group_dn'] = api.Object.group.get_dn(def_group)
                try:
                    ldap.get_entry(context['def_group_dn'],
                                   ['gidnumber', 'cn'])
                except errors.NotFound:
                    error_msg = _('Default group for new users not found')
                    raise errors.NotFound(reason=error_msg)

            context['has_upg'] = ldap.has_upg()

            valid_gids = set()
            invalid_gids = set()
            migrate_cnt = 0
            context['migrate_cnt'] = 0
            for entry_attrs in entries:
                context['migrate_cnt'] = migrate_cnt
                s = datetime.datetime.now()

                ava = entry_attrs.dn[0][0]
                if ava.attr == ldap_obj.primary_key.name:
                    # In case if pkey attribute is in the migrated object DN
                    # and the original LDAP is multivalued, make sure that
                    # we pick the correct value (the unique one stored in DN)
                    pkey = ava.value.lower()
                else:
                    pkey = entry_attrs[ldap_obj.primary_key.name][0].lower()

                if pkey in exclude:
                    continue

                entry_attrs.dn = ldap_obj.get_dn(pkey)
                entry_attrs['objectclass'] = list(
                    set(
                        config.get(ldap_obj.object_class_config,
                                   ldap_obj.object_class) +
                        [o.lower() for o in entry_attrs['objectclass']]))
                entry_attrs[ldap_obj.primary_key.name][0] = entry_attrs[
                    ldap_obj.primary_key.name][0].lower()

                callback = self.migrate_objects[ldap_obj_name]['pre_callback']
                if callable(callback):
                    try:
                        entry_attrs.dn = callback(ldap,
                                                  pkey,
                                                  entry_attrs.dn,
                                                  entry_attrs,
                                                  failed[ldap_obj_name],
                                                  config,
                                                  context,
                                                  schema=options['schema'],
                                                  search_bases=search_bases,
                                                  valid_gids=valid_gids,
                                                  invalid_gids=invalid_gids,
                                                  **blocklists)
                        if not entry_attrs.dn:
                            continue
                    except errors.NotFound as e:
                        failed[ldap_obj_name][pkey] = unicode(e.reason)
                        continue

                try:
                    ldap.add_entry(entry_attrs)
                except errors.ExecutionError as e:
                    callback = self.migrate_objects[ldap_obj_name][
                        'exc_callback']
                    if callable(callback):
                        try:
                            callback(ldap, entry_attrs.dn, entry_attrs, e,
                                     options)
                        except errors.ExecutionError as e2:
                            failed[ldap_obj_name][pkey] = unicode(e2)
                            continue
                    else:
                        failed[ldap_obj_name][pkey] = unicode(e)
                        continue

                migrated[ldap_obj_name].append(pkey)

                callback = self.migrate_objects[ldap_obj_name]['post_callback']
                if callable(callback):
                    callback(ldap, pkey, entry_attrs.dn, entry_attrs,
                             failed[ldap_obj_name], config, context)
                e = datetime.datetime.now()
                d = e - s
                total_dur = e - migration_start
                migrate_cnt += 1
                if migrate_cnt > 0 and migrate_cnt % 100 == 0:
                    logger.info("%d %ss migrated. %s elapsed.", migrate_cnt,
                                ldap_obj_name, total_dur)
                logger.debug("%d %ss migrated, duration: %s (total %s)",
                             migrate_cnt, ldap_obj_name, d, total_dur)

        if 'def_group_dn' in context:
            _update_default_group(ldap, context, True)

        return (migrated, failed)

    def execute(self, ldapuri, bindpw, **options):
        ldap = self.api.Backend.ldap2
        self.normalize_options(options)
        config = ldap.get_ipa_config()

        ds_base_dn = options.get('basedn')
        if ds_base_dn is not None:
            assert isinstance(ds_base_dn, DN)

        # check if migration mode is enabled
        if config.get('ipamigrationenabled', ('FALSE', ))[0] == 'FALSE':
            return dict(result={}, failed={}, enabled=False, compat=True)

        # connect to DS
        if options.get('cacertfile') is not None:
            # store CA cert into file
            tmp_ca_cert_f = write_tmp_file(options['cacertfile'])
            cacert = tmp_ca_cert_f.name

            # start TLS connection or STARTTLS
            ds_ldap = LDAPClient(ldapuri, cacert=cacert, start_tls=True)
            ds_ldap.simple_bind(options['binddn'], bindpw)

            tmp_ca_cert_f.close()
        else:
            ds_ldap = LDAPClient(ldapuri)
            ds_ldap.simple_bind(options['binddn'], bindpw, insecure_bind=True)

        # check whether the compat plugin is enabled
        if not options.get('compat'):
            try:
                ldap.get_entry(
                    DN(('cn', 'users'), ('cn', 'compat'), (api.env.basedn)))
                return dict(result={}, failed={}, enabled=True, compat=False)
            except errors.NotFound:
                pass

        if not ds_base_dn:
            # retrieve base DN from remote LDAP server
            entries, _truncated = ds_ldap.find_entries(
                '',
                ['namingcontexts', 'defaultnamingcontext'],
                DN(''),
                ds_ldap.SCOPE_BASE,
                size_limit=-1,
                time_limit=0,
            )
            if 'defaultnamingcontext' in entries[0]:
                ds_base_dn = DN(entries[0]['defaultnamingcontext'][0])
                assert isinstance(ds_base_dn, DN)
            else:
                try:
                    ds_base_dn = DN(entries[0]['namingcontexts'][0])
                    assert isinstance(ds_base_dn, DN)
                except (IndexError, KeyError) as e:
                    raise Exception(str(e))

        # migrate!
        (migrated, failed) = self.migrate(ldap, config, ds_ldap, ds_base_dn,
                                          options)

        return dict(result=migrated, failed=failed, enabled=True, compat=True)
Beispiel #19
0
class config(LDAPObject):
    """
    IPA configuration object
    """
    object_name = _('configuration options')
    default_attributes = [
        'ipamaxusernamelength',
        'ipahomesrootdir',
        'ipadefaultloginshell',
        'ipadefaultprimarygroup',
        'ipadefaultemaildomain',
        'ipasearchtimelimit',
        'ipasearchrecordslimit',
        'ipausersearchfields',
        'ipagroupsearchfields',
        'ipamigrationenabled',
        'ipacertificatesubjectbase',
        'ipapwdexpadvnotify',
        'ipaselinuxusermaporder',
        'ipaselinuxusermapdefault',
        'ipaconfigstring',
        'ipakrbauthzdata',
    ]

    label = _('Configuration')
    label_singular = _('Configuration')

    takes_params = (
        Int(
            'ipamaxusernamelength',
            cli_name='maxusername',
            label=_('Maximum username length'),
            minvalue=1,
        ),
        IA5Str(
            'ipahomesrootdir',
            cli_name='homedirectory',
            label=_('Home directory base'),
            doc=_('Default location of home directories'),
        ),
        Str(
            'ipadefaultloginshell',
            cli_name='defaultshell',
            label=_('Default shell'),
            doc=_('Default shell for new users'),
        ),
        Str(
            'ipadefaultprimarygroup',
            cli_name='defaultgroup',
            label=_('Default users group'),
            doc=_('Default group for new users'),
        ),
        Str(
            'ipadefaultemaildomain?',
            cli_name='emaildomain',
            label=_('Default e-mail domain'),
            doc=_('Default e-mail domain'),
        ),
        Int(
            'ipasearchtimelimit',
            validate_searchtimelimit,
            cli_name='searchtimelimit',
            label=_('Search time limit'),
            doc=
            _('Maximum amount of time (seconds) for a search (> 0, or -1 for unlimited)'
              ),
            minvalue=-1,
        ),
        Int(
            'ipasearchrecordslimit',
            cli_name='searchrecordslimit',
            label=_('Search size limit'),
            doc=_('Maximum number of records to search (-1 is unlimited)'),
            minvalue=-1,
        ),
        IA5Str(
            'ipausersearchfields',
            cli_name='usersearch',
            label=_('User search fields'),
            doc=
            _('A comma-separated list of fields to search in when searching for users'
              ),
        ),
        IA5Str(
            'ipagroupsearchfields',
            cli_name='groupsearch',
            label='Group search fields',
            doc=
            _('A comma-separated list of fields to search in when searching for groups'
              ),
        ),
        Bool(
            'ipamigrationenabled',
            cli_name='enable_migration',
            label=_('Enable migration mode'),
            doc=_('Enable migration mode'),
        ),
        DNParam(
            'ipacertificatesubjectbase',
            cli_name='subject',
            label=_('Certificate Subject base'),
            doc=_('Base for certificate subjects (OU=Test,O=Example)'),
            flags=['no_update'],
        ),
        Str(
            'ipagroupobjectclasses+',
            cli_name='groupobjectclasses',
            label=_('Default group objectclasses'),
            doc=_('Default group objectclasses (comma-separated list)'),
            csv=True,
        ),
        Str(
            'ipauserobjectclasses+',
            cli_name='userobjectclasses',
            label=_('Default user objectclasses'),
            doc=_('Default user objectclasses (comma-separated list)'),
            csv=True,
        ),
        Int(
            'ipapwdexpadvnotify',
            cli_name='pwdexpnotify',
            label=_('Password Expiration Notification (days)'),
            doc=_('Number of days\'s notice of impending password expiration'),
            minvalue=0,
        ),
        StrEnum(
            'ipaconfigstring*',
            cli_name='ipaconfigstring',
            label=_('Password plugin features'),
            doc=_('Extra hashes to generate in password plug-in'),
            values=(u'AllowLMhash', u'AllowNThash',
                    u'KDC:Disable Last Success', u'KDC:Disable Lockout'),
            csv=True,
        ),
        Str(
            'ipaselinuxusermaporder',
            label=_('SELinux user map order'),
            doc=_(
                'Order in increasing priority of SELinux users, delimited by $'
            ),
        ),
        Str(
            'ipaselinuxusermapdefault?',
            label=_('Default SELinux user'),
            doc=
            _('Default SELinux user when no match is found in SELinux map rule'
              ),
        ),
        StrEnum(
            'ipakrbauthzdata*',
            cli_name='pac_type',
            label=_('Default PAC types'),
            doc=_('Default types of PAC supported for services'),
            values=(u'MS-PAC', u'PAD'),
            csv=True,
        ),
    )

    def get_dn(self, *keys, **kwargs):
        return DN(('cn', 'ipaconfig'), ('cn', 'etc'))
Beispiel #20
0
class otptoken(LDAPObject):
    """
    OTP Token object.
    """
    container_dn = api.env.container_otp
    object_name = _('OTP token')
    object_name_plural = _('OTP tokens')
    object_class = ['ipatoken']
    possible_objectclasses = ['ipatokentotp', 'ipatokenhotp']
    default_attributes = [
        'ipatokenuniqueid', 'description', 'ipatokenowner', 'ipatokendisabled',
        'ipatokennotbefore', 'ipatokennotafter', 'ipatokenvendor',
        'ipatokenmodel', 'ipatokenserial', 'managedby'
    ]
    attribute_members = {
        'managedby': ['user'],
    }
    relationships = {
        'managedby': ('Managed by', 'man_by_', 'not_man_by_'),
    }
    allow_rename = True

    label = _('OTP Tokens')
    label_singular = _('OTP Token')

    takes_params = (
        Str(
            'ipatokenuniqueid',
            cli_name='id',
            label=_('Unique ID'),
            primary_key=True,
            flags=('optional_create'),
        ),
        StrEnum(
            'type?',
            label=_('Type'),
            doc=_('Type of the token'),
            default=u'totp',
            autofill=True,
            values=tuple(list(TOKEN_TYPES) + [x.upper() for x in TOKEN_TYPES]),
            flags=('virtual_attribute', 'no_update'),
        ),
        Str(
            'description?',
            cli_name='desc',
            label=_('Description'),
            doc=_('Token description (informational only)'),
        ),
        Str(
            'ipatokenowner?',
            cli_name='owner',
            label=_('Owner'),
            doc=_('Assigned user of the token (default: self)'),
        ),
        Str(
            'managedby_user?',
            label=_('Manager'),
            doc=_('Assigned manager of the token (default: self)'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Bool('ipatokendisabled?',
             cli_name='disabled',
             label=_('Disabled'),
             doc=_('Mark the token as disabled (default: false)')),
        DateTime(
            'ipatokennotbefore?',
            cli_name='not_before',
            label=_('Validity start'),
            doc=_('First date/time the token can be used'),
        ),
        DateTime(
            'ipatokennotafter?',
            cli_name='not_after',
            label=_('Validity end'),
            doc=_('Last date/time the token can be used'),
        ),
        Str(
            'ipatokenvendor?',
            cli_name='vendor',
            label=_('Vendor'),
            doc=_('Token vendor name (informational only)'),
        ),
        Str(
            'ipatokenmodel?',
            cli_name='model',
            label=_('Model'),
            doc=_('Token model (informational only)'),
        ),
        Str(
            'ipatokenserial?',
            cli_name='serial',
            label=_('Serial'),
            doc=_('Token serial (informational only)'),
        ),
        OTPTokenKey(
            'ipatokenotpkey?',
            cli_name='key',
            label=_('Key'),
            doc=_('Token secret (Base32; default: random)'),
            default_from=lambda: os.urandom(KEY_LENGTH),
            autofill=True,
            # force server-side conversion
            normalizer=lambda x: x,
            flags=('no_display', 'no_update', 'no_search'),
        ),
        StrEnum(
            'ipatokenotpalgorithm?',
            cli_name='algo',
            label=_('Algorithm'),
            doc=_('Token hash algorithm'),
            default=u'sha1',
            autofill=True,
            flags=('no_update'),
            values=(u'sha1', u'sha256', u'sha384', u'sha512'),
        ),
        IntEnum(
            'ipatokenotpdigits?',
            cli_name='digits',
            label=_('Digits'),
            doc=_('Number of digits each token code will have'),
            values=(6, 8),
            default=6,
            autofill=True,
            flags=('no_update'),
        ),
        Int(
            'ipatokentotpclockoffset?',
            cli_name='offset',
            label=_('Clock offset'),
            doc=_('TOTP token / FreeIPA server time difference'),
            default=0,
            autofill=True,
            flags=('no_update'),
        ),
        Int(
            'ipatokentotptimestep?',
            cli_name='interval',
            label=_('Clock interval'),
            doc=_('Length of TOTP token code validity'),
            default=30,
            autofill=True,
            minvalue=5,
            flags=('no_update'),
        ),
        Int(
            'ipatokenhotpcounter?',
            cli_name='counter',
            label=_('Counter'),
            doc=_('Initial counter for the HOTP token'),
            default=0,
            autofill=True,
            minvalue=0,
            flags=('no_update'),
        ),
        Str(
            'uri?',
            label=_('URI'),
            flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'},
        ),
    )
Beispiel #21
0
class user(baseuser):
    """
    User object.
    """

    container_dn = baseuser.active_container_dn
    label = _('Users')
    label_singular = _('User')
    object_name = _('user')
    object_name_plural = _('users')
    managed_permissions = {
        'System: Read User Standard Attributes': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'anonymous',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'objectclass', 'cn', 'sn', 'description', 'title', 'uid',
                'displayname', 'givenname', 'initials', 'manager', 'gecos',
                'gidnumber', 'homedirectory', 'loginshell', 'uidnumber',
                'ipantsecurityidentifier'
            },
        },
        'System: Read User Addressbook Attributes': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'seealso',
                'telephonenumber',
                'facsimiletelephonenumber',
                'l',
                'ou',
                'st',
                'postalcode',
                'street',
                'destinationindicator',
                'internationalisdnnumber',
                'physicaldeliveryofficename',
                'postaladdress',
                'postofficebox',
                'preferreddeliverymethod',
                'registeredaddress',
                'teletexterminalidentifier',
                'telexnumber',
                'x121address',
                'carlicense',
                'departmentnumber',
                'employeenumber',
                'employeetype',
                'preferredlanguage',
                'mail',
                'mobile',
                'pager',
                'audio',
                'businesscategory',
                'homephone',
                'homepostaladdress',
                'jpegphoto',
                'labeleduri',
                'o',
                'photo',
                'roomnumber',
                'secretary',
                'usercertificate',
                'usersmimecertificate',
                'x500uniqueidentifier',
                'inetuserhttpurl',
                'inetuserstatus',
                'ipacertmapdata',
            },
            'fixup_function': fix_addressbook_permission_bindrule,
        },
        'System: Read User IPA Attributes': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'ipauniqueid',
                'ipasshpubkey',
                'ipauserauthtype',
                'userclass',
            },
            'fixup_function': fix_addressbook_permission_bindrule,
        },
        'System: Read User Kerberos Attributes': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'krbprincipalname',
                'krbcanonicalname',
                'krbprincipalaliases',
                'krbprincipalexpiration',
                'krbpasswordexpiration',
                'krblastpwdchange',
                'nsaccountlock',
                'krbprincipaltype',
            },
        },
        'System: Read User Kerberos Login Attributes': {
            'replaces_global_anonymous_aci': True,
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'krblastsuccessfulauth',
                'krblastfailedauth',
                'krblastpwdchange',
                'krblastadminunlock',
                'krbloginfailedcount',
                'krbpwdpolicyreference',
                'krbticketpolicyreference',
                'krbupenabled',
            },
            'default_privileges': {'User Administrators'},
        },
        'System: Read User Membership': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'memberof',
            },
        },
        'System: Read UPG Definition': {
            # Required for adding users
            'replaces_global_anonymous_aci': True,
            'non_object': True,
            'ipapermlocation': UPG_DEFINITION_DN,
            'ipapermtarget': UPG_DEFINITION_DN,
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {'*'},
            'default_privileges': {'User Administrators'},
        },
        'System: Add Users': {
            'ipapermright': {'add'},
            'replaces': [
                '(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Add Users";allow (add) groupdn = "ldap:///cn=Add Users,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'User Administrators'},
        },
        'System: Add User to default group': {
            'non_object':
            True,
            'ipapermright': {'write'},
            'ipapermlocation':
            DN(api.env.container_group, api.env.basedn),
            'ipapermtarget':
            DN('cn=ipausers', api.env.container_group, api.env.basedn),
            'ipapermdefaultattr': {'member'},
            'replaces': [
                '(targetattr = "member")(target = "ldap:///cn=ipausers,cn=groups,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Add user to default group";allow (write) groupdn = "ldap:///cn=Add user to default group,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'User Administrators'},
        },
        'System: Change User password': {
            'ipapermright': {'write'},
            'ipapermtargetfilter': [
                '(objectclass=posixaccount)',
                '(!(memberOf=%s))' %
                DN('cn=admins', api.env.container_group, api.env.basedn),
            ],
            'ipapermdefaultattr': {
                'krbprincipalkey', 'passwordhistory', 'sambalmpassword',
                'sambantpassword', 'userpassword', 'krbpasswordexpiration'
            },
            'replaces': [
                '(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(targetattr = "userpassword || krbprincipalkey || sambalmpassword || sambantpassword || passwordhistory")(version 3.0;acl "permission:Change a user password";allow (write) groupdn = "ldap:///cn=Change a user password,cn=permissions,cn=pbac,$SUFFIX";)',
                '(targetfilter = "(!(memberOf=cn=admins,cn=groups,cn=accounts,$SUFFIX))")(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(targetattr = "userpassword || krbprincipalkey || sambalmpassword || sambantpassword || passwordhistory")(version 3.0;acl "permission:Change a user password";allow (write) groupdn = "ldap:///cn=Change a user password,cn=permissions,cn=pbac,$SUFFIX";)',
                '(targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Windows PassSync service can write passwords"; allow (write) userdn="ldap:///uid=passsync,cn=sysaccounts,cn=etc,$SUFFIX";)',
            ],
            'default_privileges': {
                'User Administrators',
                'Modify Users and Reset passwords',
                'PassSync Service',
            },
        },
        'System: Manage User SSH Public Keys': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {'ipasshpubkey'},
            'replaces': [
                '(targetattr = "ipasshpubkey")(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Manage User SSH Public Keys";allow (write) groupdn = "ldap:///cn=Manage User SSH Public Keys,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'User Administrators'},
        },
        'System: Manage User Certificates': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {'usercertificate'},
            'default_privileges': {
                'User Administrators',
                'Modify Users and Reset passwords',
            },
        },
        'System: Manage User Principals': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {'krbprincipalname', 'krbcanonicalname'},
            'default_privileges': {
                'User Administrators',
                'Modify Users and Reset passwords',
            },
        },
        'System: Modify Users': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {
                'businesscategory',
                'carlicense',
                'cn',
                'departmentnumber',
                'description',
                'displayname',
                'employeetype',
                'employeenumber',
                'facsimiletelephonenumber',
                'gecos',
                'givenname',
                'homephone',
                'inetuserhttpurl',
                'initials',
                'l',
                'labeleduri',
                'loginshell',
                'manager',
                'mail',
                'mepmanagedentry',
                'mobile',
                'objectclass',
                'ou',
                'pager',
                'postalcode',
                'roomnumber',
                'secretary',
                'seealso',
                'sn',
                'st',
                'street',
                'telephonenumber',
                'title',
                'userclass',
                'preferredlanguage',
            },
            'replaces': [
                '(targetattr = "givenname || sn || cn || displayname || title || initials || loginshell || gecos || homephone || mobile || pager || facsimiletelephonenumber || telephonenumber || street || roomnumber || l || st || postalcode || manager || secretary || description || carlicense || labeleduri || inetuserhttpurl || seealso || employeetype || businesscategory || ou || mepmanagedentry || objectclass")(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Modify Users";allow (write) groupdn = "ldap:///cn=Modify Users,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {
                'User Administrators',
                'Modify Users and Reset passwords',
            },
        },
        'System: Remove Users': {
            'ipapermright': {'delete'},
            'replaces': [
                '(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Remove Users";allow (delete) groupdn = "ldap:///cn=Remove Users,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'User Administrators'},
        },
        'System: Unlock User': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {
                'krblastadminunlock',
                'krbloginfailedcount',
                'nsaccountlock',
            },
            'replaces': [
                '(targetattr = "krbLastAdminUnlock || krbLoginFailedCount")(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Unlock user accounts";allow (write) groupdn = "ldap:///cn=Unlock user accounts,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'User Administrators'},
        },
        'System: Read User Compat Tree': {
            'non_object': True,
            'ipapermbindruletype': 'anonymous',
            'ipapermlocation': api.env.basedn,
            'ipapermtarget': DN('cn=users', 'cn=compat', api.env.basedn),
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'objectclass',
                'uid',
                'cn',
                'gecos',
                'gidnumber',
                'uidnumber',
                'homedirectory',
                'loginshell',
            },
        },
        'System: Read User Views Compat Tree': {
            'non_object':
            True,
            'ipapermbindruletype':
            'anonymous',
            'ipapermlocation':
            api.env.basedn,
            'ipapermtarget':
            DN('cn=users', 'cn=*', 'cn=views', 'cn=compat', api.env.basedn),
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'objectclass',
                'uid',
                'cn',
                'gecos',
                'gidnumber',
                'uidnumber',
                'homedirectory',
                'loginshell',
            },
        },
        'System: Read User NT Attributes': {
            'ipapermbindruletype': 'permission',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'ntuserdomainid',
                'ntuniqueid',
                'ntuseracctexpires',
                'ntusercodepage',
                'ntuserdeleteaccount',
                'ntuserlastlogoff',
                'ntuserlastlogon',
            },
            'default_privileges': {'PassSync Service'},
        },
        'System: Manage User Certificate Mappings': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {'ipacertmapdata', 'objectclass'},
            'default_privileges':
            {'Certificate Identity Mapping Administrators'},
        },
    }

    takes_params = baseuser.takes_params + (
        Bool(
            'nsaccountlock?',
            cli_name=('disabled'),
            default=False,
            label=_('Account disabled'),
        ),
        Bool(
            'preserved?',
            label=_('Preserved user'),
            default=False,
            flags=['virtual_attribute', 'no_create', 'no_update'],
        ),
    )

    def get_delete_dn(self, *keys, **options):
        active_dn = self.get_dn(*keys, **options)
        return DN(active_dn[0], self.delete_container_dn, api.env.basedn)

    def get_either_dn(self, *keys, **options):
        '''
        Returns the DN of a user
        The user can be active (active container) or delete (delete container)
        If the user does not exist, returns the Active user DN
        '''
        ldap = self.backend
        # Check that this value is a Active user
        try:
            active_dn = self.get_dn(*keys, **options)
            ldap.get_entry(active_dn, ['dn'])

            # The Active user exists
            dn = active_dn
        except errors.NotFound:
            # Check that this value is a Delete user
            delete_dn = self.get_delete_dn(*keys, **options)
            try:
                ldap.get_entry(delete_dn, ['dn'])

                # The Delete user exists
                dn = delete_dn
            except errors.NotFound:
                # The user is neither Active/Delete -> returns that Active DN
                dn = active_dn

        return dn

    def _normalize_manager(self, manager):
        """
        Given a userid verify the user's existence and return the dn.
        """
        return super(user, self).normalize_manager(manager,
                                                   self.active_container_dn)

    def get_preserved_attribute(self, entry, options):
        if options.get('raw', False):
            return
        delete_container_dn = DN(self.delete_container_dn, api.env.basedn)
        if entry.dn.endswith(delete_container_dn):
            entry['preserved'] = True
        elif options.get('all', False):
            entry['preserved'] = False
Beispiel #22
0
class selinuxusermap(LDAPObject):
    """
    SELinux User Map object.
    """
    container_dn = api.env.container_selinux
    object_name = _('SELinux User Map rule')
    object_name_plural = _('SELinux User Map rules')
    object_class = ['ipaassociation', 'ipaselinuxusermap']
    permission_filter_objectclasses = ['ipaselinuxusermap']
    default_attributes = [
        'cn',
        'ipaenabledflag',
        'description',
        'usercategory',
        'hostcategory',
        'ipaenabledflag',
        'memberuser',
        'memberhost',
        'seealso',
        'ipaselinuxuser',
    ]
    uuid_attribute = 'ipauniqueid'
    rdn_attribute = 'ipauniqueid'
    attribute_members = {
        'memberuser': ['user', 'group'],
        'memberhost': ['host', 'hostgroup'],
    }
    managed_permissions = {
        'System: Read SELinux User Maps': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'accesstime',
                'cn',
                'description',
                'hostcategory',
                'ipaenabledflag',
                'ipaselinuxuser',
                'ipauniqueid',
                'memberhost',
                'memberuser',
                'seealso',
                'usercategory',
                'objectclass',
                'member',
            },
        },
        'System: Add SELinux User Maps': {
            'ipapermright': {'add'},
            'replaces': [
                '(target = "ldap:///ipauniqueid=*,cn=usermap,cn=selinux,$SUFFIX")(version 3.0;acl "permission:Add SELinux User Maps";allow (add) groupdn = "ldap:///cn=Add SELinux User Maps,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'SELinux User Map Administrators'},
        },
        'System: Modify SELinux User Maps': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {
                'cn', 'ipaenabledflag', 'ipaselinuxuser', 'memberhost',
                'memberuser', 'seealso'
            },
            'replaces': [
                '(targetattr = "cn || memberuser || memberhost || seealso || ipaselinuxuser || ipaenabledflag")(target = "ldap:///ipauniqueid=*,cn=usermap,cn=selinux,$SUFFIX")(version 3.0;acl "permission:Modify SELinux User Maps";allow (write) groupdn = "ldap:///cn=Modify SELinux User Maps,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'SELinux User Map Administrators'},
        },
        'System: Remove SELinux User Maps': {
            'ipapermright': {'delete'},
            'replaces': [
                '(target = "ldap:///ipauniqueid=*,cn=usermap,cn=selinux,$SUFFIX")(version 3.0;acl "permission:Remove SELinux User Maps";allow (delete) groupdn = "ldap:///cn=Remove SELinux User Maps,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'SELinux User Map Administrators'},
        },
    }

    # These maps will not show as members of other entries

    label = _('SELinux User Maps')
    label_singular = _('SELinux User Map')

    takes_params = (
        Str(
            'cn',
            cli_name='name',
            label=_('Rule name'),
            primary_key=True,
        ),
        Str(
            'ipaselinuxuser',
            validate_selinuxuser,
            cli_name='selinuxuser',
            label=_('SELinux User'),
        ),
        Str(
            'seealso?',
            cli_name='hbacrule',
            label=_('HBAC Rule'),
            doc=_('HBAC Rule that defines the users, groups and hostgroups'),
        ),
        StrEnum(
            'usercategory?',
            cli_name='usercat',
            label=_('User category'),
            doc=_('User category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'hostcategory?',
            cli_name='hostcat',
            label=_('Host category'),
            doc=_('Host category the rule applies to'),
            values=(u'all', ),
        ),
        Str(
            'description?',
            cli_name='desc',
            label=_('Description'),
        ),
        Bool(
            'ipaenabledflag?',
            label=_('Enabled'),
            flags=['no_option'],
        ),
        Str(
            'memberuser_user?',
            label=_('Users'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberuser_group?',
            label=_('User Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_host?',
            label=_('Hosts'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_hostgroup?',
            label=_('Host Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
    )

    def _normalize_seealso(self, seealso):
        """
        Given a HBAC rule name verify its existence and return the dn.
        """
        if not seealso:
            return None

        try:
            dn = DN(seealso)
            return str(dn)
        except ValueError:
            try:
                entry_attrs = self.backend.find_entry_by_attr(
                    self.api.Object['hbacrule'].primary_key.name, seealso,
                    self.api.Object['hbacrule'].object_class, [''],
                    DN(self.api.Object['hbacrule'].container_dn,
                       api.env.basedn))
                seealso = entry_attrs.dn
            except errors.NotFound:
                raise errors.NotFound(
                    reason=_('HBAC rule %(rule)s not found') %
                    dict(rule=seealso))

        return seealso

    def _convert_seealso(self, ldap, entry_attrs, **options):
        """
        Convert an HBAC rule dn into a name
        """
        if options.get('raw', False):
            return

        if 'seealso' in entry_attrs:
            hbac_attrs = ldap.get_entry(entry_attrs['seealso'][0], ['cn'])
            entry_attrs['seealso'] = hbac_attrs['cn'][0]
Beispiel #23
0
class user_del(baseuser_del):
    __doc__ = _('Delete a user.')

    msg_summary = _('Deleted user "%(value)s"')

    takes_options = baseuser_del.takes_options + (Bool(
        'preserve?',
        exclude='cli',
    ), )

    def _preserve_user(self, pkey, delete_container, **options):
        assert isinstance(delete_container, DN)

        dn = self.obj.get_either_dn(pkey, **options)
        delete_dn = DN(dn[0], delete_container)
        ldap = self.obj.backend
        self.log.debug("preserve move %s -> %s" % (dn, delete_dn))

        if dn.endswith(delete_container):
            raise errors.ExecutionError(
                _('%s: user is already preserved' % pkey))
        # Check that this value is a Active user
        try:
            original_entry_attrs = self._exc_wrapper(pkey, options,
                                                     ldap.get_entry)(dn,
                                                                     ['dn'])
        except errors.NotFound:
            self.obj.handle_not_found(pkey)

        for callback in self.get_callbacks('pre'):
            dn = callback(self, ldap, dn, pkey, **options)
            assert isinstance(dn, DN)

        # start to move the entry to Delete container
        self._exc_wrapper(pkey, options, ldap.move_entry)(dn,
                                                          delete_dn,
                                                          del_old=True)

        # Then clear the credential attributes
        attrs_to_clear = [
            'krbPrincipalKey', 'krbLastPwdChange', 'krbPasswordExpiration',
            'userPassword'
        ]

        entry_attrs = self._exc_wrapper(pkey, options,
                                        ldap.get_entry)(delete_dn,
                                                        attrs_to_clear)

        clearedCredential = False
        for attr in attrs_to_clear:
            if attr.lower() in entry_attrs:
                del entry_attrs[attr]
                clearedCredential = True
        if clearedCredential:
            self._exc_wrapper(pkey, options, ldap.update_entry)(entry_attrs)

        # Then restore some original entry attributes
        attrs_to_restore = [
            'secretary', 'managedby', 'manager', 'ipauniqueid', 'uidnumber',
            'gidnumber', 'passwordHistory'
        ]

        entry_attrs = self._exc_wrapper(pkey, options,
                                        ldap.get_entry)(delete_dn,
                                                        attrs_to_restore)

        restoreAttr = False
        for attr in attrs_to_restore:
            if ((attr.lower() in original_entry_attrs)
                    and not (attr.lower() in entry_attrs)):
                restoreAttr = True
                entry_attrs[attr.lower()] = original_entry_attrs[attr.lower()]
        if restoreAttr:
            self._exc_wrapper(pkey, options, ldap.update_entry)(entry_attrs)

    def pre_callback(self, ldap, dn, *keys, **options):
        dn = self.obj.get_either_dn(*keys, **options)

        # For User life Cycle: user-del is a common plugin
        # command to delete active user (active container) and
        # delete user (delete container).
        # If the target entry is a Delete entry, skip the orphaning/removal
        # of OTP tokens.
        check_protected_member(keys[-1])

        if not options.get('preserve', False):
            # Remove any ID overrides tied with this user
            try:
                remove_ipaobject_overrides(self.obj.backend, self.obj.api, dn)
            except errors.NotFound:
                self.obj.handle_not_found(*keys)

        if dn.endswith(DN(self.obj.delete_container_dn, api.env.basedn)):
            return dn

        # Delete all tokens owned and managed by this user.
        # Orphan all tokens owned but not managed by this user.
        owner = self.api.Object.user.get_primary_key_from_dn(dn)
        results = self.api.Command.otptoken_find(ipatokenowner=owner,
                                                 no_members=False)['result']
        for token in results:
            orphan = not [
                x for x in token.get('managedby_user', []) if x == owner
            ]
            token = self.api.Object.otptoken.get_primary_key_from_dn(
                token['dn'])
            if orphan:
                self.api.Command.otptoken_mod(token, ipatokenowner=None)
            else:
                self.api.Command.otptoken_del(token)

        return dn

    def execute(self, *keys, **options):

        # We are going to permanent delete or the user is already in the delete container.
        delete_container = DN(self.obj.delete_container_dn,
                              self.api.env.basedn)

        # The user to delete is active and there is no 'no_preserve' option
        if options.get('preserve', False):
            failed = []
            preserved = []
            for pkey in keys[-1]:
                try:
                    self._preserve_user(pkey, delete_container, **options)
                    preserved.append(pkey_to_value(pkey, options))
                except Exception:
                    if not options.get('continue', False):
                        raise
                    failed.append(pkey_to_value(pkey, options))

            val = dict(result=dict(failed=failed), value=preserved)
            return val
        else:
            return super(user_del, self).execute(*keys, **options)
Beispiel #24
0
class config(LDAPObject):
    """
    IPA configuration object
    """
    object_name = _('configuration options')
    default_attributes = [
        'ipamaxusernamelength', 'ipahomesrootdir', 'ipadefaultloginshell',
        'ipadefaultprimarygroup', 'ipadefaultemaildomain', 'ipasearchtimelimit',
        'ipasearchrecordslimit', 'ipausersearchfields', 'ipagroupsearchfields',
        'ipamigrationenabled', 'ipacertificatesubjectbase',
        'ipapwdexpadvnotify', 'ipaselinuxusermaporder',
        'ipaselinuxusermapdefault', 'ipaconfigstring', 'ipakrbauthzdata',
        'ipauserauthtype'
    ]
    container_dn = DN(('cn', 'ipaconfig'), ('cn', 'etc'))
    permission_filter_objectclasses = ['ipaguiconfig']
    managed_permissions = {
        'System: Read Global Configuration': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'cn', 'objectclass',
                'ipacertificatesubjectbase', 'ipaconfigstring',
                'ipadefaultemaildomain', 'ipadefaultloginshell',
                'ipadefaultprimarygroup', 'ipagroupobjectclasses',
                'ipagroupsearchfields', 'ipahomesrootdir',
                'ipakrbauthzdata', 'ipamaxusernamelength',
                'ipamigrationenabled', 'ipapwdexpadvnotify',
                'ipaselinuxusermapdefault', 'ipaselinuxusermaporder',
                'ipasearchrecordslimit', 'ipasearchtimelimit',
                'ipauserauthtype', 'ipauserobjectclasses',
                'ipausersearchfields', 'ipacustomfields',
            },
        },
    }

    label = _('Configuration')
    label_singular = _('Configuration')

    takes_params = (
        Int('ipamaxusernamelength',
            cli_name='maxusername',
            label=_('Maximum username length'),
            minvalue=1,
            maxvalue=255,
        ),
        IA5Str('ipahomesrootdir',
            cli_name='homedirectory',
            label=_('Home directory base'),
            doc=_('Default location of home directories'),
        ),
        Str('ipadefaultloginshell',
            cli_name='defaultshell',
            label=_('Default shell'),
            doc=_('Default shell for new users'),
        ),
        Str('ipadefaultprimarygroup',
            cli_name='defaultgroup',
            label=_('Default users group'),
            doc=_('Default group for new users'),
        ),
        Str('ipadefaultemaildomain?',
            cli_name='emaildomain',
            label=_('Default e-mail domain'),
            doc=_('Default e-mail domain'),
        ),
        Int('ipasearchtimelimit',
            cli_name='searchtimelimit',
            label=_('Search time limit'),
            doc=_('Maximum amount of time (seconds) for a search (-1 or 0 is unlimited)'),
            minvalue=-1,
        ),
        Int('ipasearchrecordslimit',
            cli_name='searchrecordslimit',
            label=_('Search size limit'),
            doc=_('Maximum number of records to search (-1 or 0 is unlimited)'),
            minvalue=-1,
        ),
        IA5Str('ipausersearchfields',
            cli_name='usersearch',
            label=_('User search fields'),
            doc=_('A comma-separated list of fields to search in when searching for users'),
        ),
        IA5Str('ipagroupsearchfields',
            cli_name='groupsearch',
            label='Group search fields',
            doc=_('A comma-separated list of fields to search in when searching for groups'),
        ),
        Bool('ipamigrationenabled',
            cli_name='enable_migration',
            label=_('Enable migration mode'),
            doc=_('Enable migration mode'),
        ),
        DNParam('ipacertificatesubjectbase',
            cli_name='subject',
            label=_('Certificate Subject base'),
            doc=_('Base for certificate subjects (OU=Test,O=Example)'),
            flags=['no_update'],
        ),
        Str('ipagroupobjectclasses+',
            cli_name='groupobjectclasses',
            label=_('Default group objectclasses'),
            doc=_('Default group objectclasses (comma-separated list)'),
        ),
        Str('ipauserobjectclasses+',
            cli_name='userobjectclasses',
            label=_('Default user objectclasses'),
            doc=_('Default user objectclasses (comma-separated list)'),
        ),
        Int('ipapwdexpadvnotify',
            cli_name='pwdexpnotify',
            label=_('Password Expiration Notification (days)'),
            doc=_('Number of days\'s notice of impending password expiration'),
            minvalue=0,
        ),
        StrEnum('ipaconfigstring*',
            cli_name='ipaconfigstring',
            label=_('Password plugin features'),
            doc=_('Extra hashes to generate in password plug-in'),
            values=(u'AllowNThash',
                    u'KDC:Disable Last Success', u'KDC:Disable Lockout',
                    u'KDC:Disable Default Preauth for SPNs'),
        ),
        Str('ipaselinuxusermaporder',
            label=_('SELinux user map order'),
            doc=_('Order in increasing priority of SELinux users, delimited by $'),
        ),
        Str('ipaselinuxusermapdefault?',
            label=_('Default SELinux user'),
            doc=_('Default SELinux user when no match is found in SELinux map rule'),
        ),
        StrEnum('ipakrbauthzdata*',
            cli_name='pac_type',
            label=_('Default PAC types'),
            doc=_('Default types of PAC supported for services'),
            values=(u'MS-PAC', u'PAD', u'nfs:NONE'),
        ),
        StrEnum('ipauserauthtype*',
            cli_name='user_auth_type',
            label=_('Default user authentication types'),
            doc=_('Default types of supported user authentication'),
            values=(u'password', u'radius', u'otp', u'disabled'),
        ),
        Str(
            'ipa_master_server*',
            label=_('IPA masters'),
            doc=_('List of all IPA masters'),
            flags={'virtual_attribute', 'no_create', 'no_update'}
        ),
        Str(
            'ca_server_server*',
            label=_('IPA CA servers'),
            doc=_('IPA servers configured as certificate authority'),
            flags={'virtual_attribute', 'no_create', 'no_update'}
        ),
        Str(
            'ntp_server_server*',
            label=_('IPA NTP servers'),
            doc=_('IPA servers with enabled NTP'),
            flags={'virtual_attribute', 'no_create', 'no_update'}
        ),
        Str(
            'ca_renewal_master_server?',
            label=_('IPA CA renewal master'),
            doc=_('Renewal master for IPA certificate authority'),
            flags={'virtual_attribute', 'no_create'}
        )
    )

    def get_dn(self, *keys, **kwargs):
        return DN(('cn', 'ipaconfig'), ('cn', 'etc'), api.env.basedn)

    def show_servroles_attributes(self, entry_attrs, **options):
        if options.get('raw', False):
            return

        backend = self.api.Backend.serverroles

        for role in ("CA server", "IPA master", "NTP server"):
            config = backend.config_retrieve(role)
            entry_attrs.update(config)
Beispiel #25
0
class selinuxusermap(LDAPObject):
    """
    SELinux User Map object.
    """
    container_dn = api.env.container_selinux
    object_name = _('SELinux User Map rule')
    object_name_plural = _('SELinux User Map rules')
    object_class = ['ipaassociation', 'ipaselinuxusermap']
    default_attributes = [
        'cn',
        'ipaenabledflag',
        'description',
        'usercategory',
        'hostcategory',
        'ipaenabledflag',
        'memberuser',
        'memberhost',
        'memberhostgroup',
        'seealso',
        'ipaselinuxuser',
    ]
    uuid_attribute = 'ipauniqueid'
    rdn_attribute = 'ipauniqueid'
    attribute_members = {
        'memberuser': ['user', 'group'],
        'memberhost': ['host', 'hostgroup'],
    }

    # These maps will not show as members of other entries

    label = _('SELinux User Maps')
    label_singular = _('SELinux User Map')

    takes_params = (
        Str(
            'cn',
            cli_name='name',
            label=_('Rule name'),
            primary_key=True,
        ),
        Str(
            'ipaselinuxuser',
            validate_selinuxuser,
            cli_name='selinuxuser',
            label=_('SELinux User'),
        ),
        Str(
            'seealso?',
            cli_name='hbacrule',
            label=_('HBAC Rule'),
            doc=_('HBAC Rule that defines the users, groups and hostgroups'),
        ),
        StrEnum(
            'usercategory?',
            cli_name='usercat',
            label=_('User category'),
            doc=_('User category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'hostcategory?',
            cli_name='hostcat',
            label=_('Host category'),
            doc=_('Host category the rule applies to'),
            values=(u'all', ),
        ),
        Str(
            'description?',
            cli_name='desc',
            label=_('Description'),
        ),
        Bool(
            'ipaenabledflag?',
            label=_('Enabled'),
            flags=['no_option'],
        ),
        Str(
            'memberuser_user?',
            label=_('Users'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberuser_group?',
            label=_('User Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_host?',
            label=_('Hosts'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_hostgroup?',
            label=_('Host Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
    )

    def _normalize_seealso(self, seealso):
        """
        Given a HBAC rule name verify its existence and return the dn.
        """
        if not seealso:
            return None

        try:
            dn = DN(seealso)
            return str(dn)
        except ValueError:
            try:
                (dn, entry_attrs) = self.backend.find_entry_by_attr(
                    self.api.Object['hbacrule'].primary_key.name, seealso,
                    self.api.Object['hbacrule'].object_class, [''],
                    self.api.Object['hbacrule'].container_dn)
                seealso = dn
            except errors.NotFound:
                raise errors.NotFound(
                    reason=_('HBAC rule %(rule)s not found') %
                    dict(rule=seealso))

        return seealso

    def _convert_seealso(self, ldap, entry_attrs, **options):
        """
        Convert an HBAC rule dn into a name
        """
        if options.get('raw', False):
            return

        if 'seealso' in entry_attrs:
            (hbac_dn, hbac_attrs) = ldap.get_entry(entry_attrs['seealso'][0],
                                                   ['cn'])
            entry_attrs['seealso'] = hbac_attrs['cn'][0]
Beispiel #26
0
def convert_nsaccountlock(entry_attrs):
    if not 'nsaccountlock' in entry_attrs:
        entry_attrs['nsaccountlock'] = False
    else:
        nsaccountlock = Bool('temp')
        entry_attrs['nsaccountlock'] = nsaccountlock.convert(entry_attrs['nsaccountlock'][0])
Beispiel #27
0
class sudorule(LDAPObject):
    """
    Sudo Rule object.
    """
    container_dn = api.env.container_sudorule
    object_name = _('sudo rule')
    object_name_plural = _('sudo rules')
    object_class = ['ipaassociation', 'ipasudorule']
    default_attributes = [
        'cn',
        'ipaenabledflag',
        'externaluser',
        'description',
        'usercategory',
        'hostcategory',
        'cmdcategory',
        'memberuser',
        'memberhost',
        'memberallowcmd',
        'memberdenycmd',
        'ipasudoopt',
        'ipasudorunas',
        'ipasudorunasgroup',
        'ipasudorunasusercategory',
        'ipasudorunasgroupcategory',
        'sudoorder',
    ]
    uuid_attribute = 'ipauniqueid'
    rdn_attribute = 'ipauniqueid'
    attribute_members = {
        'memberuser': ['user', 'group'],
        'memberhost': ['host', 'hostgroup'],
        'memberallowcmd': ['sudocmd', 'sudocmdgroup'],
        'memberdenycmd': ['sudocmd', 'sudocmdgroup'],
        'ipasudorunas': ['user', 'group'],
        'ipasudorunasgroup': ['group'],
    }

    label = _('Sudo Rules')
    label_singular = _('Sudo Rule')

    takes_params = (
        Str(
            'cn',
            cli_name='sudorule_name',
            label=_('Rule name'),
            primary_key=True,
        ),
        Str(
            'description?',
            cli_name='desc',
            label=_('Description'),
        ),
        Bool(
            'ipaenabledflag?',
            label=_('Enabled'),
            flags=['no_option'],
        ),
        StrEnum(
            'usercategory?',
            cli_name='usercat',
            label=_('User category'),
            doc=_('User category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'hostcategory?',
            cli_name='hostcat',
            label=_('Host category'),
            doc=_('Host category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'cmdcategory?',
            cli_name='cmdcat',
            label=_('Command category'),
            doc=_('Command category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'ipasudorunasusercategory?',
            cli_name='runasusercat',
            label=_('RunAs User category'),
            doc=_('RunAs User category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'ipasudorunasgroupcategory?',
            cli_name='runasgroupcat',
            label=_('RunAs Group category'),
            doc=_('RunAs Group category the rule applies to'),
            values=(u'all', ),
        ),
        Int(
            'sudoorder?',
            cli_name='order',
            label=_('Sudo order'),
            doc=_('integer to order the Sudo rules'),
            default=0,
            minvalue=0,
        ),
        Str(
            'memberuser_user?',
            label=_('Users'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberuser_group?',
            label=_('User Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_host?',
            label=_('Hosts'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_hostgroup?',
            label=_('Host Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberallowcmd_sudocmd?',
            label=_('Sudo Allow Commands'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberdenycmd_sudocmd?',
            label=_('Sudo Deny Commands'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberallowcmd_sudocmdgroup?',
            label=_('Sudo Allow Command Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberdenycmd_sudocmdgroup?',
            label=_('Sudo Deny Command Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'ipasudorunas_user?',
            label=_('RunAs Users'),
            doc=_('Run as a user'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'ipasudorunas_group?',
            label=_('Groups of RunAs Users'),
            doc=_('Run as any user within a specified group'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'externaluser?',
            validate_externaluser,
            cli_name='externaluser',
            label=_('External User'),
            doc=_('External User the rule applies to (sudorule-find only)'),
        ),
        Str(
            'ipasudorunasextuser?',
            validate_runasextuser,
            cli_name='runasexternaluser',
            label=_('RunAs External User'),
            doc=_(
                'External User the commands can run as (sudorule-find only)'),
        ),
        Str(
            'ipasudorunasextgroup?',
            validate_runasextgroup,
            cli_name='runasexternalgroup',
            label=_('RunAs External Group'),
            doc=_(
                'External Group the commands can run as (sudorule-find only)'),
        ),
        Str(
            'ipasudoopt?',
            label=_('Sudo Option'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'ipasudorunasgroup_group?',
            label=_('RunAs Groups'),
            doc=_('Run with the gid of a specified POSIX group'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        external_host_param,
    )

    order_not_unique_msg = _(
        'order must be a unique value (%(order)d already used by %(rule)s)')

    def check_order_uniqueness(self, *keys, **options):
        if 'sudoorder' in options:
            entries = self.methods.find(
                sudoorder=options['sudoorder'])['result']
            if len(entries) > 0:
                rule_name = entries[0]['cn'][0]
                raise errors.ValidationError(name='order',
                                             error=self.order_not_unique_msg %
                                             {
                                                 'order': options['sudoorder'],
                                                 'rule': rule_name,
                                             })