Commit 7ad3dbf23b74b7e3c2807df284a651312d657f7c
Exists in
master
and in
4 other branches
Password Encryption
Passwords are now encrypted in database. Thank you to Olivier Thauvin <olivier.thauvin@latmos.ipsl.fr> for bug report.
Showing
5 changed files
with
65 additions
and
14 deletions
Show diff stats
app/auth/models.py
1 | 1 | from pprint import pprint |
2 | 2 | |
3 | -from flask_login import UserMixin, current_user | |
3 | +from werkzeug.security import generate_password_hash, check_password_hash | |
4 | +from flask_login import UserMixin | |
4 | 5 | from app.models import db |
5 | 6 | |
6 | 7 | # |
... | ... | @@ -47,8 +48,9 @@ class User(UserMixin, db.Model): |
47 | 48 | email = db.Column(db.String(100), unique=True) |
48 | 49 | name = db.Column(db.String(100)) |
49 | 50 | login = db.Column(db.String(100), unique=True) |
50 | - password = db.Column(db.String(100)) | |
51 | 51 | role = db.Column(db.Integer, default=0) |
52 | + password = db.Column(db.String(128)) | |
53 | + password_hash = db.Column(db.String(128)) | |
52 | 54 | |
53 | 55 | def __repr__(self): |
54 | 56 | return "i: {}, n: {}, e: {}, l: {}".format(self.id, self.name, self.email, self.login) |
... | ... | @@ -56,7 +58,11 @@ class User(UserMixin, db.Model): |
56 | 58 | # Set role at construction time |
57 | 59 | def __init__(self, **kwargs): |
58 | 60 | super(User, self).__init__(**kwargs) |
59 | - self.set_role(kwargs['role']) | |
61 | + if ('role' in kwargs): | |
62 | + self.set_role(kwargs['role']) | |
63 | + if ('password' in kwargs): | |
64 | + self.set_password(kwargs['password']) | |
65 | + self.password = None | |
60 | 66 | |
61 | 67 | def set_role(self, role): |
62 | 68 | self.role = _checkRole(role) |
... | ... | @@ -68,3 +74,9 @@ class User(UserMixin, db.Model): |
68 | 74 | def has_role_or_higher(self, role): |
69 | 75 | role = _checkRole(role) |
70 | 76 | return self.role and (self.role >= role) |
77 | + | |
78 | + def set_password(self, password): | |
79 | + self.password_hash = generate_password_hash(password) | |
80 | + | |
81 | + def check_password(self, password): | |
82 | + return check_password_hash(self.password_hash, password) | ... | ... |
app/auth/routes.py
... | ... | @@ -52,7 +52,7 @@ def login_post(): |
52 | 52 | user_password = request.form.get('password') |
53 | 53 | # user_remember = request.form.get('remember') |
54 | 54 | user = User.query.filter_by(login=user_login).one_or_none() |
55 | - if user and user.password == user_password: | |
55 | + if user and user.check_password(user_password): | |
56 | 56 | login_user(user) |
57 | 57 | flash("Connection Réussie !", 'success') |
58 | 58 | return redirect(url_for('main.index')) | ... | ... |
app/commands/commands.py
... | ... | @@ -12,7 +12,7 @@ from sqlalchemy.orm import Session |
12 | 12 | from sqlalchemy import create_engine |
13 | 13 | |
14 | 14 | from app.models import db, Agent, Service, Project, Capacity, Period, Charge |
15 | -from app.auth.models import User | |
15 | +from app.auth.models import User, _nameToRole | |
16 | 16 | |
17 | 17 | from . import bp |
18 | 18 | |
... | ... | @@ -199,7 +199,8 @@ def create_db(): |
199 | 199 | configure the proper database uri in the db_config.py file. |
200 | 200 | """ |
201 | 201 | db.create_all() |
202 | - admin = User(email='admin@nowhere.org', name='admin', login='admin', password='admin', role='admin') | |
202 | + admin = User(email='admin@nowhere.org', name='admin', login='admin', role='admin') | |
203 | + admin.set_password('admin') | |
203 | 204 | sqlite_uri = db.engine.url.__str__() if 'sqlite' in db.engine.url.__str__() else None |
204 | 205 | try: |
205 | 206 | db.session.add(admin) |
... | ... | @@ -219,12 +220,23 @@ def create_db(): |
219 | 220 | @click.argument('name') |
220 | 221 | @click.argument('login') |
221 | 222 | @click.argument('password') |
222 | -def user_add(email, name, login, password): | |
223 | +@click.argument('role') | |
224 | +def user_add(email, name, login, password, role): | |
223 | 225 | """ Add a new user in db.""" |
224 | - user = User(email=email, name=name, login=login, password=password) | |
226 | + user = User.query.filter(User.name == name).one_or_none() | |
227 | + if (user): | |
228 | + current_app.logger.error(f"user already exists {name}") | |
229 | + return | |
230 | + user = User(email=email, name=name, login=login, password=password, role=role) | |
225 | 231 | db.session.add(user) |
226 | 232 | db.session.commit() |
227 | - current_app.logger.info("added ", name) | |
233 | + current_app.logger.info(f"added {name}") | |
234 | + | |
235 | + | |
236 | +@bp.cli.command('show_roles') | |
237 | +def show_roles(): | |
238 | + """ List all available roles for a user""" | |
239 | + print("\n".join(list(_nameToRole))) | |
228 | 240 | |
229 | 241 | |
230 | 242 | @bp.cli.command('user_show_all') | ... | ... |
resources/pdc_config.py
... | ... | @@ -102,7 +102,7 @@ class DevConfig(Config): |
102 | 102 | class TestConfig(Config): |
103 | 103 | TESTING = True |
104 | 104 | DEBUG = True |
105 | - PDC_LOGS_LEVEL = 'ERROR' # choose within DEBUG, INFO, WARN, ERROR ( more levels in logging module ) | |
105 | + PDC_LOGS_LEVEL = 'INFO' # choose within DEBUG, INFO, WARN, ERROR ( more levels in logging module ) | |
106 | 106 | |
107 | 107 | # Trying to set specific db uri from ./db_config.py |
108 | 108 | try: | ... | ... |
tests/backend_tests.py
... | ... | @@ -11,7 +11,8 @@ class BaseTestCase(unittest.TestCase): |
11 | 11 | self.app = create_app(TestConfig) |
12 | 12 | self.app_context = self.app.app_context() |
13 | 13 | self.app_context.push() |
14 | - # TODO: shall we always copy db from resources ? | |
14 | + # TODO: shall we always copy db from resources sqlite file ? | |
15 | + # that would allow us to run all tests in sqlite:memory: | |
15 | 16 | # db.create_all() |
16 | 17 | # admin = User(email='admin@nowhere.org', name='admin', login='admin', password='admin', role='admin') |
17 | 18 | # db.session.add(admin) |
... | ... | @@ -48,16 +49,42 @@ class DbMgrTestCase(BaseTestCase): |
48 | 49 | |
49 | 50 | class AuthModelTestCase(BaseTestCase): |
50 | 51 | |
52 | + def skip_if_no_sqlitememory(self): | |
53 | + if 'memory' not in self.app.config['SQLALCHEMY_DATABASE_URI']: | |
54 | + self.skipTest("Needs in memory sqlite") | |
55 | + | |
56 | + def get_admin(self): | |
57 | + return User.query.filter(User.name == 'admin').one() | |
58 | + | |
59 | + def setUp(self): | |
60 | + BaseTestCase.setUp(self) | |
61 | + self.skip_if_no_sqlitememory() | |
62 | + db.create_all() | |
63 | + admin = User(email='admin@nowhere.org', name='admin', login='admin', role='admin') | |
64 | + db.session.add(admin) | |
65 | + db.session.commit() | |
66 | + | |
67 | + def test_in_memory(self): | |
68 | + self.app.logger.info("In memory Sqlite DB for tests") | |
69 | + self.assertTrue(True) | |
70 | + | |
51 | 71 | def test_setrole(self): |
52 | - admin = User.query.filter(User.name == 'admin').one_or_none() | |
72 | + admin = self.get_admin() | |
53 | 73 | admin.set_role("ADMIN") |
54 | 74 | db.session.commit() |
55 | - admin = User.query.filter(User.name == 'admin').one_or_none() | |
75 | + admin = self.get_admin() | |
56 | 76 | self.assertTrue(admin is not None) |
57 | 77 | self.assertTrue(admin.has_role("ADMIN")) |
58 | 78 | self.assertFalse(admin.has_role("SERVICE")) |
59 | 79 | |
60 | 80 | def test_setrole_valueerror(self): |
61 | - admin = User(email='me@nowhere.org', name='me', login='me', password='me', role='admin') | |
81 | + admin = self.get_admin() | |
62 | 82 | with self.assertRaises(ValueError) as ve: |
63 | 83 | admin.set_role("NOSUCHROLE") |
84 | + | |
85 | + def test_setcheckpassword(self): | |
86 | + admin = self.get_admin() | |
87 | + admin.set_password("hahaha") | |
88 | + db.session.commit() | |
89 | + admin2 = self.get_admin() | |
90 | + self.assertTrue(admin2.check_password("hahaha")) | ... | ... |