Skip to content

Commit 12c3b61

Browse files
committed
Syncrepl session handling
1 parent d6e8889 commit 12c3b61

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy