Skip to content

Commit 16da4aa

Browse files
BT-cjimenoLauraCForgeFlow
authored andcommitted
[FIX] password_security: password expiration 2FA
1 parent 5cfacc9 commit 16da4aa

10 files changed

Lines changed: 92 additions & 27 deletions

File tree

password_security/README.rst

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ requirements and enforces them on the user.
3333

3434
It contains features such as
3535

36-
- Password expiration days
37-
- Password length requirement
38-
- Password minimum number of lowercase letters
39-
- Password minimum number of uppercase letters
40-
- Password minimum number of numbers
41-
- Password minimum number of special characters
36+
- Password expiration days
37+
- Password length requirement
38+
- Password minimum number of lowercase letters
39+
- Password minimum number of uppercase letters
40+
- Password minimum number of numbers
41+
- Password minimum number of special characters
4242

4343
**Table of contents**
4444

@@ -92,28 +92,30 @@ Authors
9292
Contributors
9393
------------
9494

95-
- James Foster <jfoster@laslabs.com>
95+
- James Foster <jfoster@laslabs.com>
9696

97-
- Dave Lasley <dave@laslabs.com>
97+
- Dave Lasley <dave@laslabs.com>
9898

99-
- Kaushal Prajapati <kbprajapati@live.com>
99+
- Kaushal Prajapati <kbprajapati@live.com>
100100

101-
- Petar Najman <petar.najman@modoolar.com>
101+
- Petar Najman <petar.najman@modoolar.com>
102102

103-
- Shepilov Vladislav <shepilov.v@protonmail.com>
103+
- Shepilov Vladislav <shepilov.v@protonmail.com>
104104

105-
- Florian Kantelberg <florian.kantelberg@initos.com>
105+
- Florian Kantelberg <florian.kantelberg@initos.com>
106106

107-
- Dhara Solanki <dhara.solanki@initos.com>
107+
- Carlos Jimeno <carlos.jimeno@bt-group.com>
108108

109-
- `Open Source Integrators <https://opensourceintegrators.com>`__
109+
- Dhara Solanki <dhara.solanki@initos.com>
110110

111-
- Chandresh Thakkar <cthakkar@opensourceintegrators.com>
112-
- Daniel Reis <dreis@opensourceintegrators.com>
111+
- `Open Source Integrators <https://opensourceintegrators.com>`__
113112

114-
- `Onestein <https://www.onestein.nl>`__:
113+
- Chandresh Thakkar <cthakkar@opensourceintegrators.com>
114+
- Daniel Reis <dreis@opensourceintegrators.com>
115115

116-
- Andrea Stirpe <a.stirpe@onestein.nl>
116+
- `Onestein <https://www.onestein.nl>`__:
117+
118+
- Andrea Stirpe <a.stirpe@onestein.nl>
117119

118120
Maintainers
119121
-----------

password_security/__manifest__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"depends": [
1818
"auth_signup",
1919
"auth_password_policy_signup",
20+
"auth_totp",
2021
],
2122
"website": "https://github.com/OCA/server-auth",
2223
"license": "LGPL-3",

password_security/controllers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
33

