Skip to content

Commit

Permalink
Added modules ldap.controls.vlv and ldap.controls.sss for Virtual Lis…
Browse files Browse the repository at this point in the history
…t View (see draft-ietf-ldapext-ldapv3-vlv) and Server-side Sorting (see RFC 2891)
  • Loading branch information
stroeder committed Jun 22, 2015
1 parent 086e689 commit a4169eb
Show file tree
Hide file tree
Showing 3 changed files with 271 additions and 1 deletion.
5 changes: 4 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ Lib/
- Removed non-existent 'AttrTypeandValueLDIF' from ldif.__all__
* New mix-in class ldap.controls.openldap.SearchNoOpMixIn
adds convience method noop_search_st() to LDAPObject class
* Added new experimental modules which implement the control classes
for Virtual List View (see draft-ietf-ldapext-ldapv3-vlv) and
Server-side Sorting (see RFC 2891) (thanks to Benjamin Dauvergne)

----------------------------------------------------------------
Released 2.4.19 2015-01-10
Expand Down Expand Up @@ -1170,4 +1173,4 @@ Released 2.0.0pre02 2002-02-01
----------------------------------------------------------------
Released 1.10alpha3 2000-09-19

$Id: CHANGES,v 1.346 2015/06/22 11:51:07 stroeder Exp $
$Id: CHANGES,v 1.347 2015/06/22 16:47:08 stroeder Exp $
131 changes: 131 additions & 0 deletions Lib/ldap/controls/sss.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# -*- coding: utf-8 -*-
"""
ldap.controls.sss - classes for Server Side Sorting
(see RFC 2891)
See http://www.python-ldap.org/ for project details.
$Id: sss.py,v 1.1 2015/06/22 16:47:08 stroeder Exp $
"""

__all__ = [
'SSSRequestControl',
'SSSResponseControl',
'SSSVLVPagedLDAPObject'
]


import ldap
from ldap.ldapobject import LDAPObject
from ldap.controls import (RequestControl, ResponseControl,
KNOWN_RESPONSE_CONTROLS, DecodeControlTuples)

from pyasn1.type import univ, namedtype, tag, namedval, constraint
from pyasn1.codec.ber import encoder, decoder


# SortKeyList ::= SEQUENCE OF SEQUENCE {
# attributeType AttributeDescription,
# orderingRule [0] MatchingRuleId OPTIONAL,
# reverseOrder [1] BOOLEAN DEFAULT FALSE }


class SortKeyType(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('attributeType', univ.OctetString()),
namedtype.OptionalNamedType('orderingRule',
univ.OctetString().subtype(
implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)
)
),
namedtype.DefaultedNamedType('reverseOrder', univ.Boolean(False).subtype(
implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))))


class SortKeyListType(univ.SequenceOf):
componentType = SortKeyType()


class SSSRequestControl(RequestControl):
'''Order result server side
>>> s = SSSRequestControl('-cn')
'''
controlType = '1.2.840.113556.1.4.473'

def __init__(
self,
criticality=False,
ordering_rules=None,
):
RequestControl.__init__(self,self.controlType,criticality)
self.ordering_rules = ordering_rules
if isinstance(ordering_rules, basestring):
ordering_rules = [ordering_rules]
for rule in ordering_rules:
rule = rule.split(':')
assert len(rule) < 3, 'syntax for ordering rule: [-]<attribute-type>[:ordering-rule]'

def asn1(self):
p = SortKeyListType()
for i, rule in enumerate(self.ordering_rules):
q = SortKeyType()
reverse_order = rule.startswith('-')
if reverse_order:
rule = rule[1:]
if ':' in rule:
attribute_type, ordering_rule = rule.split(':')
else:
attribute_type, ordering_rule = rule, None
q.setComponentByName('attributeType', attribute_type)
if ordering_rule:
q.setComponentByName('orderingRule', ordering_rule)
if reverse_order:
q.setComponentByName('reverseOrder', 1)
p.setComponentByPosition(i, q)
return p

def encodeControlValue(self):
return encoder.encode(self.asn1())


class SortResultType(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('sortResult', univ.Enumerated().subtype(
namedValues=namedval.NamedValues(
('success', 0),
('operationsError', 1),
('timeLimitExceeded', 3),
('strongAuthRequired', 8),
('adminLimitExceeded', 11),
('noSuchAttribute', 16),
('inappropriateMatching', 18),
('insufficientAccessRights', 50),
('busy', 51),
('unwillingToPerform', 53),
('other', 80)),
subtypeSpec=univ.Enumerated.subtypeSpec + constraint.SingleValueConstraint(
0, 1, 3, 8, 11, 16, 18, 50, 51, 53, 80))),
namedtype.OptionalNamedType('attributeType',
univ.OctetString().subtype(
implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)
)
))


class SSSResponseControl(ResponseControl):
controlType = '1.2.840.113556.1.4.474'

def __init__(self,criticality=False):
ResponseControl.__init__(self,self.controlType,criticality)

