예제 #1
0
class LDAPPrincipals(OdictStorage):
    principal_attrmap = default(None)
    principal_attraliaser = default(None)
    
    @extend
    def __init__(self, props, cfg):
        context = LDAPNode(name=cfg.baseDN, props=props)
        context.search_filter = cfg.queryFilter
        context.search_scope = int(cfg.scope)
        
        context.child_defaults = dict()
        context.child_defaults['objectClass'] = cfg.objectClasses
        if hasattr(cfg, 'defaults'):
            context.child_defaults.update(cfg.defaults)
        for oc in cfg.objectClasses:
            for key, val in creation_defaults.get(oc, dict()).items():
                if not key in context.child_defaults:
                    context.child_defaults[key] = val
        
        # XXX: make these attrs public
        context._key_attr = cfg.attrmap['id']
        context._rdn_attr = cfg.attrmap['rdn']
        
        #if cfg.member_relation:
        #    context.search_relation = cfg.member_relation
        
        context._seckey_attrs = ('dn',)
        if cfg.attrmap.get('login') \
          and cfg.attrmap['login'] != cfg.attrmap['id']:
            context._seckey_attrs += (cfg.attrmap['login'],)
        
        context._load_keys()
        
        self.expiresAttr = getattr(cfg, 'expiresAttr', None)
        self.expiresUnit = getattr(cfg, 'expiresUnit', None)
        self.principal_attrmap = cfg.attrmap
        self.principal_attraliaser = DictAliaser(cfg.attrmap, cfg.strict)
        self.context = context
    
    @default
    def idbydn(self, dn, strict=False):
        """Return a principal's id for a given dn.

        Raise KeyError if not enlisted.
        """
        self.context.keys()
        idsbydn = self.context._seckeys['dn']
        try:
            return idsbydn[dn]
        except KeyError:
            # It's possible that we got a different string resulting
            # in the same DN, as every components can have individual
            # comparison rules (see also node.ext.ldap._node.LDAPNode.DN).
            # We leave the job to LDAP and try again with the resulting DN.
            #
            # This was introduced because a customer has group
            # member attributes where the DN string differs.
            #
            # This would not be necessary, if an LDAP directory
            # is consistent, i.e does not use different strings to
            # talk about the same.
            #
            # Normalization of DN in python would also be a
            # solution, but requires implementation of all comparison
            # rules defined in schemas. Maybe we can retrieve them
            # from LDAP.
            if strict:
                raise KeyError(dn)
            search = self.context.ldap_session.search
            try:
                dn = search(baseDN=dn)[0][0]
            except ldap.NO_SUCH_OBJECT:
                raise KeyError(dn)
            return idsbydn[dn]

    @extend
    @property
    def ids(self):
        return self.context.keys()

    @default
    @locktree
    def __delitem__(self, key):
        key = decode_utf8(key)
        del self.context[key]
        try:
            del self.storage[key]
        except KeyError:
            pass

    @default
    @locktree
    def __getitem__(self, key):
        key = decode_utf8(key)
        try:
            return self.storage[key]
        except KeyError:
            principal = self.principal_factory(
                self.context[key],
                attraliaser=self.principal_attraliaser)
            principal.__name__ = self.context[key].name
            principal.__parent__ = self
            self.storage[key] = principal
            return principal

    @default
    @locktree
    def __iter__(self):
        return self.context.__iter__()

    @default
    @locktree
    def __setitem__(self, name, vessel):
        """XXX: mechanism for defining a target container if search scope is
                SUBTREE
        """
        try:
            attrs = vessel.attrs
        except AttributeError:
            raise ValueError(u"no attributes found, cannot convert.")
        if name in self:
            raise KeyError(u"Key already exists: '%s'." % (name,))
        nextvessel = AttributedNode()
        nextvessel.__name__ = name
        nextvessel.attribute_access_for_attrs = False
        principal = self.principal_factory(
            nextvessel,
            attraliaser=self.principal_attraliaser)
        principal.__name__ = name
        principal.__parent__ = self
        # XXX: cache
        for key, val in attrs.iteritems():
            principal.attrs[key] = val
        self.context[name] = nextvessel
    
    @default
    @property
    def changed(self):
        return self.context.changed
    
    @default
    @locktree
    def invalidate(self, key=None):
        """Invalidate LDAPPrincipals.
        """
        key = decode_utf8(key)
        self.context.invalidate(key)
        if key is None:
            self.storage.clear()
            return
        try:
            del self.storage[key]
        except KeyError:
            pass
    
    @default
    @locktree
    def __call__(self):
        self.context()

    @default
    def _alias_dict(self, dct):
        # XXX: seem to be not reached at all
        #if dct is None:
        #    return None
        
        # XXX: this code does not work if multiple keys map to same value
        #alias = self.principal_attraliaser.alias
        #aliased_dct = dict(
        #    [(alias(key), val) for key, val in dct.iteritems()])
        #return aliased_dct
        
        # XXX: generalization in aliaser needed
        ret = dict()
        for key, val in self.principal_attraliaser.iteritems():
            for k, v in dct.iteritems():
                if val == k:
                    ret[key] = v
        return ret

    @default
    def _unalias_list(self, lst):
        if lst is None:
            return None
        unalias = self.principal_attraliaser.unalias
        return [unalias(x) for x in lst]

    @default
    def _unalias_dict(self, dct):
        if dct is None:
            return None
        unalias = self.principal_attraliaser.unalias
        unaliased_dct = dict(
            [(unalias(key), val) for key, val in dct.iteritems()])
        return unaliased_dct

    @default
    def search(self, criteria=None, attrlist=None,
               exact_match=False, or_search=False):
        results = self.context.search(
            criteria=self._unalias_dict(criteria),
            attrlist=self._unalias_list(attrlist),
            exact_match=exact_match,
            or_search=or_search)
        if attrlist is None:
            return results
        aliased_results = \
            [(uid, self._alias_dict(attrs)) for uid, attrs in results]
        return aliased_results

    @default
    def search_paged(self, criteria=None, attrlist=None,
               exact_match=False, or_search=False,
               page_size=None, cookie=None, only_values=False):
        results, cookie = self.context.search_paged(
            criteria=self._unalias_dict(criteria),
            attrlist=self._unalias_list(attrlist),
            exact_match=exact_match,
            or_search=or_search,
            page_size=page_size,
            only_values=only_values,
            cookie=cookie)
        if attrlist is None:
            return results, cookie
        aliased_results = \
            [(uid, self._alias_dict(attrs)) for uid, attrs in results]
        return aliased_results, cookie


    @default
    @locktree
    def create(self, pid, **kw):
        vessel = AttributedNode()
        for k, v in kw.items():
            vessel.attrs[k] = v
        self[pid] = vessel
        return self[pid]
