Added password change form
This commit is contained in:
parent
a9006bf1bc
commit
5536e764e7
8 changed files with 111 additions and 10 deletions
|
|
@ -6,6 +6,10 @@
|
||||||
* Now `/about/` shows Python and Flask versions.
|
* Now `/about/` shows Python and Flask versions.
|
||||||
* Now the error 404 handler returns HTTP 404.
|
* Now the error 404 handler returns HTTP 404.
|
||||||
* Added user followers and following lists, accessible via `/+<username>/followers` and `/+<username>/following` and from the profile info box, linked to the followers/following number.
|
* Added user followers and following lists, accessible via `/+<username>/followers` and `/+<username>/following` and from the profile info box, linked to the followers/following number.
|
||||||
|
* Added the page for permanent deletion of messages. Well, you cannot delete them yet. It's missing a function that checks the CSRF-Token.
|
||||||
|
* Renamed template `private_messages.html` to `feed.html`.
|
||||||
|
* Added the capability to change password.
|
||||||
|
* Corrected a bug into `pwdhash`: it accepted an argument, but pulled data from the form instead of processing it. Now it uses the argument.
|
||||||
* Schema changes: added column `telegram` to `UserProfile` table. To update schema, execute the script `migrate_0_6_to_0_7.py`
|
* Schema changes: added column `telegram` to `UserProfile` table. To update schema, execute the script `migrate_0_6_to_0_7.py`
|
||||||
|
|
||||||
## 0.6.0
|
## 0.6.0
|
||||||
|
|
|
||||||
17
app/templates/change_password.html
Normal file
17
app/templates/change_password.html
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<h2>Change Password</h2>
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
<dl>
|
||||||
|
<dt>Old password:</dt>
|
||||||
|
<dd><input type="password" name="old_password"></dd>
|
||||||
|
<dt>New password:</dt>
|
||||||
|
<dd><input type="password" name="new_password"></dd>
|
||||||
|
<dt>New password, again:</dt>
|
||||||
|
<dd><input type="password" name="confirm_password"></dd>
|
||||||
|
<dd><input type="submit" value="Save"></dd>
|
||||||
|
</dl>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
22
app/templates/confirm_delete.html
Normal file
22
app/templates/confirm_delete.html
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<h2>Confirm Deletion</h2>
|
||||||
|
|
||||||
|
<p>Are you sure you want to permanently delete this post?
|
||||||
|
Neither you nor others will be able to see it;
|
||||||
|
you cannot recover a post after it's deleted.</p>
|
||||||
|
|
||||||
|
<p>If you only want to hide it from the public,
|
||||||
|
you can <a href="/edit/{{ message.id }}">set its privacy</a> to "Only me".</p>
|
||||||
|
|
||||||
|
<p>Here's the content of the message for reference:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>{% include "includes/message.html" %}</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
<input type="submit" value="Delete">
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
<ul class="message-options">
|
<ul class="message-options">
|
||||||
{% if message.user == current_user %}
|
{% if message.user == current_user %}
|
||||||
<li><a href="/edit/{{ message.id }}">Edit or change privacy</a></li>
|
<li><a href="/edit/{{ message.id }}">Edit or change privacy</a></li>
|
||||||
<!--li><a href="/confirm_delete/{{ message.id }}">Delete</a></li-->
|
<li><a href="/delete/{{ message.id }}">Delete permanently</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<!--li><a href="/report/{{ message.id }}">Report</a></li-->
|
<!--li><a href="/report/{{ message.id }}">Report</a></li-->
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
||||||
0
app/templates/user_list.html
Executable file → Normal file
0
app/templates/user_list.html
Executable file → Normal file
34
app/utils.py
34
app/utils.py
|
|
@ -2,10 +2,9 @@
|
||||||
A list of utilities used across modules.
|
A list of utilities used across modules.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import datetime, re, base64, hashlib, string
|
import datetime, re, base64, hashlib, string, sys, json
|
||||||
from .models import User, Notification
|
from .models import User, Notification
|
||||||
from flask import abort, render_template, request, session
|
from flask import abort, render_template, request, session
|
||||||
import sys, json
|
|
||||||
|
|
||||||
_forbidden_extensions = 'com net org txt'.split()
|
_forbidden_extensions = 'com net org txt'.split()
|
||||||
_username_characters = frozenset(string.ascii_letters + string.digits + '_')
|
_username_characters = frozenset(string.ascii_letters + string.digits + '_')
|
||||||
|
|
@ -43,7 +42,7 @@ def int_to_b64(n):
|
||||||
return base64.b64encode(b).lstrip(b'A').decode()
|
return base64.b64encode(b).lstrip(b'A').decode()
|
||||||
|
|
||||||
def pwdhash(s):
|
def pwdhash(s):
|
||||||
return hashlib.md5((request.form['password']).encode('utf-8')).hexdigest()
|
return hashlib.md5(s.encode('utf-8')).hexdigest()
|
||||||
|
|
||||||
def get_object_or_404(model, *expressions):
|
def get_object_or_404(model, *expressions):
|
||||||
try:
|
try:
|
||||||
|
|
@ -160,3 +159,32 @@ def tokenize(characters, table):
|
||||||
break
|
break
|
||||||
pos = mo.end(0)
|
pos = mo.end(0)
|
||||||
return tokens
|
return tokens
|
||||||
|
|
||||||
|
def get_secret_key():
|
||||||
|
from . import app
|
||||||
|
secret_key = app.config['SECRET_KEY']
|
||||||
|
if isinstance(secret_key, str):
|
||||||
|
secret_key = secret_key.encode('utf-8')
|
||||||
|
return secret_key
|
||||||
|
|
||||||
|
def generate_access_token(user):
|
||||||
|
'''
|
||||||
|
Generate access token for public API.
|
||||||
|
'''
|
||||||
|
h = hashlib.sha256(get_secret_key())
|
||||||
|
h.update(b':')
|
||||||
|
h.update(str(user.id).encode('utf-8'))
|
||||||
|
h.update(b':')
|
||||||
|
h.update(str(user.password).encode('utf-8'))
|
||||||
|
return str(user.id) + ':' + h.hexdigest()[:32]
|
||||||
|
|
||||||
|
def check_access_token(user, token):
|
||||||
|
uid, hh = token.split(':')
|
||||||
|
if uid != user.get_id():
|
||||||
|
return False
|
||||||
|
h = hashlib.sha256(get_secret_key())
|
||||||
|
h.update(b':')
|
||||||
|
h.update(str(user.id).encode('utf-8'))
|
||||||
|
h.update(b':')
|
||||||
|
h.update(str(user.password).encode('utf-8'))
|
||||||
|
return h.hexdigest()[:32] == hh
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,7 @@ def private_timeline():
|
||||||
.where((Message.user << user.following())
|
.where((Message.user << user.following())
|
||||||
| (Message.user == user))
|
| (Message.user == user))
|
||||||
.order_by(Message.pub_date.desc()))
|
.order_by(Message.pub_date.desc()))
|
||||||
# TODO change to "feed.html"
|
return object_list('feed.html', messages, 'message_list')
|
||||||
return object_list('private_messages.html', messages, 'message_list')
|
|
||||||
|
|
||||||
@bp.route('/explore/')
|
@bp.route('/explore/')
|
||||||
def public_timeline():
|
def public_timeline():
|
||||||
|
|
@ -246,9 +245,15 @@ def edit(id):
|
||||||
return redirect(url_for('website.user_detail', username=user.username))
|
return redirect(url_for('website.user_detail', username=user.username))
|
||||||
return render_template('edit.html', message=message)
|
return render_template('edit.html', message=message)
|
||||||
|
|
||||||
#@bp.route('/delete/<int:id>', methods=['GET', 'POST'])
|
@bp.route('/delete/<int:id>', methods=['GET', 'POST'])
|
||||||
#def confirm_delete(id):
|
def confirm_delete(id):
|
||||||
# return render_template('confirm_delete.html')
|
user = get_current_user()
|
||||||
|
message = get_object_or_404(Message, Message.id == id)
|
||||||
|
if message.user != user:
|
||||||
|
abort(404)
|
||||||
|
if request.method == 'POST':
|
||||||
|
abort(501, 'CSRF-Token missing.')
|
||||||
|
return render_template('confirm_delete.html', message=message)
|
||||||
|
|
||||||
# Workaround for problems related to invalid data.
|
# Workaround for problems related to invalid data.
|
||||||
# Without that, changes will be lost across requests.
|
# Without that, changes will be lost across requests.
|
||||||
|
|
@ -261,7 +266,8 @@ def profile_checkpoint():
|
||||||
year=int(request.form['year'] if request.form.get('has_year') else '0'),
|
year=int(request.form['year'] if request.form.get('has_year') else '0'),
|
||||||
website=request.form['website'] or None,
|
website=request.form['website'] or None,
|
||||||
instagram=request.form['instagram'] or None,
|
instagram=request.form['instagram'] or None,
|
||||||
facebook=request.form['facebook'] or None
|
facebook=request.form['facebook'] or None,
|
||||||
|
telegram=request.form['telegram'] or None
|
||||||
)
|
)
|
||||||
|
|
||||||
@bp.route('/edit_profile/', methods=['GET', 'POST'])
|
@bp.route('/edit_profile/', methods=['GET', 'POST'])
|
||||||
|
|
@ -299,6 +305,30 @@ def edit_profile():
|
||||||
return redirect(url_for('website.user_detail', username=username))
|
return redirect(url_for('website.user_detail', username=username))
|
||||||
return render_template('edit_profile.html')
|
return render_template('edit_profile.html')
|
||||||
|
|
||||||
|
@bp.route('/change_password/', methods=['GET', 'POST'])
|
||||||
|
def change_password():
|
||||||
|
user = get_current_user()
|
||||||
|
if request.method == 'POST':
|
||||||
|
old_password = request.form['old_password']
|
||||||
|
new_password = request.form['new_password']
|
||||||
|
confirm_password = request.form['confirm_password']
|
||||||
|
errors = False
|
||||||
|
if not new_password:
|
||||||
|
flash('Password cannot be empty')
|
||||||
|
errors = True
|
||||||
|
if new_password != confirm_password:
|
||||||
|
flash('Password mismatch')
|
||||||
|
errors = True
|
||||||
|
if pwdhash(old_password) != user.password:
|
||||||
|
flash('The old password is incorrect')
|
||||||
|
errors = True
|
||||||
|
if not errors:
|
||||||
|
user.update(
|
||||||
|
password=pwdhash(new_password)
|
||||||
|
)
|
||||||
|
return redirect(url_for('website.edit_profile'))
|
||||||
|
return render_template('change_password.html')
|
||||||
|
|
||||||
@bp.route('/notifications/')
|
@bp.route('/notifications/')
|
||||||
@login_required
|
@login_required
|
||||||
def notifications():
|
def notifications():
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue