Back to index

plone3  3.1.7
Public Member Functions | Static Public Attributes | Private Attributes | Static Private Attributes
PasswordResetTool.PasswordResetTool.PasswordResetTool Class Reference
Inheritance diagram for PasswordResetTool.PasswordResetTool.PasswordResetTool:
Inheritance graph
[legend]
Collaboration diagram for PasswordResetTool.PasswordResetTool.PasswordResetTool:
Collaboration graph
[legend]

List of all members.

Public Member Functions

def manage_setTimeout
def manage_toggleUserCheck
def __init__
def requestReset
 Interface fulfillment ##.
def resetPassword
def setExpirationTimeout
 Implementation ##.
def getExpirationTimeout
def toggleUserCheck
def checkUser
def verifyKey
def getStats
def clearExpired
def uniqueString
def expirationDate
def getValidUser
def expired

Static Public Attributes

string id = 'portal_password_reset'
string meta_type = 'Password Reset Tool'
tuple security = ClassSecurityInfo()
tuple manage_options
tuple manage_overview = DTMLFile('dtml/explainPWResetTool', globals() )
 ZMI methods.

Private Attributes

 _requests
 _p_changed
 _timedelta
 _user_check

Static Private Attributes

tuple __implements__ = (IPWResetTool)
 other things needed for this to work skins:
int _user_check = 1
 Internal attributes.
int _timedelta = 168

Detailed Description

Provides a default implementation for a password reset scheme.

From a 'forgotten password' template, you submit your username to
a handler script that does a 'requestReset', and sends an email
with an unguessable unique hash in a url as built by 'constructURL'
to the user.

The user visits that URL (the 'reset form') and enters their username,

Definition at line 19 of file PasswordResetTool.py.


Constructor & Destructor Documentation

Definition at line 74 of file PasswordResetTool.py.

00074 
00075     def __init__(self):
00076         self._requests = {}


Member Function Documentation

Returns a boolean representing the state of 'user check' as described
in 'toggleUserCheck'. True means on, and is the default.

Definition at line 195 of file PasswordResetTool.py.

00195 
00196     def checkUser(self):
00197         """Returns a boolean representing the state of 'user check' as described
00198         in 'toggleUserCheck'. True means on, and is the default."""
00199         if not hasattr(self, '_user_check'):
00200             self._user_check = 1
00201 
00202         return self._user_check

Here is the caller graph for this function:

Destroys all expired reset request records.
Parameter controls how many days past expired it must be to disappear.

Definition at line 233 of file PasswordResetTool.py.

00233 
00234     def clearExpired(self, days=0):
00235         """Destroys all expired reset request records.
00236         Parameter controls how many days past expired it must be to disappear.
00237         """
00238         for key, record in self._requests.items():
00239             stored_user, expiry = record
00240             if self.expired(expiry, DateTime()-days):
00241                 del self._requests[key]
00242                 self._p_changed = 1

Here is the call graph for this function:

Here is the caller graph for this function:

Returns a DateTime for exipiry of a request from the
current time.

This is used by housekeeping methods (like clearEpired)
and stored in reset request records.

Definition at line 271 of file PasswordResetTool.py.

00271 
00272     def expirationDate(self):
00273         """Returns a DateTime for exipiry of a request from the
00274         current time.
00275 
00276         This is used by housekeeping methods (like clearEpired)
00277         and stored in reset request records."""
00278         if not hasattr(self, '_timedelta'):
00279             self._timedelta = 168
00280         if isinstance(self._timedelta,datetime.timedelta):
00281             expire = datetime.datetime.utcnow() + self._timedelta
00282             return DateTime(expire.year,
00283                             expire.month,
00284                             expire.day,
00285                             expire.hour,
00286                             expire.minute,
00287                             expire.second,
00288                             'UTC')
00289         expire = time.time() + self._timedelta*3600  # 60 min/hr * 60 sec/min
00290         return DateTime(expire)

Here is the caller graph for this function:

def PasswordResetTool.PasswordResetTool.PasswordResetTool.expired (   self,
  datetime,
  now = None 
)
Tells whether a DateTime or timestamp 'datetime' is expired
with regards to either 'now', if provided, or the current
time.

