From 07ba5a0ce234254964f7743cf8ac52caaf0f0f4a Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 1 Oct 2012 17:07:23 +0200 Subject: [PATCH] [IMP] auth_reset_password: new implementation based on auth_signup bzr revid: rco@openerp.com-20121001150723-hcr2gcqqt7jsytp6 --- addons/auth_reset_password/__init__.py | 22 +++ addons/auth_reset_password/__openerp__.py | 24 ++- .../auth_reset_password.py | 159 +++++------------- .../auth_reset_password.xml | 47 +----- .../controllers/__init__.py | 24 +++ .../auth_reset_password/controllers/main.py | 52 ++++++ .../static/src/css/reset_password.css | 12 -- .../static/src/js/reset_password.js | 74 ++------ .../static/src/xml/reset_password.xml | 21 +-- 9 files changed, 184 insertions(+), 251 deletions(-) create mode 100644 addons/auth_reset_password/controllers/__init__.py create mode 100644 addons/auth_reset_password/controllers/main.py delete mode 100644 addons/auth_reset_password/static/src/css/reset_password.css diff --git a/addons/auth_reset_password/__init__.py b/addons/auth_reset_password/__init__.py index 28bbdd18161..665887c6ee7 100644 --- a/addons/auth_reset_password/__init__.py +++ b/addons/auth_reset_password/__init__.py @@ -1 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2012-today OpenERP SA () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see +# +############################################################################## + +import controllers import auth_reset_password diff --git a/addons/auth_reset_password/__openerp__.py b/addons/auth_reset_password/__openerp__.py index ccf5ca53a21..d3e5e6d4bcc 100644 --- a/addons/auth_reset_password/__openerp__.py +++ b/addons/auth_reset_password/__openerp__.py @@ -1,3 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2012-today OpenERP SA () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see +# +############################################################################## + { 'name': 'Reset Password', 'description': """ @@ -9,9 +30,8 @@ Allow users to reset their password from the login page. 'category': 'Authentication', 'website': 'http://www.openerp.com', 'installable': True, - 'depends': ['auth_anonymous', 'email_template'], + 'depends': ['auth_signup', 'email_template'], 'data': ['auth_reset_password.xml'], 'js': ['static/src/js/reset_password.js'], - 'css': ['static/src/css/reset_password.css'], 'qweb': ['static/src/xml/reset_password.xml'], } diff --git a/addons/auth_reset_password/auth_reset_password.py b/addons/auth_reset_password/auth_reset_password.py index cef7bc9c212..fe7d47e13d7 100644 --- a/addons/auth_reset_password/auth_reset_password.py +++ b/addons/auth_reset_password/auth_reset_password.py @@ -1,130 +1,55 @@ -import base64 -import hashlib -import simplejson -import time -import urlparse +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2012-today OpenERP SA () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see +# +############################################################################## -from openerp.tools import config from openerp.osv import osv, fields -from openerp import SUPERUSER_ID +from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT -TWENTY_FOUR_HOURS = 24 * 60 * 60 +from datetime import datetime, timedelta -def message_sign(data, secret): - src = simplejson.dumps([data, secret], indent=None, separators=(',', ':'), sort_keys=True) - sign = hashlib.sha1(src).hexdigest() - msg = simplejson.dumps([data, sign], indent=None, separators=(',', ':'), sort_keys=True) - # pad message to avoid '=' - pad = (3 - len(msg) % 3) % 3 - msg = msg + " " * pad - msg = base64.urlsafe_b64encode(msg) - return msg, sign +def now(**kwargs): + dt = datetime.now() + timedelta(**kwargs) + return dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT) -def message_check(msg, secret): - msg = base64.urlsafe_b64decode(msg) - l = simplejson.loads(msg) - msg_data = l[0] - msg_sign = l[1] - tmp, sign = message_sign(msg_data, secret) - if msg_sign == sign: - return msg_data class res_users(osv.osv): _inherit = 'res.users' - def _auth_reset_password_secret(self, cr, uid, context=None): - uuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid') - res = { - 'dbname': cr.dbname, - 'uuid': uuid, - 'admin_passwd': config['admin_passwd'] - } - return res + def reset_password(self, cr, uid, login, context=None): + """ retrieve the user corresponding to login (login or email), + create a specific signup token, and send it to the user + """ + user_ids = self.search(cr, uid, [('login', '=', login)], context=context) + if not user_ids: + user_ids = self.search(cr, uid, [('email', '=', login)], context=context) + if len(user_ids) != 1: + raise Exception('Reset password: invalid username or email') - def _auth_reset_password_host(self, cr, uid, context=None): - return self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', '') + # prepare reset password signup + user = self.browse(cr, uid, user_ids[0], context) + user.partner_id.signup_prepare(expiration=now(days=+1)) - def _auth_reset_password_link(self, cr, uid, ids, context=None): - assert len(ids) == 1 - host = self._auth_reset_password_host(cr, uid, context) - secret = self._auth_reset_password_secret(cr, uid, context) - msg_src = { - 'time': time.time(), - 'uid': ids[0], - } - msg, sign = message_sign(msg_src, secret) - link = urlparse.urljoin(host, '/login?db=%s&login=anonymous&key=anonymous#action=reset_password&token=%s' % (cr.dbname, msg)) - return link + # send email to user with their signup url + user = self.browse(cr, uid, user.id, context) + template = self.pool.get('ir.model.data').get_object(cr, uid, 'auth_reset_password', 'reset_password_email') + assert template._name == 'email.template' + self.pool.get('email.template').send_mail(cr, uid, template.id, user.id, force_send=True, context=context) - def _auth_reset_password_check_token(self, cr, uid, token, context=None): - secret = self._auth_reset_password_secret(cr, uid, context) - data = message_check(token, secret) - if data and (time.time() - data['time'] < TWENTY_FOUR_HOURS): - return data - return None - - def _auth_reset_password_send_email(self, cr, uid, email_to, tpl_name, res_id, context=None): - model, tpl_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'auth_reset_password', tpl_name) - assert model == 'email.template' - - msg_id = self.pool.get(model).send_mail(cr, uid, tpl_id, res_id, force_send=False, context=context) - MailMessage = self.pool.get('mail.message') - MailMessage.write(cr, uid, [msg_id], {'email_to': email_to}, context=context) - MailMessage.send(cr, uid, [msg_id], context=context) - - def send_reset_password_request(self, cr, uid, email, context=None): - # TODO reseting a password knowing only an email is not good enough (email can be shared between multiple logins). - ids = self.pool.get('res.users').search(cr, SUPERUSER_ID, [('user_email', '=', email)], context=context) - if ids: - self._auth_reset_password_send_email(cr, SUPERUSER_ID, email, 'reset_password_email', ids[0], context=context) - return True - #else: - # _m, company_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'main_company') - # self._auth_reset_password_send_email(cr, uid, email, 'email_no_user', company_id, context=context) - return False - -class auth_reset_password(osv.TransientModel): - _name = 'auth.reset_password' - _rec_name = 'password' - _columns = { - 'password': fields.char('Password', size=64), - 'password_confirmation': fields.char('Confirm Password', size=64), - 'token': fields.char('Token', size=128), - 'state': fields.selection([(x, x) for x in 'draft done missmatch error'.split()], required=True), - } - _defaults = { - 'state': 'draft', - } - - def create(self, cr, uid, values, context=None): - # NOTE here, invalid values raises exceptions to avoid storing - # sensitive data into the database (which then are available to anyone) - - pw = values.get('password') - if not pw or pw != values.get('password_confirmation'): - raise osv.except_osv('Error', 'Passwords missmatch') - - Users = self.pool.get('res.users') - data = Users._auth_reset_password_check_token(cr, uid, values.get('token', '')) - if data: - Users.write(cr, SUPERUSER_ID, data['uid'], {'password': pw}, context=context) - else: - raise osv.except_osv('Error', 'Invalid token') - - # Dont store password - values = {'state': 'done'} - return super(auth_reset_password, self).create(cr, uid, values, context) - - def change(self, cr, uid, ids, context=None): return True - - def onchange_pw(self, cr, uid, ids, password, password_confirmation, context=None): - if password != password_confirmation: - return {'value': {'state': 'missmatch'}} - return {'value': {'state': 'draft'}} - - def onchange_token(self, cr, uid, ids, token, context=None): - Users = self.pool.get('res.users') - if not Users._auth_reset_password_check_token(cr, uid, token, context=context): - return {'value': {'state': 'error'}} - return {} diff --git a/addons/auth_reset_password/auth_reset_password.xml b/addons/auth_reset_password/auth_reset_password.xml index bb75da34ce8..90f08a25899 100644 --- a/addons/auth_reset_password/auth_reset_password.xml +++ b/addons/auth_reset_password/auth_reset_password.xml @@ -7,56 +7,15 @@ Reset Password ]]> - + ${object.email} Password reset A password reset was requested the OpenERP account linked to this email on ${object._auth_reset_password_host()}

+

A password reset was requested for the OpenERP account linked to this email.

-

You may change your password following this link, -or by copy-pasting the following URL in your browser: ${object._auth_reset_password_link()}

+

You may change your password following this link.

Note: If you did not ask for a password reset, you can safely ignore this email.

]]>
- - - auth.reset_password.form - auth.reset_password - -
- - - - - - -
Passwords missmatch
-
- - -
  • - +
  • Reset password
  • +