Skip to content

Commit 6b8f56e

Browse files
committed
Syncrepl session handling
1 parent 4c9b0d3 commit 6b8f56e

File tree

3 files changed

+517
-355
lines changed

3 files changed

+517
-355
lines changed

Lib/ldap/controls/syncrepl.py

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
"""
2+
ldap.controls.syncrepl - classes for the Content Synchronization Operation
3+
(a.k.a. syncrepl) controls (see RFC 4533)
4+
5+
See https://www.python-ldap.org/ for project details.
6+
"""
7+
8+
__all__ = [
9+
'SyncRequestControl',
10+
'SyncStateControl', 'SyncDoneControl',
11+
]
12+
13+
from pyasn1.type import tag, namedtype, namedval, univ, constraint
14+
from pyasn1.codec.ber import encoder, decoder
15+
from uuid import UUID
16+
17+
import ldap.controls
18+
from ldap.controls import RequestControl, ResponseControl
19+
20+
21+
class SyncUUID(univ.OctetString):
22+
"""
23+
syncUUID ::= OCTET STRING (SIZE(16))
24+
"""
25+
subtypeSpec = constraint.ValueSizeConstraint(16, 16)
26+
27+
28+
class SyncCookie(univ.OctetString):
29+
"""
30+
syncCookie ::= OCTET STRING
31+
"""
32+
33+
34+
class SyncRequestMode(univ.Enumerated):
35+
"""
36+
mode ENUMERATED {
37+
-- 0 unused
38+
refreshOnly (1),
39+
-- 2 reserved
40+
refreshAndPersist (3)
41+
},
42+
"""
43+
namedValues = namedval.NamedValues(
44+
('refreshOnly', 1),
45+
('refreshAndPersist', 3)
46+
)
47+
subtypeSpec = univ.Enumerated.subtypeSpec + \
48+
constraint.SingleValueConstraint(1, 3)
49+
50+
51+
class SyncRequestValue(univ.Sequence):
52+
"""
53+
syncRequestValue ::= SEQUENCE {
54+
mode ENUMERATED {
55+
-- 0 unused
56+
refreshOnly (1),
57+
-- 2 reserved
58+
refreshAndPersist (3)
59+
},
60+
cookie syncCookie OPTIONAL,
61+
reloadHint BOOLEAN DEFAULT FALSE
62+
}
63+
"""
64+
componentType = namedtype.NamedTypes(
65+
namedtype.NamedType('mode', SyncRequestMode()),
66+
namedtype.OptionalNamedType('cookie', SyncCookie()),
67+
namedtype.DefaultedNamedType('reloadHint', univ.Boolean(False))
68+
)
69+
70+
71+
class SyncRequestControl(RequestControl):
72+
"""
73+
The Sync Request Control is an LDAP Control [RFC4511] where the
74+
controlType is the object identifier 1.3.6.1.4.1.4203.1.9.1.1 and the
75+
controlValue, an OCTET STRING, contains a BER-encoded
76+
syncRequestValue. The criticality field is either TRUE or FALSE.
77+
[..]
78+
The Sync Request Control is only applicable to the SearchRequest
79+
Message.
80+
"""
81+
controlType = '1.3.6.1.4.1.4203.1.9.1.1'
82+
83+
def __init__(self, criticality=1, cookie=None, mode='refreshOnly',
84+
reloadHint=False):
85+
self.criticality = criticality
86+
self.cookie = cookie
87+
self.mode = mode
88+
self.reloadHint = reloadHint
89+
90+
def encodeControlValue(self):
91+
rcv = SyncRequestValue()
92+
rcv.setComponentByName('mode', SyncRequestMode(self.mode))
93+
if self.cookie is not None:
94+
rcv.setComponentByName('cookie', SyncCookie(self.cookie))
95+
if self.reloadHint is not None:
96+
rcv.setComponentByName('reloadHint', univ.Boolean(self.reloadHint))
97+
return encoder.encode(rcv)
98+
99+
def __repr__(self):
100+
return '{}(cookie={!r}, mode={!r}, reloadHint={!r})'.format(
101+
self.__class__.__name__,
102+
self.cookie,
103+
self.mode,
104+
self.reloadHint
105+
)
106+
107+
def __rich_repr__(self):
108+
yield 'criticality', self.criticality, 1
109+
yield 'cookie', self.cookie, None
110+
yield 'mode', self.mode
111+
yield 'reloadHint', self.reloadHint, False
112+
113+
114+
class SyncStateOp(univ.Enumerated):
115+
"""
116+
state ENUMERATED {
117+
present (0),
118+
add (1),
119+
modify (2),
120+
delete (3)
121+
},
122+
"""
123+
namedValues = namedval.NamedValues(
124+
('present', 0),
125+
('add', 1),
126+
('modify', 2),
127+
('delete', 3)
128+
)
129+
subtypeSpec = univ.Enumerated.subtypeSpec + \
130+
constraint.SingleValueConstraint(0, 1, 2, 3)
131+
132+
133+
class SyncStateValue(univ.Sequence):
134+
"""
135+
syncStateValue ::= SEQUENCE {
136+
state ENUMERATED {
137+
present (0),
138+
add (1),
139+
modify (2),
140+
delete (3)
141+
},
142+
entryUUID syncUUID,
143+
cookie syncCookie OPTIONAL
144+
}
145+
"""
146+
componentType = namedtype.NamedTypes(
147+
namedtype.NamedType('state', SyncStateOp()),
148+
namedtype.NamedType('entryUUID', SyncUUID()),
149+
namedtype.OptionalNamedType('cookie', SyncCookie())
150+
)
151+
152+
153+
class SyncStateControl(ResponseControl):
154+
"""
155+
The Sync State Control is an LDAP Control [RFC4511] where the
156+
controlType is the object identifier 1.3.6.1.4.1.4203.1.9.1.2 and the
157+
controlValue, an OCTET STRING, contains a BER-encoded SyncStateValue.
158+
The criticality is FALSE.
159+
[..]
160+
The Sync State Control is only applicable to SearchResultEntry and
161+
SearchResultReference Messages.
162+
"""
163+
controlType = '1.3.6.1.4.1.4203.1.9.1.2'
164+
165+
def decodeControlValue(self, encodedControlValue):
166+
d = decoder.decode(encodedControlValue, asn1Spec=SyncStateValue())
167+
state = d[0].getComponentByName('state')
168+
uuid = UUID(bytes=bytes(d[0].getComponentByName('entryUUID')))
169+
cookie = d[0].getComponentByName('cookie')
170+
if cookie is not None and cookie.hasValue():
171+
self.cookie = bytes(cookie)
172+
else:
173+
self.cookie = None
174+
self.state = state.prettyPrint()
175+
self.entryUUID = str(uuid)
176+
177+
def __repr__(self):
178+
optional = ''
179+
if self.cookie is not None:
180+
optional += ', cookie={!r}'.format(self.cookie)
181+
return '{}(state={!r}, entryUUID={!r}{})'.format(
182+
self.__class__.__name__,
183+
self.state,
184+
self.entryUUID,
185+
optional,
186+
)
187+
188+
def __rich_repr__(self):
189+
yield 'state', self.state
190+
yield 'entryUUID', self.entryUUID
191+
yield 'cookie', self.cookie, None
192+
193+
194+
class SyncDoneValue(univ.Sequence):
195+
"""
196+
syncDoneValue ::= SEQUENCE {
197+
cookie syncCookie OPTIONAL,
198+
refreshDeletes BOOLEAN DEFAULT FALSE
199+
}
200+
"""
201+
componentType = namedtype.NamedTypes(
202+
namedtype.OptionalNamedType('cookie', SyncCookie()),
203+
namedtype.DefaultedNamedType('refreshDeletes', univ.Boolean(False))
204+
)
205+
206+
207+
class SyncDoneControl(ResponseControl):
208+
"""
209+
The Sync Done Control is an LDAP Control [RFC4511] where the
210+
controlType is the object identifier 1.3.6.1.4.1.4203.1.9.1.3 and the
211+
controlValue contains a BER-encoded syncDoneValue. The criticality
212+
is FALSE (and hence absent).
213+
[..]
214+
The Sync Done Control is only applicable to the SearchResultDone
215+
Message.
216+
"""
217+
controlType = '1.3.6.1.4.1.4203.1.9.1.3'
218+
219+
def decodeControlValue(self, encodedControlValue):
220+
d = decoder.decode(encodedControlValue, asn1Spec=SyncDoneValue())
221+
cookie = d[0].getComponentByName('cookie')
222+
if cookie.hasValue():
223+
self.cookie = bytes(cookie)
224+
else:
225+
self.cookie = None
226+
refresh_deletes = d[0].getComponentByName('refreshDeletes')
227+
if refresh_deletes.hasValue():
228+
self.refreshDeletes = bool(refresh_deletes)
229+
else:
230+
self.refreshDeletes = None
231+
232+
def __repr__(self):
233+
optional = []
234+
if self.refreshDeletes is not None:
235+
optional.append('refreshDeletes={!r}'.format(self.refreshDeletes))
236+
if self.cookie is not None:
237+
optional.append('cookie={!r}'.format(self.cookie))
238+
return '{}({})'.format(
239+
self.__class__.__name__,
240+
', '.join(optional)
241+
)
242+
243+
def __rich_repr__(self):
244+
yield 'refreshDeletes', self.refreshDeletes, None
245+
yield 'cookie', self.cookie, None

0 commit comments

Comments
 (0)