44
from . import main
5+
from . import home
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2022 brain-tec AG (https://bt-group.com)
2+
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
3+
4+
from odoo import http
5+
from odoo.http import request
6+
7+
from odoo.addons.auth_totp.controllers.home import Home
8+
9+
10+
class PasswordSecurity2FAHome(Home):
11+
@http.route()
12+
def web_totp(self, redirect=None, **kwargs):
13+
already_logged_in = request.session.uid
14+
result = super().web_totp(redirect, **kwargs)
15+
if already_logged_in or not (
16+
request.session.uid and request.env.user._password_has_expired()
17+
):
18+
return result
19+
# My password is expired, kick me out
20+
request.env.user.action_expire_password()
21+
request.session.logout(keep_db=True)
22+
redirect = request.env.user.partner_id._get_signup_url()
23+
return request.redirect(redirect)

password_security/controllers/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ def web_login(self, *args, **kw):
3030
if not request.env.user:
3131
return response
3232
# Now, I'm an authenticated user
33-
if not request.env.user._password_has_expired():
33+
# With 2FA there is a second step, and we would not be completely logged in
34+
if not (request.session.uid and request.env.user._password_has_expired()):
3435
return response
3536
# My password is expired, kick me out
3637
request.env.user.action_expire_password()

password_security/readme/CONTRIBUTORS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010

1111
- Florian Kantelberg \<<florian.kantelberg@initos.com>\>
1212

13+
- Carlos Jimeno \<<carlos.jimeno@bt-group.com>\>
14+
1315
- Dhara Solanki \<<dhara.solanki@initos.com>\>
1416

1517
- [Open Source Integrators](https://opensourceintegrators.com)
1618

1719
> - Chandresh Thakkar \<<cthakkar@opensourceintegrators.com>\>
1820
> - Daniel Reis \<<dreis@opensourceintegrators.com>\>
1921
20-
- [Onestein](https://www.onestein.nl):
22+
- [Onestein](https://www.onestein.nl):
2123
- Andrea Stirpe \<<a.stirpe@onestein.nl>\>

password_security/static/description/index.html

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88

99
/*
1010
:Author: David Goodger (goodger@python.org)
11-
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
11+
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
1212
:Copyright: This stylesheet has been placed in the public domain.
1313
1414
Default cascading style sheet for the HTML output of Docutils.
15+
Despite the name, some widely supported CSS2 features are used.
1516
1617
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
1718
customize this style sheet.
@@ -274,7 +275,7 @@
274275
margin-left: 2em ;
275276
margin-right: 2em }
276277

277-
pre.code .ln { color: grey; } /* line numbers */
278+
pre.code .ln { color: gray; } /* line numbers */
278279
pre.code, code { background-color: #eeeeee }
279280
pre.code .comment, code .comment { color: #5C6576 }
280281
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
@@ -300,7 +301,7 @@
300301
span.pre {
301302
white-space: pre }
302303

303-
span.problematic {
304+
span.problematic, pre.problematic {
304305
color: red }
305306

306307
span.section-subtitle {
@@ -445,6 +446,8 @@ <h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
445446
</li>
446447
<li><p class="first">Florian Kantelberg &lt;<a class="reference external" href="mailto:florian.kantelberg&#64;initos.com">florian.kantelberg&#64;initos.com</a>&gt;</p>
447448
</li>
449+
<li><p class="first">Carlos Jimeno &lt;<a class="reference external" href="mailto:carlos.jimeno&#64;bt-group.com">carlos.jimeno&#64;bt-group.com</a>&gt;</p>
450+
</li>
448451
<li><p class="first">Dhara Solanki &lt;<a class="reference external" href="mailto:dhara.solanki&#64;initos.com">dhara.solanki&#64;initos.com</a>&gt;</p>
449452
</li>
450453
<li><p class="first"><a class="reference external" href="https://opensourceintegrators.com">Open Source Integrators</a></p>
@@ -465,7 +468,9 @@ <h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
465468
<div class="section" id="maintainers">
466469
<h2><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h2>
467470
<p>This module is maintained by the OCA.</p>
468-
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
471+
<a class="reference external image-reference" href="https://odoo-community.org">
472+
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
473+
</a>
469474
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
470475
mission is to support the collaborative development of Odoo features and
471476
promote its widespread use.</p>

password_security/tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
from . import test_password_history
55
from . import test_reset_password
66
from . import test_signup
7+
from . import test_totp

password_security/tests/test_login.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
from datetime import datetime, timedelta
55
from unittest import mock
66

7-
from odoo import http, registry
7+
from odoo import http
88
from odoo.exceptions import UserError, ValidationError
9+
from odoo.modules.registry import Registry
910
from odoo.tests.common import HOST, HttpCase, Opener, get_db_name, new_test_user, tagged
1011

1112

@@ -81,7 +82,7 @@ def test_05_web_login_expire_pass(self):
8182
# Make password expired
8283
three_days_ago = datetime.now() - timedelta(days=3)
8384

84-
with registry(get_db_name()).cursor() as cr:
85+
with Registry(get_db_name()).cursor() as cr:
8586
env = self.env(cr)
8687
user = env["res.users"].search([("login", "=", self.username)])
8788
user.password_write_date = three_days_ago
@@ -108,7 +109,7 @@ def test_06_web_login_log_out_if_expired(self):
108109
# Make password expired while still logged in
109110
three_days_ago = datetime.now() - timedelta(days=3)
110111

111-
with registry(get_db_name()).cursor() as cr:
112+
with Registry(get_db_name()).cursor() as cr:
112113
env = self.env(cr)
113114
user = env["res.users"].search([("login", "=", self.username)])
114115
user.password_write_date = three_days_ago
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright 2022 Braintec AG
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
3+
4+
from datetime import datetime, timedelta
5+
6+
from odoo.tests import HttpCase, tagged
7+
8+
9+
@tagged("post_install", "-at_install")
10+
class TestTOTP(HttpCase):
11+
def test_totp(self):
12+
# 1. Login with demo user
13+
uid = self.env.ref("base.user_demo").id
14+
self.assertEqual(uid, self.env.ref("base.user_demo").id)
15+
16+
# 2. Check that we are logged in
17+
self.authenticate(user="demo", password="demo")
18+
self.assertEqual(self.session.uid, uid)
19+
20+
# 3. Check expired password
21+
# signup_type has been set to "reset"
22+
self.assertEqual(self.env.user._password_has_expired(), False)
23+
self.assertEqual(self.env.user.partner_id.signup_type, False)
24+
self.env.user.action_expire_password()
25+
self.assertEqual(self.env.user.partner_id.signup_type, "reset")
26+
27+
self.logout()
28+
self.assertNotEqual(self.session.uid, uid)

0 commit comments

Comments
 (0)