Definition at line 300 of file PasswordResetTool.py.

00300 
00301     def expired(self, datetime, now=None):
00302         """Tells whether a DateTime or timestamp 'datetime' is expired
00303         with regards to either 'now', if provided, or the current
00304         time."""
00305         if not now:
00306             now = DateTime()
00307         return now.greaterThanEqualTo(datetime)
00308 
00309 # these are possible customization points I'm not really sure we need.
00310 #
00311 #    def getRequestRecord(self, randomstring):
00312 #        """Returns a tuple (userid,expiration) that maps to a specific
00313 #        reset request, as keyed by the 'randomstring'.
00314 #
00315 #        Expiration is a DateTime."""
00316 #
00317 #    def setRequestRecord(self, randomstring, userid, expiry):
00318 #        """Create a reset request record keyed by 'randomstring'
00319 #        containing 'userid' and 'expiry' (which should be a DateTime)."""
00320 #
00321 #    def removeRequestRecord(self, randomstring):
00322 #        """Destroy the request reset record keyed by 'randomstring'."""
00323 #
00324 #    def getAllRequests(self):
00325 #        """Returns a list of all reset requests in a tuple
00326 #        '(randomstring, userid, expiry)'.
00327 #
00328 #        Used primarily for housekeeping. Expiry is a DateTime."""

Here is the caller graph for this function:

Get the length of time a reset request will be valid.

In hours, possibly fractional. Ignores seconds and shorter.

Definition at line 173 of file PasswordResetTool.py.

00173 
00174     def getExpirationTimeout(self):
00175         """Get the length of time a reset request will be valid.
00176 
00177         In hours, possibly fractional. Ignores seconds and shorter."""
00178         try:
00179             if isinstance(self._timedelta,datetime.timedelta):
00180                 return self._timedelta.days / 24
00181         except NameError:
00182             pass  # that's okay, it must be a number of hours...
00183         return self._timedelta

Return a dictionary like so:
    {"open":3, "expired":0}
about the number of open and expired reset requests.

Definition at line 219 of file PasswordResetTool.py.

00219 
00220     def getStats(self):
00221         """Return a dictionary like so:
00222             {"open":3, "expired":0}
00223         about the number of open and expired reset requests.
00224         """
00225         good = 0
00226         bad = 0
00227         for stored_user, expiry in self._requests.values():
00228             if self.expired(expiry): bad += 1
00229             else: good += 1
00230 
00231         return {"open":good, "expired":bad}
    

Here is the call graph for this function:

Returns the member with 'userid' if available and None otherwise.

Definition at line 292 of file PasswordResetTool.py.

00292 
00293     def getValidUser(self, userid):
00294         """Returns the member with 'userid' if available and None otherwise."""
00295         membertool = getToolByName(self, 'portal_membership')
00296         return membertool.getMemberById(userid)
    

Here is the call graph for this function:

Here is the caller graph for this function:

def PasswordResetTool.PasswordResetTool.PasswordResetTool.manage_setTimeout (   self,
  hours = 168,
  REQUEST = None 
)
ZMI method for setting the expiration timeout in hours.

Definition at line 61 of file PasswordResetTool.py.

00061 
00062     def manage_setTimeout(self, hours=168, REQUEST=None):
00063        """ZMI method for setting the expiration timeout in hours."""
00064        self.setExpirationTimeout(int(hours))
00065         return self.manage_overview(manage_tabs_message="Timeout set to %s hours" % hours)   

ZMI method for toggling the flag for checking user names on return.

Definition at line 67 of file PasswordResetTool.py.

00067 
00068     def manage_toggleUserCheck(self, REQUEST=None):
00069        """ZMI method for toggling the flag for checking user names on return."""
00070        self.toggleUserCheck()
00071         m = self.checkUser() and 'on' or 'off'
00072         return self.manage_overview(manage_tabs_message="Returning username check turned %s" % m)
00073 

Interface fulfillment ##.

Ask the system to start the password reset procedure for
user 'userid'.

