Skip to content

Commit 1e6e6be

Browse files
authored
Fix setting argon2 parameters in app config (#10)
* Fix parameter overrides * Add GitHub action workflow
1 parent 2dec14c commit 1e6e6be

File tree

5 files changed

+110
-99
lines changed

5 files changed

+110
-99
lines changed

.github/workflows/main.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: pytest
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
strategy:
9+
matrix:
10+
python-version: ['3.7', '3.8', '3.9', '3.10']
11+
12+
steps:
13+
- uses: actions/checkout@v3
14+
with:
15+
fetch-depth: 1
16+
- name: Set up Python ${{ matrix.python-version }}
17+
uses: actions/setup-python@v3
18+
with:
19+
python-version: ${{ matrix.python-version }}
20+
- name: Install dependencies
21+
run: |
22+
python -m pip install --upgrade pip
23+
pip install -r requirements.txt
24+
- name: Test with pytest
25+
run: |
26+
pip install pytest
27+
pytest test_flask_argon2.py

.travis.yml

Lines changed: 0 additions & 28 deletions
This file was deleted.

flask_argon2.py

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@
44
55
A Flask extension providing argon2 hashing and comparison.
66
7-
:copyright: (c) 2019 by DominusTemporis.
7+
:copyright: (c) 2022 by red-coracle.
88
:license: MIT, see LICENSE for more details.
99
"""
1010

11-
from __future__ import absolute_import
12-
from __future__ import print_function
13-
# from sys import version_info
1411

1512
__version_info__ = ('0', '2', '0', '0')
1613
__version__ = '.'.join(__version_info__)
@@ -26,8 +23,6 @@
2623
print('argon2_cffi is required to use Flask-Argon2')
2724
raise e
2825

29-
# PY3 = version_info[0] >= 3
30-
3126

3227
def generate_password_hash(password):
3328
'''
@@ -110,43 +105,48 @@ class Argon2(object):
110105

111106
def __init__(self,
112107
app=None,
113-
time_cost=_time_cost,
114-
memory_cost=_memory_cost,
115-
parallelism=_parallelism,
116-
hash_len=_hash_len,
117-
salt_len=_salt_len,
118-
encoding=_encoding):
108+
time_cost: int = None,
109+
memory_cost: int = None,
110+
parallelism: int = None,
111+
hash_len: int = None,
112+
salt_len: int = None,
113+
encoding: str = None):
114+
# Keep for backwards compatibility
119115
self.time_cost = time_cost
120116
self.memory_cost = memory_cost
121117
self.parallelism = parallelism
122118
self.hash_len = hash_len
123119
self.salt_len = salt_len
124120
self.encoding = encoding
125-
self.ph = argon2.PasswordHasher(self.time_cost,
126-
self.memory_cost,
127-
self.parallelism,
128-
self.hash_len,
129-
self.salt_len,
130-
self.encoding)
131-
121+
self.init_app(app)
122+
132123
def init_app(self, app):
133124
'''Initalizes the application with the extension.
134125
:param app: The Flask application object.
135126
'''
136-
self.time_cost = app.config.get('ARGON2_TIME_COST', self._time_cost)
137-
self.memory_cost = app.config.get('ARGON2_MEMORY_COST', self._memory_cost)
138-
self.parallelism = app.config.get('ARGON2_PARALLELISM', self._parallelism)
139-
self.hash_len = app.config.get('ARGON2_HASH_LENGTH', self._hash_len)
140-
self.salt_len = app.config.get('ARGON2_SALT_LENGTH', self._salt_len)
141-
self.encoding = app.config.get('ARGON2_ENCODING', self._encoding)
127+
128+
self.time_cost = self.time_cost or self._time_cost
129+
self.memory_cost = self.memory_cost or self._memory_cost
130+
self.parallelism = self.parallelism or self._parallelism
131+
self.hash_len = self.hash_len or self._hash_len
132+
self.salt_len = self.salt_len or self._salt_len
133+
self.encoding = self.encoding or self._encoding
134+
135+
if app is not None:
136+
self.time_cost = app.config.get('ARGON2_TIME_COST', self.time_cost)
137+
self.memory_cost = app.config.get('ARGON2_MEMORY_COST', self.memory_cost)
138+
self.parallelism = app.config.get('ARGON2_PARALLELISM', self.parallelism)
139+
self.hash_len = app.config.get('ARGON2_HASH_LENGTH', self.hash_len)
140+
self.salt_len = app.config.get('ARGON2_SALT_LENGTH', self.salt_len)
141+
self.encoding = app.config.get('ARGON2_ENCODING', self.encoding)
142+
142143
self.ph = argon2.PasswordHasher(self.time_cost,
143144
self.memory_cost,
144145
self.parallelism,
145146
self.hash_len,
146147
self.salt_len,
147148
self.encoding)
148149

149-
150150
def generate_password_hash(self, password):
151151
'''Generates a password hash using argon2.
152152
Example usage of :class:`generate_password_hash` might look something
@@ -158,13 +158,6 @@ def generate_password_hash(self, password):
158158
if not password:
159159
raise ValueError('Password must be non-empty.')
160160

161-
# if PY3:
162-
# if isinstance(password, str):
163-
# password = bytes(password, 'utf-8')
164-
# else:
165-
# if isinstance(password, unicode):
166-
# password = password.encode('utf-8')
167-
168161
return self.ph.hash(password)
169162

170163
def check_password_hash(self, pw_hash, password):

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
argon2_cffi>=20.1.0
2-
Flask>=1.*
1+
argon2_cffi>=21.0.0
2+
Flask>=2.*

test_flask_argon2.py

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,67 @@
1-
# coding:utf-8
2-
3-
import unittest
41
import flask
5-
from flask_argon2 import (Argon2, check_password_hash, generate_password_hash)
2+
import pytest
3+
from argon2.profiles import CHEAPEST
4+
5+
from flask_argon2 import Argon2, check_password_hash, generate_password_hash
6+
7+
8+
@pytest.fixture(scope='module')
9+
def _argon2():
10+
app = flask.Flask(__name__)
11+
return Argon2(app)
12+
13+
14+
def test_check_hash(_argon2):
15+
pw_hash = _argon2.generate_password_hash('secret')
16+
assert check_password_hash(pw_hash, 'secret') is True
17+
pw_hash = _argon2.generate_password_hash(u'\u2603')
18+
assert _argon2.check_password_hash(pw_hash, u'\u2603') is True
19+
pw_hash = generate_password_hash('hunter2')
20+
assert check_password_hash(pw_hash, 'hunter2') is True
21+
22+
23+
def test_unicode_hash(_argon2):
24+
password = u'東京'
25+
pw_hash = _argon2.generate_password_hash(password)
26+
assert _argon2.check_password_hash(pw_hash, password) is True
27+
628

29+
def test_hash_error(_argon2):
30+
pw_hash = _argon2.generate_password_hash('secret')
31+
assert _argon2.check_password_hash(pw_hash, 'hunter2') is False
732

8-
class BasicTestCase(unittest.TestCase):
933

10-
def setUp(self):
11-
app = flask.Flask(__name__)
12-
self.argon2 = Argon2(app)
34+
def test_invalid_hash(_argon2):
35+
assert _argon2.check_password_hash('secret', 'hunter2') is False
1336

14-
def test_check_hash(self):
15-
pw_hash = self.argon2.generate_password_hash('secret')
16-
self.assertTrue(self.argon2.check_password_hash(pw_hash, 'secret'))
17-
pw_hash = self.argon2.generate_password_hash(u'\u2603')
18-
self.assertTrue(self.argon2.check_password_hash(pw_hash, u'\u2603'))
19-
pw_hash = generate_password_hash('hunter2')
20-
self.assertTrue(check_password_hash(pw_hash, 'hunter2'))
2137

22-
def test_unicode_hash(self):
23-
password = u'東京'
24-
pw_hash = generate_password_hash(password)
25-
self.assertTrue(check_password_hash(pw_hash, password))
38+
def test_init_override():
39+
app = flask.Flask(__name__)
40+
_argon2 = Argon2(app, parallelism=3)
41+
pw_hash = _argon2.generate_password_hash('secret')
42+
assert _argon2.parallelism == 3
43+
assert _argon2.check_password_hash(pw_hash, 'secret') is True
2644

27-
def test_hash_error(self):
28-
pw_hash = self.argon2.generate_password_hash('secret')
29-
self.assertFalse(self.argon2.check_password_hash(pw_hash, 'hunter2'))
30-
31-
def test_hash_invalid_hash(self):
32-
""" Check that an InvalidHash is raised when an unhased password is used. """
33-
self.assertFalse(self.argon2.check_password_hash('secret', 'hunter2'))
3445

46+
def test_app_config_override():
47+
app = flask.Flask(__name__)
48+
app.config['ARGON2_MEMORY_COST'] = CHEAPEST.memory_cost
49+
_argon2 = Argon2(app)
50+
assert _argon2.memory_cost == CHEAPEST.memory_cost
51+
assert _argon2.ph.memory_cost == CHEAPEST.memory_cost
3552

36-
class OverridesTestCase(unittest.TestCase):
3753

38-
def setUp(self):
39-
app = flask.Flask(__name__)
40-
self.argon2 = Argon2(app, parallelism=3)
54+
def test_multiple_overrides():
55+
app = flask.Flask(__name__)
56+
app.config['ARGON2_PARALLELISM'] = CHEAPEST.parallelism
57+
_argon2 = Argon2(app, hash_len=16)
58+
assert _argon2.parallelism == CHEAPEST.parallelism
59+
assert _argon2.hash_len == 16
4160

42-
def test_changed_parallelism(self):
43-
pw_hash = self.argon2.generate_password_hash('secret')
44-
self.assertTrue(self.argon2.parallelism == 3)
45-
self.assertTrue(self.argon2.check_password_hash(pw_hash, 'secret'))
4661

47-
if __name__ == '__main__':
48-
unittest.main()
62+
def test_multiple_override_same_parameter():
63+
app = flask.Flask(__name__)
64+
app.config['ARGON2_PARALLELISM'] = CHEAPEST.parallelism
65+
_argon2 = Argon2(app, parallelism=8)
66+
assert _argon2.parallelism == CHEAPEST.parallelism
67+
assert _argon2.ph.parallelism == CHEAPEST.parallelism

0 commit comments

Comments
 (0)