Package ldaptor :: Package apps :: Package webui :: Module change_password
[hide private]
[frames] | no frames]

Source Code for Module ldaptor.apps.webui.change_password

  1  from zope.interface import implements 
  2  from twisted.internet import reactor 
  3  from twisted.internet import defer 
  4  from webut.skin import iskin 
  5  from ldaptor.protocols import pureldap 
  6  from ldaptor.protocols.ldap import ldapsyntax, distinguishedname 
  7  from ldaptor import generate_password, interfaces 
  8  from ldaptor.apps.webui.uriquote import uriUnquote 
  9  from ldaptor import weave 
 10  from ldaptor.apps.webui.i18n import _ 
 11  from ldaptor.apps.webui import i18n 
 12   
 13  import os 
 14  from nevow import rend, inevow, loaders, url, tags 
 15  from formless import annotate, webform, iformless, configurable 
 16   
17 -def getEntry(ctx, dn):
18 user = ctx.locate(inevow.ISession).getLoggedInRoot().loggedIn 19 e=ldapsyntax.LDAPEntry(client=user.client, dn=dn) 20 return e
21
22 -def getEntryWithAttributes(ctx, dn, *attributes):
23 e = getEntry(ctx, dn) 24 d = e.fetch(*attributes) 25 return d
26
27 -def getServiceName(ctx, dn):
28 d = getEntryWithAttributes(ctx, dn, 'cn') 29 def _cb(e): 30 for cn in e.get('cn', []): 31 return cn 32 raise RuntimeError, \ 33 _("Service password entry has no attribute cn: %r") % e
34 d.addCallback(_cb) 35 return d 36 37
38 -def checkPasswordTypos(newPassword, again):
39 if newPassword != again: 40 raise annotate.ValidateError( 41 {}, 42 formErrorMessage=_('Passwords do not match.'))
43
44 -class RemoveServicePassword(configurable.Configurable):
45 - def __init__(self, dn):
46 super(RemoveServicePassword, self).__init__(None) 47 self.dn = dn
48
49 - def getBindingNames(self, ctx):
50 return ['remove']
51
52 - def bind_remove(self, ctx):
53 return annotate.MethodBinding( 54 'remove', 55 annotate.Method(arguments=[ 56 annotate.Argument('ctx', annotate.Context()), 57 ], 58 label=_('Remove')), 59 action=_('Remove'))
60
61 - def remove(self, ctx):
62 e = getEntry(ctx, self.dn) 63 d = getServiceName(ctx, self.dn) 64 65 def _delete(name, e): 66 d = e.delete() 67 d.addCallback(lambda _: name) 68 return d
69 d.addCallback(_delete, e) 70 71 def _report(name): 72 return _('Removed service %r') % name
73 d.addCallback(_report) 74 75 return d 76
77 -class SetServicePassword(configurable.Configurable):
78 - def __init__(self, dn):
79 super(SetServicePassword, self).__init__(None) 80 self.dn = dn
81
82 - def getBindingNames(self, ctx):
83 return ['setServicePassword']
84
85 - def bind_setServicePassword(self, ctx):
86 return annotate.MethodBinding( 87 'setServicePassword', 88 annotate.Method(arguments=[ 89 annotate.Argument('ctx', annotate.Context()), 90 annotate.Argument('newPassword', annotate.PasswordEntry(required=True, 91 label=_('New password'))), 92 annotate.Argument('again', annotate.PasswordEntry(required=True, 93 label=_('Again'))), 94 ], 95 label=_('Set password')), 96 action=_('Set password'))
97
98 - def _isPasswordAcceptable(self, ctx, newPassword, again):
99 return checkPasswordTypos(newPassword, again)
100
101 - def setServicePassword(self, ctx, newPassword, again):
102 d = defer.maybeDeferred(self._isPasswordAcceptable, ctx, newPassword, again) 103 def _setPassword(ctx, dn, newPassword): 104 e = getEntry(ctx, dn) 105 d=defer.maybeDeferred(e.setPassword, newPasswd=newPassword) 106 return d
107 d.addCallback(lambda _: _setPassword(ctx, self.dn, newPassword)) 108 109 def _getName(_, ctx): 110 d = getServiceName(ctx, self.dn) 111 return d
112 d.addCallback(_getName, ctx) 113 114 def _report(name): 115 return _('Set password for service %r') % name 116 d.addCallback(_report) 117 118 return d 119
120 -class SetRandomServicePassword(configurable.Configurable):
121 - def __init__(self, dn):
122 super(SetRandomServicePassword, self).__init__(None) 123 self.dn = dn
124
125 - def getBindingNames(self, ctx):
126 return ['generateRandom']
127
128 - def bind_generateRandom(self, ctx):
129 return annotate.MethodBinding( 130 'generateRandom', 131 annotate.Method(arguments=[ 132 annotate.Argument('ctx', annotate.Context()), 133 ], 134 label=_('Generate random')), 135 action=_('Generate random'))
136
137 - def generateRandom(self, ctx):
138 d=generate_password.generate(reactor) 139 def _first(passwords): 140 assert len(passwords)==1 141 return passwords[0]
142 d.addCallback(_first) 143 144 def _setPass(newPassword, ctx): 145 e = getEntry(ctx, self.dn) 146 d = e.setPassword(newPassword) 147 148 def _getName(_, ctx): 149 d = getServiceName(ctx, self.dn) 150 return d
151 d.addCallback(_getName, ctx) 152 153 def _report(name, newPassword): 154 return _('Service %r password set to %s') % (name, newPassword) 155 d.addCallback(_report, newPassword) 156 157 return d 158 159 d.addCallback(_setPass, ctx) 160 return d 161
162 -class AddService(configurable.Configurable):
163 - def __init__(self, dn):
164 super(AddService, self).__init__(None) 165 self.dn = dn
166
167 - def getBindingNames(self, ctx):
168 return ['add']
169
170 - def bind_add(self, ctx):
171 return annotate.MethodBinding( 172 'add', 173 annotate.Method(arguments=[ 174 annotate.Argument('ctx', annotate.Context()), 175 annotate.Argument('serviceName', annotate.String(required=True, 176 label=_('Service name'))), 177 annotate.Argument('newPassword', annotate.PasswordEntry(required=False, 178 label=_('New password'), 179 description=_("Leave empty to generate random password."))), 180 annotate.Argument('again', annotate.PasswordEntry(required=False, 181 label=_('Again'))), 182 ], 183 label=_('Add')), 184 action=_('Add'))
185
186 - def add(self, ctx, serviceName, newPassword, again):
187 if newPassword or again: 188 checkPasswordTypos(newPassword, again) 189 190 if not newPassword: 191 return self._generate(ctx, serviceName) 192 else: 193 return self._add(ctx, newPassword, serviceName) 194 return d
195
196 - def _cbSetPassword(self, ctx, newPassword, serviceName):
197 e = getEntry(ctx, self.dn) 198 rdn = distinguishedname.RelativeDistinguishedName( 199 attributeTypesAndValues=[ 200 distinguishedname.LDAPAttributeTypeAndValue( 201 attributeType='cn', value=serviceName), 202 distinguishedname.LDAPAttributeTypeAndValue( 203 attributeType='owner', value=str(self.dn)) 204 ]) 205 d = e.addChild(rdn, { 206 'objectClass': ['serviceSecurityObject'], 207 'cn': [serviceName], 208 'owner': [str(self.dn)], 209 'userPassword': ['{crypt}!'], 210 }) 211 def _setPass(e, newPassword): 212 d = e.setPassword(newPassword) 213 return d
214 d.addCallback(_setPass, newPassword) 215 return d
216
217 - def _generate(self, ctx, serviceName):
218 d=generate_password.generate(reactor) 219 def _first(passwords): 220 assert len(passwords)==1 221 return passwords[0]
222 d.addCallback(_first) 223 224 def _cb(newPassword, serviceName): 225 d = self._cbSetPassword(ctx, newPassword, serviceName) 226 d.addCallback(lambda dummy: _('Added service %r with password %s') % (serviceName, newPassword)) 227 return d 228 d.addCallback(_cb, serviceName) 229 230 return d 231
232 - def _add(self, ctx, newPassword, serviceName):
233 d = self._cbSetPassword(ctx, newPassword, serviceName) 234 def _report(dummy, name): 235 return _('Added service %r') % name
236 d.addCallback(_report, serviceName) 237 238 return d 239 240
241 -class ServicePasswordChangeMixin(object):
242 - def __init__(self, dn):
243 super(ServicePasswordChangeMixin, self).__init__() 244 self.dn = dn
245
247 l = [(int(pri), name) 248 for x, pri, name in [name.split('_', 2) for name in dir(self) 249 if name.startswith('servicePasswordAction_')]] 250 l.sort() 251 for pri, name in l: 252 yield name
253
254 - def getServicePasswordAction(self, name):
255 for attrName in dir(self): 256 if not attrName.startswith('servicePasswordAction_'): 257 continue 258 259 x, pri, actionName = attrName.split('_', 2) 260 261 if actionName == name: 262 return getattr(self, attrName) 263 return None
264
265 - def render_servicePasswords(self, ctx, data):
266 docFactory = loaders.xmlfile( 267 'change_service_passwords.xhtml', 268 templateDir=os.path.split(os.path.abspath(__file__))[0]) 269 r = inevow.IQ(docFactory).onePattern('main') 270 return r
271
272 - def render_hideIfNot(self, ctx, data):
273 if data: 274 return ctx.tag 275 else: 276 return tags.invisible()
277
278 - def data_servicePasswords(self, ctx, data):
279 user = ctx.locate(inevow.ISession).getLoggedInRoot().loggedIn 280 config = interfaces.ILDAPConfig(ctx) 281 e=ldapsyntax.LDAPEntry(client=user.client, dn=config.getBaseDN()) 282 d = e.search(filterObject=pureldap.LDAPFilter_and([ 283 pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription('objectClass'), 284 assertionValue=pureldap.LDAPAssertionValue('serviceSecurityObject')), 285 pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription('owner'), 286 assertionValue=pureldap.LDAPAssertionValue(str(self.dn))), 287 pureldap.LDAPFilter_present('cn'), 288 ]), 289 attributes=['cn']) 290 291 return d
292
293 - def render_form_service(self, ctx, data):
294 # TODO error messages for one password change form display in 295 # all of them. 296 e = inevow.IData(ctx) 297 for name in self.listServicePasswordActions(): 298 yield webform.renderForms('service_%s_%s' % (name, e.dn))[ctx.tag()]
299
300 - def locateConfigurable(self, ctx, name):
301 try: 302 return super(ServicePasswordChangeMixin, self).locateConfigurable(ctx, name) 303 except AttributeError: 304 if name.startswith('service_'): 305 pass 306 else: 307 raise 308 309 rest = name[len('service_'):] 310 l = rest.split('_', 1) 311 if len(l) != 2: 312 raise AttributeError, name 313 314 c = self.getServicePasswordAction(l[0]) 315 if c is None: 316 raise AttributeError, name 317 return iformless.IConfigurable(c(l[1]))
318 319 320 render_zebra = weave.zebra() 321 322 render_i18n = i18n.render()
323 324
325 -class ConfirmChange(ServicePasswordChangeMixin, rend.Page):
326 implements(iskin.ISkinnable) 327 328 title = _('Ldaptor Password Change Page') 329 330 addSlash = True 331 332 docFactory = loaders.xmlfile( 333 'change_password.xhtml', 334 templateDir=os.path.split(os.path.abspath(__file__))[0]) 335
336 - def getBindingNames(self, ctx):
337 return ['setPassword', 'generateRandom']
338
339 - def bind_setPassword(self, ctx):
340 return annotate.MethodBinding( 341 'setPassword', 342 annotate.Method(arguments=[ 343 annotate.Argument('ctx', annotate.Context()), 344 annotate.Argument('newPassword', annotate.PasswordEntry(required=True, 345 label=_('New password'))), 346 annotate.Argument('again', annotate.PasswordEntry(required=True, 347 label=_('Again'))), 348 ], 349 label=_('Set password')), 350 action=_('Set password'))
351
352 - def bind_generateRandom(self, ctx):
353 return annotate.MethodBinding( 354 'generateRandom', 355 annotate.Method(arguments=[ 356 annotate.Argument('ctx', annotate.Context()), 357 ], 358 label=_('Generate random')), 359 action=_('Generate random'))
360 361 servicePasswordAction_10_remove = RemoveServicePassword 362 servicePasswordAction_20_set = SetServicePassword 363 servicePasswordAction_30_random = SetRandomServicePassword 364
365 - def _setPassword(self, ctx, password):
366 e = getEntry(ctx, self.dn) 367 d=defer.maybeDeferred(e.setPassword, newPasswd=password) 368 return d
369
370 - def setPassword(self, ctx, newPassword, again):
371 d = defer.maybeDeferred(checkPasswordTypos, newPassword, again) 372 d.addCallback(lambda dummy: self._setPassword(ctx, newPassword)) 373 d.addCallback(lambda dummy: _('Password set.')) 374 def eb(fail): 375 return _("Failed: %s") % fail.getErrorMessage()
376 d.addErrback(eb) 377 return d
378
379 - def generateRandom(self, ctx):
380 d=generate_password.generate(reactor) 381 def _first(passwords): 382 assert len(passwords)==1 383 return passwords[0]
384 d.addCallback(_first) 385 386 def _status(newPassword, ctx): 387 d = self._setPassword(ctx, newPassword) 388 d.addCallback(lambda dummy: _('Password set to %s') % newPassword) 389 return d 390 d.addCallback(_status, ctx) 391 def eb(fail): 392 return _("Failed: %s") % fail.getErrorMessage() 393 d.addErrback(eb) 394 return d 395
396 - def data_status(self, ctx, data):
397 try: 398 return ctx.locate(inevow.IStatusMessage) 399 except KeyError: 400 return ''
401
402 - def data_dn(self, ctx, data):
403 return self.dn
404
405 - def render_form(self, ctx, data):
406 return webform.renderForms()
407
408 - def render_passthrough(self, ctx, data):
409 return ctx.tag.clear()[data]
410
411 - def data_header(self, ctx, data):
412 u=url.URL.fromContext(ctx) 413 u=u.parentdir().parentdir().clear() 414 l=[] 415 l.append(tags.a(href=u.sibling("search"))[_("Search")]) 416 l.append(tags.a(href=u.sibling("add"))[_("add new entry")]) 417 l.append(tags.a(href=u.sibling("edit").child(str(self.dn)))[_("edit")]) 418 l.append(tags.a(href=u.sibling("delete").child(str(self.dn)))[_("delete")]) 419 return l
420
421 - def render_add(self, ctx, data):
422 return webform.renderForms('add')
423
424 - def configurable_add(self, ctx):
425 return AddService(self.dn)
426 427 render_i18n = i18n.render() 428
429 - def render_data(self, ctx, data):
430 return ctx.tag.clear()[data]
431
432 -class GetDN(rend.Page):
433 addSlash = True 434
435 - def child_(self, ctx):
436 entry = inevow.ISession(ctx).getLoggedInRoot().loggedIn 437 u = inevow.IRequest(ctx).URLPath() 438 return u.child(str(entry.dn))
439
440 - def childFactory(self, ctx, name):
441 unquoted=uriUnquote(name) 442 try: 443 dn = distinguishedname.DistinguishedName(stringValue=unquoted) 444 except distinguishedname.InvalidRelativeDistinguishedName, e: 445 # TODO There's no way to throw a FormException at this stage. 446 return None 447 r=ConfirmChange(dn=dn) 448 return r
449
450 -def getResource():
451 return GetDN()
452