Returns a dictionary with the random string that must be
used to reset the password in 'randomstring', the expiration date
as a DateTime in 'expires', and the userid (for convenience) in
'userid'. Returns None if no such user.

Definition at line 83 of file PasswordResetTool.py.

00083 
00084     def requestReset(self, userid):
00085         """Ask the system to start the password reset procedure for
00086         user 'userid'.
00087 
00088         Returns a dictionary with the random string that must be
00089         used to reset the password in 'randomstring', the expiration date
00090         as a DateTime in 'expires', and the userid (for convenience) in
00091         'userid'. Returns None if no such user.
00092         """
00093         if not self.getValidUser(userid):
00094             return None
00095         randomstring = self.uniqueString(userid)
00096         expiry = self.expirationDate()
00097         self._requests[randomstring] = (userid, expiry)
00098         
00099         self.clearExpired(10)   # clear out untouched records more than 10 days old
00100                                 # this is a cheap sort of "automatic" clearing
00101         self._p_changed = 1
00102         
00103         retval = {}
00104         retval['randomstring'] = randomstring
00105         retval['expires'] = expiry
00106         retval['userid'] = userid
00107         return retval

Here is the call graph for this function:

def PasswordResetTool.PasswordResetTool.PasswordResetTool.resetPassword (   self,
  userid,
  randomstring,
  password 
)
Set the password (in 'password') for the user who maps to
the string in 'randomstring' iff the entered 'userid' is equal
to the mapped userid. (This can be turned off with the
'toggleUserCheck' method.)

Note that this method will *not* check password validity: this
must be done by the caller.

Throws an 'ExpiredRequestError' if request is expired.
Throws an 'InvalidRequestError' if no such record exists,
or 'userid' is not in the record.

Definition at line 109 of file PasswordResetTool.py.

00109 
00110     def resetPassword(self, userid, randomstring, password):
00111         """Set the password (in 'password') for the user who maps to
00112         the string in 'randomstring' iff the entered 'userid' is equal
00113         to the mapped userid. (This can be turned off with the
00114         'toggleUserCheck' method.)
00115 
00116         Note that this method will *not* check password validity: this
00117         must be done by the caller.
00118 
00119         Throws an 'ExpiredRequestError' if request is expired.
00120         Throws an 'InvalidRequestError' if no such record exists,
00121         or 'userid' is not in the record.
00122         """
00123         try:
00124             stored_user, expiry = self._requests[randomstring]
00125         except KeyError:
00126             raise 'InvalidRequestError'
00127         
00128         if self.checkUser() and (userid != stored_user):
00129             raise 'InvalidRequestError'
00130         if self.expired(expiry):
00131             del self._requests[randomstring]
00132             self._p_changed = 1
00133             raise 'ExpiredRequestError'
00134 
00135         member = self.getValidUser(stored_user)
00136         if not member:
00137             raise 'InvalidRequestError'
00138 
00139         # actually change password
00140         user = member.getUser()
00141         uf = getToolByName(self, 'acl_users')
00142         if getattr(uf, 'userSetPassword', None) is not None:
00143             uf.userSetPassword(stored_user, password)  # GRUF 3
00144         else:
00145             try:
00146                 user.changePassword(password)  # GRUF 2
00147             except AttributeError:
00148                 # this sets __ directly (via MemberDataTool) which is the usual
00149                 # (and stupid!) way to change a password in Zope
00150                 member.setSecurityProfile(password=password)
00151 
00152         member.setMemberProperties(dict(must_change_password=0))
00153 
00154         # clean out the request
00155         del self._requests[randomstring]
00156         self._p_changed = 1
00157         

Here is the call graph for this function:

Implementation ##.

Set the length of time a reset request will be valid.

Takes a 'datetime.timedelta' object (if available, since it's
new in Python 2.3) or a number of hours, possibly
fractional. Since a negative delta makes no sense, the
timedelta's absolute value will be used.

Definition at line 163 of file PasswordResetTool.py.

00163 
00164     def setExpirationTimeout(self, timedelta):
00165         """Set the length of time a reset request will be valid.
00166 
00167         Takes a 'datetime.timedelta' object (if available, since it's
00168         new in Python 2.3) or a number of hours, possibly
00169         fractional. Since a negative delta makes no sense, the
00170         timedelta's absolute value will be used."""
00171         self._timedelta = abs(timedelta)

