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
-
-
-
-
-
-
- Reset Password
- ir.actions.act_window
- auth.reset_password
- form
- form
- new
-
-
-
diff --git a/addons/auth_reset_password/controllers/__init__.py b/addons/auth_reset_password/controllers/__init__.py
new file mode 100644
index 00000000000..f40f0ee976e
--- /dev/null
+++ b/addons/auth_reset_password/controllers/__init__.py
@@ -0,0 +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
+#
+##############################################################################
+
+import main
+
+# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/auth_reset_password/controllers/main.py b/addons/auth_reset_password/controllers/main.py
new file mode 100644
index 00000000000..5185a65c3ce
--- /dev/null
+++ b/addons/auth_reset_password/controllers/main.py
@@ -0,0 +1,52 @@
+# -*- 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 import SUPERUSER_ID
+from openerp.modules.registry import RegistryManager
+import openerp.addons.web.common.http as openerpweb
+
+import werkzeug
+
+import logging
+_logger = logging.getLogger(__name__)
+
+class Controller(openerpweb.Controller):
+ _cp_path = '/auth_reset_password'
+
+ @openerpweb.httprequest
+ def reset_password(self, req, dbname, login):
+ """ retrieve user, and perform reset password """
+ url = '/'
+ registry = RegistryManager.get(dbname)
+ with registry.cursor() as cr:
+ try:
+ res_users = registry.get('res.users')
+ res_users.reset_password(cr, SUPERUSER_ID, login)
+ cr.commit()
+ message = 'An email has been sent with credentials to reset your password'
+ except Exception as e:
+ # signup error
+ _logger.exception('error when resetting password')
+ message = e.message
+ url = "/#action=login&error_message=%s" % werkzeug.urls.url_quote(message)
+ return werkzeug.utils.redirect(url)
+
+# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/auth_reset_password/static/src/css/reset_password.css b/addons/auth_reset_password/static/src/css/reset_password.css
deleted file mode 100644
index dd590365918..00000000000
--- a/addons/auth_reset_password/static/src/css/reset_password.css
+++ /dev/null
@@ -1,12 +0,0 @@
-.openerp .oe_login .oe_login_pane {
- height: 152px;
-}
-.openerp .oe_login .oe_login_pane ul.oe_login_switch a {
- color: #eeeeee;
- margin: 0 8px;
-}
-.openerp .oe_login .oe_login_pane ul.oe_login_switch a:hover {
- text-decoration: underline;
-}
-
-
diff --git a/addons/auth_reset_password/static/src/js/reset_password.js b/addons/auth_reset_password/static/src/js/reset_password.js
index 4e09b5c5318..13fe2a39ce5 100644
--- a/addons/auth_reset_password/static/src/js/reset_password.js
+++ b/addons/auth_reset_password/static/src/js/reset_password.js
@@ -1,72 +1,30 @@
openerp.auth_reset_password = function(instance) {
var _t = instance.web._t;
+
instance.web.Login.include({
start: function() {
- var $e = this.$el;
- $e.find('a.oe_login_switch').click(function() {
- $e.find('ul.oe_login_switch').toggle();
- var $m = $e.find('form input[name=is_reset_pw]');
- $m.attr('checked', !$m.is(':checked'));
- });
+ this.$('a.oe_reset_password').click(this.do_reset_password);
return this._super();
},
- on_submit: function(ev) {
- if(ev) {
+ do_reset_password: function(ev) {
+ if (ev) {
ev.preventDefault();
}
-
- var $e = this.$el;
- var db = $e.find("form [name=db]").val();
+ var db = this.$("form [name=db]").val();
+ var login = this.$("form input[name=login]").val();
if (!db) {
- this.do_warn(_t("Login"), _t("No database selected !"));
+ this.do_warn("Login", "No database selected !");
+ return false;
+ } else if (!login) {
+ this.do_warn("Login", "Please enter a username or email address.")
return false;
}
- var $m = $e.find('form input[name=is_reset_pw]');
- if ($m.is(':checked')) {
- var email = $e.find('form input[name=email]').val();
- return this.do_reset_password(db, email);
- } else {
- return this._super(ev);
- }
- },
- do_reset_password: function(db, email) {
- var self = this;
- instance.session.session_authenticate(db, 'anonymous', 'anonymous', true).pipe(function () {
- var func = new instance.web.Model("res.users").get_func("send_reset_password_request");
- return func(email).then(function(res) {
- // show message
- self.do_notify(_t('Reset Password'), _.str.sprintf(_t('We have sent an email to %s with further instructions'), email), true);
- }, function(error, event) {
- // no traceback please
- event.preventDefault();
- });
- }).fail(function(error, event) {
- // cannot log as anonymous or reset_password not installed
- self.do_warn(_t('Reset Password'), _.str.sprintf(_t('Reset Password functionnality is not available for database %s'), db), true);
- });
+ var params = {
+ dbname : db,
+ login: login,
+ };
+ var url = "/auth_reset_password/reset_password?" + $.param(params);
+ window.location = url;
}
});
-
- instance.reset_password = {};
- instance.reset_password.ResetPassword = instance.web.Widget.extend({
- init: function(parent, params) {
- this._super(parent);
- this.token = (params && params.token) || false;
- },
- start: function() {
- this.do_action({
- name: 'Reset Password',
- type: 'ir.actions.act_window',
- context: {default_token: this.token},
- res_model: 'auth.reset_password',
- target: 'new',
- views: [[false, 'form']]
- });
- }
- });
-
-
- instance.web.client_actions.add("reset_password", "instance.reset_password.ResetPassword");
-
-
};
diff --git a/addons/auth_reset_password/static/src/xml/reset_password.xml b/addons/auth_reset_password/static/src/xml/reset_password.xml
index e57334b9c15..841f3906622 100644
--- a/addons/auth_reset_password/static/src/xml/reset_password.xml
+++ b/addons/auth_reset_password/static/src/xml/reset_password.xml
@@ -1,26 +1,11 @@
-
+
-
- // addClass does not work :(
- this.attr('class', (this.attr('class') || '') + ' oe_login_switch');
-
-
- Forgot your password?
-
-
-
-
+ Reset password
+