예제 #2
0
class Principals(AbstractNode):
    """Turn a nodespace into a source of principals

    XXX: Should be based on a LazyNode to cache principal objects
    """

    principals_factory = None

    def __init__(self, context, principal_attrmap=None):
        self.context = context
        # XXX: it seems currently an aliaser is needed any way, is that good?
        self.principal_attrmap = principal_attrmap
        self.principal_attraliaser = DictAliaser(principal_attrmap)

    @property
    def __name__(self):
        return self.context.__name__

    # principals have ids
    @property
    def ids(self):
        return self.context.keys

    def __delitem__(self, key):
        del self.context[key]

    def __getitem__(self, key):
        # XXX: should use lazynodes caching, for now:
        # users['foo'] is not users['foo']
        principal = self.principal_factory(self.context[key], attraliaser=self.principal_attraliaser)
        principal.__name__ = self.context[key].__name__
        principal.__parent__ = self
        return principal

    def __iter__(self):
        return self.context.__iter__()

    def __setitem__(self, name, vessel):
        try:
            attrs = vessel.nodespaces["__attrs__"]
        except KeyError:
            raise ValueError(u"Attributes need to be set.")

        nextvessel = AttributedNode()
        nextvessel.__name__ = name
        nextvessel.attribute_access_for_attrs = False
        principal = self.principal_factory(nextvessel, attraliaser=self.principal_attraliaser)
        principal.__name__ = name
        principal.__parent__ = self
        # XXX: we could cache here, given that we are based on a LazyNode or
        # similar
        for key, val in attrs.iteritems():
            principal.attrs[key] = val
        self.context[name] = nextvessel

    def _alias_dict(self, dct):
        if dct is None:
            return None
        # this code does not work if multiple keys map to same value
        # alias = self.principal_attraliaser.alias
        # aliased_dct = dict(
        #        [(alias(key), val) for key, val in dct.iteritems()]
        #        )
        # return aliased_dct
        # XXX: maybe some generalization in aliaser needed
        ret = dict()
        for key, val in self.principal_attraliaser.iteritems():
            for k, v in dct.iteritems():
                if val == k:
                    ret[key] = v
        return ret

    def _unalias_list(self, lst):
        if lst is None:
            return None
        unalias = self.principal_attraliaser.unalias
        return [unalias(x) for x in lst]

    def _unalias_dict(self, dct):
        if dct is None:
            return None
        unalias = self.principal_attraliaser.unalias
        unaliased_dct = dict([(unalias(key), val) for key, val in dct.iteritems()])
        return unaliased_dct

    def search(self, criteria=None, attrlist=None, exact_match=False, or_search=False):
        # XXX: stripped down for now, compare to LDAPNode.search
        # XXX: are single values always lists in results?
        #      is this what we want?
        results = self.context.search(
            criteria=self._unalias_dict(criteria),
            attrlist=self._unalias_list(attrlist),
            exact_match=exact_match,
            or_search=or_search,
        )
        if attrlist is None:
            return results
        aliased_results = [(id, self._alias_dict(attrs)) for id, attrs in results]
        return aliased_results