Changes whether or not the tool requires someone to give the uerid
they're trying to change on a 'password reset' page. Highly recommended
to LEAVE THIS ON.

Definition at line 185 of file PasswordResetTool.py.

00185 
00186     def toggleUserCheck(self):
00187         """Changes whether or not the tool requires someone to give the uerid
00188         they're trying to change on a 'password reset' page. Highly recommended
00189         to LEAVE THIS ON."""
00190         if not hasattr(self, '_user_check'):
00191             self._user_check = 1
00192 
00193         self._user_check = not self._user_check

Returns a string that is random and unguessable, or at
least as close as possible.

This is used by 'requestReset' to generate the auth
string. Override if you wish different format.

This implementation ignores userid and simply generates a
UUID. That parameter is for convenience of extenders, and
will be passed properly in the default implementation.

Definition at line 246 of file PasswordResetTool.py.

00246 
00247     def uniqueString(self, userid):
00248         """Returns a string that is random and unguessable, or at
00249         least as close as possible.
00250 
00251         This is used by 'requestReset' to generate the auth
00252         string. Override if you wish different format.
00253 
00254         This implementation ignores userid and simply generates a
00255         UUID. That parameter is for convenience of extenders, and
00256         will be passed properly in the default implementation."""
00257         # this is the informal UUID algorithm of
00258         # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/213761
00259         # by Carl Free Jr
00260         t = long( time.time() * 1000 )
00261         r = long( random.random()*100000000000000000L )
00262         try:
00263             a = socket.gethostbyname( socket.gethostname() )
00264         except:
00265             # if we can't get a network address, just imagine one
00266             a = random.random()*100000000000000000L
00267         data = str(t)+' '+str(r)+' '+str(a)#+' '+str(args)
00268         data = md5.md5(data).hexdigest()
00269         return str(data)

Here is the caller graph for this function:

Verify a key. Raises an exception if the key is invalid or expired

Definition at line 204 of file PasswordResetTool.py.

00204 
00205     def verifyKey(self, key):
00206         """Verify a key. Raises an exception if the key is invalid or expired"""
00207 
00208         try:
00209             u, expiry = self._requests[key]
00210         except KeyError:
00211             raise 'InvalidRequestError'
00212         
00213         if self.expired(expiry):
00214             raise 'ExpiredRequestError'
00215 
00216         if not self.getValidUser(u):
00217             raise 'InvalidRequestError', 'No such user'
        

Here is the call graph for this function:


Member Data Documentation

other things needed for this to work skins:

  • handler script for forgotten password form (probably over-riding existing Plone one
  • email template
  • password reset form
  • password reset form handler script Tool/CMF/Zope machinery

Definition at line 42 of file PasswordResetTool.py.

Definition at line 100 of file PasswordResetTool.py.

Definition at line 75 of file PasswordResetTool.py.

Definition at line 79 of file PasswordResetTool.py.

Definition at line 170 of file PasswordResetTool.py.

Internal attributes.

Definition at line 78 of file PasswordResetTool.py.

Definition at line 190 of file PasswordResetTool.py.

string PasswordResetTool.PasswordResetTool.PasswordResetTool.id = 'portal_password_reset' [static]

Definition at line 44 of file PasswordResetTool.py.

Initial value:
(( { 'label' : 'Overview'
                        , 'action' : 'manage_overview'
                        },
                      ) + SimpleItem.manage_options
                    )

Definition at line 50 of file PasswordResetTool.py.

tuple PasswordResetTool.PasswordResetTool.PasswordResetTool.manage_overview = DTMLFile('dtml/explainPWResetTool', globals() ) [static]

ZMI methods.

Definition at line 58 of file PasswordResetTool.py.

string PasswordResetTool.PasswordResetTool.PasswordResetTool.meta_type = 'Password Reset Tool' [static]

Definition at line 45 of file PasswordResetTool.py.

Definition at line 48 of file PasswordResetTool.py.


The documentation for this class was generated from the following file: