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)  

ppolicy.py
Go to the documentation of this file.
1# -*- coding: ascii -*-
2"""
3web2ldap plugin classes for attributes defined in draft-behera-ldap-password-policy
4"""
5
6import time
7import datetime
8from typing import Dict
9
10from ldap0 import LDAPError
11
12from ...utctime import strptime, ts2repr
13from ..searchform import (
14 SEARCH_OPT_LE_THAN,
15 SEARCH_OPT_IS_EQUAL,
16 SEARCH_OPT_GE_THAN,
17)
18from ..schema.syntaxes import (
19 SelectList,
20 DynamicDNSelectList,
21 Timespan,
22 GeneralizedTime,
23 syntax_registry,
24)
25from .quirks import UserPassword
26from ... import cmp
27
28
30 oid: str = 'PwdCheckQuality-oid'
31 desc: str = 'Password quality checking enforced'
32 attr_value_dict: Dict[str, str] = {
33 '0': 'quality checking not be enforced',
34 '1': 'quality checking enforced, accepting un-checkable passwords',
35 '2': 'quality checking always enforced',
36 }
37
38syntax_registry.reg_at(
39 PwdCheckQuality.oid, [
40 '1.3.6.1.4.1.42.2.27.8.1.5', # pwdCheckQuality
41 ]
42)
43
44
46 oid: str = 'PwdAttribute-oid'
47 desc: str = 'Password attribute'
48 attr_value_dict: Dict[str, str] = {
49 '2.5.4.35': 'userPassword',
50 }
51
52 def _validate(self, attr_value: bytes) -> bool:
53 return (
54 not attr_value or
55 attr_value.lower() in {b'2.5.4.35', b'userpassword'}
56 )
57
58syntax_registry.reg_at(
59 PwdAttribute.oid, [
60 '1.3.6.1.4.1.42.2.27.8.1.1', # pwdAttribute
61 ]
62)
63
64
66 oid: str = 'PwdPolicySubentry-oid'
67 desc: str = 'DN of the pwdPolicy entry to be used for a certain entry'
68 ldap_url = 'ldap:///_??sub?(|(objectClass=pwdPolicy)(objectClass=ds-cfg-password-policy))'
69
70syntax_registry.reg_at(
71 PwdPolicySubentry.oid, [
72 '1.3.6.1.4.1.42.2.27.8.1.23', # pwdPolicySubentry
73 ]
74)
75
76
78 oid: str = 'PwdMaxAge-oid'
79 desc: str = 'pwdPolicy entry: Maximum age of user password'
80 link_text = 'Search expired'
81 title_text = 'Search for entries with this password policy and expired password'
82
83 @staticmethod
84 def _search_timestamp(diff_secs):
85 return time.strftime('%Y%m%d%H%M%SZ', time.gmtime(time.time()-diff_secs))
86
88 return (
89 ('search_attr', 'pwdChangedTime'),
90 ('search_option', SEARCH_OPT_LE_THAN),
91 ('search_string', self._search_timestamp(int(self.av_u.strip()))),
92 )
93
94 def display(self, vidx, links) -> str:
95 ts_dv = Timespan.display(self, vidx, links)
96 # Possibly display a link
97 ocs = self._entry.object_class_oid_set()
98 if not links or 'pwdPolicy' not in ocs:
99 return ts_dv
100 try:
101 ts_search_params = self._timespan_search_params()
102 except (ValueError, KeyError):
103 return ts_dv
104 search_link = self._app.anchor(
105 'searchform', self.link_text,
106 (
107 ('dn', self._dn),
108 ('searchform_mode', 'adv'),
109 ('search_attr', 'pwdPolicySubentry'),
110 ('search_option', SEARCH_OPT_IS_EQUAL),
111 ('search_string', self._dn),
112 ) + ts_search_params,
113 title=self.title_text,
114 )
115 return ' '.join((ts_dv, search_link))
116
117syntax_registry.reg_at(
118 PwdMaxAge.oid, [
119 '1.3.6.1.4.1.42.2.27.8.1.3', # pwdMaxAge
120 ]
121)
122
123
125 oid: str = 'PwdExpireWarning-oid'
126 desc: str = 'pwdPolicy entry: Password warning period'
127 link_text = 'Search soon to expire'
128 title_text = 'Search for entries with this password policy and soon to expire password'
129
131 pwd_expire_warning = int(self.av_u.strip())
132 pwd_max_age = int(self._entry['pwdMaxAge'][0].decode('ascii').strip())
133 warn_timestamp = pwd_max_age-pwd_expire_warning
134 return (
135 ('search_attr', 'pwdChangedTime'),
136 ('search_option', SEARCH_OPT_GE_THAN),
137 ('search_string', self._search_timestamp(pwd_max_age)),
138 ('search_attr', 'pwdChangedTime'),
139 ('search_option', SEARCH_OPT_LE_THAN),
140 ('search_string', self._search_timestamp(warn_timestamp)),
141 )
142
143syntax_registry.reg_at(
144 PwdExpireWarning.oid, [
145 '1.3.6.1.4.1.42.2.27.8.1.7', # pwdExpireWarning
146 ]
147)
148
149
151 oid: str = 'PwdAccountLockedTime-oid'
152 desc: str = 'user entry: time that the account was locked'
153 magic_values = {
154 b'000001010000Z': 'permanently locked',
155 }
156
157 def _validate(self, attr_value: bytes) -> bool:
158 return attr_value in self.magic_values or GeneralizedTime._validate(self, attr_value)
159
160 def display(self, vidx, links) -> str:
161 gt_disp_html = GeneralizedTime.display(self, vidx, links)
162 if self._av in self.magic_values:
163 return '%s (%s)' % (gt_disp_html, self.magic_values[self._av])
164 return gt_disp_html
165
166syntax_registry.reg_at(
167 PwdAccountLockedTime.oid, [
168 '1.3.6.1.4.1.42.2.27.8.1.17', # pwdAccountLockedTime
169 ]
170)
171
172
174 oid: str = 'PwdChangedTime-oid'
175 desc: str = 'user entry: Last password change time'
176 time_divisors = Timespan.time_divisors
177
178 def display(self, vidx, links) -> str:
179 gt_disp_html = GeneralizedTime.display(self, vidx, links)
180 try:
181 pwd_changed_dt = strptime(self._av)
182 except ValueError:
183 return gt_disp_html
184 try:
185 pwdpolicysubentry_dn = self._entry['pwdPolicySubentry'][0].decode(self._app.ls.charset)
186 except KeyError:
187 return gt_disp_html
188 try:
189 pwd_policy = self._app.ls.l.read_s(
190 pwdpolicysubentry_dn,
191 filterstr='(objectClass=pwdPolicy)',
192 attrlist=['pwdMaxAge', 'pwdExpireWarning'],
193 )
194 except LDAPError:
195 return gt_disp_html
196 try:
197 pwd_max_age_secs = int(pwd_policy.entry_s['pwdMaxAge'][0])
198 except KeyError:
199 expire_msg = 'will never expire'
200 except ValueError:
201 return gt_disp_html
202 else:
203 if pwd_max_age_secs:
204 pwd_max_age = datetime.timedelta(seconds=pwd_max_age_secs)
205 current_time = datetime.datetime.utcnow()
206 expire_dt = pwd_changed_dt+pwd_max_age
207 expired_since = (expire_dt-current_time).total_seconds()
208 expire_cmp = cmp(expire_dt, current_time)
209 expire_msg = '%s %s (%s %s)' % (
210 {
211 -1: 'expired since',
212 0: '',
213 1: 'will expire',
214 }[expire_cmp],
215 expire_dt.strftime('%c'),
216 self._app.form.s2d(
217 ts2repr(
218 self.time_divisors,
219 ' ',
220 abs(expired_since),
221 )
222 ),
223 {
224 -1: 'ago',
225 0: '',
226 1: 'ahead',
227 }[expire_cmp],
228 )
229 else:
230 expire_msg = 'will never expire'
231 return self.read_sep.join((gt_disp_html, expire_msg))
232
233
234syntax_registry.reg_at(
235 PwdChangedTime.oid, [
236 '1.3.6.1.4.1.42.2.27.8.1.16', # pwdChangedTime
237 ]
238)
239
240
241syntax_registry.reg_at(
242 UserPassword.oid, [
243 '1.3.6.1.4.1.42.2.27.8.1.20', # pwdHistory
244 ]
245)
246
247
248syntax_registry.reg_at(
249 Timespan.oid, [
250 '1.3.6.1.4.1.42.2.27.8.1.2', # pwdMinAge
251 '1.3.6.1.4.1.42.2.27.8.1.12', # pwdFailureCountInterval
252 '1.3.6.1.4.1.42.2.27.8.1.10', # pwdLockoutDuration
253 ]
254)
255
256
257# Register all syntax classes in this module
258syntax_registry.reg_syntaxes(__name__)
bool _validate(self, bytes attr_value)
Definition: ppolicy.py:157
bool _validate(self, bytes attr_value)
Definition: ppolicy.py:52
str display(self, vidx, links)
Definition: ppolicy.py:94
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