Commit 7ad3dbf23b74b7e3c2807df284a651312d657f7c

Authored by hitier
2 parents 57125da2 149f7875

Password Encryption

Passwords are now encrypted in database.
Thank you to Olivier Thauvin <olivier.thauvin@latmos.ipsl.fr> for bug
report.
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"))
... ...