"""
Account configuration and management features, via the web.
This is a pitiful implementation of these concepts (hence the pitiful
module name). It will be replaced by a real implementation when
clustering is ready for general use.
"""
import pytz
from zope.interface import implements
from twisted.python.components import registerAdapter
from twisted.internet import defer
from nevow import inevow, athena
from nevow.athena import expose
from epsilon import extime
from axiom import item, attributes, userbase
from xmantissa import ixmantissa, websession
class InvalidPassword(Exception):
pass
class NonExistentAccount(Exception):
pass
class NoSuchSession(Exception):
pass
class AuthenticationApplication(item.Item):
typeName = 'mantissa_web_authentication_application'
schemaVersion = 1
lastCredentialsChange = attributes.timestamp(allowNone=False)
def __init__(self, **kw):
if 'lastCredentialsChange' not in kw:
kw['lastCredentialsChange'] = extime.Time()
super(AuthenticationApplication, self).__init__(**kw)
def _account(self):
substore = self.store.parent.getItemByID(self.store.idInParent)
for account in self.store.parent.query(userbase.LoginAccount,
userbase.LoginAccount.avatars == substore):
return account
raise NonExistentAccount()
def _username(self):
for (localpart, domain) in userbase.getAccountNames(self.store):
return (localpart + '@' + domain).encode('utf-8')
def hasCurrentPassword(self):
return defer.succeed(self._account().password is not None)
def changePassword(self, oldPassword, newPassword):
account = self._account()
if account.password is not None and account.password != oldPassword:
raise InvalidPassword()
else:
account.password = newPassword
self.lastCredentialsChange = extime.Time()
def persistentSessions(self):
username = self._username()
return self.store.parent.query(
websession.PersistentSession,
websession.PersistentSession.authenticatedAs == username)
def cancelPersistentSession(self, uid):
username = self._username()
for sess in self.store.parent.query(websession.PersistentSession,
attributes.AND(websession.PersistentSession.authenticatedAs == username,
websession.PersistentSession.sessionKey == uid)):
sess.deleteFromStore()
break
else:
raise NoSuchSession()
class AuthenticationFragment(athena.LiveFragment):
implements(ixmantissa.INavigableFragment)
fragmentName = 'authentication-configuration'
live = 'athena'
title = 'Change Password'
jsClass = u'Mantissa.Authentication'
def __init__(self, original):
self.store = original.store
athena.LiveFragment.__init__(self, original)
def head(self):
return None
def render_currentPasswordField(self, ctx, data):
d = self.original.hasCurrentPassword()
def cb(result):
if result:
patName = 'current-password'
else:
patName = 'no-current-password'
return inevow.IQ(self.docFactory).onePattern(patName)
return d.addCallback(cb)
def render_cancel(self, ctx, data):
# XXX See previous XXX 19th
handler = 'Mantissa.Authentication.get(this).cancel(%r); return false'
return ctx.tag(onclick=handler % (data['session'].sessionKey,))
def changePassword(self, currentPassword, newPassword):
try:
self.original.changePassword(unicode(currentPassword), unicode(newPassword))
except NonExistentAccount:
raise NonExistentAccount('You do not seem to exist. Password unchanged.')
except InvalidPassword:
raise InvalidPassword('Incorrect password! Nothing changed.')
else:
return u'Password Changed!'
expose(changePassword)
def cancel(self, uid):
try:
self.original.cancelPersistentSession(str(uid))
except NoSuchSession:
raise NoSuchSession('That session seems to have vanished.')
else:
return u'Session discontinued'
expose(cancel)
def data_persistentSessions(self, ctx, data):
zone = pytz.timezone('US/Eastern')
for session in self.original.persistentSessions():
yield dict(lastUsed=session.lastUsed.asHumanly(zone) + ' ' + zone.zone,
session=session)
registerAdapter(AuthenticationFragment, AuthenticationApplication, ixmantissa.INavigableFragment)
syntax highlighted by Code2HTML, v. 0.9.1