summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/__init__.py2
-rwxr-xr-xipalib/aci.py26
-rw-r--r--ipalib/config.py5
-rw-r--r--ipalib/constants.py73
-rw-r--r--ipalib/dn.py1337
-rw-r--r--ipalib/encoder.py213
-rw-r--r--ipalib/parameters.py19
-rw-r--r--ipalib/plugins/aci.py52
-rw-r--r--ipalib/plugins/automember.py25
-rw-r--r--ipalib/plugins/automount.py6
-rw-r--r--ipalib/plugins/baseldap.py188
-rw-r--r--ipalib/plugins/config.py7
-rw-r--r--ipalib/plugins/dns.py34
-rw-r--r--ipalib/plugins/entitle.py9
-rw-r--r--ipalib/plugins/group.py8
-rw-r--r--ipalib/plugins/hbacrule.py9
-rw-r--r--ipalib/plugins/host.py15
-rw-r--r--ipalib/plugins/hostgroup.py8
-rw-r--r--ipalib/plugins/krbtpolicy.py5
-rw-r--r--ipalib/plugins/migration.py86
-rw-r--r--ipalib/plugins/netgroup.py7
-rw-r--r--ipalib/plugins/passwd.py3
-rw-r--r--ipalib/plugins/permission.py19
-rw-r--r--ipalib/plugins/pkinit.py5
-rw-r--r--ipalib/plugins/pwpolicy.py26
-rw-r--r--ipalib/plugins/range.py8
-rw-r--r--ipalib/plugins/selinuxusermap.py7
-rw-r--r--ipalib/plugins/service.py7
-rw-r--r--ipalib/plugins/sudorule.py16
-rw-r--r--ipalib/plugins/trust.py8
-rw-r--r--ipalib/plugins/user.py18
-rw-r--r--ipalib/plugins/virtual.py3
-rw-r--r--ipalib/rpc.py5
-rw-r--r--ipalib/session.py2
-rw-r--r--ipalib/util.py9
-rw-r--r--ipalib/x509.py2
36 files changed, 454 insertions, 1818 deletions
diff --git a/ipalib/__init__.py b/ipalib/__init__.py
index dd861a826..8bf37f048 100644
--- a/ipalib/__init__.py
+++ b/ipalib/__init__.py
@@ -878,7 +878,7 @@ from backend import Backend
from frontend import Command, LocalOrRemote, Updater
from frontend import Object, Method, Property
from crud import Create, Retrieve, Update, Delete, Search
-from parameters import DefaultFrom, Bool, Flag, Int, Decimal, Bytes, Str, IA5Str, Password
+from parameters import DefaultFrom, Bool, Flag, Int, Decimal, Bytes, Str, IA5Str, Password, DNParam
from parameters import BytesEnum, StrEnum, AccessTime, File
from errors import SkipPluginModule
from text import _, ngettext, GettextFactory, NGettextFactory
diff --git a/ipalib/aci.py b/ipalib/aci.py
index 1b607a393..53c0053c6 100755
--- a/ipalib/aci.py
+++ b/ipalib/aci.py
@@ -254,32 +254,6 @@ class ACI:
# We got this far so lets declare them the same
return True
-def extract_group_cns(aci_list, client):
- """
- Extracts all the cn's from a list of aci's and returns them as a hash
- from group_dn to group_cn.
-
- It first tries to cheat by looking at the first rdn for the
- group dn. If that's not cn for some reason, it looks up the group.
- """
- group_dn_to_cn = {}
- for aci in aci_list:
- for dn in (aci.source_group, aci.dest_group):
- if not group_dn_to_cn.has_key(dn):
- rdn_list = ldap.explode_dn(dn, 0)
- first_rdn = rdn_list[0]
- (type,value) = first_rdn.split('=')
- if type == "cn":
- group_dn_to_cn[dn] = value
- else:
- try:
- group = client.get_entry_by_dn(dn, ['cn'])
- group_dn_to_cn[dn] = group.getValue('cn')
- except Exception:
- group_dn_to_cn[dn] = 'unknown'
-
- return group_dn_to_cn
-
if __name__ == '__main__':
# a = ACI('(targetattr="title")(targetfilter="(memberOf=cn=bar,cn=groups,cn=accounts ,dc=example,dc=com)")(version 3.0;acl "foobar";allow (write) groupdn="ldap:///cn=foo,cn=groups,cn=accounts,dc=example,dc=com";)')
# print a
diff --git a/ipalib/config.py b/ipalib/config.py
index 5e3ef8d9b..3c9aeaa28 100644
--- a/ipalib/config.py
+++ b/ipalib/config.py
@@ -35,6 +35,7 @@ import os
from os import path
import sys
from socket import getfqdn
+from ipapython.dn import DN
from base import check_name
from constants import CONFIG_SECTION
@@ -256,12 +257,14 @@ class Env(object):
value = m[value]
elif value.isdigit():
value = int(value)
+ elif key in ('basedn'):
+ value = DN(value)
else:
try:
value = float(value)
except (TypeError, ValueError):
pass
- assert type(value) in (unicode, int, float, bool, NoneType)
+ assert type(value) in (unicode, int, float, bool, NoneType, DN)
object.__setattr__(self, key, value)
self.__d[key] = value
diff --git a/ipalib/constants.py b/ipalib/constants.py
index f0f89a3b3..59649865b 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -22,6 +22,7 @@
All constants centralised in one file.
"""
import socket
+from ipapython.dn import DN
from ipapython.version import VERSION
try:
FQDN = socket.getfqdn()
@@ -69,44 +70,44 @@ DEFAULT_CONFIG = (
# Domain, realm, basedn:
('domain', 'example.com'),
('realm', 'EXAMPLE.COM'),
- ('basedn', 'dc=example,dc=com'),
+ ('basedn', DN(('dc', 'example'), ('dc', 'com'))),
# LDAP containers:
- ('container_accounts', 'cn=accounts'),
- ('container_user', 'cn=users,cn=accounts'),
- ('container_group', 'cn=groups,cn=accounts'),
- ('container_service', 'cn=services,cn=accounts'),
- ('container_host', 'cn=computers,cn=accounts'),
- ('container_hostgroup', 'cn=hostgroups,cn=accounts'),
- ('container_rolegroup', 'cn=roles,cn=accounts'),
- ('container_permission', 'cn=permissions,cn=pbac'),
- ('container_privilege', 'cn=privileges,cn=pbac'),
- ('container_automount', 'cn=automount'),
- ('container_policies', 'cn=policies'),
- ('container_configs', 'cn=configs,cn=policies'),
- ('container_roles', 'cn=roles,cn=policies'),
- ('container_applications', 'cn=applications,cn=configs,cn=policies'),
- ('container_policygroups', 'cn=policygroups,cn=configs,cn=policies'),
- ('container_policylinks', 'cn=policylinks,cn=configs,cn=policies'),
- ('container_netgroup', 'cn=ng,cn=alt'),
- ('container_hbac', 'cn=hbac'),
- ('container_hbacservice', 'cn=hbacservices,cn=hbac'),
- ('container_hbacservicegroup', 'cn=hbacservicegroups,cn=hbac'),
- ('container_dns', 'cn=dns'),
- ('container_virtual', 'cn=virtual operations,cn=etc'),
- ('container_sudorule', 'cn=sudorules,cn=sudo'),
- ('container_sudocmd', 'cn=sudocmds,cn=sudo'),
- ('container_sudocmdgroup', 'cn=sudocmdgroups,cn=sudo'),
- ('container_entitlements', 'cn=entitlements,cn=etc'),
- ('container_automember', 'cn=automember,cn=etc'),
- ('container_selinux', 'cn=usermap,cn=selinux'),
- ('container_s4u2proxy', 'cn=s4u2proxy,cn=etc'),
- ('container_cifsdomains', 'cn=ad,cn=etc'),
- ('container_trusts', 'cn=trusts'),
- ('container_adtrusts', 'cn=ad,cn=trusts'),
- ('container_ranges', 'cn=ranges,cn=etc'),
- ('container_dna', 'cn=dna,cn=ipa,cn=etc'),
- ('container_dna_posix_ids', 'cn=posix-ids,cn=dna,cn=ipa,cn=etc'),
+ ('container_accounts', DN(('cn', 'accounts'))),
+ ('container_user', DN(('cn', 'users'), ('cn', 'accounts'))),
+ ('container_group', DN(('cn', 'groups'), ('cn', 'accounts'))),
+ ('container_service', DN(('cn', 'services'), ('cn', 'accounts'))),
+ ('container_host', DN(('cn', 'computers'), ('cn', 'accounts'))),
+ ('container_hostgroup', DN(('cn', 'hostgroups'), ('cn', 'accounts'))),
+ ('container_rolegroup', DN(('cn', 'roles'), ('cn', 'accounts'))),
+ ('container_permission', DN(('cn', 'permissions'), ('cn', 'pbac'))),
+ ('container_privilege', DN(('cn', 'privileges'), ('cn', 'pbac'))),
+ ('container_automount', DN(('cn', 'automount'))),
+ ('container_policies', DN(('cn', 'policies'))),
+ ('container_configs', DN(('cn', 'configs'), ('cn', 'policies'))),
+ ('container_roles', DN(('cn', 'roles'), ('cn', 'policies'))),
+ ('container_applications', DN(('cn', 'applications'), ('cn', 'configs'), ('cn', 'policies'))),
+ ('container_policygroups', DN(('cn', 'policygroups'), ('cn', 'configs'), ('cn', 'policies'))),
+ ('container_policylinks', DN(('cn', 'policylinks'), ('cn', 'configs'), ('cn', 'policies'))),
+ ('container_netgroup', DN(('cn', 'ng'), ('cn', 'alt'))),
+ ('container_hbac', DN(('cn', 'hbac'))),
+ ('container_hbacservice', DN(('cn', 'hbacservices'), ('cn', 'hbac'))),
+ ('container_hbacservicegroup', DN(('cn', 'hbacservicegroups'), ('cn', 'hbac'))),
+ ('container_dns', DN(('cn', 'dns'))),
+ ('container_virtual', DN(('cn', 'virtual operations'), ('cn', 'etc'))),
+ ('container_sudorule', DN(('cn', 'sudorules'), ('cn', 'sudo'))),
+ ('container_sudocmd', DN(('cn', 'sudocmds'), ('cn', 'sudo'))),
+ ('container_sudocmdgroup', DN(('cn', 'sudocmdgroups'), ('cn', 'sudo'))),
+ ('container_entitlements', DN(('cn', 'entitlements'), ('cn', 'etc'))),
+ ('container_automember', DN(('cn', 'automember'), ('cn', 'etc'))),
+ ('container_selinux', DN(('cn', 'usermap'), ('cn', 'selinux'))),
+ ('container_s4u2proxy', DN(('cn', 's4u2proxy'), ('cn', 'etc'))),
+ ('container_cifsdomains', DN(('cn', 'ad'), ('cn', 'etc'))),
+ ('container_trusts', DN(('cn', 'trusts'))),
+ ('container_adtrusts', DN(('cn', 'ad'), ('cn', 'trusts'))),
+ ('container_ranges', DN(('cn', 'ranges'), ('cn', 'etc'))),
+ ('container_dna', DN(('cn', 'dna'), ('cn', 'ipa'), ('cn', 'etc'))),
+ ('container_dna_posix_ids', DN(('cn', 'posix-ids'), ('cn', 'dna'), ('cn', 'ipa'), ('cn', 'etc'))),
# Ports, hosts, and URIs:
# FIXME: let's renamed xmlrpc_uri to rpc_xml_uri
diff --git a/ipalib/dn.py b/ipalib/dn.py
deleted file mode 100644
index 6f2f7deb5..000000000
--- a/ipalib/dn.py
+++ /dev/null
@@ -1,1337 +0,0 @@
-# Authors:
-# John Dennis <jdennis@redhat.com>
-#
-# Copyright (C) 2011 Red Hat
-# see file 'COPYING' for use and warranty information
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from ldap.dn import str2dn, dn2str
-from ldap import DECODING_ERROR
-import codecs
-import sys
-
-utf8_codec = codecs.lookup('utf-8')
-
-__all__ = ['AVA', 'RDN', 'DN']
-
-'''
-
-Goal
-----
-
-To allow a Python programmer the ability to operate on DN's
-(Distinguished Names) in a simple intuitive manner supporting all the
-Pythonic mechanisms for manipulating objects such that the simple
-majority case remains simple with simple code, yet the corner cases
-are fully supported. With the result both simple and complex cases are
-100% correct.
-
-This is achieved with a fair of amount of syntax sugar which is best
-described as "Do What I Mean" (i.e. DWIM). The class implementations
-take simple expressions and internally convert them to their more
-complex full definitions hiding much of the complexity from the
-programmer.
-
-Anatomy of a DN
----------------
-
-Some definitions:
-
-AVA
- An AVA is an Attribute Value Assertion. In more simple terms it's
- an attribute value pair typically expressed as attr=value
- (e.g. cn=Bob). Both the attr and value in an AVA when expressed in
- a string representation are subject to encoding rules.
-
-RDN
- A RDN is a Relative Distinguished Name. A RDN is a non-empty set of
- AVA's. In the common case a RDN is single valued consisting of 1
- AVA (e.g. cn=Bob). But a RDN may be multi-valued consisting of
- more than one AVA. Because the RDN is a set of AVA's the AVA's are
- unordered when they appear in a multi-valued RDN. In the string
- representation of a RDN AVA's are separated by the plus sign (+).
-
-DN
- A DN is a ordered sequence of 1 or more RDN's. In the string
- representation of a DN each RDN is separated by a comma (,)
-
-Thus a DN is:
-
-Sequence of set of <encoded attr, encoded value> pairs
-
-The following are valid DN's
-
-# 1 RDN with 1 AVA (e.g. cn=Bob)
-RDN(AVA)
-
-# 2 RDN's each with 1 AVA (e.g. cn=Bob,dc=redhat.com)
-RDN(AVA),RDN(AVA)
-
-# 2 RDN's the first RDN is multi-valued with 2 AVA's
-# the second RDN is singled valued with 1 AVA
-# (e.g. cn=Bob+ou=people,dc=redhat.com
-RDN({AVA,AVA}),RDN(AVA)
-
-Common programming mistakes
----------------------------
-
-DN's present a pernicious problem for programmers. They appear to have
-a very simple string format in the majority case, a sequence of
-attr=value pairs separated by commas. For example:
-
-dn='cn=Bob,ou=people,dc=redhat,dc=com'
-
-As such there is a tendency to believe you can form DN's by simple
-string manipulations such as:
-
-dn='%s=%s' % ('cn','Bob') + ',ou=people,dc=redhat,dc=com'
-
-Or to extract a attr & value by searching the string, for example:
-
-attr=dn[0 : dn.find('=')]
-value=dn[dn.find('=')+1 : dn.find(',')]
-
-Or compare a value returned by an LDAP query to a known value:
-
-if value == 'Bob'
-
-All of these simple coding assumptions are WRONG and will FAIL when a
-DN is not one of the simple DN's (simple DN's are probably the 95% of
-all DN's). This is what makes DN handling pernicious. What works in
-95% of the cases and is simple, fails for the 5% of DN's which are not
-simple.
-
-Examples of where the simple assumptions fail are:
-
-* A RDN may be multi-valued
-
-* A multi-valued RDN has no ordering on it's components
-
-* Attr's and values must be UTF-8 encoded
-
-* String representations of AVA's, RDN's and DN's must be completely UTF-8
-
-* An attr or value may have reserved characters which must be escaped.
-
-* Whitespace needs special handling
-
-To complicate matters a bit more the RFC for the string representation
-of DN's (RFC 4514) permits a variety of different syntax's each of
-which can evaluate to exactly the same DN but have different string
-representations. For example, the attr "r,w" which contains a reserved
-character (the comma) can be encoded as a string in these different
-ways:
-
-'r\,w' # backslash escape
-'r\2cw' # hexadecimal ascii escape
-'#722C77' # binary encoded
-
-It should be clear a DN string may NOT be a simple string, rather a DN
-string is ENCODED. For simple strings the encoding of the DN is
-identical to the simple string value (this common case leads to
-erroneous assumptions and bugs because it does not account for
-encodings).
-
-The openldap library we use at the client level uses the backslash
-escape form. The LDAP server we use uses the hexadecimal ascii escape
-form. Thus 'r,w' appears as 'r\,w' when sent from the client to the
-LDAP server as part of a DN. But when it's returned as a DN from the
-server in an LDAP search it's returned as 'r\2cw'. Any attempt to
-compare 'r\,w' to 'r\2cw' for equality will fail despite the fact they
-are indeed equal once decoded. Such a test fails because you're
-comparing two different encodings of the same value. In MIME you
-wouldn't expect the base64 encoding of a string to be equal to the
-same string encoded as quoted-printable would you?
-
-When you are comparing attrs or values which are part of a DN and
-other string you MUST:
-
-* Know if either of the strings have been encoded and make sure you're
- comparing only decoded components component-wise.
-
-* Extract the component from the DN and decode it. You CANNOT decode
- the entire DN as a string and operate on it. Why? Consider a value
- with a comma embedded in it. For example:
-
- cn=r\2cw,cn=privilege
-
- Is a DN with 2 RDN components: cn=r,w followed by "cn=privilege"
-
- But if you decode the entire DN string as a whole you would get:
-
- cn=r,w,cn=privilege
-
- Which is a malformed DN with 3 RDN's, the 2nd RDN is invalid.
-
-* Determine if a RDN is multi-valued, if so you must account
- for the fact each AVA component in the multi-valued RDN can appear
- in any order and still be equivalent. For example the following two
- RDN's are equal:
-
- cn=Bob+ou=people
- ou=people+cn=Bob
-
- In addition each AVA (cn=Bob & ou=people) needs to be
- INDEPENDENTLY decoded prior to comparing the unordered set of AVA's
- in the multi-valued RDN.
-
-If you are trying to form a new DN or RDN from a raw string you cannot
-simply do string concatenation or string formatting unless you ESCAPE
-the components independently prior to concatenation, for example:
-
- base = 'dc=redhat,dc=com'
- value = 'r,w'
- dn = 'cn=%s,%s' % (value, base)
-
-Will result in the malformed DN 'cn=r,w,dc=redhat,dc=com'
-
-Syntax Sugar
-------------
-
-The majority of DN's have a simple string form:
-
-attr=value,attr=value
-
-We want the programmer to be able to create DN's, compare them, and
-operate on their components as simply and concisely as possible so
-the classes are implemented to provide a lot of syntax sugar.
-
-The classes automatically handle UTF-8 <-> Unicode conversions. Every
-attr and value which is returned from a class will be Unicode. Every
-attr and value assigned into an object will be promoted to
-Unicode. All string representations in RFC 4514 format will be UTF-8
-and properly escaped. Thus at the "user" or "API" level every string
-is Unicode with the single exception that the str() method returns RFC
-compliant escaped UTF-8.
-
-RDN's are assumed to be single-valued. If you need a multi-valued RDN
-(an exception) you must explicitly create a multi-valued RDN.
-
-Thus DN's are assumed to be a sequence of attr, value pairs, which is
-equivalent to a sequence of RDN's. The attr and value in the pair MUST
-be strings.
-
-The DN and RDN constructors take a sequence, the constructor parses
-the sequence to find items it knows about.
-
-The DN constructor will accept in it's sequence:
- * tuple of 2 strings, converting it to an RDN
- * list of 2 strings, converting it to an RDN
- * a RDN object
- * a DN syntax string (e.g. 'cn=Bob,dc=redhat.com')
-
-Note DN syntax strings should be avoided if possible when passing to a
-constructor because they run afoul of the problems outlined above
-which the DN, RDN & AVA classes are meant to overcome. But sometimes a
-DN syntax string is all you have to work with. DN strings which come
-from a LDAP library or server will be properly formed and it's safe to
-use those. However DN strings provided via user input should be
-treated suspiciously as they may be improperly formed. You can test
-for this by passing the string to the DN constructor and see if it
-throws an exception.
-
-The sequence passed to the DN constructor takes each item in order,
-produces one or more RDN's from it and appends those RDN in order to
-its internal RDN sequence.
-
-For example:
-
- DN(('cn', 'Bob'), ('dc', 'redhat.com'))
-
-This is equivalent to the DN string:
-
- cn=Bob,dc=redhat.com
-
-And is exactly equal to:
-
- DN(RDN(AVA('cn','Bob')),RDN(AVA('dc','redhat.com')))
-
-The following are alternative syntax's which are all exactly
-equivalent to the above example.
-
- DN(['cn', 'Bob'], ['dc', 'redhat.com'])
- DN(RDN('cn', 'Bob'), RDN('dc', 'redhat.com'))
-
-You can provide a properly escaped string representation.
-
- DN('cn=Bob,dc=redhat.com')
-
-You can mix and match any of the forms in the constructor parameter
-list.
-
- DN(('cn', 'Bob'), 'dc=redhat.com')
- DN(('cn', 'Bob'), RDN('dc', 'redhat.com'))
-
-AVA's have an attr and value property, thus if you have an AVA
-
-# Get the attr and value
-ava.attr -> u'cn'
-ava.value -> u'Bob'
-
-# Set the attr and value
-ava.attr = 'cn'
-ava.value = 'Bob'
-
-Since RDN's are assumed to be single valued, exactly the same
-behavior applies to an RDN. If the RDN is multi-valued then the attr
-property returns the attr of the first AVA, likewise for the value.
-
-# Get the attr and value
-rdn.attr -> u'cn'
-rdn.value -> u'Bob'
-
-# Set the attr and value
-rdn.attr = 'cn'
-rdn.value = 'Bob'
-
-Also RDN's can be indexed by name or position (see the RDN class doc
-for details).
-
-rdn['cn'] -> u'Bob'
-rdn[0] -> AVA('cn', 'Bob')
-
-A DN is a sequence of RDN's, as such any of Python's container
-operators can be applied to a DN in a intuitive way.
-
-# How many RDN's in a DN?
-len(dn)
-
-# WARNING, this a count of RDN's not how characters there are in the
-# string representation the dn, instead that would be:
-len(str(dn))
-
-# Iterate over each RDN in a DN
-for rdn in dn:
-
-# Get the first RDN in a DN
-dn[0] -> RDN('cn', 'Bob')
-
-# Get the value of the first RDN in a DN
-dn[0].value -> u'Bob'
-
-# Get the value of the first RDN by indexing by attr name
-dn['cn'] -> u'Bob'
-
-# WARNING, when a string is used as an index key the FIRST RDN's value
-# in the sequence whose attr matches the key is returned. Thus if you
-# have a DN like this "cn=foo,cn=bar" then dn['cn'] will always return
-# 'foo' even though there is another attr with the name 'cn'. This is
-# almost always what the programmer wants. See the class doc for how
-# you can override this default behavior and get a list of every value
-# whose attr matches the key.
-
-# Set the first RDN in the DN (all are equivalent)
-dn[0] = ('cn', 'Bob')
-dn[0] = ['cn', 'Bob']
-dn[0] = RDN('cn', 'Bob')
-
-dn[0].attr = 'cn'
-dn[0].value = 'Bob'
-
-# Get the first two RDN's using slices
-dn[0:2]
-
-# Get the last two RDN's using slices
-dn[-2:]
-
-# Get a list of all RDN's using slices
-dn[:]
-
-# Set the 2nd and 3rd RDN using slices (all are equivalent)
-dn[1:3] = ('cn', 'Bob), ('dc', 'redhat.com')
-dn[1:3] = RDN('cn', 'Bob), RDN('dc', 'redhat.com')
-
-String representations and escapes:
-
-# To get an RFC compliant string representation of a DN, RDN or AVA
-# simply call str() on it or evaluate it in a string context.
-str(dn) -> 'cn=Bob,dc=redhat.com'
-
-# When working with attr's and values you do not have to worry about
-# escapes, simply use the raw unescaped string in a natural fashion.
-
-rdn = RDN('cn', 'r,w')
-
-# Thus:
-rdn.value == 'r,w' -> True
-
-# But:
-str(rdn) == 'cn=r,w' -> False
-# Because:
-str(rdn) -> 'cn=r\2cw' or 'cn='r\,w' # depending on the underlying LDAP library
-
-Equality and Comparing:
-
-# All DN's, RDN's and AVA's support equality testing in an intuitive
-# manner.
-dn1 = DN(('cn', 'Bob'))
-dn2 = DN(RDN('cn', 'Bob'))
-dn1 == dn2 -> True
-dn1[0] == dn2[0] -> True
-dn1[0].value = 'Bobby'
-dn1 == dn2 -> False
-
-DN objects implement startswith(), endswith() and the "in" membership
-operator. You may pass a DN or RDN object to these. Examples:
-
-if dn.endswith(base_dn):
-if dn.startswith(rdn1):
-if container_dn in dn:
-
-# See the class doc for how DN's, RDN's and AVA's compare
-# (e.g. cmp()). The general rule is for objects supporting multiple
-# values first their lengths are compared, then if the lengths match
-# the respective components of each are pair-wise compared until one
-# is discovered to be non-equal. The comparision is case insensitive.
-
-Concatenation and In-Place Addition:
-
-# DN's and RDN's can be concatenated.
-# Return a new DN by appending the RDN's of dn2 to dn1
-dn3 = dn1 + dn2
-
-# Append a RDN to DN's RDN sequence (all are equivalent)
-dn += ('cn', 'Bob')
-dn += RDN('cn', 'Bob')
-
-# Append a DN to an existing DN
-dn1 += dn2
-
-Finally see the unittest for a more complete set of ways you can
-manipulate these objects.
-
-'''
-
-def _adjust_indices(start, end, length):
- 'helper to fixup start/end slice values'
-
- if end > length:
- end = length
- elif end < 0:
- end += length
- if end < 0:
- end = 0
-
- if start < 0:
- start += length
- if start < 0:
- start = 0
-
- return start, end
-
-class AVA(object):
- '''
- AVA(arg0, ...)
-
- An AVA is an LDAP Attribute Value Assertion. It is convenient to think of
- AVA's as a <attr,value> pair. AVA's are members of RDN's (Relative
- Distinguished Name).
-
- The AVA constructor is passed a sequence of args and a set of
- keyword parameters used for configuration.
-
- The arg sequence may be:
-
- 1) With 2 string (or unicode) arguments, the first argument will be the
- attr, the 2nd the value.
-
- 2) With a sigle list or tuple argument containing exactly 2 string (or unicode
- members), the first member is the attr and the second is the value.
-
- 3) With a single string (or unicode) argument, in this case the string will
- be interpretted using the DN syntax described in RFC 4514 to yield a AVA
- <attr,value> pair. The parsing recognizes the DN syntax escaping rules.
-
- For example:
-
- ava = AVA('cn', 'Bob') # case 1: two strings
- ava = AVA(('cn', 'Bob')) # case 2: 2-valued tuple
- ava = AVA(['cn', 'Bob']) # case 2: 2-valued list
- ava = AVA('cn=Bob') # case 3: DN syntax
-
- AVA object have two properties for accessing their data:
-
- attr: the attribute name, cn in our exmaple
- value: the attribute's value, Bob in our example
-
- When attr and value are returned they will always be unicode. When
- attr or value are set they will be promoted to unicode.
-
- AVA objects support indexing by name, e.g.
-
- ava['cn']
-
- returns the value (Bob in our example). If the index does key does not match
- the attr then a KeyError will be raised.
-
- AVA objects support equality testing and comparsion (e.g. cmp()). When they
- are compared the attr is compared first, if the 2 attr's are equal then the
- values are compared. The comparision is case insensitive (because attr's map
- to numeric OID's and their values derive from from the 'name' atribute type
- (OID 2.5.4.41) whose EQUALITY MATCH RULE is caseIgnoreMatch.
-
- The str method of an AVA returns the string representation in RFC 4514 DN
- syntax with proper escaping.
- '''
- flags = 0
-
- def __init__(self, *args, **kwds):
- if len(args) == 1:
- arg = args[0]
- if isinstance(arg, basestring):
- try:
- rdns = str2dn(arg.encode('utf-8'))
- except DECODING_ERROR:
- raise ValueError("malformed AVA string = \"%s\"" % arg)
- if len(rdns) != 1:
- raise ValueError("multiple RDN's specified by \"%s\"" % (arg))
- rdn = rdns[0]
- if len(rdn) != 1:
- raise ValueError("multiple AVA's specified by \"%s\"" % (arg))
- ava = rdn[0]
- elif isinstance(arg, (tuple, list)):
- ava = arg
- if len(ava) != 2:
- raise ValueError("tuple or list must be 2-valued, not \"%s\"" % (ava))
- else:
- raise TypeError("with 1 argument, argument must be str,unicode,tuple or list, got %s instead" % \
- arg.__class__.__name__)
-
- attr = ava[0]
- value = ava[1]
- elif len(args) == 2:
- attr = args[0]
- value = args[1]
- else:
- raise TypeError("takes 1 or 2 arguments (%d given)" % (len(args)))
-
- if not isinstance(attr, basestring):
- raise TypeError("attr must be basestring, got %s instead" % attr.__class__.__name__)
- if not isinstance(value, basestring):
- raise TypeError("value must be basestring, got %s instead" % value.__class__.__name__)
-
- self.attr = attr
- self.value = value
-
- def _get_attr(self):
- return self._attr_unicode
-
- def _set_attr(self, new_attr):
- if not isinstance(new_attr, basestring):
- raise TypeError("attr must be basestring, got %s instead" % new_attr.__class__.__name__)
-
- if isinstance(new_attr, unicode):
- self._attr_unicode = new_attr
- else:
- self._attr_unicode = utf8_codec.decode(new_attr)[0]
-
- attr = property(_get_attr, _set_attr)
-
- def _get_value(self):
- return self._value_unicode
-
- def _set_value(self, new_value):
- if not isinstance(new_value, basestring):
- raise TypeError("value must be basestring, got %s instead" % new_value.__class__.__name__)
-
- if isinstance(new_value, unicode):
- self._value_unicode = new_value
- else:
- self._value_unicode = utf8_codec.decode(new_value)[0]
-
- value = property(_get_value, _set_value)
-
- def _to_openldap(self):
- return [[(self._attr_unicode.encode('utf-8'), self._value_unicode.encode('utf-8'), self.flags)]]
-
- def __str__(self):
- return dn2str(self._to_openldap())
-
- def __getitem__(self, key):
- if isinstance(key, basestring):
- if key == self._attr_unicode:
- return self._value_unicode
- raise KeyError("\"%s\" not found in %s" % (key, self.__str__()))
- else:
- raise TypeError("unsupported type for AVA indexing, must be basestring; not %s" % \
- (key.__class__.__name__))
-
- def __eq__(self, other):
- '''
- The attr comparison is case insensitive because attr is
- really an LDAP attribute type which means it's specified with
- an OID (dotted number) and not a string. Since OID's are
- numeric the human readable name which maps to the OID is not
- significant in case.
-
- The value comparison is also case insensitive because the all
- attribute types used in a DN are derived from the 'name'
- atribute type (OID 2.5.4.41) whose EQUALITY MATCH RULE is
- caseIgnoreMatch.
- '''
- if not isinstance(other, self.__class__):
- raise TypeError("expected AVA but got %s" % (other.__class__.__name__))
-
- return self._attr_unicode.lower() == other.attr.lower() and \
- self._value_unicode.lower() == other.value.lower()
-
- def __cmp__(self, other):
- 'comparision is case insensitive, see __eq__ doc for explanation'
-
- if not isinstance(other, self.__class__):
- raise TypeError("expected AVA but got %s" % (other.__class__.__name__))
-
- result = cmp(self._attr_unicode.lower(), other.attr.lower())
- if result != 0:
- return result
- result = cmp(self._value_unicode.lower(), other.value.lower())
- return result
-
-class RDN(object):
- '''
- RDN(arg0, ..., first_key_match=True)
-
- An RDN is a LDAP Relative Distinguished Name. RDN's are members of DN's
- (Distinguished Name). An RDN contains 1 or more AVA's. If the RDN contains
- more than one AVA it is said to be a multi-valued RDN. When an RDN is
- multi-valued the AVA's are unorderd comprising a set. However this
- implementation orders the AVA's according to the AVA comparison function to
- make equality and comparison testing easier. Think of this a canonical
- normalization (however LDAP does not impose any ordering on multiple AVA's
- within an RDN). Single valued RDN's are the norm and thus the RDN
- constructor has simple syntax for them.
-
- The RDN constructor is passed a sequence of args and a set of
- keyword parameters used for configuration.
-
- The constructor iterates though the sequence and adds AVA's to the RDN.
-
- The arg sequence may be:
-
- * A 2-valued tuple or list denotes the <attr,value> pair of an AVA. The
- first member is the attr and the second member is the value, both members
- must be strings (or unicode). The tuple or list is passed to the AVA
- constructor and the resulting AVA is added to the RDN. Multiple tuples or
- lists may appear in the argument list, each adds one additional AVA to the
- RDN.
-
- * A single string (or unicode) argument, in this case the string will
- be interpretted using the DN syntax described in RFC 4514 to yield one or
- more AVA <attr,value> pairs. The parsing recognizes the DN syntax escaping
- rules.
-
- * A AVA object, the AVA will be copied into the new RDN respecting
- the constructors keyword configuration parameters.
-
- * A RDN object, the AVA's in the RDN are copied into the new RDN
- respecting the constructors keyword configuration parameters.
-
- Single AVA Examples:
-
- RDN(('cn', 'Bob')) # tuple yields 1 AVA
- RDN('cn=Bob') # DN syntax with 1 AVA
- RDN(AVA('cn', 'Bob')) # AVA object adds 1 AVA
-
- Multiple AVA Examples:
-
- RDN(('cn', 'Bob'),('ou', 'people')) # 2 tuples yields 2 AVA's
- RDN('cn=Bob+ou=people') # DN syntax with 2 AVA's
- RDN(AVA('cn', 'Bob'),AVA('ou', 'people')) # 2 AVA objects adds 2 AVA's
- RDN(('cn', 'Bob'), 'ou=people') # 2 args, 1st tuple forms 1 AVA,
- # 2nd DN syntax string adds 1 AVA,
- # 2 AVA's in total
-
- Note: The RHS of a slice assignment is interpreted exactly in the
- same manner as the constructor argument list (see above examples).
-
- RDN objects support iteration over their AVA members. You can iterate all
- AVA members via any Python iteration syntax. RDN objects support full Python
- indexing using bracket [] notation. Examples:
-
- len(rdn) # return the number of AVA's
- rdn[0] # indexing the first AVA
- rdn['cn'] # index by AVA attr, returns AVA value
- for ava in rdn: # iterate over each AVA
- rdn[:] # a slice, in this case a copy of each AVA
-
- WARNING: When indexing by attr (e.g. rdn['cn']) there is a possibility more
- than one AVA has the same attr name as the index key. The default behavior
- is to return the value of the first AVA whose attr matches the index
- key. This behavior can be modified by setting the first_key_match property
- to false in the RDN object. If first_key_match is False a list of all values
- will be returned instead. The first_key_match behavior is the default and is
- useful because duplicate attr names in multi-valued RDN's are rare. We seek
- the most useful common case for programmer friendliness, but you should be
- aware of the caveat.
-
- RDN objects support the AVA attr and value properties as another programmer
- convenience because the vast majority of RDN's are single valued. The attr
- and value properties return the attr and value properties of the first AVA
- in the RDN, for example:
-
- rdn = RDN(('cn', 'Bob')) # rdn has 1 AVA whose attr == 'cn' and value == 'Bob'
- len(rdn) -> 1
- rdn.attr -> u'cn' # exactly equivalent to rdn[0].attr
- rdn.value -> u'Bob' # exactly equivalent to rdn[0].value
-
- When attr and value are returned they will always be unicode. When
- attr or value are set they will be promoted to unicode.
-
- If an RDN is multi-valued the attr and value properties still return only
- the first AVA's properties, programmer beware! Recall the AVA's in the RDN
- are sorted according the to AVA collating semantics.
-
- RDN objects support equality testing and comparision. See AVA for the
- definition of the comparision method.
-
- RDN objects support concatenation and addition with other RDN's or AVA's
-
- rdn1 + rdn2 # yields a new RDN object with the contents of each RDN.
- rdn1 + ava1 # yields a new RDN object with the contents of rdn1 and ava1
-
- RDN objects can add AVA's objects via in-place addition.
-
- rdn1 += rdn2 # rdn1 now contains the sum of rdn1 and rdn2
- rdn1 += ava1 # rdn1 has ava1 added to it.
-
- The str method of an RDN returns the string representation in RFC 4514 DN
- syntax with proper escaping.
- '''
-
- flags = 0
-
- def __init__(self, *args, **kwds):
- self.first_key_match = kwds.get('first_key_match', True)
- self.avas = self._avas_from_sequence(args)
- self.avas.sort()
-
- def _ava_from_value(self, value):
- if isinstance(value, AVA):
- return AVA(value.attr, value.value)
- elif isinstance(value, RDN):
- avas = []
- for ava in value.avas:
- avas.append(AVA(ava.attr, ava.value))
- if len(avas) == 1:
- return avas[0]
- else:
- return avas
- elif isinstance(value, basestring):
- try:
- rdns = str2dn(value.encode('utf-8'))
- if len(rdns) != 1:
- raise ValueError("multiple RDN's specified by \"%s\"" % (value))
- rdn = rdns[0]
- if len(rdn) == 1:
- return AVA(rdn[0][0], rdn[0][1])
- else:
- avas = []
- for ava_tuple in rdn:
- avas.append(AVA(ava_tuple[0], ava_tuple[1]))
- return avas
- except DECODING_ERROR:
- raise ValueError("malformed RDN string = \"%s\"" % value)
- elif isinstance(value, (tuple, list)):
- if len(value) != 2:
- raise ValueError("tuple or list must be 2-valued, not \"%s\"" % (value))
- return AVA(value)
- else:
- raise TypeError("must be str,unicode,tuple, or AVA, got %s instead" % \
- value.__class__.__name__)
-
-
- def _avas_from_sequence(self, seq):
- avas = []
-
- for item in seq:
- ava = self._ava_from_value(item)
- if isinstance(ava, list):
- avas.extend(ava)
- else:
- avas.append(ava)
- return avas
-
- def _to_openldap(self):
- return [[(ava.attr.encode('utf-8'), ava.value.encode('utf-8'), self.flags) for ava in self.avas]]
-
- def __str__(self):
- return dn2str(self._to_openldap())
-
- def _next(self):
- for ava in self.avas:
- yield ava
-
- def __iter__(self):
- return self._next()
-
- def __len__(self):
- return len(self.avas)
-
- def __getitem__(self, key):
- if isinstance(key, (int, long, slice)):
- return self.avas[key]
- elif isinstance(key, basestring):
- if self.first_key_match:
- for ava in self.avas:
- if key == ava.attr:
- return ava.value
- raise KeyError("\"%s\" not found in %s" % (key, self.__str__()))
- else:
- avas = []
- for ava in self.avas:
- if key == ava.attr:
- avas.append(ava.value)
- if len(avas) > 0:
- return avas
- raise KeyError("\"%s\" not found in %s" % (key, self.__str__()))
- else:
- raise TypeError("unsupported type for RDN indexing, must be int, basestring or slice; not %s" % \
- (key.__class__.__name__))
-
- def __setitem__(self, key, value):
- if isinstance(key, (int, long)):
- new_ava = self._ava_from_value(value)
- if isinstance(new_ava, list):
- raise TypeError("cannot assign multiple AVA's to single entry")
- self.avas[key] = new_ava
- elif isinstance(key, slice):
- avas = self._avas_from_sequence(value)
- self.avas[key] = avas
- elif isinstance(key, basestring):
- new_ava = self._ava_from_value(value)
- if isinstance(new_ava, list):
- raise TypeError("cannot assign multiple AVA's to single entry")
- found = False
- i = 0
- while i < len(self.avas):
- if key == self.avas[i].attr:
- found = True
- self.avas[i] = new_ava
- if self.first_key_match:
- break
- i += 1
- if not found:
- raise KeyError("\"%s\" not found in %s" % (key, self.__str__()))
- else:
- raise TypeError("unsupported type for RDN indexing, must be int, basestring or slice; not %s" % \
- (key.__class__.__name__))
- self.avas.sort()
-
- def _get_attr(self):
- if len(self.avas) == 0:
- raise IndexError("No AVA's in this RDN")
- return self.avas[0].attr
-
- def _set_attr(self, new_attr):
- if len(self.avas) == 0:
- raise IndexError("No AVA's in this RDN")
-
- if not isinstance(new_attr, basestring):
- raise TypeError("attr must be basestring, got %s instead" % new_attr.__class__.__name__)
-
- self.avas[0].attr = new_attr
-
- attr = property(_get_attr, _set_attr)
-
- def _get_value(self):
- if len(self.avas) == 0:
- raise IndexError("No AVA's in this RDN")
- return self.avas[0].value
-
- def _set_value(self, new_value):
- if len(self.avas) == 0:
- raise IndexError("No AVA's in this RDN")
-
- if not isinstance(new_value, basestring):
- raise TypeError("value must be basestring, got %s instead" % new_value.__class__.__name__)
-
- self.avas[0].value = new_value
-
- value = property(_get_value, _set_value)
-
- def __eq__(self, other):
- if not isinstance(other, self.__class__):
- raise TypeError("expected RDN but got %s" % (other.__class__.__name__))
-
- return self.avas == other.avas
-
- def __cmp__(self, other):
- if not isinstance(other, self.__class__):
- raise TypeError("expected RDN but got %s" % (other.__class__.__name__))
-
- result = cmp(len(self), len(other))
- if result != 0:
- return result
- i = 0
- while i < len(self):
- result = cmp(self[i], other[i])
- if result != 0:
- return result
- i += 1
- return 0
-
- def __add__(self, other):
- result = RDN(self, first_key_match=self.first_key_match)
- if isinstance(other, RDN):
- for ava in other.avas:
- result.avas.append(AVA(ava.attr, ava.value))
- elif isinstance(other, AVA):
- result.avas.append(AVA(other.attr, other.value))
- elif isinstance(other, basestring):
- rdn = RDN(other)
- for ava in rdn.avas:
- result.avas.append(AVA(ava.attr, ava.value))
- else:
- raise TypeError("expected RDN, AVA or basestring but got %s" % (other.__class__.__name__))
-
- result.avas.sort()
- return result
-
- def __iadd__(self, other):
- if isinstance(other, RDN):
- for ava in other.avas:
- self.avas.append(AVA(ava.attr, ava.value))
- elif isinstance(other, AVA):
- self.avas.append(AVA(other.attr, other.value))
- elif isinstance(other, basestring):
- rdn = RDN(other)
- for ava in rdn.avas:
- self.avas.append(AVA(ava.attr, ava.value))
- else:
- raise TypeError("expected RDN, AVA or basestring but got %s" % (other.__class__.__name__))
-
- self.avas.sort()
- return self
-
-class DN(object):
- '''
- DN(arg0, ..., first_key_match=True)
-
- A DN is a LDAP Distinguished Name. A DN is an ordered sequence of RDN's.
-
- The DN constructor is passed a sequence of args and a set of
- keyword parameters used for configuration. normalize means the
- attr and value will be converted to lower case.
-
- The constructor iterates through the sequence and adds the RDN's
- it finds in order to the DN object. Each item in the sequence may
- be:
-
- * A 2-valued tuple or list. The first member is the attr and the
- second member is the value of an RDN, both members must be
- strings (or unicode). The tuple or list is passed to the RDN
- constructor and the resulting RDN is appended to the
- DN. Multiple tuples or lists may appear in the argument list,
- each adds one additional RDN to the DN.
-
- * A single string (or unicode) argument, in this case the string
- will be interpretted using the DN syntax described in RFC 4514
- to yield one or more RDN's which will be appended in order to
- the DN. The parsing recognizes the DN syntax escaping rules.
-
- * A RDN object, the RDN will copied respecting the constructors
- keyword configuration parameters and appended in order.
-
- * A DN object, the RDN's in the DN are copied respecting the
- constructors keyword configuration parameters and appended in
- order.
-
- Single DN Examples:
-
- DN(('cn', 'Bob')) # tuple yields 1 RDN
- DN(['cn', 'Bob']) # list yields 1 RDN
- DN('cn=Bob') # DN syntax with 1 RDN
- DN(RDN('cn', 'Bob')) # RDN object adds 1 RDN
-
- Multiple RDN Examples:
-
- DN(('cn', 'Bob'),('ou', 'people')) # 2 tuples yields 2 RDN's
- # 2 RDN's total
- DN('cn=Bob,ou=people') # DN syntax with 2 RDN's
- # 2 RDN's total
- DN(RDN('cn', 'Bob'),RDN('ou', 'people')) # 2 RDN objects
- # 2 RDN's total
- DN(('cn', 'Bob'), "ou=people') # 1st tuple adds 1 RDN
- # 2nd DN syntax string adds 1 RDN
- # 2 RDN's total
- base_dn = DN('dc=redhat,dc=com')
- container_dn = DN('cn=sudorules,cn=sudo')
- DN(('cn', 'Bob'), container_dn, base_dn)
- # 1st arg adds 1 RDN, cn=Bob
- # 2nd arg adds 2 RDN's, cn=sudorules,cn=sudo
- # 3rd arg adds 2 RDN's, dc=redhat,dc=com
- # 5 RDN's total
-
-
- Note: The RHS of a slice assignment is interpreted exactly in the
- same manner as the constructor argument list (see above examples).
-
- DN objects support iteration over their RDN members. You can iterate all
- RDN members via any Python iteration syntax. DN objects support full Python
- indexing using bracket [] notation. Examples:
-
- len(rdn) # return the number of RDN's
- rdn[0] # indexing the first RDN
- rdn['cn'] # index by RDN attr, returns RDN value
- for ava in rdn: # iterate over each RDN
- rdn[:] # a slice, in this case a copy of each RDN
-
- WARNING: When indexing by attr (e.g. rdn['cn']) there is a possibility more
- than one RDN has the same attr name as the index key. The default behavior
- is to return the value of the first RDN whose attr matches the index
- key. This behavior can be modified by setting the first_key_match property
- to false in the RDN object. If first_key_match is False a list of all values
- will be returned instead. The first_key_match behavior is the default and is
- useful because typical usage is to seek the first matching RDN. We seek
- the most useful common case for programmer friendliness, but you should be
- aware of the caveat.
-
- DN object support slices.
-
- # Get the first two RDN's using slices
- dn[0:2]
-
- # Get the last two RDN's using slices
- dn[-2:]
-
- # Get a list of all RDN's using slices
- dn[:]
-
- # Set the 2nd and 3rd RDN using slices (all are equivalent)
- dn[1:3] = ('cn', 'Bob'), ('dc', 'redhat.com')
- dn[1:3] = [['cn', 'Bob'], ['dc', 'redhat.com']]
- dn[1:3] = RDN('cn', 'Bob'), RDN('dc', 'redhat.com')
-
- DN objects support the insert operation.
-
- dn.insert(i,x) is exactly equivalent to dn[i:i] = [x], thus the following
- are all equivalent:
-
- dn.insert(i, ('cn','Bob'))
- dn.insert(i, ['cn','Bob'])
- dn.insert(i, RDN(('cn','Bob')))
- dn[i:i] = [('cn','Bob')]
-
- DN objects support equality testing and comparision. See RDN for the
- definition of the comparision method.
-
- DN objects implement startswith(), endswith() and the "in" membership
- operator. You may pass a DN or RDN object to these. Examples:
-
- # Test if dn ends with the contents of base_dn
- if dn.endswith(base_dn):
- # Test if dn starts with a rdn
- if dn.startswith(rdn1):
- # Test if a container is present in a dn
- if container_dn in dn:
-
- DN objects support concatenation and addition with other DN's or RDN's
- or strings (interpreted as RFC 4514 DN syntax).
-
- # yields a new DN object with the RDN's of dn2 appended to the RDN's of dn1
- dn1 + dn2
-
- # yields a new DN object with the rdn1 appended to the RDN's of dn1
- dn1 + rdn1
-
- DN objects can add RDN's objects via in-place addition.
-
- dn1 += dn2 # dn2 RDN's are appended to the dn1's RDN's
- dn1 += rdn1 # dn1 has rdn appended to its RDN's
- dn1 += "dc=redhat.com" # string is converted to DN, then appended
-
- The str method of an DN returns the string representation in RFC 4514 DN
- syntax with proper escaping.
- '''
-
- flags = 0
-
- def __init__(self, *args, **kwds):
- self.first_key_match = kwds.get('first_key_match', True)
- self.first_key_match = True
- self.rdns = self._rdns_from_sequence(args)
-
- def _rdn_from_value(self, value):
- if isinstance(value, RDN):
- return RDN(value, first_key_match=self.first_key_match)
- elif isinstance(value, DN):
- rdns = []
- for rdn in value.rdns:
- rdns.append(RDN(rdn, first_key_match=self.first_key_match))
- if len(rdns) == 1:
- return rdns[0]
- else:
- return rdns
- elif isinstance(value, basestring):
- rdns = []
- try:
- dn_list = str2dn(value.encode('utf-8'))
- for rdn_list in dn_list:
- avas = []
- for ava_tuple in rdn_list:
- avas.append(AVA(ava_tuple[0], ava_tuple[1]))
- rdn = RDN(*avas, first_key_match=self.first_key_match)
- rdns.append(rdn)
- except DECODING_ERROR:
- raise ValueError("malformed RDN string = \"%s\"" % value)
- if len(rdns) == 1:
- return rdns[0]
- else:
- return rdns
- elif isinstance(value, (tuple, list)):
- if len(value) != 2:
- raise ValueError("tuple or list must be 2-valued, not \"%s\"" % (value))
- rdn = RDN(value, first_key_match=self.first_key_match)
- return rdn
- else:
- raise TypeError("must be str,unicode,tuple, or RDN, got %s instead" % \
- value.__class__.__name__)
-
- def _rdns_from_sequence(self, seq):
- rdns = []
-
- for item in seq:
- rdn = self._rdn_from_value(item)
- if isinstance(rdn, list):
- rdns.extend(rdn)
- else:
- rdns.append(rdn)
- return rdns
-
- def _to_openldap(self):
- return [[(ava.attr.encode('utf-8'), ava.value.encode('utf-8'), self.flags) for ava in rdn] for rdn in self.rdns]
-
- def __str__(self):
- return dn2str(self._to_openldap())
-
- def _next(self):
- for rdn in self.rdns:
- yield rdn
-
- def __iter__(self):
- return self._next()
-
- def __len__(self):
- return len(self.rdns)
-
- def __getitem__(self, key):
- if isinstance(key, (int, long, slice)):
- return self.rdns[key]
- elif isinstance(key, basestring):
- if self.first_key_match:
- for rdn in self.rdns:
- if key == rdn.attr:
- return rdn.value
- raise KeyError("\"%s\" not found in %s" % (key, self.__str__()))
- else:
- rdns = []
- for rdn in self.rdns:
- if key == rdn.attr:
- rdns.append(rdn.value)
- if len(rdns) > 0:
- return rdns
- raise KeyError("\"%s\" not found in %s" % (key, self.__str__()))
- else:
- raise TypeError("unsupported type for DN indexing, must be int, basestring or slice; not %s" % \
- (key.__class__.__name__))
-
- def __setitem__(self, key, value):
- if isinstance(key, (int, long)):
- new_rdn = self._rdn_from_value(value)
- if isinstance(new_rdn, list):
- raise TypeError("cannot assign multiple RDN's to single entry")
- self.rdns[key] = new_rdn
- elif isinstance(key, slice):
- rdns = self._rdns_from_sequence(value)
- self.rdns[key] = rdns
- elif isinstance(key, basestring):
- new_rdn = self._rdn_from_value(value)
- if isinstance(new_rdn, list):
- raise TypeError("cannot assign multiple values to single entry")
- found = False
- i = 0
- while i < len(self.rdns):
- if key == self.rdns[i].attr:
- found = True
- self.rdns[i] = new_rdn
- if self.first_key_match: break
- i += 1
- if not found:
- raise KeyError("\"%s\" not found in %s" % (key, self.__str__()))
- else:
- raise TypeError("unsupported type for DN indexing, must be int, basestring or slice; not %s" % \
- (key.__class__.__name__))
-
- def __eq__(self, other):
- if not isinstance(other, self.__class__):
- raise TypeError("expected DN but got %s" % (other.__class__.__name__))
-
- return self.rdns == other.rdns
-
- def __cmp__(self, other):
- if not isinstance(other, self.__class__):
- raise TypeError("expected DN but got %s" % (other.__class__.__name__))
-
- result = cmp(len(self), len(other))
- if result != 0:
- return result
- return self._cmp_sequence(other, 0, len(self))
-
- def _cmp_sequence(self, pattern, self_start, pat_len):
- self_idx = self_start
- pat_idx = 0
- while pat_idx < pat_len:
- result = cmp(self[self_idx], pattern[pat_idx])
- if result != 0:
- return result
- self_idx += 1
- pat_idx += 1
- return 0
-
- def __add__(self, other):
- result = DN(self, first_key_match=self.first_key_match)
- if isinstance(other, self.__class__):
- for rdn in other.rdns:
- result.rdns.append(RDN(rdn, first_key_match=self.first_key_match))
- elif isinstance(other, RDN):
- result.rdns.append(RDN(other, first_key_match=self.first_key_match))
- elif isinstance(other, basestring):
- dn = DN(other, first_key_match=self.first_key_match)
- for rdn in dn.rdns:
- result.rdns.append(rdn)
- else:
- raise TypeError("expected DN, RDN or basestring but got %s" % (other.__class__.__name__))
-
- return result
-
- def __iadd__(self, other):
- if isinstance(other, DN):
- for rdn in other.rdns:
- self.rdns.append(RDN(rdn, first_key_match=self.first_key_match))
- elif isinstance(other, RDN):
- self.rdns.append(RDN(other, first_key_match=self.first_key_match))
- elif isinstance(other, basestring):
- dn = DN(other, first_key_match=self.first_key_match)
- self.__iadd__(dn)
- else:
- raise TypeError("expected DN, RDN or basestring but got %s" % (other.__class__.__name__))
-
- return self
-
- def insert(self, i, x):
- '''
- x must be a 2-value tuple or list promotable to an RDN object,
- or a RDN object.
-
- dn.insert(i, x) is the same as s[i:i] = [x]
-
- When a negative index is passed as the first parameter to the
- insert() method, the list length is added, as for slice
- indices. If it is still negative, it is truncated to zero, as
- for slice indices.
- '''
- self.rdns.insert(i, self._rdn_from_value(x))
-
- # The implementation of startswith, endswith, tailmatch, adjust_indices
- # was based on the Python's stringobject.c implementation
-
- def startswith(self, prefix, start=0, end=sys.maxsize):
- '''
- Return True if the dn starts with the specified prefix (either a DN or
- RDN object), False otherwise. With optional start, test dn beginning at
- that position. With optional end, stop comparing dn at that position.
- prefix can also be a tuple of dn's or rdn's to try.
- '''
- if isinstance(prefix, tuple):
- for pat in prefix:
- if self._tailmatch(pat, start, end, -1):
- return True
- return False
-
- return self._tailmatch(prefix, start, end, -1)
-
- def endswith(self, suffix, start=0, end=sys.maxsize):
- '''
- Return True if dn ends with the specified suffix (either a DN or RDN
- object), False otherwise. With optional start, test dn beginning at
- that position. With optional end, stop comparing dn at that position.
- suffix can also be a tuple of dn's or rdn's to try.
- '''
- if isinstance(suffix, tuple):
- for pat in suffix:
- if self._tailmatch(pat, start, end, +1):
- return True
- return False
-
- return self._tailmatch(suffix, start, end, +1)
-
- def _tailmatch(self, pattern, start, end, direction):
- '''
- Matches the end (direction >= 0) or start (direction < 0) of self
- against pattern (either a DN or RDN), using the start and end
- arguments. Returns 0 if not found and 1 if found.
- '''
-
- if isinstance(pattern, DN):
- pat_len = len(pattern)
- elif isinstance(pattern, RDN):
- pat_len = 1
- else:
- raise TypeError("expected DN or RDN but got %s" % (pattern.__class__.__name__))
-
- self_len = len(self)
-
- start, end = _adjust_indices(start, end, self_len)
-
- if direction < 0: # starswith
- if start+pat_len > self_len:
- return 0
- else: # endswith
- if end-start < pat_len or start > self_len:
- return 0
-
- if end-pat_len >= start:
- start = end - pat_len
-
- if isinstance(pattern, DN):
- if end-start >= pat_len:
- return not self._cmp_sequence(pattern, start, pat_len)
- return 0
- else:
- return self.rdns[start] == pattern
-
- def __contains__(self, other):
- 'Return the outcome of the test other in self. Note the reversed operands.'
-
- if isinstance(other, DN):
- other_len = len(other)
- end = len(self) - other_len
- i = 0
- while i <= end:
- result = self._cmp_sequence(other, i, other_len)
- if result == 0:
- return True
- i += 1
- return False
-
- elif isinstance(other, RDN):
- return other in self.rdns
- else:
- raise TypeError("expected DN or RDN but got %s" % (other.__class__.__name__))
-
-
-
-
-
diff --git a/ipalib/encoder.py b/ipalib/encoder.py
deleted file mode 100644
index 691f4d8ca..000000000
--- a/ipalib/encoder.py
+++ /dev/null
@@ -1,213 +0,0 @@
-# Authors:
-# Pavel Zuna <pzuna@redhat.com>
-#
-# Copyright (C) 2009 Red Hat
-# see file 'COPYING' for use and warranty information
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""
-Encoding capabilities.
-"""
-
-from decimal import Decimal
-
-class EncoderSettings(object):
- """
- Container for encoder settings.
- """
- encode_to = 'utf-8'
- encode_none = False
- encode_dict_keys = False
- encode_dict_keys_postprocess = True
- encode_dict_vals = True
- encode_dict_vals_postprocess = True
- encode_postprocessor = staticmethod(lambda x: x)
-
- decode_from = 'utf-8'
- decode_none = False
- decode_dict_keys = False
- decode_dict_keys_postprocess = True
- decode_dict_vals = True
- decode_dict_vals_postprocess = True
- decode_dict_vals_table = dict()
- decode_dict_vals_table_keygen = staticmethod(lambda x, y: x)
- decode_postprocessor = staticmethod(lambda x: x)
-
-
-class Encoder(object):
- """
- Base class implementing encoding of python scalar types to strings
- and vise-versa.
- """
-
- encoder_settings = EncoderSettings()
-
- def __init__(self):
- # each instance should have its own settings
- self.encoder_settings = EncoderSettings()
-
- def _decode_dict_val(self, key, val):
- f = self.encoder_settings.decode_dict_vals_table.get(
- self.encoder_settings.decode_dict_vals_table_keygen(key, val)
- )
- if f:
- return val
- return self.decode(val)
-
- def encode(self, var):
- """
- Encode any python built-in python type variable into `self.encode_to`.
-
- Compound types have their individual members encoded.
-
- Returns an encoded copy of 'var'.
- """
- if isinstance(var, str):
- return var
- elif isinstance(var, unicode):
- return self.encoder_settings.encode_postprocessor(
- var.encode(self.encoder_settings.encode_to)
- )
- elif isinstance(var, bool):
- if var:
- var = 'TRUE'
- else:
- var = 'FALSE'
- return self.encoder_settings.encode_postprocessor(
- var.encode(self.encoder_settings.encode_to)
- )
- elif isinstance(var, (float, Decimal, int, long)):
- return self.encoder_settings.encode_postprocessor(
- str(var).encode(self.encoder_settings.encode_to)
- )
- elif isinstance(var, list):
- return [self.encode(m) for m in var]
- elif isinstance(var, tuple):
- return tuple(self.encode(m) for m in var)
- elif isinstance(var, dict):
- if self.encoder_settings.encode_dict_keys:
- dct = dict()
- if not self.encoder_settings.encode_dict_keys_postprocess:
- tmp = self.encoder_settings.encode_postprocessor
- self.encoder_settings.encode_postprocessor = lambda x: x
- for (k, v) in var.iteritems():
- dct[self.encode(k)] = v
- if not self.encoder_settings.encode_dict_keys_postprocess:
- self.encoder_settings.encode_postprocessor = tmp
- else:
- dct = dict(var)
- if self.encoder_settings.encode_dict_vals:
- if not self.encoder_settings.encode_dict_vals_postprocess:
- tmp = self.encoder_settings.encode_postprocessor
- self.encoder_settings.encode_postprocessor = lambda x: x
- for (k, v) in dct.iteritems():
- dct[k] = self.encode(v)
- if not self.encoder_settings.encode_dict_vals_postprocess:
- self.encoder_settings.encode_postprocessor = tmp
- return dct
- elif var is None:
- if self.encoder_settings.encode_none:
- return self.encoder_settings.encode_postprocessor(
- str(var).encode(self.encoder_settings.encode_to)
- )
- return None
- raise TypeError('python built-in type expected, got \'%s\'', type(var))
-
- def decode(self, var):
- """
- Decode strings in `self.decode_from` into python strings.
-
- Compound types have their individual members decoded.
-
- Dictionaries can have their values decoded into other types
- by looking up keys in `self.decode_dict_vals_table`.
-
- Returns a decoded copy of 'var'.
- """
- if isinstance(var, unicode):
- return var
- elif isinstance(var, str):
- return self.encoder_settings.decode_postprocessor(
- var.decode(self.encoder_settings.decode_from)
- )
- elif isinstance(var, (bool, float, Decimal, int, long)):
- return var
- elif isinstance(var, list):
- return [self.decode(m) for m in var]
- elif isinstance(var, tuple):
- return tuple(self.decode(m) for m in var)
- elif isinstance(var, dict):
- if self.encoder_settings.decode_dict_keys:
- dct = dict()
- if not self.encoder_settings.decode_dict_keys_postprocess:
- tmp = self.encoder_settings.decode_postprocessor
- self.encoder_settings.decode_postprocessor = lambda x: x
- for (k, v) in var.iteritems():
- dct[self.decode(k)] = v
- if not self.encoder_settings.decode_dict_keys_postprocess:
- self.encoder_settings.decode_postprocessor = tmp
- else:
- dct = dict(var)
- if self.encoder_settings.decode_dict_vals:
- if not self.encoder_settings.decode_dict_vals_postprocess:
- tmp = self.encoder_settings.decode_postprocessor
- self.encoder_settings.decode_postprocessor = lambda x: x
- for (k, v) in dct.iteritems():
- dct[k] = self._decode_dict_val(k, v)
- if not self.encoder_settings.decode_dict_vals_postprocess:
- self.encoder_settings.decode_postprocessor = tmp
- return dct
- elif var is None:
- if self.encoder_settings.decode_none:
- return self.encoder_settings.decode_postprocessor(
- str(var).decode(self.encoder_settings.decode_from)
- )
- return None
- raise TypeError('python built-in type expected, got \'%s\'', type(var))
-
-## ENCODER METHOD DECORATORS
-
-def encode_args(*outer_args):
- def decorate(f):
- def new_f(*args, **kwargs):
- assert isinstance(args[0], Encoder), \
- 'first argument not Encoder instance'
- new_args = list(args)
- for a in outer_args:
- if isinstance(a, int):
- if a < len(args):
- new_args[a] = args[0].encode(args[a])
- elif isinstance(a, basestring):
- if a in kwargs:
- kwargs[a] = args[0].encode(kwargs[a])
- else:
- raise TypeError(
- 'encode_args takes a list of ints and basestrings'
- )
- return f(*new_args, **kwargs)
- new_f.func_name = f.func_name
- return new_f
- return decorate
-
-
-def decode_retval():
- def decorate(f):
- def new_f(*args, **kwargs):
- assert isinstance(args[0], Encoder), \
- 'first argument not Encoder instance'
- return args[0].decode(f(*args, **kwargs))
- new_f.func_name = f.func_name
- return new_f
- return decorate
-
diff --git a/ipalib/parameters.py b/ipalib/parameters.py
index 98b02dd6d..dd505a179 100644
--- a/ipalib/parameters.py
+++ b/ipalib/parameters.py
@@ -112,7 +112,7 @@ from errors import ConversionError, RequirementError, ValidationError
from errors import PasswordMismatch
from constants import NULLS, TYPE_ERROR, CALLABLE_ERROR
from text import Gettext, FixMe
-
+from ipapython.dn import DN
class DefaultFrom(ReadOnly):
"""
@@ -1845,6 +1845,23 @@ class AccessTime(Str):
return None
+class DNParam(Param):
+ type = DN
+
+ def _convert_scalar(self, value, index=None):
+ """
+ Convert a single scalar value.
+ """
+ if type(value) is self.type:
+ return value
+
+ try:
+ dn = DN(value)
+ except Exception, e:
+ raise ConversionError(name=self.get_param_name(), index=index,
+ error=ugettext(e))
+ return dn
+
def create_param(spec):
"""
Create an `Str` instance from the shorthand ``spec``.
diff --git a/ipalib/plugins/aci.py b/ipalib/plugins/aci.py
index 7a27ce116..7d5bf504c 100644
--- a/ipalib/plugins/aci.py
+++ b/ipalib/plugins/aci.py
@@ -123,24 +123,22 @@ from ipalib import api, crud, errors
from ipalib import Object, Command
from ipalib import Flag, Int, Str, StrEnum
from ipalib.aci import ACI
-from ipalib.dn import DN
from ipalib import output
from ipalib import _, ngettext
from ipalib.plugins.baseldap import gen_pkey_only_option
-if api.env.in_server and api.env.context in ['lite', 'server']:
- from ldap import explode_dn
from ipapython.ipa_log_manager import *
+from ipapython.dn import DN
ACI_NAME_PREFIX_SEP = ":"
_type_map = {
- 'user': 'ldap:///uid=*,%s,%s' % (api.env.container_user, api.env.basedn),
- 'group': 'ldap:///cn=*,%s,%s' % (api.env.container_group, api.env.basedn),
- 'host': 'ldap:///fqdn=*,%s,%s' % (api.env.container_host, api.env.basedn),
- 'hostgroup': 'ldap:///cn=*,%s,%s' % (api.env.container_hostgroup, api.env.basedn),
- 'service': 'ldap:///krbprincipalname=*,%s,%s' % (api.env.container_service, api.env.basedn),
- 'netgroup': 'ldap:///ipauniqueid=*,%s,%s' % (api.env.container_netgroup, api.env.basedn),
- 'dnsrecord': 'ldap:///idnsname=*,%s,%s' % (api.env.container_dns, api.env.basedn),
+ 'user': 'ldap:///' + str(DN(('uid', '*'), api.env.container_user, api.env.basedn)),
+ 'group': 'ldap:///' + str(DN(('cn', '*'), api.env.container_group, api.env.basedn)),
+ 'host': 'ldap:///' + str(DN(('fqdn', '*'), api.env.container_host, api.env.basedn)),
+ 'hostgroup': 'ldap:///' + str(DN(('cn', '*'), api.env.container_hostgroup, api.env.basedn)),
+ 'service': 'ldap:///' + str(DN(('krbprincipalname', '*'), api.env.container_service, api.env.basedn)),
+ 'netgroup': 'ldap:///' + str(DN(('ipauniqueid', '*'), api.env.container_netgroup, api.env.basedn)),
+ 'dnsrecord': 'ldap:///' + str(DN(('idnsname', '*'), api.env.container_dns, api.env.basedn)),
}
_valid_permissions_values = [
@@ -247,7 +245,7 @@ def _make_aci(ldap, current, aciname, kw):
if 'test' in kw and not kw.get('test'):
raise e
else:
- entry_attrs = {'dn': 'cn=%s,%s' % (kw['permission'], api.env.container_permission)}
+ entry_attrs = {'dn': DN(('cn', kw['permission']), api.env.container_permission)}
elif group:
# Not so friendly with groups. This will raise
try:
@@ -343,10 +341,9 @@ def _aci_to_kw(ldap, a, test=False, pkey_only=False):
else:
# See if the target is a group. If so we set the
# targetgroup attr, otherwise we consider it a subtree
- if api.env.container_group in target:
- targetdn = unicode(target.replace('ldap:///',''))
- target = DN(targetdn)
- kw['targetgroup'] = target['cn']
+ targetdn = DN(target.replace('ldap:///',''))
+ if targetdn.endswith(DN(api.env.container_group, api.env.basedn)):
+ kw['targetgroup'] = targetdn[0]['cn']
else:
kw['subtree'] = unicode(target)
@@ -357,15 +354,16 @@ def _aci_to_kw(ldap, a, test=False, pkey_only=False):
elif groupdn == 'anyone':
pass
else:
- if groupdn.startswith('cn='):
- dn = ''
+ groupdn = DN(groupdn)
+ if len(groupdn) and groupdn[0].attr == 'cn':
+ dn = DN()
entry_attrs = {}
try:
(dn, entry_attrs) = ldap.get_entry(groupdn, ['cn'])
except errors.NotFound, e:
# FIXME, use real name here
if test:
- dn = 'cn=%s,%s' % ('test', api.env.container_permission)
+ dn = DN(('cn', 'test'), api.env.container_permission)
entry_attrs = {'cn': [u'test']}
if api.env.container_permission in dn:
kw['permission'] = entry_attrs['cn'][0]
@@ -801,11 +799,11 @@ class aci_find(crud.Search):
if kw.get('group'):
for a in acis:
groupdn = a.bindrule['expression']
- groupdn = groupdn.replace('ldap:///','')
- cn = None
- if groupdn.startswith('cn='):
- cn = explode_dn(groupdn)[0]
- cn = cn.replace('cn=','')
+ groupdn = DN(groupdn.replace('ldap:///',''))
+ try:
+ cn = groupdn[0]['cn'].value
+ except (IndexError, KeyError):
+ cn = None
if cn is None or cn != kw['group']:
try:
results.remove(a)
@@ -818,9 +816,11 @@ class aci_find(crud.Search):
if 'target' in a.target:
target = a.target['target']['expression']
if api.env.container_group in target:
- targetdn = unicode(target.replace('ldap:///',''))
- cn = explode_dn(targetdn)[0]
- cn = cn.replace('cn=','')
+ targetdn = DN(target.replace('ldap:///',''))
+ try:
+ cn = targetdn[0]['cn']
+ except (IndexError, KeyError):
+ cn = None
if cn == kw['targetgroup']:
found = True
if not found:
diff --git a/ipalib/plugins/automember.py b/ipalib/plugins/automember.py
index 7ccb0bb2f..40359604b 100644
--- a/ipalib/plugins/automember.py
+++ b/ipalib/plugins/automember.py
@@ -22,8 +22,8 @@ from ipalib import Str, StrEnum
from ipalib.plugins.baseldap import *
from ipalib import _, ngettext
from ipalib.request import context
-from ipalib.dn import *
import ldap as _ldap
+from ipapython.dn import DN
__doc__ = _("""
Auto Membership Rule.
@@ -200,19 +200,16 @@ class automember(LDAPObject):
parent_dn = self.container_dn
grouptype = options['type']
try:
- ndn = DN(('cn', keys[-1]), ('cn', grouptype), DN(parent_dn))
+ ndn = DN(('cn', keys[-1]), ('cn', grouptype), parent_dn)
except IndexError:
- ndn = DN(('cn', grouptype), DN(parent_dn))
- parent_dn = str(ndn)
- return parent_dn
+ ndn = DN(('cn', grouptype), parent_dn)
+ return ndn
def check_attr(self, attr):
"""
Verify that the user supplied key is a valid attribute in the schema
"""
ldap = self.api.Backend.ldap2
- if not ldap.schema:
- ldap.get_schema()
obj = ldap.schema.get_obj(_ldap.schema.AttributeType, attr)
if obj is not None:
return obj
@@ -238,6 +235,7 @@ class automember_add(LDAPCreate):
msg_summary = _('Added automember rule "%(value)s"')
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
entry_attrs['cn'] = keys[-1]
if not automember_container_exists(self.api.Backend.ldap2):
@@ -284,6 +282,7 @@ class automember_add_condition(LDAPUpdate):
)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
# Check to see if the automember rule exists
try:
(tdn, test_attrs) = ldap.get_entry(dn, [])
@@ -370,6 +369,7 @@ class automember_remove_condition(LDAPUpdate):
)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
# Check to see if the automember rule exists
try:
(tdn, test_attrs) = ldap.get_entry(dn, [])
@@ -479,10 +479,10 @@ class automember_find(LDAPSearch):
)
def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options):
+ assert isinstance(base_dn, DN)
scope = ldap.SCOPE_SUBTREE
- ndn = DN(('cn', options['type']), DN(base_dn))
- base_dn = str(ndn)
- return (filters, base_dn, scope)
+ ndn = DN(('cn', options['type']), base_dn)
+ return (filters, ndn, scope)
api.register(automember_find)
@@ -520,7 +520,6 @@ class automember_default_group_set(LDAPUpdate):
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
dn = DN(('cn', options['type']), api.env.container_automember)
- dn = str(dn)
entry_attrs['automemberdefaultgroup'] = self.obj.dn_exists(options['type'], options['automemberdefaultgroup'])
return dn
@@ -542,7 +541,6 @@ class automember_default_group_remove(LDAPUpdate):
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
dn = DN(('cn', options['type']), api.env.container_automember)
- dn = str(dn)
attr = 'automemberdefaultgroup'
(dn, entry_attrs_) = ldap.get_entry(
@@ -556,6 +554,7 @@ class automember_default_group_remove(LDAPUpdate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
if 'automemberdefaultgroup' not in entry_attrs:
entry_attrs['automemberdefaultgroup'] = unicode(_('No default (fallback) group set'))
return dn
@@ -576,10 +575,10 @@ class automember_default_group_show(LDAPRetrieve):
def pre_callback(self, ldap, dn, attrs_list, *keys, **options):
dn = DN(('cn', options['type']), api.env.container_automember)
- dn = str(dn)
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
if 'automemberdefaultgroup' not in entry_attrs:
entry_attrs['automemberdefaultgroup'] = unicode(_('No default (fallback) group set'))
return dn
diff --git a/ipalib/plugins/automount.py b/ipalib/plugins/automount.py
index 5c9f42b4c..8e9eb5745 100644
--- a/ipalib/plugins/automount.py
+++ b/ipalib/plugins/automount.py
@@ -227,6 +227,7 @@ class automountlocation_add(LDAPCreate):
msg_summary = _('Added automount location "%(value)s"')
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
# create auto.master for the new location
self.api.Command['automountmap_add'](keys[-1], u'auto.master')
@@ -595,6 +596,7 @@ class automountmap_del(LDAPDelete):
msg_summary = _('Deleted automount map "%(value)s"')
def post_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
# delete optional parental connection (direct maps may not have this)
try:
(dn_, entry_attrs) = ldap.find_entry_by_attr(
@@ -718,7 +720,7 @@ class automountkey(LDAPObject):
(kwargs['automountkey'], kwargs['automountinformation'])
else:
sfilter = '(automountkey=%s)' % kwargs['automountkey']
- basedn = 'automountmapname=%s,cn=%s,%s' % (parent_keys[1], parent_keys[0], self.container_dn)
+ basedn = DN(('automountmapname', parent_keys[1]), ('cn', parent_keys[0]), self.container_dn)
attrs_list = ['*']
(entries, truncated) = ldap.find_entries(sfilter, attrs_list,
basedn, _ldap.SCOPE_ONELEVEL)
@@ -790,6 +792,7 @@ class automountkey_add(LDAPCreate):
internal_options = ['description', 'add_operation']
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
options.pop('add_operation', None)
options.pop('description', None)
self.obj.check_key_uniqueness(keys[-2], keys[-1], **options)
@@ -926,6 +929,7 @@ class automountkey_mod(LDAPUpdate):
yield key
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
if 'newautomountkey' in options:
entry_attrs['automountkey'] = options['newautomountkey']
if 'newautomountinformation' in options:
diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py
index 32dae5160..e05f59ff4 100644
--- a/ipalib/plugins/baseldap.py
+++ b/ipalib/plugins/baseldap.py
@@ -34,7 +34,7 @@ from ipalib.cli import to_cli, from_cli
from ipalib import output
from ipalib.text import _
from ipalib.util import json_serialize, validate_hostname
-from ipalib.dn import *
+from ipapython.dn import DN, RDN
global_output_params = (
Flag('has_password',
@@ -200,6 +200,7 @@ def validate_attribute(ugettext, name, attr):
raise errors.ValidationError(name=name, error='Invalid format. Should be name=value')
def get_effective_rights(ldap, dn, attrs=None):
+ assert isinstance(dn, DN)
if attrs is None:
attrs = ['*', 'nsaccountlock', 'cospriority']
rights = ldap.get_effective_rights(dn, attrs)
@@ -332,6 +333,7 @@ def add_external_pre_callback(membertype, ldap, dn, keys, options):
membertype is the type of member
"""
+ assert isinstance(dn, DN)
# validate hostname with allowed underscore characters, non-fqdn
# hostnames are allowed
def validate_host(hostname):
@@ -361,12 +363,14 @@ def add_external_post_callback(memberattr, membertype, externalattr, ldap, compl
membertype is the type of member: user,
externalattr is one of externaluser,
"""
+ assert isinstance(dn, DN)
completed_external = 0
normalize = options.get('external_callback_normalize', True)
# Sift through the failures. We assume that these are all
# entries that aren't stored in IPA, aka external entries.
if memberattr in failed and membertype in failed[memberattr]:
(dn, entry_attrs_) = ldap.get_entry(dn, [externalattr])
+ assert isinstance(dn, DN)
members = entry_attrs.get(memberattr, [])
external_entries = entry_attrs_.get(externalattr, [])
lc_external_entries = set(e.lower() for e in external_entries)
@@ -374,6 +378,7 @@ def add_external_post_callback(memberattr, membertype, externalattr, ldap, compl
for entry in failed[memberattr][membertype]:
membername = entry[0].lower()
member_dn = api.Object[membertype].get_dn(membername)
+ assert isinstance(member_dn, DN)
if (membername not in lc_external_entries and
member_dn not in members):
# Not an IPA entry, assume external
@@ -406,6 +411,7 @@ def add_external_post_callback(memberattr, membertype, externalattr, ldap, compl
return (completed + completed_external, dn)
def remove_external_post_callback(memberattr, membertype, externalattr, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
# Run through the failures and gracefully remove any member defined
# as an external member.
if memberattr in failed and membertype in failed[memberattr]:
@@ -440,7 +446,7 @@ def host_is_master(ldap, fqdn):
Raises an exception if a master, otherwise returns nothing.
"""
- master_dn = str(DN('cn=%s' % fqdn, 'cn=masters,cn=ipa,cn=etc', api.env.basedn))
+ master_dn = DN(('cn', fqdn), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
try:
(dn, entry_attrs) = ldap.get_entry(master_dn, ['objectclass'])
raise errors.ValidationError(name='hostname', error=_('An IPA master host cannot be deleted or disabled'))
@@ -518,9 +524,11 @@ class LDAPObject(Object):
return self.backend.make_dn_from_attr(
self.primary_key.name, keys[-1], parent_dn
)
+ assert isinstance(parent_dn, DN)
return parent_dn
def get_primary_key_from_dn(self, dn):
+ assert isinstance(dn, DN)
try:
if self.rdn_attribute:
(dn, entry_attrs) = self.backend.get_entry(
@@ -532,8 +540,6 @@ class LDAPObject(Object):
return ''
except errors.NotFound:
pass
- # DN object assures we're returning a decoded (unescaped) value
- dn = DN(dn)
try:
return dn[self.primary_key.name]
except KeyError:
@@ -567,7 +573,7 @@ class LDAPObject(Object):
for member in entry_attrs.setdefault(attr, []):
for ldap_obj_name in self.attribute_members[attr]:
ldap_obj = self.api.Object[ldap_obj_name]
- if member.find(ldap_obj.container_dn) > 0:
+ if ldap_obj.container_dn in member:
new_attr = '%s_%s' % (attr, ldap_obj.name)
entry_attrs.setdefault(new_attr, []).append(
ldap_obj.get_primary_key_from_dn(member)
@@ -623,7 +629,6 @@ class LDAPObject(Object):
def __json__(self):
ldap = self.backend
- ldap.get_schema()
json_dict = dict(
(a, getattr(self, a)) for a in self.json_friendly_attributes
)
@@ -777,52 +782,47 @@ last, after all sets and adds."""),
_callback_registry = dict(pre={}, post={}, exc={}, interactive_prompt={})
- def _convert_2_dict(self, attrs):
+ def _convert_2_dict(self, ldap, attrs):
"""
Convert a string in the form of name/value pairs into a dictionary.
:param attrs: A list of name/value pair strings, in the "name=value"
format. May also be a single string, or None.
"""
- if attrs is None:
- return {}
-
- if not isinstance(attrs, (tuple, list)):
- attrs = [attrs]
newdict = {}
+ if attrs is None:
+ attrs = []
+ elif not type(attrs) in (list, tuple):
+ attrs = [attrs]
for a in attrs:
- m = re.match("^\s*(?P<attr>.*?)\s*=\s*(?P<value>.*?)\s*$", a)
- attr = str(m.group('attr').lower())
- value = m.group('value')
-
+ m = re.match("\s*(.*?)\s*=\s*(.*?)\s*$", a)
+ attr = str(m.group(1)).lower()
+ value = m.group(2)
if attr in self.obj.params and attr not in self.params:
# The attribute is managed by IPA, but it didn't get cloned
# to the command. This happens with no_update/no_create attrs.
raise errors.ValidationError(
name=attr, error=_('attribute is not configurable'))
+ if len(value) == 0:
+ # None means "delete this attribute"
+ value = None
- newdict.setdefault(attr, []).append(value)
+ if ldap.has_dn_syntax(attr):
+ value = DN(value)
+ if attr in newdict:
+ if type(value) in (tuple,):
+ newdict[attr] += list(value)
+ else:
+ newdict[attr].append(value)
+ else:
+ if type(value) in (tuple,):
+ newdict[attr] = list(value)
+ else:
+ newdict[attr] = [value]
return newdict
- def _convert_entry(self, entry_attrs):
- result = {}
- for attr, val in entry_attrs.iteritems():
- if val is None:
- val = []
- elif not isinstance(val, (tuple, list)):
- val = [val]
-
- result[attr] = []
- for v in val:
- if isinstance(v, str):
- # This is a Binary value, base64 encode it
- v = base64.b64encode(v)
- result[attr].append(unicode(v))
-
- return result
-
def process_attr_options(self, entry_attrs, dn, keys, options):
"""
Process all --setattr, --addattr, and --delattr options and add the
@@ -847,14 +847,15 @@ last, after all sets and adds."""),
:param keys: List of command arguments
:param options: List of options
"""
+
if all(k not in options for k in ("setattr", "addattr", "delattr")):
return
ldap = self.obj.backend
- adddict = self._convert_2_dict(options.get('addattr', []))
- setdict = self._convert_2_dict(options.get('setattr', []))
- deldict = self._convert_2_dict(options.get('delattr', []))
+ adddict = self._convert_2_dict(ldap, options.get('addattr', []))
+ setdict = self._convert_2_dict(ldap, options.get('setattr', []))
+ deldict = self._convert_2_dict(ldap, options.get('delattr', []))
setattrs = set(setdict.keys())
addattrs = set(adddict.keys())
@@ -865,24 +866,24 @@ last, after all sets and adds."""),
direct_del = delattrs
needldapattrs = []
else:
+ assert isinstance(dn, DN)
direct_add = setattrs & addattrs
direct_del = setattrs & delattrs
needldapattrs = list((addattrs | delattrs) - setattrs)
- mod_attrs = self._convert_entry(entry_attrs)
-
for attr, val in setdict.iteritems():
- mod_attrs[attr] = val
+ entry_attrs[attr] = val
for attr in direct_add:
- mod_attrs.setdefault(attr, []).extend(adddict[attr])
+ entry_attrs.setdefault(attr, []).extend(adddict[attr])
for attr in direct_del:
for delval in deldict[attr]:
try:
- mod_attrs[attr].remove(delval)
+ entry_attrs[attr].remove(delval)
except ValueError:
- raise errors.AttrValueNotFound(attr=attr, value=delval)
+ raise errors.AttrValueNotFound(attr=attr,
+ value=delval)
if needldapattrs:
try:
@@ -901,27 +902,28 @@ last, after all sets and adds."""),
raise errors.ValidationError(name=del_nonexisting.pop(),
error=_('No such attribute on this entry'))
- old_entry = self._convert_entry(old_entry)
-
for attr in needldapattrs:
- mod_attrs[attr] = old_entry.get(attr, [])
+ entry_attrs[attr] = old_entry.get(attr, [])
if attr in addattrs:
- mod_attrs[attr].extend(adddict.get(attr, []))
+ entry_attrs[attr].extend(adddict.get(attr, []))
for delval in deldict.get(attr, []):
try:
- mod_attrs[attr].remove(delval)
+ entry_attrs[attr].remove(delval)
except ValueError:
+ if isinstance(delval, str):
+ # This is a Binary value, base64 encode it
+ delval = unicode(base64.b64encode(delval))
raise errors.AttrValueNotFound(attr=attr, value=delval)
# normalize all values
changedattrs = setattrs | addattrs | delattrs
for attr in changedattrs:
- value = mod_attrs[attr]
if attr in self.params and self.params[attr].attribute:
- param = self.params[attr]
# convert single-value params to scalars
+ param = self.params[attr]
+ value = entry_attrs[attr]
if not param.multivalue:
if len(value) == 1:
value = value[0]
@@ -931,19 +933,19 @@ last, after all sets and adds."""),
raise errors.OnlyOneValueAllowed(attr=attr)
# validate, convert and encode params
try:
- value = param(value)
+ value = param(value)
except errors.ValidationError, err:
raise errors.ValidationError(name=attr, error=err.error)
except errors.ConversionError, err:
raise errors.ConversionError(name=attr, error=err.error)
+ entry_attrs[attr] = value
else:
# unknown attribute: remove duplicite and invalid values
- value = list(set([val for val in value if val]))
- if not value:
- value = None
- elif isinstance(value, (tuple, list)) and len(value) == 1:
- value = value[0]
- entry_attrs[attr] = value
+ entry_attrs[attr] = list(set([val for val in entry_attrs[attr] if val]))
+ if not entry_attrs[attr]:
+ entry_attrs[attr] = None
+ elif isinstance(entry_attrs[attr], (tuple, list)) and len(entry_attrs[attr]) == 1:
+ entry_attrs[attr] = entry_attrs[attr][0]
@classmethod
def register_pre_callback(cls, callback, first=False):
@@ -1021,8 +1023,13 @@ class LDAPCreate(BaseLDAPCommand, crud.Create):
entry_attrs[self.obj.uuid_attribute] = 'autogenerate'
dn = self.obj.get_dn(*keys, **options)
+ assert isinstance(dn, DN)
if self.obj.rdn_attribute:
- if not dn.startswith('%s=' % self.obj.primary_key.name):
+ try:
+ dn_attr = dn[0].attr
+ except (IndexError, KeyError):
+ dn_attr = None
+ if dn_attr != self.obj.primary_key.name:
self.obj.handle_duplicate_entry(*keys)
dn = ldap.make_dn(
entry_attrs, self.obj.rdn_attribute, self.obj.container_dn
@@ -1038,9 +1045,9 @@ class LDAPCreate(BaseLDAPCommand, crud.Create):
for callback in self.get_callbacks('pre'):
dn = callback(
self, ldap, dn, entry_attrs, attrs_list, *keys, **options)
+ assert isinstance(dn, DN)
_check_single_value_attrs(self.params, entry_attrs)
- ldap.get_schema()
_check_limit_object_class(self.api.Backend.ldap2.schema.attribute_types(self.obj.limit_object_classes), entry_attrs.keys(), allow_only=True)
_check_limit_object_class(self.api.Backend.ldap2.schema.attribute_types(self.obj.disallow_object_classes), entry_attrs.keys(), allow_only=False)
@@ -1074,16 +1081,19 @@ class LDAPCreate(BaseLDAPCommand, crud.Create):
self.obj.primary_key.name, keys[-1], object_class, attrs_list,
self.obj.container_dn
)
+ assert isinstance(dn, DN)
else:
(dn, entry_attrs) = self._exc_wrapper(keys, options, ldap.get_entry)(
dn, attrs_list, normalize=self.obj.normalize_dn
)
+ assert isinstance(dn, DN)
except errors.NotFound:
self.obj.handle_not_found(*keys)
for callback in self.get_callbacks('post'):
dn = callback(self, ldap, dn, entry_attrs, *keys, **options)
+ assert isinstance(dn, DN)
entry_attrs['dn'] = dn
self.obj.convert_attribute_members(entry_attrs, *keys, **options)
@@ -1092,9 +1102,11 @@ class LDAPCreate(BaseLDAPCommand, crud.Create):
return dict(result=entry_attrs, value=u'')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return dn
def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs):
@@ -1181,6 +1193,7 @@ class LDAPRetrieve(LDAPQuery):
ldap = self.obj.backend
dn = self.obj.get_dn(*keys, **options)
+ assert isinstance(dn, DN)
if options.get('all', False):
attrs_list = ['*'] + self.obj.default_attributes
@@ -1189,11 +1202,13 @@ class LDAPRetrieve(LDAPQuery):
for callback in self.get_callbacks('pre'):
dn = callback(self, ldap, dn, attrs_list, *keys, **options)
+ assert isinstance(dn, DN)
try:
(dn, entry_attrs) = self._exc_wrapper(keys, options, ldap.get_entry)(
dn, attrs_list, normalize=self.obj.normalize_dn
)
+ assert isinstance(dn, DN)
except errors.NotFound:
self.obj.handle_not_found(*keys)
@@ -1202,17 +1217,21 @@ class LDAPRetrieve(LDAPQuery):
for callback in self.get_callbacks('post'):
dn = callback(self, ldap, dn, entry_attrs, *keys, **options)
+ assert isinstance(dn, DN)
self.obj.convert_attribute_members(entry_attrs, *keys, **options)
+ assert isinstance(dn, DN)
entry_attrs['dn'] = dn
if self.obj.primary_key and keys[-1] is not None:
return dict(result=entry_attrs, value=keys[-1])
return dict(result=entry_attrs, value=u'')
def pre_callback(self, ldap, dn, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return dn
def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs):
@@ -1261,6 +1280,7 @@ class LDAPUpdate(LDAPQuery, crud.Update):
raise errors.EmptyModlist()
dn = self.obj.get_dn(*keys, **options)
+ assert isinstance(dn, DN)
entry_attrs = self.args_options_2_entry(**options)
@@ -1279,8 +1299,8 @@ class LDAPUpdate(LDAPQuery, crud.Update):
for callback in self.get_callbacks('pre'):
dn = callback(
self, ldap, dn, entry_attrs, attrs_list, *keys, **options)
+ assert isinstance(dn, DN)
- ldap.get_schema()
_check_limit_object_class(self.api.Backend.ldap2.schema.attribute_types(self.obj.limit_object_classes), entry_attrs.keys(), allow_only=True)
_check_limit_object_class(self.api.Backend.ldap2.schema.attribute_types(self.obj.disallow_object_classes), entry_attrs.keys(), allow_only=False)
@@ -1294,10 +1314,10 @@ class LDAPUpdate(LDAPQuery, crud.Update):
if self.obj.rdn_is_primary_key and self.obj.primary_key.name in entry_attrs:
# RDN change
self._exc_wrapper(keys, options, ldap.update_entry_rdn)(dn,
- unicode('%s=%s' % (self.obj.primary_key.name,
- entry_attrs[self.obj.primary_key.name])))
+ RDN((self.obj.primary_key.name, entry_attrs[self.obj.primary_key.name])))
rdnkeys = keys[:-1] + (entry_attrs[self.obj.primary_key.name], )
dn = self.obj.get_dn(*rdnkeys)
+ assert isinstance(dn, DN)
del entry_attrs[self.obj.primary_key.name]
options['rdnupdate'] = True
rdnupdate = True
@@ -1306,6 +1326,7 @@ class LDAPUpdate(LDAPQuery, crud.Update):
# to decide what to do. An EmptyModlist in this context doesn't
# mean an error occurred, just that there were no other updates to
# perform.
+ assert isinstance(dn, DN)
self._exc_wrapper(keys, options, ldap.update_entry)(dn, entry_attrs, normalize=self.obj.normalize_dn)
except errors.EmptyModlist, e:
if not rdnupdate:
@@ -1327,6 +1348,7 @@ class LDAPUpdate(LDAPQuery, crud.Update):
for callback in self.get_callbacks('post'):
dn = callback(self, ldap, dn, entry_attrs, *keys, **options)
+ assert isinstance(dn, DN)
self.obj.convert_attribute_members(entry_attrs, *keys, **options)
if self.obj.primary_key and keys[-1] is not None:
@@ -1334,9 +1356,11 @@ class LDAPUpdate(LDAPQuery, crud.Update):
return dict(result=entry_attrs, value=u'')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return dn
def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs):
@@ -1360,11 +1384,14 @@ class LDAPDelete(LDAPMultiQuery):
def delete_entry(pkey):
nkeys = keys[:-1] + (pkey, )
dn = self.obj.get_dn(*nkeys, **options)
+ assert isinstance(dn, DN)
for callback in self.get_callbacks('pre'):
dn = callback(self, ldap, dn, *nkeys, **options)
+ assert isinstance(dn, DN)
def delete_subtree(base_dn):
+ assert isinstance(base_dn, DN)
truncated = True
while truncated:
try:
@@ -1412,9 +1439,11 @@ class LDAPDelete(LDAPMultiQuery):
return dict(result=dict(failed=u''), value=u'')
def pre_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
return dn
def post_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
return True
def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs):
@@ -1495,14 +1524,17 @@ class LDAPAddMember(LDAPModMember):
(member_dns, failed) = self.get_member_dns(**options)
dn = self.obj.get_dn(*keys, **options)
+ assert isinstance(dn, DN)
for callback in self.get_callbacks('pre'):
dn = callback(self, ldap, dn, member_dns, failed, *keys, **options)
+ assert isinstance(dn, DN)
completed = 0
for (attr, objs) in member_dns.iteritems():
for ldap_obj_name in objs:
for m_dn in member_dns[attr][ldap_obj_name]:
+ assert isinstance(m_dn, DN)
if not m_dn:
continue
try:
@@ -1534,7 +1566,9 @@ class LDAPAddMember(LDAPModMember):
(completed, dn) = callback(
self, ldap, completed, failed, dn, entry_attrs, *keys,
**options)
+ assert isinstance(dn, DN)
+ assert isinstance(dn, DN)
entry_attrs['dn'] = dn
self.obj.convert_attribute_members(entry_attrs, *keys, **options)
return dict(
@@ -1544,9 +1578,11 @@ class LDAPAddMember(LDAPModMember):
)
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
return dn
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return (completed, dn)
def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs):
@@ -1583,14 +1619,17 @@ class LDAPRemoveMember(LDAPModMember):
(member_dns, failed) = self.get_member_dns(**options)
dn = self.obj.get_dn(*keys, **options)
+ assert isinstance(dn, DN)
for callback in self.get_callbacks('pre'):
dn = callback(self, ldap, dn, member_dns, failed, *keys, **options)
+ assert isinstance(dn, DN)
completed = 0
for (attr, objs) in member_dns.iteritems():
for ldap_obj_name, m_dns in objs.iteritems():
for m_dn in m_dns:
+ assert isinstance(m_dn, DN)
if not m_dn:
continue
try:
@@ -1625,7 +1664,9 @@ class LDAPRemoveMember(LDAPModMember):
(completed, dn) = callback(
self, ldap, completed, failed, dn, entry_attrs, *keys,
**options)
+ assert isinstance(dn, DN)
+ assert isinstance(dn, DN)
entry_attrs['dn'] = dn
self.obj.convert_attribute_members(entry_attrs, *keys, **options)
@@ -1636,9 +1677,11 @@ class LDAPRemoveMember(LDAPModMember):
)
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
return dn
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return (completed, dn)
def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs):
@@ -1765,6 +1808,7 @@ class LDAPSearch(BaseLDAPCommand, crud.Search):
base_dn = self.api.Object[self.obj.parent_object].get_dn(*args[:-1])
else:
base_dn = self.obj.container_dn
+ assert isinstance(base_dn, DN)
search_kw = self.args_options_2_entry(**options)
@@ -1812,6 +1856,7 @@ class LDAPSearch(BaseLDAPCommand, crud.Search):
for callback in self.get_callbacks('pre'):
(filter, base_dn, scope) = callback(
self, ldap, filter, attrs_list, base_dn, scope, *args, **options)
+ assert isinstance(base_dn, DN)
try:
(entries, truncated) = self._exc_wrapper(args, options, ldap.find_entries)(
@@ -1827,14 +1872,16 @@ class LDAPSearch(BaseLDAPCommand, crud.Search):
if self.sort_result_entries:
if self.obj.primary_key:
- sortfn=lambda x,y: cmp(x[1][self.obj.primary_key.name][0].lower(), y[1][self.obj.primary_key.name][0].lower())
- entries.sort(sortfn)
+ def sort_key(x):
+ return x[1][self.obj.primary_key.name][0].lower()
+ entries.sort(key=sort_key)
if not options.get('raw', False):
for e in entries:
self.obj.convert_attribute_members(e[1], *args, **options)
for e in entries:
+ assert isinstance(e[0], DN)
e[1]['dn'] = e[0]
entries = [e for (dn, e) in entries]
@@ -1845,6 +1892,7 @@ class LDAPSearch(BaseLDAPCommand, crud.Search):
)
def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options):
+ assert isinstance(base_dn, DN)
return (filters, base_dn, scope)
def post_callback(self, ldap, entries, truncated, *args, **options):
@@ -1926,9 +1974,11 @@ class LDAPAddReverseMember(LDAPModReverseMember):
# Ensure our target exists
result = self.api.Command[self.show_command](keys[-1])['result']
dn = result['dn']
+ assert isinstance(dn, DN)
for callback in self.get_callbacks('pre'):
dn = callback(self, ldap, dn, *keys, **options)
+ assert isinstance(dn, DN)
if options.get('all', False):
attrs_list = ['*'] + self.obj.default_attributes
@@ -1967,7 +2017,9 @@ class LDAPAddReverseMember(LDAPModReverseMember):
(completed, dn) = callback(
self, ldap, completed, failed, dn, entry_attrs, *keys,
**options)
+ assert isinstance(dn, DN)
+ assert isinstance(dn, DN)
entry_attrs['dn'] = dn
return dict(
completed=completed,
@@ -1976,9 +2028,11 @@ class LDAPAddReverseMember(LDAPModReverseMember):
)
def pre_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
return dn
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return (completed, dn)
def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs):
@@ -2022,9 +2076,11 @@ class LDAPRemoveReverseMember(LDAPModReverseMember):
# Ensure our target exists
result = self.api.Command[self.show_command](keys[-1])['result']
dn = result['dn']
+ assert isinstance(dn, DN)
for callback in self.get_callbacks('pre'):
dn = callback(self, ldap, dn, *keys, **options)
+ assert isinstance(dn, DN)
if options.get('all', False):
attrs_list = ['*'] + self.obj.default_attributes
@@ -2063,7 +2119,9 @@ class LDAPRemoveReverseMember(LDAPModReverseMember):
(completed, dn) = callback(
self, ldap, completed, failed, dn, entry_attrs, *keys,
**options)
+ assert isinstance(dn, DN)
+ assert isinstance(dn, DN)
entry_attrs['dn'] = dn
return dict(
completed=completed,
@@ -2072,9 +2130,11 @@ class LDAPRemoveReverseMember(LDAPModReverseMember):
)
def pre_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
return dn
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return (completed, dn)
def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs):
diff --git a/ipalib/plugins/config.py b/ipalib/plugins/config.py
index 9573bbb65..ef0fd79fa 100644
--- a/ipalib/plugins/config.py
+++ b/ipalib/plugins/config.py
@@ -19,7 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipalib import api
-from ipalib import Bool, Int, Str, IA5Str, StrEnum
+from ipalib import Bool, Int, Str, IA5Str, StrEnum, DNParam
from ipalib.plugins.baseldap import *
from ipalib import _
from ipalib.errors import ValidationError
@@ -149,7 +149,7 @@ class config(LDAPObject):
label=_('Enable migration mode'),
doc=_('Enable migration mode'),
),
- Str('ipacertificatesubjectbase',
+ DNParam('ipacertificatesubjectbase',
cli_name='subject',
label=_('Certificate Subject base'),
doc=_('Base for certificate subjects (OU=Test,O=Example)'),
@@ -199,7 +199,7 @@ class config(LDAPObject):
)
def get_dn(self, *keys, **kwargs):
- return 'cn=ipaconfig,cn=etc'
+ return DN(('cn', 'ipaconfig'), ('cn', 'etc'))
api.register(config)
@@ -208,6 +208,7 @@ class config_mod(LDAPUpdate):
__doc__ = _('Modify configuration options.')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
if 'ipadefaultprimarygroup' in entry_attrs:
group=entry_attrs['ipadefaultprimarygroup']
try:
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index 6727d052f..991eb0164 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -35,7 +35,6 @@ from ipalib.util import (validate_zonemgr, normalize_zonemgr,
validate_hostname, validate_dns_label, validate_domain_name,
get_dns_forward_zone_update_policy, get_dns_reverse_zone_update_policy)
from ipapython.ipautil import valid_ip, CheckedIPAddress, is_host_resolvable
-from ldap import explode_dn
__doc__ = _("""
Domain Name System (DNS)
@@ -802,10 +801,10 @@ class DNSRecord(Str):
# callbacks for per-type special record behavior
def dnsrecord_add_pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
- pass
+ assert isinstance(dn, DN)
def dnsrecord_add_post_callback(self, ldap, dn, entry_attrs, *keys, **options):
- pass
+ assert isinstance(dn, DN)
class ForwardRecord(DNSRecord):
extra = (
@@ -817,6 +816,7 @@ class ForwardRecord(DNSRecord):
)
def dnsrecord_add_pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
reverse_option = self._convert_dnsrecord_extra(self.extra[0])
if options.get(reverse_option.name):
records = entry_attrs.get(self.name, [])
@@ -832,6 +832,7 @@ class ForwardRecord(DNSRecord):
setattr(context, '%s_reverse' % self.name, entry_attrs.get(self.name))
def dnsrecord_add_post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
rev_records = getattr(context, '%s_reverse' % self.name, [])
if rev_records:
@@ -1727,6 +1728,7 @@ class dnszone_add(LDAPCreate):
)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
if not dns_container_exists(self.api.Backend.ldap2):
raise errors.NotFound(reason=_('DNS is not configured'))
@@ -1751,6 +1753,7 @@ class dnszone_add(LDAPCreate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
if 'ip_address' in options:
nameserver = entry_attrs['idnssoamname'][0][:-1] # ends with a dot
nsparts = nameserver.split('.')
@@ -1813,6 +1816,7 @@ class dnszone_find(LDAPSearch):
)
def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *args, **options):
+ assert isinstance(base_dn, DN)
if options.get('forward_only', False):
search_kw = {}
search_kw['idnsname'] = _valid_reverse_zones.keys()
@@ -1980,6 +1984,7 @@ class dnsrecord(LDAPObject):
)
def _nsrecord_pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
nsrecords = entry_attrs.get('nsrecord')
if options.get('force', False) or nsrecords is None:
return
@@ -1987,6 +1992,7 @@ class dnsrecord(LDAPObject):
check_ns_rec_resolvable(keys[0], nsrecord)
def _ptrrecord_pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
ptrrecords = entry_attrs.get('ptrrecord')
if ptrrecords is None:
return
@@ -2015,6 +2021,7 @@ class dnsrecord(LDAPObject):
% dict(name=zone_name, count=zone_len, user_count=ip_addr_comp_count)))
def run_precallback_validators(self, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
ldap = self.api.Backend.ldap2
for rtype in entry_attrs:
@@ -2049,7 +2056,7 @@ class dnsrecord(LDAPObject):
def get_dns_masters(self):
ldap = self.api.Backend.ldap2
- base_dn = 'cn=masters,cn=ipa,cn=etc,%s' % self.api.env.basedn
+ base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), self.api.env.basedn)
ldap_filter = '(&(objectClass=ipaConfigObject)(cn=DNS))'
dns_masters = []
@@ -2058,9 +2065,12 @@ class dnsrecord(LDAPObject):
for entry in entries:
master_dn = entry[0]
- if master_dn.startswith('cn='):
- master = explode_dn(master_dn)[1].replace('cn=','')
+ assert isinstance(master_dn, DN)
+ try:
+ master = master_dn[1]['cn']
dns_masters.append(master)
+ except (IndexError, KeyError):
+ pass
except errors.NotFound:
return []
@@ -2254,6 +2264,7 @@ class dnsrecord_add(LDAPCreate):
kw.update(user_options)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
precallback_attrs = []
processed_attrs = []
for option in options:
@@ -2354,6 +2365,7 @@ class dnsrecord_add(LDAPCreate):
raise exc
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
for attr in getattr(context, 'dnsrecord_precallback_attrs', []):
param = self.params[attr]
param.dnsrecord_add_post_callback(ldap, dn, entry_attrs, *keys, **options)
@@ -2382,6 +2394,7 @@ class dnsrecord_mod(LDAPUpdate):
return super(dnsrecord_mod, self).args_options_2_entry(*keys, **options)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
if options.get('rename') and self.obj.is_pkey_zone_record(*keys):
# zone rename is not allowed
raise errors.ValidationError(name='rename',
@@ -2466,10 +2479,12 @@ class dnsrecord_mod(LDAPUpdate):
return result
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
if self.obj.is_pkey_zone_record(*keys):
entry_attrs[self.obj.primary_key.name] = [_dns_zone_record]
self.obj.postprocess_record(entry_attrs, **options)
+ return dn
def interactive_prompt_callback(self, kw):
try:
@@ -2564,6 +2579,7 @@ class dnsrecord_del(LDAPUpdate):
yield option
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
try:
(dn_, old_entry) = ldap.get_entry(
dn, _record_attributes,
@@ -2626,9 +2642,11 @@ class dnsrecord_del(LDAPUpdate):
return result
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
if self.obj.is_pkey_zone_record(*keys):
entry_attrs[self.obj.primary_key.name] = [_dns_zone_record]
self.obj.postprocess_record(entry_attrs, **options)
+ return dn
def args_options_2_entry(self, *keys, **options):
self.obj.has_cli_options(options, self.no_option_msg)
@@ -2697,6 +2715,7 @@ class dnsrecord_show(LDAPRetrieve):
)
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
if self.obj.is_pkey_zone_record(*keys):
entry_attrs[self.obj.primary_key.name] = [_dns_zone_record]
self.obj.postprocess_record(entry_attrs, **options)
@@ -2723,6 +2742,7 @@ class dnsrecord_find(LDAPSearch):
yield option
def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *args, **options):
+ assert isinstance(base_dn, DN)
# include zone record (root entry) in the search
return (filter, base_dn, ldap.SCOPE_SUBTREE)
@@ -2774,7 +2794,7 @@ class dns_is_enabled(Command):
NO_CLI = True
has_output = output.standard_value
- base_dn = 'cn=masters,cn=ipa,cn=etc,%s' % api.env.basedn
+ base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
filter = '(&(objectClass=ipaConfigObject)(cn=DNS))'
def execute(self, *args, **options):
diff --git a/ipalib/plugins/entitle.py b/ipalib/plugins/entitle.py
index 5ccdb6510..67e7f959e 100644
--- a/ipalib/plugins/entitle.py
+++ b/ipalib/plugins/entitle.py
@@ -318,6 +318,7 @@ class entitle_consume(LDAPUpdate):
return result
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
quantity = keys[-1]
os.environ['LANG'] = 'en_US'
@@ -361,6 +362,7 @@ class entitle_consume(LDAPUpdate):
Returning the certificates isn't very interesting. Return the
status of entitlements instead.
"""
+ assert isinstance(dn, DN)
if 'usercertificate' in entry_attrs:
del entry_attrs['usercertificate']
if 'userpkcs12' in entry_attrs:
@@ -504,7 +506,7 @@ class entitle_register(LDAPCreate):
"""
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
- dn = '%s,%s' % (self.obj.container_dn, self.api.env.basedn)
+ dn = DN(self.obj.container_dn, self.api.env.basedn)
if not ldap.can_add(dn):
raise errors.ACIError(info='No permission to register')
os.environ['LANG'] = 'en_US'
@@ -604,6 +606,7 @@ class entitle_import(LDAPUpdate):
)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
try:
(db, uuid, certfile, keyfile) = get_uuid(ldap)
if db is not None:
@@ -622,7 +625,7 @@ class entitle_import(LDAPUpdate):
raise errors.CertificateFormatError(error=_('Not an entitlement certificate'))
except M2Crypto.X509.X509Error:
raise errors.CertificateFormatError(error=_('Not an entitlement certificate'))
- dn = 'ipaentitlementid=%s,%s' % (entry_attrs['ipaentitlementid'], dn)
+ dn = DN(('ipaentitlementid', entry_attrs['ipaentitlementid']), dn)
(dn, current_attrs) = ldap.get_entry(
dn, ['*'], normalize=self.obj.normalize_dn
)
@@ -689,6 +692,7 @@ class entitle_sync(LDAPUpdate):
)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
os.environ['LANG'] = 'en_US'
locale.setlocale(locale.LC_ALL, '')
@@ -719,6 +723,7 @@ class entitle_sync(LDAPUpdate):
Returning the certificates isn't very interesting. Return the
status of entitlements instead.
"""
+ assert isinstance(dn, DN)
if 'usercertificate' in entry_attrs:
del entry_attrs['usercertificate']
if 'userpkcs12' in entry_attrs:
diff --git a/ipalib/plugins/group.py b/ipalib/plugins/group.py
index 74bea1700..011587206 100644
--- a/ipalib/plugins/group.py
+++ b/ipalib/plugins/group.py
@@ -156,6 +156,7 @@ class group_add(LDAPCreate):
# As both 'external' and 'nonposix' options have default= set for
# them, they will always be present in options dict, thus we can
# safely reference the values
+ assert isinstance(dn, DN)
if options['external']:
entry_attrs['objectclass'].append('ipaexternalgroup')
if 'gidnumber' in options:
@@ -176,6 +177,7 @@ class group_del(LDAPDelete):
msg_summary = _('Deleted group "%(value)s"')
def pre_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
config = ldap.get_ipa_config()[1]
def_primary_group = config.get('ipadefaultprimarygroup', '')
def_primary_group_dn = group_dn = self.obj.get_dn(def_primary_group)
@@ -192,6 +194,7 @@ class group_del(LDAPDelete):
return dn
def post_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
try:
api.Command['pwpolicy_del'](keys[-1])
except errors.NotFound:
@@ -220,6 +223,7 @@ class group_mod(LDAPUpdate):
)
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
if ('posix' in options and options['posix']) or 'gidnumber' in options:
(dn, old_entry_attrs) = ldap.get_entry(dn, ['objectclass'])
if 'ipaexternalgroup' in old_entry_attrs['objectclass']:
@@ -275,6 +279,7 @@ class group_find(LDAPSearch):
)
def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *args, **options):
+ assert isinstance(base_dn, DN)
# if looking for private groups, we need to create a new search filter,
# because private groups have different object classes
if options['private']:
@@ -319,6 +324,7 @@ class group_add_member(LDAPAddMember):
)
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
result = (completed, dn)
if 'ipaexternalmember' in options:
if not _dcerpc_bindings_installed:
@@ -367,6 +373,7 @@ class group_remove_member(LDAPRemoveMember):
)
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
if keys[0] == protected_group_name:
result = api.Command.group_show(protected_group_name)
users_left = set(result['result'].get('member_user', []))
@@ -377,6 +384,7 @@ class group_remove_member(LDAPRemoveMember):
return dn
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
result = (completed, dn)
if 'ipaexternalmember' in options:
sids = options['ipaexternalmember']
diff --git a/ipalib/plugins/hbacrule.py b/ipalib/plugins/hbacrule.py
index 460083622..94f0d0e20 100644
--- a/ipalib/plugins/hbacrule.py
+++ b/ipalib/plugins/hbacrule.py
@@ -231,6 +231,7 @@ class hbacrule_add(LDAPCreate):
msg_summary = _('Added HBAC rule "%(value)s"')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
# HBAC rules are enabled by default
entry_attrs['ipaenabledflag'] = 'TRUE'
return dn
@@ -244,6 +245,7 @@ class hbacrule_del(LDAPDelete):
msg_summary = _('Deleted HBAC rule "%(value)s"')
def pre_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
kw = dict(seealso=keys[0])
_entries = api.Command.selinuxusermap_find(None, **kw)
if _entries['count']:
@@ -260,6 +262,7 @@ class hbacrule_mod(LDAPUpdate):
msg_summary = _('Modified HBAC rule "%(value)s"')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
try:
(dn, entry_attrs) = ldap.get_entry(dn, attrs_list)
except errors.NotFound:
@@ -436,6 +439,7 @@ class hbacrule_add_user(LDAPAddMember):
member_count_out = ('%i object added.', '%i objects added.')
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
try:
(dn, entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes)
except errors.NotFound:
@@ -464,6 +468,7 @@ class hbacrule_add_host(LDAPAddMember):
member_count_out = ('%i object added.', '%i objects added.')
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
try:
(dn, entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes)
except errors.NotFound:
@@ -492,6 +497,7 @@ class hbacrule_add_sourcehost(LDAPAddMember):
member_count_out = ('%i object added.', '%i objects added.')
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
try:
(dn, entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes)
except errors.NotFound:
@@ -502,6 +508,7 @@ class hbacrule_add_sourcehost(LDAPAddMember):
return add_external_pre_callback('host', ldap, dn, keys, options)
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return add_external_post_callback('sourcehost', 'host', 'externalhost', ldap, completed, failed, dn, entry_attrs, keys, options)
api.register(hbacrule_add_sourcehost)
@@ -514,6 +521,7 @@ class hbacrule_remove_sourcehost(LDAPRemoveMember):
member_count_out = ('%i object removed.', '%i objects removed.')
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return remove_external_post_callback('sourcehost', 'host', 'externalhost', ldap, completed, failed, dn, entry_attrs, keys, options)
api.register(hbacrule_remove_sourcehost)
@@ -526,6 +534,7 @@ class hbacrule_add_service(LDAPAddMember):
member_count_out = ('%i object added.', '%i objects added.')
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
try:
(dn, entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes)
except errors.NotFound:
diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py
index e8967b8c0..a417ba0f7 100644
--- a/ipalib/plugins/host.py
+++ b/ipalib/plugins/host.py
@@ -38,10 +38,10 @@ from ipalib.plugins.dns import (dns_container_exists, _record_types,
from ipalib.plugins.dns import get_reverse_zone
from ipalib import _, ngettext
from ipalib import x509
-from ipalib.dn import *
from ipalib.request import context
from ipalib.util import validate_sshpubkey, output_sshpubkey
from ipapython.ipautil import ipa_generate_password, CheckedIPAddress, make_sshfp
+from ipapython.dn import DN
__doc__ = _("""
Hosts/Machines
@@ -379,6 +379,7 @@ class host_add(LDAPCreate):
)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
if options.get('ip_address') and dns_container_exists(ldap):
parts = keys[-1].split('.')
host = parts[0]
@@ -423,6 +424,7 @@ class host_add(LDAPCreate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
exc = None
if dns_container_exists(ldap):
try:
@@ -483,6 +485,7 @@ class host_del(LDAPDelete):
)
def pre_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
# If we aren't given a fqdn, find it
if _hostname_validator(None, keys[-1]) is not None:
hostentry = api.Command['host_show'](keys[-1])['result']
@@ -599,6 +602,7 @@ class host_mod(LDAPUpdate):
)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
# Allow an existing OTP to be reset but don't allow a OTP to be
# added to an enrolled host.
if options.get('userpassword') or options.get('random'):
@@ -690,6 +694,7 @@ class host_mod(LDAPUpdate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
if options.get('random', False):
entry_attrs['randompassword'] = unicode(getattr(context, 'randompassword'))
set_certificate_attrs(entry_attrs)
@@ -728,6 +733,7 @@ class host_find(LDAPSearch):
yield option
def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *args, **options):
+ assert isinstance(base_dn, DN)
if 'locality' in attrs_list:
attrs_list.remove('locality')
attrs_list.append('l')
@@ -808,6 +814,7 @@ class host_show(LDAPRetrieve):
member_attributes = ['managedby']
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.get_password_attributes(ldap, dn, entry_attrs)
if entry_attrs['has_password']:
# If an OTP is set there is no keytab, at least not one
@@ -891,8 +898,7 @@ class host_disable(LDAPQuery):
try:
serial = unicode(x509.get_serial_number(cert, x509.DER))
try:
- result = api.Command['cert_show'](unicode(serial))['result'
-]
+ result = api.Command['cert_show'](unicode(serial))['result']
if 'revocation_reason' not in result:
try:
api.Command['cert_revoke'](unicode(serial), revocation_reason=4)
@@ -928,6 +934,7 @@ class host_disable(LDAPQuery):
)
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.suppress_netgroup_memberof(entry_attrs)
return dn
@@ -941,6 +948,7 @@ class host_add_managedby(LDAPAddMember):
allow_same = True
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.suppress_netgroup_memberof(entry_attrs)
return (completed, dn)
@@ -954,6 +962,7 @@ class host_remove_managedby(LDAPRemoveMember):
has_output_params = LDAPRemoveMember.has_output_params + host_output_params
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.suppress_netgroup_memberof(entry_attrs)
return (completed, dn)
diff --git a/ipalib/plugins/hostgroup.py b/ipalib/plugins/hostgroup.py
index b68c45842..7e6fe6d65 100644
--- a/ipalib/plugins/hostgroup.py
+++ b/ipalib/plugins/hostgroup.py
@@ -21,7 +21,7 @@
from ipalib.plugins.baseldap import *
from ipalib import api, Int, _, ngettext, errors
from ipalib.plugins.netgroup import NETGROUP_PATTERN, NETGROUP_PATTERN_ERRMSG
-from ipalib.dn import DN
+from ipapython.dn import DN
__doc__ = _("""
Groups of hosts.
@@ -119,6 +119,7 @@ class hostgroup_add(LDAPCreate):
msg_summary = _('Added hostgroup "%(value)s"')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
try:
# check duplicity with hostgroups first to provide proper error
netgroup = api.Command['hostgroup_show'](keys[-1])
@@ -140,6 +141,7 @@ class hostgroup_add(LDAPCreate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
# Always wait for the associated netgroup to be created so we can
# be sure to ignore it in memberOf
newentry = wait_for_value(ldap, dn, 'objectclass', 'mepOriginEntry')
@@ -166,6 +168,7 @@ class hostgroup_mod(LDAPUpdate):
msg_summary = _('Modified hostgroup "%(value)s"')
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.suppress_netgroup_memberof(dn, entry_attrs)
return dn
@@ -195,6 +198,7 @@ class hostgroup_show(LDAPRetrieve):
__doc__ = _('Display information about a hostgroup.')
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.suppress_netgroup_memberof( dn, entry_attrs)
return dn
@@ -205,6 +209,7 @@ class hostgroup_add_member(LDAPAddMember):
__doc__ = _('Add members to a hostgroup.')
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.suppress_netgroup_memberof(dn, entry_attrs)
return (completed, dn)
@@ -215,6 +220,7 @@ class hostgroup_remove_member(LDAPRemoveMember):
__doc__ = _('Remove members from a hostgroup.')
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.suppress_netgroup_memberof(dn, entry_attrs)
return (completed, dn)
diff --git a/ipalib/plugins/krbtpolicy.py b/ipalib/plugins/krbtpolicy.py
index 0f80c770c..60674cc06 100644
--- a/ipalib/plugins/krbtpolicy.py
+++ b/ipalib/plugins/krbtpolicy.py
@@ -71,7 +71,7 @@ class krbtpolicy(LDAPObject):
"""
Kerberos Ticket Policy object
"""
- container_dn = 'cn=%s,cn=kerberos' % api.env.realm
+ container_dn = DN(('cn', api.env.realm), ('cn', 'kerberos'))
object_name = _('kerberos ticket policy settings')
default_attributes = ['krbmaxticketlife', 'krbmaxrenewableage']
limit_object_classes = ['krbticketpolicyaux']
@@ -112,6 +112,7 @@ class krbtpolicy_mod(LDAPUpdate):
__doc__ = _('Modify Kerberos ticket policy.')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
# disable all flag
# ticket policies are attached to objects with unrelated attributes
if options.get('all'):
@@ -125,6 +126,7 @@ class krbtpolicy_show(LDAPRetrieve):
__doc__ = _('Display the current Kerberos ticket policy.')
def pre_callback(self, ldap, dn, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
# disable all flag
# ticket policies are attached to objects with unrelated attributes
if options.get('all'):
@@ -132,6 +134,7 @@ class krbtpolicy_show(LDAPRetrieve):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
if keys[-1] is not None:
# if policy for a specific user isn't set, display global values
if 'krbmaxticketlife' not in entry_attrs or \
diff --git a/ipalib/plugins/migration.py b/ipalib/plugins/migration.py
index a8c1c6d82..157ab4447 100644
--- a/ipalib/plugins/migration.py
+++ b/ipalib/plugins/migration.py
@@ -21,10 +21,8 @@ import re
import ldap as _ldap
from ipalib import api, errors, output
-from ipalib import Command, Password, Str, Flag, StrEnum
+from ipalib import Command, Password, Str, Flag, StrEnum, DNParam
from ipalib.cli import to_cli
-from ipalib.util import validate_dn_param
-from ipalib.dn import *
from ipalib.plugins.user import NO_UPG_MAGIC
if api.env.in_server and api.env.context in ['lite', 'server']:
try:
@@ -32,6 +30,7 @@ if api.env.in_server and api.env.context in ['lite', 'server']:
except StandardError, e:
raise e
from ipalib import _
+from ipapython.dn import DN
__doc__ = _("""
Migration to IPA
@@ -114,18 +113,10 @@ _dn_err_msg = _('Malformed DN')
_supported_schemas = (u'RFC2307bis', u'RFC2307')
-_compat_dn = "cn=Schema Compatibility,cn=plugins,cn=config"
-
-def is_DN_syntax(ldap, attr):
- """
- Check the schema to see if the attribute uses DN syntax.
-
- Returns True/False
- """
- obj = ldap.schema.get_obj(_ldap.schema.AttributeType, attr)
- return obj is not None and obj.syntax == '1.3.6.1.4.1.1466.115.121.1.12'
+_compat_dn = DN(('cn', 'Schema Compatibility'), ('cn', 'plugins'), ('cn', 'config'))
def _pre_migrate_user(ldap, pkey, dn, entry_attrs, failed, config, ctx, **kwargs):
+ assert isinstance(dn, DN)
attr_blacklist = ['krbprincipalkey','memberofindirect','memberindirect']
attr_blacklist.extend(kwargs.get('attr_blacklist', []))
ds_ldap = ctx['ds_ldap']
@@ -198,17 +189,18 @@ def _pre_migrate_user(ldap, pkey, dn, entry_attrs, failed, config, ctx, **kwargs
# tree
for attr in entry_attrs.keys():
- if is_DN_syntax(ldap, attr):
+ if ldap.has_dn_syntax(attr):
for ind, value in enumerate(entry_attrs[attr]):
+ assert isinstance(value, DN)
try:
(remote_dn, remote_entry) = ds_ldap.get_entry(value, [api.Object.user.primary_key.name, api.Object.group.primary_key.name])
except errors.NotFound:
api.log.warn('%s: attribute %s refers to non-existent entry %s' % (pkey, attr, value))
continue
- if value.lower().endswith(search_bases['user']):
+ if value.endswith(search_bases['user']):
primary_key = api.Object.user.primary_key.name
container = api.env.container_user
- elif value.lower().endswith(search_bases['group']):
+ elif value.endswith(search_bases['group']):
primary_key = api.Object.group.primary_key.name
container = api.env.container_group
else:
@@ -221,15 +213,13 @@ def _pre_migrate_user(ldap, pkey, dn, entry_attrs, failed, config, ctx, **kwargs
api.log.debug('converting DN value %s for %s in %s' % (value, attr, dn))
rdnval = remote_entry[primary_key][0].lower()
- entry_attrs[attr][ind] = \
- str(DN((primary_key, rdnval),
- container,
- api.env.basedn))
+ entry_attrs[attr][ind] = DN((primary_key, rdnval), container, api.env.basedn)
return dn
def _post_migrate_user(ldap, pkey, dn, entry_attrs, failed, config, ctx):
+ assert isinstance(dn, DN)
# add user to the default group
try:
ldap.add_entry_to_group(dn, ctx['def_group_dn'])
@@ -246,6 +236,7 @@ def _post_migrate_user(ldap, pkey, dn, entry_attrs, failed, config, ctx):
# GROUP MIGRATION CALLBACKS AND VARS
def _pre_migrate_group(ldap, pkey, dn, entry_attrs, failed, config, ctx, **kwargs):
+
def convert_members_rfc2307bis(member_attr, search_bases, overwrite=False):
"""
Convert DNs in member attributes to work in IPA.
@@ -253,24 +244,19 @@ def _pre_migrate_group(ldap, pkey, dn, entry_attrs, failed, config, ctx, **kwarg
new_members = []
entry_attrs.setdefault(member_attr, [])
for m in entry_attrs[member_attr]:
+ assert isinstance(m, DN)
try:
- # what str2dn returns looks like [[('cn', 'foo', 4)], [('dc', 'example', 1)], [('dc', 'com', 1)]]
- rdn = _ldap.dn.str2dn(m ,flags=_ldap.DN_FORMAT_LDAPV3)[0]
- rdnval = rdn[0][1]
+ rdnval = m[0].value
except IndexError:
api.log.error('Malformed DN %s has no RDN?' % m)
continue
- if m.lower().endswith(search_bases['user']):
+ if m.endswith(search_bases['user']):
api.log.info('migrating user %s' % m)
- m = '%s=%s,%s' % (api.Object.user.primary_key.name,
- rdnval,
- api.env.container_user)
- elif m.lower().endswith(search_bases['group']):
+ m = DN((api.Object.user.primary_key.name, rdnval), api.env.container_user)
+ elif m.endswith(search_bases['group']):
api.log.info('migrating group %s' % m)
- m = '%s=%s,%s' % (api.Object.group.primary_key.name,
- rdnval,
- api.env.container_group)
+ m = DN((api.Object.group.primary_key.name, rdnval), api.env.container_group)
else:
api.log.error('entry %s does not belong into any known container' % m)
continue
@@ -290,12 +276,11 @@ def _pre_migrate_group(ldap, pkey, dn, entry_attrs, failed, config, ctx, **kwarg
new_members = []
entry_attrs.setdefault(member_attr, [])
for m in entry_attrs[member_attr]:
- memberdn = '%s=%s,%s' % (api.Object.user.primary_key.name,
- m,
- api.env.container_user)
+ memberdn = DN((api.Object.user.primary_key.name, m), api.env.container_user)
new_members.append(ldap.normalize_dn(memberdn))
entry_attrs['member'] = new_members
+ assert isinstance(dn, DN)
attr_blacklist = ['memberofindirect','memberindirect']
attr_blacklist.extend(kwargs.get('attr_blacklist', []))
@@ -330,6 +315,7 @@ def _pre_migrate_group(ldap, pkey, dn, entry_attrs, failed, config, ctx, **kwarg
def _group_exc_callback(ldap, dn, entry_attrs, exc, options):
+ assert isinstance(dn, DN)
if isinstance(exc, errors.DuplicateEntry):
if options.get('groupoverwritegid', False) and \
entry_attrs.get('gidnumber') is not None:
@@ -424,24 +410,24 @@ class migrate_ds(Command):
)
takes_options = (
- Str('binddn?', validate_dn_param,
+ DNParam('binddn?',
cli_name='bind_dn',
label=_('Bind DN'),
- default=u'cn=directory manager',
+ default=DN(('cn', 'directory manager')),
autofill=True,
),
- Str('usercontainer', validate_dn_param,
+ DNParam('usercontainer',
cli_name='user_container',
label=_('User container'),
doc=_('DN of container for users in DS relative to base DN'),
- default=u'ou=people',
+ default=DN(('ou', 'people')),
autofill=True,
),
- Str('groupcontainer', validate_dn_param,
+ DNParam('groupcontainer',
cli_name='group_container',
label=_('Group container'),
doc=_('DN of container for groups in DS relative to base DN'),
- default=u'ou=groups',
+ default=DN(('ou', 'groups')),
autofill=True,
),
Str('userobjectclass+',
@@ -511,7 +497,7 @@ class migrate_ds(Command):
doc=_('Continuous operation mode. Errors are reported but the process continues'),
default=False,
),
- Str('basedn?',
+ DNParam('basedn?',
cli_name='base_dn',
label=_('Base DN'),
doc=_('Base DN on remote LDAP server'),
@@ -594,11 +580,9 @@ can use their Kerberos accounts.''')
def _get_search_bases(self, options, ds_base_dn, migrate_order):
search_bases = dict()
- ds_base_dn = DN(ds_base_dn)
for ldap_obj_name in migrate_order:
container = options.get('%scontainer' % to_cli(ldap_obj_name))
if container:
- container = DN(container)
# Don't append base dn if user already appended it in the container dn
if container.endswith(ds_base_dn):
search_base = container
@@ -606,13 +590,14 @@ can use their Kerberos accounts.''')
search_base = DN(container, ds_base_dn)
else:
search_base = ds_base_dn
- search_bases[ldap_obj_name] = str(search_base)
+ 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)
@@ -703,6 +688,7 @@ can use their Kerberos accounts.''')
continue
dn = ldap_obj.get_dn(pkey)
+ assert isinstance(dn, DN)
entry_attrs['objectclass'] = list(
set(
config.get(
@@ -722,6 +708,7 @@ can use their Kerberos accounts.''')
valid_gids = valid_gids,
**blacklists
)
+ assert isinstance(dn, DN)
if not dn:
continue
except errors.NotFound, e:
@@ -760,6 +747,8 @@ can use their Kerberos accounts.''')
config = ldap.get_ipa_config()[1]
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':
@@ -773,6 +762,7 @@ can use their Kerberos accounts.''')
if not options.get('compat'):
try:
(dn,check_compat) = ldap.get_entry(_compat_dn, normalize=False)
+ assert isinstance(dn, DN)
if check_compat is not None and \
check_compat.get('nsslapd-pluginenabled', [''])[0].lower() == 'on':
return dict(result={}, failed={}, enabled=True, compat=False)
@@ -782,14 +772,16 @@ can use their Kerberos accounts.''')
if not ds_base_dn:
# retrieve base DN from remote LDAP server
(entries, truncated) = ds_ldap.find_entries(
- '', ['namingcontexts', 'defaultnamingcontext'], '',
+ '', ['namingcontexts', 'defaultnamingcontext'], DN(''),
_ldap.SCOPE_BASE, size_limit=-1, time_limit=0,
)
if 'defaultnamingcontext' in entries[0][1]:
- ds_base_dn = entries[0][1]['defaultnamingcontext'][0]
+ ds_base_dn = DN(entries[0][1]['defaultnamingcontext'][0])
+ assert isinstance(ds_base_dn, DN)
else:
try:
- ds_base_dn = entries[0][1]['namingcontexts'][0]
+ ds_base_dn = DN(entries[0][1]['namingcontexts'][0])
+ assert isinstance(ds_base_dn, DN)
except (IndexError, KeyError), e:
raise StandardError(str(e))
diff --git a/ipalib/plugins/netgroup.py b/ipalib/plugins/netgroup.py
index 4236feeb7..263d66b55 100644
--- a/ipalib/plugins/netgroup.py
+++ b/ipalib/plugins/netgroup.py
@@ -162,6 +162,7 @@ class netgroup_add(LDAPCreate):
u'Hostgroups and netgroups share a common namespace')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
entry_attrs.setdefault('nisdomainname', self.api.env.domain)
try:
@@ -203,6 +204,7 @@ class netgroup_mod(LDAPUpdate):
msg_summary = _('Modified netgroup "%(value)s"')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
try:
(dn, entry_attrs) = ldap.get_entry(dn, attrs_list)
except errors.NotFound:
@@ -238,6 +240,7 @@ class netgroup_find(LDAPSearch):
)
def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *args, **options):
+ assert isinstance(base_dn, DN)
# Do not display private mepManagedEntry netgroups by default
# If looking for managed groups, we need to omit the negation search filter
@@ -267,8 +270,11 @@ class netgroup_add_member(LDAPAddMember):
member_attributes = ['memberuser', 'memberhost', 'member']
has_output_params = LDAPAddMember.has_output_params + output_params
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
return add_external_pre_callback('host', ldap, dn, keys, options)
+
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return add_external_post_callback('memberhost', 'host', 'externalhost', ldap, completed, failed, dn, entry_attrs, keys, options)
api.register(netgroup_add_member)
@@ -280,6 +286,7 @@ class netgroup_remove_member(LDAPRemoveMember):
member_attributes = ['memberuser', 'memberhost', 'member']
has_output_params = LDAPRemoveMember.has_output_params + output_params
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return remove_external_post_callback('memberhost', 'host', 'externalhost', ldap, completed, failed, dn, entry_attrs, keys, options)
api.register(netgroup_remove_member)
diff --git a/ipalib/plugins/passwd.py b/ipalib/plugins/passwd.py
index 9bee314ec..2c83560b1 100644
--- a/ipalib/plugins/passwd.py
+++ b/ipalib/plugins/passwd.py
@@ -24,6 +24,7 @@ from ipalib import _
from ipalib import output
from ipalib.plugins.user import split_principal, validate_principal, normalize_principal
from ipalib.request import context
+from ipapython.dn import DN
__doc__ = _("""
Set a user's password
@@ -104,7 +105,7 @@ class passwd(Command):
(dn, entry_attrs) = ldap.find_entry_by_attr(
'krbprincipalname', principal, 'posixaccount', [''],
- ",".join([api.env.container_user, api.env.basedn])
+ DN(api.env.container_user, api.env.basedn)
)
if principal == getattr(context, 'principal') and \
diff --git a/ipalib/plugins/permission.py b/ipalib/plugins/permission.py
index 89f9eaa62..befa74df8 100644
--- a/ipalib/plugins/permission.py
+++ b/ipalib/plugins/permission.py
@@ -22,6 +22,7 @@ from ipalib import api, _, ngettext
from ipalib import Flag, Str, StrEnum
from ipalib.request import context
from ipalib import errors
+from ipapython.dn import DN, EditableDN
__doc__ = _("""
Permissions
@@ -202,6 +203,7 @@ class permission_add(LDAPCreate):
has_output_params = LDAPCreate.has_output_params + output_params
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
# Test the ACI before going any further
opts = self.obj.filter_aci_attributes(options)
opts['test'] = True
@@ -219,6 +221,7 @@ class permission_add(LDAPCreate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
# Now actually add the aci.
opts = self.obj.filter_aci_attributes(options)
opts['test'] = False
@@ -275,6 +278,7 @@ class permission_add_noaci(LDAPCreate):
yield option
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
permission_type = options.get('permissiontype')
if permission_type:
entry_attrs['ipapermissiontype'] = [ permission_type ]
@@ -297,6 +301,7 @@ class permission_del(LDAPDelete):
)
def pre_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
if not options.get('force') and not self.obj.check_system(ldap, dn, *keys):
raise errors.ACIError(info='A SYSTEM permission may not be removed')
# remove permission even when the underlying ACI is missing
@@ -316,6 +321,7 @@ class permission_mod(LDAPUpdate):
has_output_params = LDAPUpdate.has_output_params + output_params
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
if not self.obj.check_system(ldap, dn, *keys):
raise errors.ACIError(info='A SYSTEM permission may not be modified')
@@ -332,10 +338,13 @@ class permission_mod(LDAPUpdate):
if 'rename' in options:
if options['rename']:
try:
- new_dn = dn.replace(keys[-1].lower(), options['rename'], 1)
- (new_dn, attrs) = ldap.get_entry(
- new_dn, attrs_list, normalize=self.obj.normalize_dn
- )
+ try:
+ new_dn = EditableDN(dn)
+ new_dn[0]['cn'] # assure the first RDN has cn as it's type
+ except (IndexError, KeyError), e:
+ raise ValueError("expected dn starting with 'cn=' but got '%s'" % dn)
+ new_dn[0].value = options['rename']
+ (new_dn, attrs) = ldap.get_entry(new_dn, attrs_list, normalize=self.obj.normalize_dn)
raise errors.DuplicateEntry()
except errors.NotFound:
pass # permission may be renamed, continue
@@ -371,6 +380,7 @@ class permission_mod(LDAPUpdate):
raise exc
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
# rename the underlying ACI after the change to permission
cn = keys[-1]
@@ -480,6 +490,7 @@ class permission_show(LDAPRetrieve):
has_output_params = LDAPRetrieve.has_output_params + output_params
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
try:
common_options = filter_options(options, ['all', 'raw'])
aci = self.api.Command.aci_show(keys[-1], aciprefix=ACI_PREFIX,
diff --git a/ipalib/plugins/pkinit.py b/ipalib/plugins/pkinit.py
index cb3b9c2fc..2d11f0244 100644
--- a/ipalib/plugins/pkinit.py
+++ b/ipalib/plugins/pkinit.py
@@ -21,6 +21,7 @@ from ipalib import api, errors
from ipalib import Int, Str
from ipalib import Object, Command
from ipalib import _
+from ipapython.dn import DN
__doc__ = _("""
Kerberos pkinit options
@@ -67,9 +68,7 @@ class pkinit_anonymous(Command):
__doc__ = _('Enable or Disable Anonymous PKINIT.')
princ_name = 'WELLKNOWN/ANONYMOUS@%s' % api.env.realm
- default_dn = 'krbprincipalname=%s,cn=%s,cn=kerberos,%s' % (
- princ_name, api.env.realm, api.env.basedn
- )
+ default_dn = DN(('krbprincipalname', princ_name), ('cn', api.env.realm), ('cn', 'kerberos'), api.env.basedn)
takes_args = (
Str('action', valid_arg),
diff --git a/ipalib/plugins/pwpolicy.py b/ipalib/plugins/pwpolicy.py
index 77e6f2c79..33c8329f7 100644
--- a/ipalib/plugins/pwpolicy.py
+++ b/ipalib/plugins/pwpolicy.py
@@ -19,11 +19,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipalib import api
-from ipalib import Int, Str
+from ipalib import Int, Str, DNParam
from ipalib.plugins.baseldap import *
from ipalib import _
from ipalib.request import context
from ipapython.ipautil import run
+from ipapython.dn import DN
from distutils import version
__doc__ = _("""
@@ -75,13 +76,13 @@ class cosentry(LDAPObject):
"""
NO_CLI = True
- container_dn = 'cn=costemplates,%s' % api.env.container_accounts
+ container_dn = DN(('cn', 'costemplates'), api.env.container_accounts)
object_class = ['top', 'costemplate', 'extensibleobject', 'krbcontainer']
default_attributes = ['cn', 'cospriority', 'krbpwdpolicyreference']
takes_params = (
Str('cn', primary_key=True),
- Str('krbpwdpolicyreference'),
+ DNParam('krbpwdpolicyreference'),
Int('cospriority', minvalue=0),
)
@@ -102,8 +103,7 @@ class cosentry(LDAPObject):
)['result']
if len(entries) > 0:
group_name = self.api.Object.group.get_primary_key_from_dn(
- entries[0]['cn'][0]
- )
+ DN(entries[0]['cn'][0]))
raise errors.ValidationError(
name='priority',
error=self.priority_not_unique_msg % {
@@ -119,6 +119,7 @@ class cosentry_add(LDAPCreate):
NO_CLI = True
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
# check for existence of the group
result = self.api.Command.group_show(keys[-1], all=True)['result']
oc = map(lambda x:x.lower(),result['objectclass'])
@@ -141,6 +142,7 @@ class cosentry_mod(LDAPUpdate):
NO_CLI = True
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
new_cospriority = options.get('cospriority')
if new_cospriority is not None:
cos_entry = self.api.Command.cosentry_show(keys[-1])['result']
@@ -167,13 +169,13 @@ api.register(cosentry_find)
global_policy_name = 'global_policy'
-global_policy_dn = 'cn=%s,cn=%s,cn=kerberos,%s' % (global_policy_name, api.env.realm, api.env.basedn)
+global_policy_dn = DN(('cn', global_policy_name), ('cn', api.env.realm), ('cn', 'kerberos'), api.env.basedn)
class pwpolicy(LDAPObject):
"""
Password Policy object
"""
- container_dn = 'cn=%s,cn=kerberos' % api.env.realm
+ container_dn = DN(('cn', api.env.realm), ('cn', 'kerberos'))
object_name = _('password policy')
object_name_plural = _('password policies')
object_class = ['top', 'nscontainer', 'krbpwdpolicy']
@@ -339,6 +341,7 @@ class pwpolicy_add(LDAPCreate):
yield self.obj.primary_key.clone(attribute=True, required=True)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.convert_time_on_input(entry_attrs)
self.obj.validate_lifetime(entry_attrs, True)
self.api.Command.cosentry_add(
@@ -348,6 +351,7 @@ class pwpolicy_add(LDAPCreate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.log.info('%r' % entry_attrs)
# attribute rights are not allowed for pwpolicy_add
self.obj.add_cospriority(entry_attrs, keys[-1], rights=False)
@@ -366,7 +370,8 @@ class pwpolicy_del(LDAPDelete):
)
def pre_callback(self, ldap, dn, *keys, **options):
- if dn.lower() == global_policy_dn.lower():
+ assert isinstance(dn, DN)
+ if dn == global_policy_dn:
raise errors.ValidationError(
name='group',
error=_('cannot delete global password policy')
@@ -374,6 +379,7 @@ class pwpolicy_del(LDAPDelete):
return dn
def post_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
try:
self.api.Command.cosentry_del(keys[-1])
except errors.NotFound:
@@ -387,6 +393,7 @@ class pwpolicy_mod(LDAPUpdate):
__doc__ = _('Modify a group password policy.')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.convert_time_on_input(entry_attrs)
self.obj.validate_lifetime(entry_attrs, False, *keys)
setattr(context, 'cosupdate', False)
@@ -408,6 +415,7 @@ class pwpolicy_mod(LDAPUpdate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
rights = options.get('all', False) and options.get('rights', False)
self.obj.add_cospriority(entry_attrs, keys[-1], rights)
self.obj.convert_time_for_output(entry_attrs, **options)
@@ -436,6 +444,7 @@ class pwpolicy_show(LDAPRetrieve):
)
def pre_callback(self, ldap, dn, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
if options.get('user') is not None:
user_entry = self.api.Command.user_show(
options['user'], all=True
@@ -445,6 +454,7 @@ class pwpolicy_show(LDAPRetrieve):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
rights = options.get('all', False) and options.get('rights', False)
self.obj.add_cospriority(entry_attrs, keys[-1], rights)
self.obj.convert_time_for_output(entry_attrs, **options)
diff --git a/ipalib/plugins/range.py b/ipalib/plugins/range.py
index 39849b661..c1d918679 100644
--- a/ipalib/plugins/range.py
+++ b/ipalib/plugins/range.py
@@ -96,6 +96,7 @@ class range_add(LDAPCreate):
msg_summary = _('Added ID range "%(value)s"')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
if ('ipanttrusteddomainsid' not in options and
'ipasecondarybaserid' not in options):
raise errors.ValidationError(name=_('Range setup'),
@@ -110,6 +111,7 @@ class range_add(LDAPCreate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.handle_iparangetype(entry_attrs, options, keep_objectclass=True)
return dn
@@ -128,6 +130,7 @@ class range_find(LDAPSearch):
# Since all range types are stored within separate containers under
# 'cn=ranges,cn=etc' search can be done on a one-level scope
def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options):
+ assert isinstance(base_dn, DN)
attrs_list.append('objectclass')
return (filters, base_dn, ldap.SCOPE_ONELEVEL)
@@ -140,10 +143,12 @@ class range_show(LDAPRetrieve):
__doc__ = _('Display information about a range.')
def pre_callback(self, ldap, dn, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
attrs_list.append('objectclass')
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.handle_iparangetype(entry_attrs, options)
return dn
@@ -153,10 +158,12 @@ class range_mod(LDAPUpdate):
msg_summary = _('Modified ID range "%(value)s"')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
attrs_list.append('objectclass')
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.handle_iparangetype(entry_attrs, options)
return dn
@@ -166,4 +173,3 @@ api.register(range_mod)
api.register(range_del)
api.register(range_find)
api.register(range_show)
-
diff --git a/ipalib/plugins/selinuxusermap.py b/ipalib/plugins/selinuxusermap.py
index 160e68c96..988cb4f0b 100644
--- a/ipalib/plugins/selinuxusermap.py
+++ b/ipalib/plugins/selinuxusermap.py
@@ -234,6 +234,7 @@ class selinuxusermap_add(LDAPCreate):
msg_summary = _('Added SELinux User Map "%(value)s"')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
# rules are enabled by default
entry_attrs['ipaenabledflag'] = 'TRUE'
validate_selinuxuser_inlist(ldap, entry_attrs['ipaselinuxuser'])
@@ -243,6 +244,7 @@ class selinuxusermap_add(LDAPCreate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj._convert_seealso(ldap, entry_attrs, **options)
return dn
@@ -264,6 +266,7 @@ class selinuxusermap_mod(LDAPUpdate):
msg_summary = _('Modified SELinux User Map "%(value)s"')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
try:
(_dn, _entry_attrs) = ldap.get_entry(dn, attrs_list)
except errors.NotFound:
@@ -288,6 +291,7 @@ class selinuxusermap_mod(LDAPUpdate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj._convert_seealso(ldap, entry_attrs, **options)
return dn
@@ -331,6 +335,7 @@ class selinuxusermap_show(LDAPRetrieve):
__doc__ = _('Display the properties of a SELinux User Map rule.')
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj._convert_seealso(ldap, entry_attrs, **options)
return dn
@@ -398,6 +403,7 @@ class selinuxusermap_add_user(LDAPAddMember):
member_count_out = ('%i object added.', '%i objects added.')
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
try:
(dn, entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes)
except errors.NotFound:
@@ -428,6 +434,7 @@ class selinuxusermap_add_host(LDAPAddMember):
member_count_out = ('%i object added.', '%i objects added.')
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
try:
(dn, entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes)
except errors.NotFound:
diff --git a/ipalib/plugins/service.py b/ipalib/plugins/service.py
index 7cb7b8030..213711ab3 100644
--- a/ipalib/plugins/service.py
+++ b/ipalib/plugins/service.py
@@ -278,6 +278,7 @@ class service_add(LDAPCreate):
),
)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
(service, hostname, realm) = split_principal(keys[-1])
if service.lower() == 'host' and not options['force']:
raise errors.HostService()
@@ -322,6 +323,7 @@ class service_del(LDAPDelete):
msg_summary = _('Deleted service "%(value)s"')
member_attributes = ['managedby']
def pre_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
# In the case of services we don't want IPA master services to be
# deleted. This is a limited few though. If the user has their own
# custom services allow them to manage them.
@@ -367,6 +369,7 @@ class service_mod(LDAPUpdate):
member_attributes = ['managedby']
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
if 'usercertificate' in options:
(service, hostname, realm) = split_principal(keys[-1])
cert = options.get('usercertificate')
@@ -386,7 +389,9 @@ class service_mod(LDAPUpdate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
set_certificate_attrs(entry_attrs)
+ return dn
api.register(service_mod)
@@ -402,6 +407,7 @@ class service_find(LDAPSearch):
has_output_params = LDAPSearch.has_output_params + output_params
def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *args, **options):
+ assert isinstance(base_dn, DN)
# lisp style!
custom_filter = '(&(objectclass=ipaService)' \
'(!(objectClass=posixAccount))' \
@@ -439,6 +445,7 @@ class service_show(LDAPRetrieve):
has_output_params = LDAPRetrieve.has_output_params + output_params
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.get_password_attributes(ldap, dn, entry_attrs)
set_certificate_attrs(entry_attrs)
diff --git a/ipalib/plugins/sudorule.py b/ipalib/plugins/sudorule.py
index 723cce2e4..e2937949b 100644
--- a/ipalib/plugins/sudorule.py
+++ b/ipalib/plugins/sudorule.py
@@ -246,6 +246,7 @@ class sudorule_add(LDAPCreate):
__doc__ = _('Create new Sudo Rule.')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
self.obj.check_order_uniqueness(*keys, **options)
# Sudo Rules are enabled by default
entry_attrs['ipaenabledflag'] = 'TRUE'
@@ -269,6 +270,7 @@ class sudorule_mod(LDAPUpdate):
msg_summary = _('Modified Sudo Rule "%(value)s"')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
if 'sudoorder' in options:
new_order = options.get('sudoorder')
old_entry = self.api.Command.sudorule_show(keys[-1])['result']
@@ -371,6 +373,7 @@ class sudorule_add_allow_command(LDAPAddMember):
member_count_out = ('%i object added.', '%i objects added.')
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
try:
(_dn, _entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes)
except errors.NotFound:
@@ -399,6 +402,7 @@ class sudorule_add_deny_command(LDAPAddMember):
member_count_out = ('%i object added.', '%i objects added.')
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
try:
(_dn, _entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes)
except errors.NotFound:
@@ -426,6 +430,7 @@ class sudorule_add_user(LDAPAddMember):
member_count_out = ('%i object added.', '%i objects added.')
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
try:
(_dn, _entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes)
except errors.NotFound:
@@ -435,6 +440,7 @@ class sudorule_add_user(LDAPAddMember):
return add_external_pre_callback('user', ldap, dn, keys, options)
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return add_external_post_callback('memberuser', 'user', 'externaluser', ldap, completed, failed, dn, entry_attrs, keys, options)
api.register(sudorule_add_user)
@@ -447,6 +453,7 @@ class sudorule_remove_user(LDAPRemoveMember):
member_count_out = ('%i object removed.', '%i objects removed.')
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return remove_external_post_callback('memberuser', 'user', 'externaluser', ldap, completed, failed, dn, entry_attrs, keys, options)
api.register(sudorule_remove_user)
@@ -459,6 +466,7 @@ class sudorule_add_host(LDAPAddMember):
member_count_out = ('%i object added.', '%i objects added.')
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+ assert isinstance(dn, DN)
try:
(_dn, _entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes)
except errors.NotFound:
@@ -468,6 +476,7 @@ class sudorule_add_host(LDAPAddMember):
return add_external_pre_callback('host', ldap, dn, keys, options)
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return add_external_post_callback('memberhost', 'host', 'externalhost', ldap, completed, failed, dn, entry_attrs, keys, options)
api.register(sudorule_add_host)
@@ -480,6 +489,7 @@ class sudorule_remove_host(LDAPRemoveMember):
member_count_out = ('%i object removed.', '%i objects removed.')
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return remove_external_post_callback('memberhost', 'host', 'externalhost', ldap, completed, failed, dn, entry_attrs, keys, options)
api.register(sudorule_remove_host)
@@ -491,6 +501,7 @@ class sudorule_add_runasuser(LDAPAddMember):
member_count_out = ('%i object added.', '%i objects added.')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
def check_validity(runas):
v = unicode(runas)
if v.upper() == u'ALL':
@@ -521,6 +532,7 @@ class sudorule_add_runasuser(LDAPAddMember):
return add_external_pre_callback('user', ldap, dn, keys, options)
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return add_external_post_callback('ipasudorunas', 'user', 'ipasudorunasextuser', ldap, completed, failed, dn, entry_attrs, keys, options)
api.register(sudorule_add_runasuser)
@@ -533,6 +545,7 @@ class sudorule_remove_runasuser(LDAPRemoveMember):
member_count_out = ('%i object removed.', '%i objects removed.')
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return remove_external_post_callback('ipasudorunas', 'user', 'ipasudorunasextuser', ldap, completed, failed, dn, entry_attrs, keys, options)
api.register(sudorule_remove_runasuser)
@@ -545,6 +558,7 @@ class sudorule_add_runasgroup(LDAPAddMember):
member_count_out = ('%i object added.', '%i objects added.')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
def check_validity(runas):
v = unicode(runas)
if v.upper() == u'ALL':
@@ -569,6 +583,7 @@ class sudorule_add_runasgroup(LDAPAddMember):
return add_external_pre_callback('group', ldap, dn, keys, options)
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return add_external_post_callback('ipasudorunasgroup', 'group', 'ipasudorunasextgroup', ldap, completed, failed, dn, entry_attrs, keys, options)
api.register(sudorule_add_runasgroup)
@@ -581,6 +596,7 @@ class sudorule_remove_runasgroup(LDAPRemoveMember):
member_count_out = ('%i object removed.', '%i objects removed.')
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
return remove_external_post_callback('ipasudorunasgroup', 'group', 'ipasudorunasextgroup', ldap, completed, failed, dn, entry_attrs, keys, options)
api.register(sudorule_remove_runasgroup)
diff --git a/ipalib/plugins/trust.py b/ipalib/plugins/trust.py
index acb65b388..a70293bff 100644
--- a/ipalib/plugins/trust.py
+++ b/ipalib/plugins/trust.py
@@ -107,9 +107,10 @@ class trust(LDAPObject):
)
def make_trust_dn(env, trust_type, dn):
+ assert isinstance(dn, DN)
if trust_type in trust.trust_types:
container_dn = DN(('cn', trust_type), env.container_trusts, env.basedn)
- return unicode(DN(DN(dn)[0], container_dn))
+ return DN(dn[0], container_dn)
return dn
class trust_add(LDAPCreate):
@@ -214,6 +215,7 @@ class trust_del(LDAPDelete):
msg_summary = _('Deleted trust "%(value)s"')
def pre_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
try:
result = self.api.Command.trust_show(keys[-1])
except errors.NotFound, e:
@@ -226,6 +228,7 @@ class trust_mod(LDAPUpdate):
msg_summary = _('Modified trust "%(value)s"')
def pre_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
result = None
try:
result = self.api.Command.trust_show(keys[-1])
@@ -245,6 +248,7 @@ class trust_find(LDAPSearch):
# Since all trusts types are stored within separate containers under 'cn=trusts',
# search needs to be done on a sub-tree scope
def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options):
+ assert isinstance(base_dn, DN)
return (filters, base_dn, ldap.SCOPE_SUBTREE)
class trust_show(LDAPRetrieve):
@@ -271,6 +275,7 @@ class trust_show(LDAPRetrieve):
return result
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
if 'trust_show_type' in options:
return make_trust_dn(self.env, options['trust_show_type'], dn)
return dn
@@ -281,4 +286,3 @@ api.register(trust_mod)
api.register(trust_del)
api.register(trust_find)
api.register(trust_show)
-
diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py
index c19d9a666..529699f9a 100644
--- a/ipalib/plugins/user.py
+++ b/ipalib/plugins/user.py
@@ -375,11 +375,12 @@ class user(LDAPObject):
if not manager:
return None
- if isinstance(manager, basestring):
+ if not isinstance(manager, list):
manager = [manager]
try:
+ container_dn = DN(self.container_dn, api.env.basedn)
for m in xrange(len(manager)):
- if manager[m].endswith('%s,%s' % (self.container_dn, api.env.basedn)):
+ 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, [''],
@@ -420,6 +421,7 @@ class user_add(LDAPCreate):
)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
if not options.get('noprivate', False):
try:
# The Managed Entries plugin will allow a user to be created
@@ -459,7 +461,9 @@ class user_add(LDAPCreate):
homes_root = config.get('ipahomesrootdir', ['/home'])[0]
# build user's home directory based on his uid
entry_attrs['homedirectory'] = posixpath.join(homes_root, keys[-1])
- entry_attrs.setdefault('krbpwdpolicyreference', 'cn=global_policy,cn=%s,cn=kerberos,%s' % (api.env.realm, api.env.basedn))
+ entry_attrs.setdefault('krbpwdpolicyreference',
+ DN(('cn', 'global_policy'), ('cn', api.env.realm), ('cn', 'kerberos'),
+ api.env.basedn))
entry_attrs.setdefault('krbprincipalname', '%s@%s' % (entry_attrs['uid'], api.env.realm))
if entry_attrs.get('gidnumber', DNA_MAGIC) == DNA_MAGIC:
@@ -496,6 +500,7 @@ class user_add(LDAPCreate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
config = ldap.get_ipa_config()[1]
# add the user we just created into the default primary group
def_primary_group = config.get('ipadefaultprimarygroup')
@@ -544,6 +549,7 @@ class user_del(LDAPDelete):
msg_summary = _('Deleted user "%(value)s"')
def pre_callback(self, ldap, dn, *keys, **options):
+ assert isinstance(dn, DN)
protected_group_name = u'admins'
result = api.Command.group_show(protected_group_name)
if result['result'].get('member_user', []) == [keys[-1]]:
@@ -562,6 +568,7 @@ class user_mod(LDAPUpdate):
has_output_params = LDAPUpdate.has_output_params + user_output_params
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
if options.get('rename') is not None:
config = ldap.get_ipa_config()[1]
if 'ipamaxusernamelength' in config:
@@ -592,6 +599,7 @@ class user_mod(LDAPUpdate):
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
if options.get('random', False):
try:
entry_attrs['randompassword'] = unicode(getattr(context, 'randompassword'))
@@ -621,6 +629,7 @@ class user_find(LDAPSearch):
)
def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *keys, **options):
+ assert isinstance(base_dn, DN)
if options.get('whoami'):
return ("(&(objectclass=posixaccount)(krbprincipalname=%s))"%\
getattr(context, 'principal'), base_dn, scope)
@@ -651,6 +660,7 @@ class user_show(LDAPRetrieve):
has_output_params = LDAPRetrieve.has_output_params + user_output_params
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
convert_nsaccountlock(entry_attrs)
self.obj._convert_manager(entry_attrs, **options)
self.obj.get_password_attributes(ldap, dn, entry_attrs)
@@ -763,7 +773,7 @@ class user_status(LDAPQuery):
# Get list of masters
try:
(masters, truncated) = ldap.find_entries(
- None, ['*'], 'cn=masters,cn=ipa,cn=etc,%s' % api.env.basedn,
+ None, ['*'], DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn),
ldap.SCOPE_ONELEVEL
)
except errors.NotFound:
diff --git a/ipalib/plugins/virtual.py b/ipalib/plugins/virtual.py
index c827d3d9d..8db1a9653 100644
--- a/ipalib/plugins/virtual.py
+++ b/ipalib/plugins/virtual.py
@@ -23,6 +23,7 @@ Base classes for non-LDAP backend plugins.
from ipalib import api
from ipalib import Command
from ipalib import errors
+from ipapython.dn import DN
class VirtualCommand(Command):
"""
@@ -55,7 +56,7 @@ class VirtualCommand(Command):
ldap = self.api.Backend.ldap2
self.log.debug("IPA: virtual verify %s" % operation)
- operationdn = "cn=%s,%s,%s" % (operation, self.api.env.container_virtual, self.api.env.basedn)
+ operationdn = DN(('cn', operation), self.api.env.container_virtual, self.api.env.basedn)
try:
if not ldap.can_write(operationdn, "objectclass"):
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
index 8a6a51088..a22fae505 100644
--- a/ipalib/rpc.py
+++ b/ipalib/rpc.py
@@ -57,6 +57,7 @@ from nss.error import NSPRError
from urllib2 import urlparse
from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT_EXPIRED, \
KRB5_FCC_PERM, KRB5_FCC_NOFILE, KRB5_CC_FORMAT, KRB5_REALM_CANT_RESOLVE
+from ipapython.dn import DN
COOKIE_NAME = 'ipa_session_cookie:%s'
@@ -83,7 +84,7 @@ def xml_wrap(value):
"""
if type(value) in (list, tuple):
return tuple(xml_wrap(v) for v in value)
- if type(value) is dict:
+ if isinstance(value, dict):
return dict(
(k, xml_wrap(v)) for (k, v) in value.iteritems()
)
@@ -92,6 +93,8 @@ def xml_wrap(value):
if type(value) is Decimal:
# transfer Decimal as a string
return unicode(value)
+ if isinstance(value, DN):
+ return str(value)
assert type(value) in (unicode, int, float, bool, NoneType)
return value
diff --git a/ipalib/session.py b/ipalib/session.py
index 4b783bbcb..87fa44825 100644
--- a/ipalib/session.py
+++ b/ipalib/session.py
@@ -628,7 +628,7 @@ mod_auth_kerb. Everything else remains the same.
default_max_session_duration = 60*60 # number of seconds
-ISO8601_DATETIME_FMT = '%Y-%m-%dT%H:%M:%S' # FIXME jrd, this should be defined elsewhere
+ISO8601_DATETIME_FMT = '%Y-%m-%dT%H:%M:%S' # FIXME, this should be defined elsewhere
def fmt_time(timestamp):
return time.strftime(ISO8601_DATETIME_FMT, time.localtime(timestamp))
diff --git a/ipalib/util.py b/ipalib/util.py
index 039ffb06d..abd50da7d 100644
--- a/ipalib/util.py
+++ b/ipalib/util.py
@@ -33,8 +33,8 @@ from dns.exception import DNSException
from ipalib import errors
from ipalib.text import _
-from ipalib.dn import DN, RDN
from ipapython.ipautil import decode_ssh_pubkey
+from ipapython.dn import DN, RDN
def json_serialize(obj):
@@ -472,10 +472,3 @@ def validate_rdn_param(ugettext, value):
except Exception, e:
return str(e)
return None
-
-def validate_dn_param(ugettext, value):
- try:
- rdn = DN(value)
- except Exception, e:
- return str(e)
- return None
diff --git a/ipalib/x509.py b/ipalib/x509.py
index 1b133adfb..84c1acfcd 100644
--- a/ipalib/x509.py
+++ b/ipalib/x509.py
@@ -42,7 +42,7 @@ from ipalib import api
from ipalib import _
from ipalib import util
from ipalib import errors
-from ipalib.dn import DN
+from ipapython.dn import DN
PEM = 0
DER = 1