web2ldap  1.7.7
About: web2ldap is a full-featured web-based LDAPv3 client.
  Fossies Dox: web2ldap-1.7.7.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

oath.py
Go to the documentation of this file.
1# -*- coding: ascii -*-
2"""
3web2ldap plugin classes for OATH-LDAP
4
5see https://www.stroeder.com/oath-ldap.html
6"""
7
8import re
9import datetime
10import base64
11from typing import Dict
12
13from ldap0 import LDAPError
14
15from ... import cmp
16from ...utctime import strptime, ts2repr
17from ..schema.syntaxes import (
18 DirectoryString,
19 DynamicDNSelectList,
20 GeneralizedTime,
21 HMACAlgorithmOID,
22 JSONValue,
23 LDAPv3ResultCode,
24 OctetString,
25 SelectList,
26 Timespan,
27 syntax_registry,
28)
29
30
31syntax_registry.reg_at(
32 JSONValue.oid, [
33 '1.3.6.1.4.1.5427.1.389.4226.4.12', # oathEncKey
34 '1.3.6.1.4.1.5427.1.389.4226.4.14', # oathTokenPIN
35 ]
36)
37
38
40 oid: str = 'OathOTPLength-oid'
41 desc: str = 'number of OTP digits'
42 attr_value_dict: Dict[str, str] = {
43 '6': '6',
44 '8': '8',
45 }
46
47syntax_registry.reg_at(
48 OathOTPLength.oid, [
49 '1.3.6.1.4.1.5427.1.389.4226.4.5', # oathOTPLength
50 ]
51)
52
53
55 oid: str = 'OathHOTPParams-oid'
56 desc: str = 'DN of the oathHOTPParams entry'
57 ldap_url = 'ldap:///_?cn?sub?(objectClass=oathHOTPParams)'
58 ref_attrs = (
59 (None, 'Same params', None, None),
60 )
61
62syntax_registry.reg_at(
63 OathHOTPParams.oid, [
64 '1.3.6.1.4.1.5427.1.389.4226.4.5.1', # oathHOTPParams
65 ]
66)
67
68
70 oid: str = 'OathResultCode-oid'
71
72syntax_registry.reg_at(
73 OathResultCode.oid, [
74 '1.3.6.1.4.1.5427.1.389.4226.4.13.1', # oathSuccessResultCode
75 '1.3.6.1.4.1.5427.1.389.4226.4.13.2', # oathFailureResultCode
76 ]
77)
78
79
81 oid: str = 'OathHOTPToken-oid'
82 desc: str = 'DN of the oathHOTPToken entry'
83 ldap_url = 'ldap:///_?oathTokenSerialNumber?sub?(objectClass=oathHOTPToken)'
84 ref_attrs = (
85 (None, 'Users', None, None),
86 )
87
88syntax_registry.reg_at(
89 OathHOTPToken.oid, [
90 '1.3.6.1.4.1.5427.1.389.4226.4.9.1', # oathHOTPToken
91 ]
92)
93
94
96 oid: str = 'OathTOTPParams-oid'
97 desc: str = 'DN of the oathTOTPParams entry'
98 ldap_url = 'ldap:///_?cn?sub?(objectClass=oathTOTPParams)'
99 ref_attrs = (
100 (None, 'Same params', None, None),
101 )
102
103syntax_registry.reg_at(
104 OathTOTPParams.oid, [
105 '1.3.6.1.4.1.5427.1.389.4226.4.5.2', # oathTOTPParams
106 ]
107)
108
109
111 oid: str = 'OathTOTPToken-oid'
112 desc: str = 'DN of the oathTOTPToken entry'
113 ldap_url = 'ldap:///_?oathTokenSerialNumber?sub?(objectClass=oathTOTPToken)'
114 ref_attrs = (
115 (None, 'Users', None, None),
116 )
117
118syntax_registry.reg_at(
119 OathTOTPToken.oid, [
120 '1.3.6.1.4.1.5427.1.389.4226.4.9.2', # oathTOTPToken
121 ]
122)
123
124
126 """
127 see http://openauthentication.org/specification/tokenSpecs
128 """
129 oid: str = 'OathTokenIdentifier-oid'
130 desc: str = 'Globally unique token identifier'
131 max_len: str = 12
132 pattern = re.compile(r'^[a-zA-Z0-9]{12}$')
133
134syntax_registry.reg_at(
135 OathTokenIdentifier.oid, [
136 '1.3.6.1.4.1.5427.1.389.4226.4.3', # oathTokenIdentifier
137 ]
138)
139
140
142 oid: str = 'OathInitPwAlphabet-oid'
143 desc: str = 'Alphabet used to generate init passwords'
144
145 def sanitize(self, attr_value: bytes) -> bytes:
146 return b''.join([
147 self._app.ls.uc_encode(c)[0]
148 for c in sorted(set(
149 self._app.ls.uc_decode(attr_value or '')[0].replace(' ', '')
150 ))
151 ])
152
153
154syntax_registry.reg_at(
155 HMACAlgorithmOID.oid, [
156 '1.3.6.1.4.1.5427.1.389.4226.4.6', # oathHMACAlgorithm
157 ]
158)
159
160
161syntax_registry.reg_at(
162 Timespan.oid, [
163 '1.3.6.1.4.1.5427.1.389.4226.4.4.1', # oathTOTPTimeStepPeriod
164 '1.3.6.1.4.1.5427.1.389.4226.4.8', # oathSecretMaxAge
165 ]
166)
167
168
170 oid: str = 'OathSecret-oid'
171 desc: str = 'OATH shared secret'
172
173 def display(self, vidx, links) -> str:
174 return '<br>'.join((
175 self._app.form.s2d(base64.b32encode(self._av).decode('ascii')),
176 OctetString.display(self, vidx, links),
177 ))
178
179syntax_registry.reg_at(
180 OathSecret.oid, [
181 '1.3.6.1.4.1.5427.1.389.4226.4.1', # oathSecret
182 ]
183)
184
185
187 oid: str = 'OathSecretTime-oid'
188 desc: str = 'OATH secret change time'
189 time_divisors = Timespan.time_divisors
190
191 def display(self, vidx, links) -> str:
192 ocs = self._entry.object_class_oid_set()
193 gt_disp_html = GeneralizedTime.display(self, vidx, links)
194 if 'oathHOTPToken' in ocs:
195 oath_params_dn_attr = 'oathHOTPParams'
196 elif 'oathTOTPToken' in ocs:
197 oath_params_dn_attr = 'oathTOTPParams'
198 else:
199 return gt_disp_html
200 try:
201 oath_secret_time_dt = strptime(self._av)
202 except ValueError:
203 return gt_disp_html
204 try:
205 oath_params_dn = self._entry[oath_params_dn_attr][0].decode(self._app.ls.charset)
206 except KeyError:
207 return gt_disp_html
208 try:
209 oath_params = self._app.ls.l.read_s(oath_params_dn, attrlist=['oathSecretMaxAge'])
210 except LDAPError:
211 return gt_disp_html
212 try:
213 oath_secret_max_age_secs = int(oath_params.entry_s['oathSecretMaxAge'][0])
214 except KeyError:
215 expire_msg = 'will never expire'
216 except ValueError:
217 return gt_disp_html
218 else:
219 if oath_secret_max_age_secs:
220 oath_secret_max_age = datetime.timedelta(seconds=oath_secret_max_age_secs)
221 current_time = datetime.datetime.utcnow()
222 expire_dt = oath_secret_time_dt+oath_secret_max_age
223 expired_since = (expire_dt-current_time).total_seconds()
224 expire_cmp = cmp(expire_dt, current_time)
225 expire_msg = '%s %s (%s %s)' % (
226 {
227 -1: 'expired since',
228 0: '',
229 1: 'will expire',
230 }[expire_cmp],
231 expire_dt.strftime('%c'),
232 self._app.form.s2d(
233 ts2repr(
234 self.time_divisors,
235 ' ',
236 abs(expired_since),
237 )
238 ),
239 {
240 -1: 'ago',
241 0: '',
242 1: 'ahead',
243 }[expire_cmp],
244 )
245 else:
246 expire_msg = 'will never expire'
247 return self.read_sep.join((gt_disp_html, expire_msg))
248
249
250syntax_registry.reg_at(
251 OathSecretTime.oid, [
252 '1.3.6.1.4.1.5427.1.389.4226.4.7.3', # oathSecretTime
253 ]
254)
255
256
257# Register all syntax classes in this module
258syntax_registry.reg_syntaxes(__name__)
bytes sanitize(self, bytes attr_value)
Definition: oath.py:145
str display(self, vidx, links)
Definition: oath.py:191
str display(self, vidx, links)
Definition: oath.py:173
str ts2repr(Sequence[Tuple[str, int]] time_divisors, str ts_sep, Union[str, bytes] ts_value)
Definition: utctime.py:79
def strptime(s)
Definition: utctime.py:23
def cmp(val1, val2)
Definition: __init__.py:57