def decodeControlValue(self, encoded):
p, rest = decoder.decode(encoded, asn1Spec=SortResultType())
assert not rest, 'all data could not be decoded'
self.result = int(p.getComponentByName('sortResult'))
self.result_code = p.getComponentByName('sortResult').prettyOut(self.result)
self.attribute_type_error = p.getComponentByName('attributeType')


KNOWN_RESPONSE_CONTROLS[SSSRequestControl.controlType] = SSSRequestControl
KNOWN_RESPONSE_CONTROLS[SSSResponseControl.controlType] = SSSResponseControl
136 changes: 136 additions & 0 deletions Lib/ldap/controls/vlv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
"""
ldap.controls.vlv - classes for Simple Paged control
(see draft-ietf-ldapext-ldapv3-vlv)
See http://www.python-ldap.org/ for project details.
$Id: vlv.py,v 1.1 2015/06/22 16:47:08 stroeder Exp $
"""

__all__ = [
'VLVRequestControl',
'VLVResponseControl',
]

import ldap
from ldap.ldapobject import LDAPObject
from ldap.controls import (RequestControl, ResponseControl,
KNOWN_RESPONSE_CONTROLS, DecodeControlTuples)

from pyasn1.type import univ, namedtype, tag, namedval, constraint
from pyasn1.codec.ber import encoder, decoder


class ByOffsetType(univ.Sequence):
tagSet = univ.Sequence.tagSet.tagImplicitly(
tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
componentType = namedtype.NamedTypes(
namedtype.NamedType('offset', univ.Integer()),
namedtype.NamedType('contentCount', univ.Integer()))


class TargetType(univ.Choice):
componentType = namedtype.NamedTypes(
namedtype.NamedType('byOffset', ByOffsetType()),
namedtype.NamedType('greaterThanOrEqual', univ.OctetString().subtype(
implicitTag=tag.Tag(tag.tagClassContext,
tag.tagFormatSimple, 1))))


class VirtualListViewRequestType(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('beforeCount', univ.Integer()),
namedtype.NamedType('afterCount', univ.Integer()),
namedtype.NamedType('target', TargetType()),
namedtype.OptionalNamedType('contextID', univ.OctetString()))

class VLVRequestControl(RequestControl):
controlType = '2.16.840.1.113730.3.4.9'

def __init__(
self,
criticality=False,
before_count=0,
after_count=0,
offset=None,
content_count=None,
greater_than_or_equal=None,
context_id=None,
):
RequestControl.__init__(self,self.controlType,criticality)
assert (offset is not None and content_count is not None) or greater_than_or_equal, 'offset and ' \
'content_count must be set together or greater_than_or_equal must be ' \
'used'
self.before_count = before_count
self.after_count = after_count
self.offset = offset
self.content_count = content_count
self.greater_than_or_equal = greater_than_or_equal
self.context_id = context_id

def encodeControlValue(self):
p = VirtualListViewRequestType()
p.setComponentByName('beforeCount', self.before_count)
p.setComponentByName('afterCount', self.after_count)
if self.offset is not None and self.content_count is not None:
by_offset = ByOffsetType()
by_offset.setComponentByName('offset', self.offset)
by_offset.setComponentByName('contentCount', self.content_count)
target = TargetType()
target.setComponentByName('byOffset', by_offset)
elif self.greater_than_or_equal:
target = TargetType()
target.setComponentByName('greaterThanOrEqual',
self.greater_than_or_equal)
else:
raise NotImplementedError
p.setComponentByName('target', target)
return encoder.encode(p)

KNOWN_RESPONSE_CONTROLS[VLVRequestControl.controlType] = VLVRequestControl


class VirtualListViewResultType(univ.Enumerated):
namedValues = namedval.NamedValues(
('success', 0),
('operationsError', 1),
('protocolError', 3),
('unwillingToPerform', 53),
('insufficientAccessRights', 50),
('adminLimitExceeded', 11),
('innapropriateMatching', 18),
('sortControlMissing', 60),
('offsetRangeError', 61),
('other', 80),
)


class VirtualListViewResponseType(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('targetPosition', univ.Integer()),
namedtype.NamedType('contentCount', univ.Integer()),
namedtype.NamedType('virtualListViewResult',
VirtualListViewResultType()),
namedtype.OptionalNamedType('contextID', univ.OctetString()))


class VLVResponseControl(ResponseControl):
controlType = '2.16.840.1.113730.3.4.10'

def __init__(self,criticality=False):
ResponseControl.__init__(self,self.controlType,criticality)

def decodeControlValue(self,encoded):
p, rest = decoder.decode(encoded, asn1Spec=VirtualListViewResponseType())
assert not rest, 'all data could not be decoded'
self.target_position = int(p.getComponentByName('targetPosition'))
self.content_count = int(p.getComponentByName('contentCount'))
self.result = int(p.getComponentByName('virtualListViewResult'))
self.result_code = p.getComponentByName('virtualListViewResult') \
.prettyOut(self.result)
self.context_id = p.getComponentByName('contextID')
if self.context_id:
self.context_id = str(self.context_id)

KNOWN_RESPONSE_CONTROLS[VLVResponseControl.controlType] = VLVResponseControl

0 comments on commit a4169eb

Please sign in to comment.