aboutsummaryrefslogtreecommitdiff
path: root/src/web
diff options
context:
space:
mode:
Diffstat (limited to 'src/web')
-rw-r--r--src/web/controllers/user.py8
-rw-r--r--src/web/forms.py9
-rw-r--r--src/web/lib/user_utils.py23
-rw-r--r--src/web/models/__init__.py2
-rw-r--r--src/web/models/user.py3
-rw-r--r--src/web/notifications.py6
-rw-r--r--src/web/templates/admin/dashboard.html4
-rw-r--r--src/web/views/admin.py11
-rw-r--r--src/web/views/api/common.py3
-rw-r--r--src/web/views/user.py12
10 files changed, 48 insertions, 33 deletions
diff --git a/src/web/controllers/user.py b/src/web/controllers/user.py
index d8bf1fa1..ae169b05 100644
--- a/src/web/controllers/user.py
+++ b/src/web/controllers/user.py
@@ -9,14 +9,6 @@ class UserController(AbstractController):
_db_cls = User
_user_id_key = 'id'
- def unset_activation_key(self, obj_id):
- self.update({'id': obj_id}, {'activation_key': ""})
-
- def set_activation_key(self, obj_id):
- key = str(random.getrandbits(256)).encode("utf-8")
- key = hashlib.sha512(key).hexdigest()[:86]
- self.update({'id': obj_id}, {'activation_key': key})
-
def _handle_password(self, attrs):
if attrs.get('password'):
attrs['pwdhash'] = generate_password_hash(attrs.pop('password'))
diff --git a/src/web/forms.py b/src/web/forms.py
index 172f31a8..b17d2f7a 100644
--- a/src/web/forms.py
+++ b/src/web/forms.py
@@ -99,10 +99,9 @@ class SigninForm(RedirectForm):
return False
user = User.query.filter(User.email == self.email.data).first()
- if user and user.check_password(self.password.data) \
- and user.activation_key == "":
+ if user and user.check_password(self.password.data) and user.enabled:
return True
- elif user and user.activation_key != "":
+ elif user and not user.enabled:
flash(lazy_gettext('Account not confirmed'), 'danger')
return False
else:
@@ -207,9 +206,9 @@ class RecoverPasswordForm(Form):
return False
user = User.query.filter(User.email == self.email.data).first()
- if user and user.activation_key == "":
+ if user and user.enabled:
return True
- elif user and user.activation_key != "":
+ elif user and not user.enabled:
flash(lazy_gettext('Account not confirmed.'), 'danger')
return False
else:
diff --git a/src/web/lib/user_utils.py b/src/web/lib/user_utils.py
new file mode 100644
index 00000000..78468379
--- /dev/null
+++ b/src/web/lib/user_utils.py
@@ -0,0 +1,23 @@
+
+
+from itsdangerous import URLSafeTimedSerializer
+
+from bootstrap import application
+
+
+def generate_confirmation_token(email):
+ serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])
+ return serializer.dumps(email, salt=app.config['SECURITY_PASSWORD_SALT'])
+
+
+def confirm_token(token, expiration=3600):
+ serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])
+ try:
+ email = serializer.loads(
+ token,
+ salt=app.config['SECURITY_PASSWORD_SALT'],
+ max_age=expiration
+ )
+ except:
+ return False
+ return email
diff --git a/src/web/models/__init__.py b/src/web/models/__init__.py
index e6615ab4..d9489dbb 100644
--- a/src/web/models/__init__.py
+++ b/src/web/models/__init__.py
@@ -96,7 +96,7 @@ def db_create(db):
"root@jarr.localhost"),
pwdhash=generate_password_hash(
os.environ.get("ADMIN_PASSWORD", "password")),
- activation_key="")
+ enabled=True)
user1.roles.extend([role_admin, role_user])
db.session.add(user1)
diff --git a/src/web/models/user.py b/src/web/models/user.py
index cdbfb457..1a276f7e 100644
--- a/src/web/models/user.py
+++ b/src/web/models/user.py
@@ -45,8 +45,7 @@ class User(db.Model, UserMixin):
email = db.Column(db.String(254), index=True, unique=True)
pwdhash = db.Column(db.String())
roles = db.relationship('Role', backref='user', lazy='dynamic')
- activation_key = db.Column(db.String(128), default=hashlib.sha512(
- str(random.getrandbits(256)).encode("utf-8")).hexdigest()[:86])
+ enabled = db.Column(db.Boolean(), default=False)
date_created = db.Column(db.DateTime(), default=datetime.now)
last_seen = db.Column(db.DateTime(), default=datetime.now)
feeds = db.relationship('Feed', backref='subscriber', lazy='dynamic',
diff --git a/src/web/notifications.py b/src/web/notifications.py
index c0d4fb1c..309da2a3 100644
--- a/src/web/notifications.py
+++ b/src/web/notifications.py
@@ -21,6 +21,7 @@
import conf
from web import emails
+from web.lib.user_utils import generate_confirmation_token
def information_message(subject, plaintext):
@@ -30,7 +31,7 @@ def information_message(subject, plaintext):
from web.models import User
users = User.query.all()
# Only send email for activated accounts.
- user_emails = [user.email for user in users if user.activation_key == ""]
+ user_emails = [user.email for user in users if user.enabled]
# Postmark has a limit of twenty recipients per message in total.
for i in xrange(0, len(user_emails), 19):
emails.send(to=conf.NOTIFICATION_EMAIL,
@@ -41,8 +42,9 @@ def new_account_notification(user):
"""
Account creation notification.
"""
+ token = generate_confirmation_token(user.email)
plaintext = """Hello,\n\nYour account has been created. Click on the following link to confirm it:\n%s\n\nSee you,""" % \
- (conf.PLATFORM_URL + 'user/confirm_account/' + user.activation_key)
+ (conf.PLATFORM_URL + 'user/confirm_account/' + token)
emails.send(to=user.email, bcc=conf.NOTIFICATION_EMAIL,
subject="[jarr] Account creation", plaintext=plaintext)
diff --git a/src/web/templates/admin/dashboard.html b/src/web/templates/admin/dashboard.html
index 22e82349..57b20bb5 100644
--- a/src/web/templates/admin/dashboard.html
+++ b/src/web/templates/admin/dashboard.html
@@ -18,7 +18,7 @@
</thead>
<tbody>
{% for user in users|sort(attribute="last_seen")|reverse %}
- <tr {% if user.activation_key != "" %}class="warning"{% endif %}>
+ <tr {% if not user.enabled %}class="warning"{% endif %}>
<td>{{ loop.index }}</td>
<td>{{ user.nickname }}{% if user.id == current_user.id %} (It's you!){% endif %}</td>
<td><a href="mailto:{{ user.email }}">{{ user.email }}</a></td>
@@ -28,7 +28,7 @@
<a href="{{ url_for("admin.user_form", user_id=user.id) }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this user') }}"></i></a>
{% if user.id != current_user.id %}
<a href="{{ url_for("admin.toggle_user", user_id=user.id) }}">
- {% if user.activation_key == "" %}
+ {% if user.enabled %}
<i class="glyphicon glyphicon-ban-circle" title="{{ _("Disable this account") }}"></i>
{% else %}
<i class="glyphicon glyphicon-ok-circle" title="{{ _("Enable this account") }}"></i>
diff --git a/src/web/views/admin.py b/src/web/views/admin.py
index 30758f63..832c134d 100644
--- a/src/web/views/admin.py
+++ b/src/web/views/admin.py
@@ -83,13 +83,13 @@ def process_user_form(user_id=None):
flash(gettext('User %(nick)s successfully updated',
nick=user.nickname), 'success')
else:
- # Create a new user
+ # Create a new user (by the admin)
user = user_contr.create(nickname=form.nickname.data,
email=form.email.data,
password=form.password.data,
roles=[role_user],
refresh_rate=form.refresh_rate.data,
- activation_key="")
+ enabled=True)
flash(gettext('User %(nick)s successfully created',
nick=user.nickname), 'success')
return redirect(url_for('admin.user_form', user_id=user.id))
@@ -144,12 +144,11 @@ def toggle_user(user_id=None):
flash(gettext('This user does not exist.'), 'danger')
return redirect(url_for('admin.dashboard'))
- if user.activation_key != "":
-
+ if not user.enabled:
# Send the confirmation email
try:
notifications.new_account_activation(user)
- user_contr.unset_activation_key(user.id)
+ user_contr.update({'id': user.id}, {'enabled': True})
message = gettext('Account of the user %(nick)s successfully '
'activated.', nick=user.nickname)
except Exception as error:
@@ -158,7 +157,7 @@ def toggle_user(user_id=None):
return redirect(url_for('admin.dashboard'))
else:
- user_contr.set_activation_key(user.id)
+ user_contr.update({'id': user.id}, {'enabled': False})
message = gettext('Account of the user %(nick)s successfully disabled',
nick=user.nickname)
flash(message, 'success')
diff --git a/src/web/views/api/common.py b/src/web/views/api/common.py
index 3476cad9..c155a254 100644
--- a/src/web/views/api/common.py
+++ b/src/web/views/api/common.py
@@ -54,8 +54,7 @@ def authenticate(func):
if auth is not None:
user = User.query.filter(
User.nickname == auth.username).first()
- if user and user.check_password(auth.password) \
- and user.activation_key == "":
+ if user and user.check_password(auth.password) and user.enabled:
g.user = user
logged_in = True
if logged_in:
diff --git a/src/web/views/user.py b/src/web/views/user.py
index 754d3b9a..0f9fe612 100644
--- a/src/web/views/user.py
+++ b/src/web/views/user.py
@@ -7,6 +7,7 @@ from flask.ext.login import login_required
import conf
from web import utils, notifications
+from web.lib.user_utils import confirm_token
from web.controllers import (UserController, FeedController, ArticleController)
from web.forms import ProfileForm, RecoverPasswordForm
@@ -102,16 +103,17 @@ def delete_account():
return redirect(url_for('login'))
-@user_bp.route('/confirm_account/<string:activation_key>', methods=['GET'])
-def confirm_account(activation_key=None):
+@user_bp.route('/confirm_account/<string:token>', methods=['GET'])
+def confirm_account(token=None):
"""
Confirm the account of a user.
"""
user_contr = UserController()
- if activation_key != "":
- user = user_contr.read(activation_key=activation_key).first()
+ if token != "":
+ email = confirm_token(token, expiration=3600)
+ user = user_contr.read(email=email).first()
if user is not None:
- user_contr.update({'id': user.id}, {'activation_key': ''})
+ user_contr.update({'id': user.id}, {'enabled': True})
flash(gettext('Your account has been confirmed.'), 'success')
else:
flash(gettext('Impossible to confirm this account.'), 'danger')
bgstack15