Skip to content

Commit

Permalink
[9.0][ADD] Password Security Settings (OCA#531)
Browse files Browse the repository at this point in the history
* [ADD] res_users_password_security: New module
* Create new module to lock down user passwords

* [REF] res_users_password_security: PR Review fixes
* Also add beta pass history rule

* [ADD] res_users_password_security: Pass history and min time
* Add pass history memory and threshold
* Add minimum time for pass resets through web reset

* Begin controller tests

* Fix copyright, wrong year for new file

* Add tests for password_security_home
* Left to do web_auth_reset_password

* Fix minimum reset threshold and finish tests

* Bug fixes per review

* [REF] password_security: PR review improvements
* Change tech name to password_security
* Use new except format
* Limit 1 & new api
* Cascade deletion for pass history

* [REF] password_security: Fix travis + style
* Fix travis errors
* self to cls
* Better variable names in tests

* [FIX] password_security: Fix travis errors
  • Loading branch information
lasley authored and ilyas caluwe committed Jan 31, 2024
1 parent cd9e49b commit 3ae181c
Show file tree
Hide file tree
Showing 18 changed files with 1,016 additions and 0 deletions.
90 changes: 90 additions & 0 deletions password_security/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
.. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3

=================
Password Security
=================

This module allows admin to set company-level password security requirements
and enforces them on the user.

It contains features such as

* Password expiration days
* Password length requirement
* Password minimum number of lowercase letters
* Password minimum number of uppercase letters
* Password minimum number of numbers
* Password minimum number of special characters

Configuration
=============

# Navigate to company you would like to set requirements on
# Click the ``Password Policy`` page
# Set the policies to your liking.

Password complexity requirements will be enforced upon next password change for
any user in that company.


Settings & Defaults
-------------------

These are defined at the company level:

===================== ======= ===================================================
Name Default Description
===================== ======= ===================================================
password_expiration 60 Days until passwords expire
password_length 12 Minimum number of characters in password
password_lower True Require lowercase letter in password
password_upper True Require uppercase letters in password
password_numeric True Require number in password
password_special True Require special character in password
password_history 30 Disallow reuse of this many previous passwords
password_minimum 24 Amount of hours that must pass until another reset
===================== ======= ===================================================

Known Issues / Roadmap
======================


Bug Tracker
===========

Bugs are tracked on `GitHub Issues
<https://github.com/LasLabs/odoo-base/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us to smash it by providing detailed and welcomed feedback.


Credits
=======

Images
------

* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.

Contributors
------------

* James Foster <[email protected]>
* Dave Lasley <[email protected]>

Maintainer
----------

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

This module is maintained by the OCA.

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

To contribute to this module, please visit https://odoo-community.org.
6 changes: 6 additions & 0 deletions password_security/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2015 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).

from . import controllers
from . import models
23 changes: 23 additions & 0 deletions password_security/__openerp__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Copyright 2015 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
{

'name': 'Password Security',
"summary": "Allow admin to set password security requirements.",
'version': '9.0.1.0.2',
'author': "LasLabs, Odoo Community Association (OCA)",
'category': 'Base',
'depends': [
'auth_crypt',
'auth_signup',
],
"website": "https://laslabs.com",
"license": "LGPL-3",
"data": [
'views/res_company_view.xml',
'security/ir.model.access.csv',
'security/res_users_pass_history.xml',
],
'installable': True,
}
5 changes: 5 additions & 0 deletions password_security/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2015 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).

from . import main
93 changes: 93 additions & 0 deletions password_security/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
# Copyright 2015 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).

import operator

from openerp import http
from openerp.http import request
from openerp.addons.auth_signup.controllers.main import AuthSignupHome
from openerp.addons.web.controllers.main import ensure_db, Session

from ..exceptions import PassError


class PasswordSecuritySession(Session):

@http.route()
def change_password(self, fields):
new_password = operator.itemgetter('new_password')(
dict(map(operator.itemgetter('name', 'value'), fields))
)
user_id = request.env.user
user_id.check_password(new_password)
return super(PasswordSecuritySession, self).change_password(fields)


class PasswordSecurityHome(AuthSignupHome):

def do_signup(self, qcontext):
password = qcontext.get('password')
user_id = request.env.user
user_id.check_password(password)
return super(PasswordSecurityHome, self).do_signup(qcontext)

@http.route()
def web_login(self, *args, **kw):
ensure_db()
response = super(PasswordSecurityHome, self).web_login(*args, **kw)
if not request.httprequest.method == 'POST':
return response
uid = request.session.authenticate(
request.session.db,
request.params['login'],
request.params['password']
)
if not uid:
return response
users_obj = request.env['res.users'].sudo()
user_id = users_obj.browse(request.uid)
if not user_id._password_has_expired():
return response
user_id.action_expire_password()
redirect = user_id.partner_id.signup_url
return http.redirect_with_hash(redirect)

@http.route()
def web_auth_signup(self, *args, **kw):
try:
return super(PasswordSecurityHome, self).web_auth_signup(
*args, **kw
)
except PassError as e:
qcontext = self.get_auth_signup_qcontext()
qcontext['error'] = e.message
return request.render('auth_signup.signup', qcontext)

@http.route()
def web_auth_reset_password(self, *args, **kw):
""" It provides hook to disallow front-facing resets inside of min
Unfortuantely had to reimplement some core logic here because of
nested logic in parent
"""
qcontext = self.get_auth_signup_qcontext()
if (
request.httprequest.method == 'POST' and
qcontext.get('login') and
'error' not in qcontext and
'token' not in qcontext
):
login = qcontext.get('login')
user_ids = request.env.sudo().search(
[('login', '=', login)],
limit=1,
)
if not user_ids:
user_ids = request.env.sudo().search(
[('email', '=', login)],
limit=1,
)
user_ids._validate_pass_reset()
return super(PasswordSecurityHome, self).web_auth_reset_password(
*args, **kw
)
12 changes: 12 additions & 0 deletions password_security/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright 2015 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).

from openerp.exceptions import Warning as UserError


class PassError(UserError):
""" Example: When you try to create an insecure password."""
def __init__(self, msg):
self.message = msg
super(PassError, self).__init__(msg)
7 changes: 7 additions & 0 deletions password_security/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright 2015 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).

from . import res_users
from . import res_company
from . import res_users_pass_history
51 changes: 51 additions & 0 deletions password_security/models/res_company.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Copyright 2015 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).

from openerp import models, fields


class ResCompany(models.Model):
_inherit = 'res.company'

password_expiration = fields.Integer(
'Days',
default=60,
help='How many days until passwords expire',
)
password_length = fields.Integer(
'Characters',
default=12,
help='Minimum number of characters',
)
password_lower = fields.Boolean(
'Lowercase',
default=True,
help='Require lowercase letters',
)
password_upper = fields.Boolean(
'Uppercase',
default=True,
help='Require uppercase letters',
)
password_numeric = fields.Boolean(
'Numeric',
default=True,
help='Require numeric digits',
)
password_special = fields.Boolean(
'Special',
default=True,
help='Require special characters',
)
password_history = fields.Integer(
'History',
default=30,
help='Disallow reuse of this many previous passwords - use negative '
'number for infinite, or 0 to disable',
)
password_minimum = fields.Integer(
'Minimum Hours',
default=24,
help='Amount of hours until a user may change password again',
)
Loading

0 comments on commit 3ae181c

Please sign in to comment.