PasswordTool.py 7.49 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
##############################################################################
#
# Copyright (c) 2008 Nexedi SARL and Contributors. All Rights Reserved.
#                    Aurelien Calonne <aurel@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################


from AccessControl import ClassSecurityInfo
from Globals import InitializeClass, DTMLFile, get_request
from Products.ERP5Type.Tool.BaseTool import BaseTool
from Products.ERP5Type import Permissions
from Products.ERP5 import _dtmldir
from zLOG import LOG
import time, random, md5
from DateTime import DateTime
from Products.ERP5Type.Message import Message
from Acquisition import aq_base
from BTrees.OOBTree import OOBTree
N_ = lambda msgid, **kw: Message('ui', msgid, **kw)

class PasswordTool(BaseTool):
  """
    PasswoordTool is used to allow a user to change its password
  """
  title = 'Password Tool'
  id = 'portal_password'
  meta_type = 'ERP5 Password Tool'
  portal_type = 'Password Tool'
  allowed_types = ()

  # Declarative Security
  security = ClassSecurityInfo()

  security.declareProtected(Permissions.ManagePortal, 'manage_overview' )
  manage_overview = DTMLFile( 'explainPasswordTool', _dtmldir )


  _expiration_day = 1
  password_request_dict = {}
  
  def __init__(self):
    self.password_request_dict = OOBTree()

  def mailPasswordResetRequest(self, user_login=None, REQUEST=None):
    """
    Create a ramdom string and expiration date for request
    """
    if user_login is None:
      user_login = REQUEST["user_login"]

    # check user exists
    user_list = self.portal_catalog.unrestrictedSearchResults(portal_type="Person", reference=user_login)
    if len(user_list) == 0:
      msg = N_("User ${user} doesn't exist.",
               mapping={'user': user_login})        
      if REQUEST is not None:
        ret_url = '%s/login_form?portal_status_message=%s' % \
                  (self.getPortalObject().absolute_url(),msg)
        return REQUEST.RESPONSE.redirect( ret_url )
      else:
        return msg

    user = user_list[0].getObject()
    # generate a ramdom string
    random_url = self._generateUUID()
    url = "%s/portal_password/resetPassword?key=%s" %(self.getPortalObject().absolute_url() , random_url)
    # generate expiration date
    expiration_date = DateTime() + self._expiration_day
    # register request
    self.password_request_dict = {random_url : (user_login, expiration_date)}

    # send mail
    subject = "[%s] Reset of your password" %(self.getPortalObject().getTitle())
    message = "\nYou requested to reset your %s account password.\n\n" \
              "Please copy and paste the following link into your browser: \n%s\n\n" \
              "Please note that this link will be valid only one time, until %s.\n" \
              "After this date, or after having used this link, you will have to make " \
              "a new request\n\n" \
              "Thank you" %(self.getPortalObject().getTitle(), url, expiration_date)    
    self.portal_notifications.sendMessage(sender=None, recipient=[user,], subject=subject, message=message)
    if REQUEST is not None:
      msg = N_("An email has been sent to you.")        
      ret_url = '%s/login_form?portal_status_message=%s' % \
                (self.getPortalObject().absolute_url(),msg)
      return REQUEST.RESPONSE.redirect( ret_url )
  

  def _generateUUID(self, args=""):
    """
    Generate a unique id that will be used as url for password
    """
    # this code is based on
    # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/213761
    # by Carl Free Jr
    # as uuid module is only available in pyhton 2.5
    t = long( time.time() * 1000 )
    r = long( random.random()*100000000000000000L )
    try:
      a = socket.gethostbyname( socket.gethostname() )
    except:
      # if we can't get a network address, just imagine one
      a = random.random()*100000000000000000L
    data = str(t)+' '+str(r)+' '+str(a)+' '+str(args)
    data = md5.md5(data).hexdigest()
    return data


  def resetPassword(self, key=None, REQUEST=None):
    """
    """
    if REQUEST is None:
      REQUEST = get_request()
    user_login, expiration_date = self.password_request_dict.get(key, (None, None))
    if key is None or user_login is None:
      ret_url = '%s/login_form' % self.getPortalObject().absolute_url()
      return REQUEST.RESPONSE.redirect( ret_url )

    # check date
    current_date = DateTime()
    if current_date > expiration_date:
      msg = N_("Date has expire.",)
      ret_url = '%s/login_form?portal_status_message=%s' % \
                (self.getPortalObject().absolute_url(), msg)
      return REQUEST.RESPONSE.redirect( ret_url )
      
    # redirect to form as all is ok
    REQUEST.set("password_key", key)
    return self.getPortalObject().reset_password_form(REQUEST=REQUEST)


  def removeExpiredRequests(self, **kw):
    """
    Browse dict and remove expired request
    """
    current_date = DateTime()
    for key, (login, date) in self.password_request_dict.items():
      if date < current_date:
        self.password_request_dict.pop(key)
        
         
  def changeUserPassword(self, user_login, password, password_confirmation, password_key, REQUEST=None):
    """
    Reset the password for a given login    
    """
    # check the key
    register_user_login, expiration_date = self.password_request_dict.get(password_key, (None, None))

    current_date = DateTime()
    msg = None
    if register_user_login is None:
      msg = ""
    elif register_user_login != user_login:
      msg = N_("Bad login provided.",)
    elif current_date > expiration_date:
      msg = N_("Date has expire.",)
    elif password != password_confirmation:
      msg = N_("Password are not identical.",)
    if msg is not None:
      if REQUEST is not None:
        ret_url = '%s/login_form?portal_status_message=%s' % \
                  (self.getPortalObject().absolute_url(), msg)
        return REQUEST.RESPONSE.redirect( ret_url )
      else:
        return msg

    # all is OK, change password and remove it from request dict
    self.password_request_dict.pop(password_key)
    persons = self.acl_users.erp5_users.getUserByLogin(user_login)              
    person = persons[0]
    person._setPassword(password)
    person.reindexObject()
    if REQUEST is not None:
      msg = N_("Password changed.",)
      ret_url = '%s/login_form?portal_status_message=%s' % \
                (self.getPortalObject().absolute_url(), msg)
      return REQUEST.RESPONSE.redirect( ret_url )
    
InitializeClass(PasswordTool)