added login cookie, added change pwd screen
This commit is contained in:
@@ -0,0 +1,99 @@
|
|||||||
|
from flask import Blueprint, render_template, redirect, url_for, request
|
||||||
|
from flask_login import login_required, current_user
|
||||||
|
from models import db
|
||||||
|
from models import User, Item
|
||||||
|
import json
|
||||||
|
from sqlalchemy import desc, text
|
||||||
|
|
||||||
|
api_bp = Blueprint("api", __name__, url_prefix="/api")
|
||||||
|
|
||||||
|
@api_bp.route("/validate_key", methods=["POST"])
|
||||||
|
def validate_key():
|
||||||
|
result={}
|
||||||
|
result['status']=0
|
||||||
|
result['data'] = ''
|
||||||
|
if "key" in request.form:
|
||||||
|
key = request.form["key"]
|
||||||
|
print(key)
|
||||||
|
if key == 'NogNietNodigHier':
|
||||||
|
result['status'] = 1
|
||||||
|
else:
|
||||||
|
result['message'] = 'Unknown Application-Key'
|
||||||
|
return json.dumps(result), 200
|
||||||
|
|
||||||
|
@api_bp.route("/login", methods=["POST"])
|
||||||
|
def login():
|
||||||
|
result={}
|
||||||
|
result['status']=0
|
||||||
|
result['data'] = ''
|
||||||
|
if "user" in request.form and "password" in request.form:
|
||||||
|
user = request.form["user"]
|
||||||
|
password = request.form["password"]
|
||||||
|
print(user)
|
||||||
|
user = User.query.filter_by(name=user).first()
|
||||||
|
if user and user.check_password(password):
|
||||||
|
if not user.is_approved:
|
||||||
|
result['message'] = 'User is pending approval.'
|
||||||
|
else:
|
||||||
|
result['status'] = 1
|
||||||
|
result['data'] = user.as_dict()
|
||||||
|
else:
|
||||||
|
result['message'] = 'User or password unknown'
|
||||||
|
return json.dumps(result), 200
|
||||||
|
|
||||||
|
@api_bp.route("/register", methods=["POST"])
|
||||||
|
def register():
|
||||||
|
result={}
|
||||||
|
result['status']=0
|
||||||
|
result['data'] = ''
|
||||||
|
if "user" in request.form and "password" in request.form:
|
||||||
|
username = request.form["user"]
|
||||||
|
password = request.form["password"]
|
||||||
|
print(username)
|
||||||
|
user = User.query.filter_by(name=username).first()
|
||||||
|
if user:
|
||||||
|
result['message'] = 'Username is not unique. Pick something else please.'
|
||||||
|
else:
|
||||||
|
result['status'] = 2
|
||||||
|
result['message'] = 'Registration Ok. Awaiting approval.'
|
||||||
|
user = User(name=username)
|
||||||
|
user.set_password(password)
|
||||||
|
db.session.add(user)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return json.dumps(result), 200
|
||||||
|
|
||||||
|
@api_bp.route("/load_lists", methods=["POST"])
|
||||||
|
def load_lists():
|
||||||
|
print("Trying to load lists")
|
||||||
|
result={}
|
||||||
|
result['status']=1 # from this call the result is always 1, empty or not
|
||||||
|
result['data'] = ''
|
||||||
|
if "userid" in request.form:
|
||||||
|
userid = int(request.form["userid"])
|
||||||
|
|
||||||
|
print(userid)
|
||||||
|
user = User.query.filter_by(id=userid).first()
|
||||||
|
if user:
|
||||||
|
result['data'] = user.all_my_lists_as_dict()
|
||||||
|
|
||||||
|
return json.dumps(result), 200
|
||||||
|
|
||||||
|
@api_bp.route("/load_items", methods=["POST"])
|
||||||
|
def load_items():
|
||||||
|
print("Trying to load items")
|
||||||
|
result={}
|
||||||
|
result['status']=1 # from this call the result is always 1, empty or not
|
||||||
|
result['data'] = ''
|
||||||
|
if "listid" in request.form:
|
||||||
|
listid = int(request.form["listid"])
|
||||||
|
|
||||||
|
print(listid)
|
||||||
|
items=Item.query.filter_by(listofitems_id=listid, is_suggestion=False).order_by(text("is_checked, category, label")).all()
|
||||||
|
print(items)
|
||||||
|
if items:
|
||||||
|
result['data'] = [ i.as_dict() for i in items]
|
||||||
|
|
||||||
|
print(result)
|
||||||
|
return json.dumps(result), 200
|
||||||
|
|
||||||
@@ -1,14 +1,82 @@
|
|||||||
from flask import Blueprint, render_template, request, redirect, url_for, flash
|
from flask import Blueprint, render_template, request, redirect, url_for, flash, make_response
|
||||||
from flask_login import login_user, logout_user, login_required, current_user
|
from flask_login import login_user, logout_user, login_required, current_user
|
||||||
from models import db
|
from models import db
|
||||||
from models import User
|
from models import User
|
||||||
from log import Log
|
from log import Log
|
||||||
|
import pickle
|
||||||
|
from cryptography.fernet import Fernet
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
auth_bp = Blueprint("auth", __name__)
|
auth_bp = Blueprint("auth", __name__)
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# SECRET KEY (only your program has this)
|
||||||
|
# =====================================================
|
||||||
|
# Generate once using: Fernet.generate_key()
|
||||||
|
_SECRET_KEY = b'YFK7QCyTzhyLO4vqrnRxvDAI5uu8mXEYrInEjbRoQgs='
|
||||||
|
|
||||||
|
fernet = Fernet(_SECRET_KEY)
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# ENCRYPT
|
||||||
|
# =====================================================
|
||||||
|
def encrypt_object(obj) -> bytes:
|
||||||
|
"""
|
||||||
|
Encrypt any Python object and return encrypted bytes.
|
||||||
|
"""
|
||||||
|
serialized = pickle.dumps(obj)
|
||||||
|
encrypted = fernet.encrypt(serialized)
|
||||||
|
return encrypted
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# DECRYPT
|
||||||
|
# =====================================================
|
||||||
|
def decrypt_object(encrypted_data: bytes):
|
||||||
|
"""
|
||||||
|
Decrypt bytes back into the original Python object.
|
||||||
|
"""
|
||||||
|
decrypted = fernet.decrypt(encrypted_data)
|
||||||
|
obj = pickle.loads(decrypted)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def create_cookie(userid):
|
||||||
|
cookie_data = {
|
||||||
|
"user": userid,
|
||||||
|
"datetime": datetime.datetime.now()
|
||||||
|
}
|
||||||
|
return encrypt_object(cookie_data).decode('utf-8')
|
||||||
|
|
||||||
|
def is_cookie_ok_to_autologin(cookie_content):
|
||||||
|
user = 0
|
||||||
|
autologin = False
|
||||||
|
try:
|
||||||
|
cookie_data = decrypt_object(cookie_content)
|
||||||
|
user = cookie_data["user"]
|
||||||
|
dt = cookie_data["datetime"]
|
||||||
|
lastweek = datetime.datetime.now() - datetime.timedelta(days=7)
|
||||||
|
autologin = dt > lastweek
|
||||||
|
except:
|
||||||
|
Log.info("Cookie error")
|
||||||
|
if autologin:
|
||||||
|
return user
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#. @auth_bp.route("/", methods=["GET", "POST"])
|
#. @auth_bp.route("/", methods=["GET", "POST"])
|
||||||
@auth_bp.route("/login", methods=["GET", "POST"])
|
@auth_bp.route("/login", methods=["GET", "POST"])
|
||||||
def login():
|
def login():
|
||||||
|
username = request.cookies.get('username')
|
||||||
|
|
||||||
|
userid = is_cookie_ok_to_autologin(username)
|
||||||
|
if userid>0:
|
||||||
|
user = User.query.filter_by(id=userid).first()
|
||||||
|
if user and user.is_approved:
|
||||||
|
login_user(user)
|
||||||
|
return redirect(url_for("lists.home"))
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
user = User.query.filter_by(name=request.form["name"]).first()
|
user = User.query.filter_by(name=request.form["name"]).first()
|
||||||
if user and user.check_password(request.form["password"]):
|
if user and user.check_password(request.form["password"]):
|
||||||
@@ -18,7 +86,12 @@ def login():
|
|||||||
return redirect(url_for("lists.home"))
|
return redirect(url_for("lists.home"))
|
||||||
flash("Invalid credentials")
|
flash("Invalid credentials")
|
||||||
Log.info(f"Authorization failure for user '{request.form["name"]}' ")
|
Log.info(f"Authorization failure for user '{request.form["name"]}' ")
|
||||||
return render_template("login.html")
|
|
||||||
|
|
||||||
|
resp = make_response(render_template("login.html"))
|
||||||
|
resp.set_cookie('username', '')
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@auth_bp.route("/register", methods=["GET", "POST"])
|
@auth_bp.route("/register", methods=["GET", "POST"])
|
||||||
def register():
|
def register():
|
||||||
@@ -34,5 +107,27 @@ def register():
|
|||||||
@login_required
|
@login_required
|
||||||
def logout():
|
def logout():
|
||||||
logout_user()
|
logout_user()
|
||||||
return redirect(url_for("auth.login"))
|
resp = make_response(redirect(url_for("auth.login")))
|
||||||
|
resp.set_cookie('username', '')
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
@auth_bp.route("/change_pwd", methods=["GET", "POST"])
|
||||||
|
def change_pwd():
|
||||||
|
if request.method == "POST":
|
||||||
|
pwd_old = request.form["password_old"]
|
||||||
|
pwd_new1 = request.form["password_new1"]
|
||||||
|
pwd_new2 = request.form["password_new2"]
|
||||||
|
print(f" >>{pwd_old} {pwd_new1} {pwd_new2}")
|
||||||
|
print(f"cp: {current_user.name}")
|
||||||
|
if current_user.check_password(pwd_old):
|
||||||
|
if pwd_new1 == pwd_new2:
|
||||||
|
current_user.set_password(pwd_new2)
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for("lists.home"))
|
||||||
|
flash("New Passwords are not identical")
|
||||||
|
flash("Invalid credentials")
|
||||||
|
Log.info(f"Authorization failure for user '{request.form["name"]}' ")
|
||||||
|
|
||||||
|
return render_template(url_for("lists.home"))
|
||||||
|
return render_template("change_pwd.html")
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
from flask import Blueprint, render_template, redirect, url_for, request
|
from flask import Blueprint, render_template, redirect, url_for, request, make_response
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from models import db
|
from models import db
|
||||||
from models import Item
|
from models import Item
|
||||||
from sqlalchemy import desc, text
|
from sqlalchemy import desc, text
|
||||||
import os
|
import os
|
||||||
from log import Log
|
from log import Log
|
||||||
|
from auth import create_cookie
|
||||||
|
|
||||||
items_bp = Blueprint("items", __name__)
|
items_bp = Blueprint("items", __name__)
|
||||||
|
|
||||||
@@ -14,12 +15,14 @@ ICONPATH = "static/categories/" #without starting / and with ending /
|
|||||||
@items_bp.route("/items/<listid>")
|
@items_bp.route("/items/<listid>")
|
||||||
@login_required
|
@login_required
|
||||||
def items(listid):
|
def items(listid):
|
||||||
return render_template("items_show.html", user=current_user,
|
resp = make_response(render_template("items_show.html", user=current_user,
|
||||||
items=Item.query.filter_by(listofitems_id=listid, is_suggestion=False).order_by(text("is_checked, category, label")).all(),
|
items=Item.query.filter_by(listofitems_id=listid, is_suggestion=False).order_by(text("is_checked, category, label")).all(),
|
||||||
button_top_url1 = url_for("items.items_append", listid=listid),
|
button_top_url1 = url_for("items.items_append", listid=listid),
|
||||||
button_top_txt1 = "add",
|
button_top_txt1 = "add",
|
||||||
button_top_url2 = url_for("items.items_clean", listid=listid),
|
button_top_url2 = url_for("items.items_clean", listid=listid),
|
||||||
button_top_txt2 = "clean")
|
button_top_txt2 = "clean"))
|
||||||
|
resp.set_cookie('username', create_cookie(current_user.id))
|
||||||
|
return resp
|
||||||
|
|
||||||
# webservice for updating checked state of one item
|
# webservice for updating checked state of one item
|
||||||
@items_bp.route("/item_update/<itemid>/<itemchecked>")
|
@items_bp.route("/item_update/<itemid>/<itemchecked>")
|
||||||
@@ -124,12 +127,14 @@ def items_append(listid):
|
|||||||
if not i.startswith('.'):
|
if not i.startswith('.'):
|
||||||
icon_names.append(os.path.splitext(i)[0])
|
icon_names.append(os.path.splitext(i)[0])
|
||||||
|
|
||||||
return render_template("items_append.html", user=current_user,
|
resp = make_response(render_template("items_append.html", user=current_user,
|
||||||
listid=listid,
|
listid=listid,
|
||||||
items=Item.query.filter_by(listofitems_id=listid).order_by(Item.label).all(),
|
items=Item.query.filter_by(listofitems_id=listid).order_by(Item.label).all(),
|
||||||
icons = icon_names,
|
icons = icon_names,
|
||||||
iconpath = '/'+ICONPATH,
|
iconpath = '/'+ICONPATH,
|
||||||
logo_url=url_for("items.items", listid=listid))
|
logo_url=url_for("items.items", listid=listid)))
|
||||||
|
resp.set_cookie('username', create_cookie(current_user.id))
|
||||||
|
return resp
|
||||||
|
|
||||||
# user adds items to list
|
# user adds items to list
|
||||||
@items_bp.route("/items_multiappend/<listid>", methods=["GET", "POST"])
|
@items_bp.route("/items_multiappend/<listid>", methods=["GET", "POST"])
|
||||||
@@ -188,9 +193,12 @@ def item_addone(itemid):
|
|||||||
if not i.unit:
|
if not i.unit:
|
||||||
i.unit = 'x'
|
i.unit = 'x'
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return render_template("items_append.html", user=current_user,
|
|
||||||
|
resp = make_response(render_template("items_append.html", user=current_user,
|
||||||
items=Item.query.filter_by(listofitems_id=listid).order_by(Item.label).all(),
|
items=Item.query.filter_by(listofitems_id=listid).order_by(Item.label).all(),
|
||||||
logo_url=url_for("items.items", listid=listid))
|
logo_url=url_for("items.items", listid=listid)))
|
||||||
|
resp.set_cookie('username', create_cookie(current_user.id))
|
||||||
|
return resp
|
||||||
|
|
||||||
# webservice for adding one item with a quantity/unit
|
# webservice for adding one item with a quantity/unit
|
||||||
@items_bp.route("/item_addquantity/<itemid>/<quantity>")
|
@items_bp.route("/item_addquantity/<itemid>/<quantity>")
|
||||||
@@ -206,9 +214,12 @@ def item_addquantity(itemid, quantity):
|
|||||||
else:
|
else:
|
||||||
i.quantity += int(quantity)
|
i.quantity += int(quantity)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return render_template("items_append.html", user=current_user,
|
|
||||||
|
resp = make_response(render_template("items_append.html", user=current_user,
|
||||||
items=Item.query.filter_by(listofitems_id=listid).order_by(Item.label).all(),
|
items=Item.query.filter_by(listofitems_id=listid).order_by(Item.label).all(),
|
||||||
logo_url=url_for("items.items", listid=listid))
|
logo_url=url_for("items.items", listid=listid)))
|
||||||
|
resp.set_cookie('username', create_cookie(current_user.id))
|
||||||
|
return resp
|
||||||
|
|
||||||
# webservice for updating category of one item
|
# webservice for updating category of one item
|
||||||
@items_bp.route("/item_update_category/<itemid>/<category>")
|
@items_bp.route("/item_update_category/<itemid>/<category>")
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from auth import auth_bp
|
|||||||
from admin import admin_bp
|
from admin import admin_bp
|
||||||
from lists import lists_bp
|
from lists import lists_bp
|
||||||
from items import items_bp
|
from items import items_bp
|
||||||
|
from api import api_bp
|
||||||
|
|
||||||
login_manager = LoginManager()
|
login_manager = LoginManager()
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ def create_app():
|
|||||||
app.register_blueprint(admin_bp)
|
app.register_blueprint(admin_bp)
|
||||||
app.register_blueprint(lists_bp)
|
app.register_blueprint(lists_bp)
|
||||||
app.register_blueprint(items_bp)
|
app.register_blueprint(items_bp)
|
||||||
|
app.register_blueprint(api_bp)
|
||||||
|
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
db.create_all()
|
db.create_all()
|
||||||
@@ -36,4 +38,4 @@ def create_app():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
my_app = create_app()
|
my_app = create_app()
|
||||||
# if needed - initialize data
|
# if needed - initialize data
|
||||||
my_app.run(debug=True, host='0.0.0.0', port=5001)
|
my_app.run(debug=True, host='127.0.0.1', port=5001)
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
from flask import Blueprint, render_template, redirect, url_for, request
|
from flask import Blueprint, render_template, redirect, url_for, request, make_response
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from models import db
|
from models import db
|
||||||
from models import User, ListOfItems, Item, Shared
|
from models import User, ListOfItems, Item, Shared
|
||||||
from log import Log
|
from log import Log
|
||||||
|
from auth import create_cookie
|
||||||
|
|
||||||
lists_bp = Blueprint("lists", __name__)
|
lists_bp = Blueprint("lists", __name__)
|
||||||
|
|
||||||
@@ -17,7 +18,11 @@ def not_current_user(current_user_id):
|
|||||||
@lists_bp.route("/")
|
@lists_bp.route("/")
|
||||||
@login_required
|
@login_required
|
||||||
def home():
|
def home():
|
||||||
return render_template("lists_show.html", user=current_user, ilists=current_user.all_my_lists())
|
resp = make_response(render_template("lists_show.html", user=current_user, ilists=current_user.all_my_lists(),
|
||||||
|
button_top_url1 = url_for("auth.change_pwd"),
|
||||||
|
button_top_txt1 = "add",))
|
||||||
|
resp.set_cookie('username', create_cookie(current_user.id))
|
||||||
|
return resp
|
||||||
|
|
||||||
@lists_bp.route("/lists_edit", methods=["GET", "POST"])
|
@lists_bp.route("/lists_edit", methods=["GET", "POST"])
|
||||||
@login_required
|
@login_required
|
||||||
@@ -77,7 +82,9 @@ def edit():
|
|||||||
return home()
|
return home()
|
||||||
|
|
||||||
|
|
||||||
return render_template("lists_edit.html", user=current_user,
|
resp = make_response(render_template("lists_edit.html", user=current_user,
|
||||||
ilists=current_user.my_owned_lists(with_inactive=True),
|
ilists=current_user.my_owned_lists(with_inactive=True),
|
||||||
users=not_current_user(current_user.id),
|
users=not_current_user(current_user.id),
|
||||||
newlist=newlist)
|
newlist=newlist))
|
||||||
|
resp.set_cookie('username', create_cookie(current_user.id))
|
||||||
|
return resp
|
||||||
|
|||||||
@@ -15,7 +15,14 @@ class User(UserMixin, db.Model):
|
|||||||
is_private = db.Column(db.Boolean, default=False)
|
is_private = db.Column(db.Boolean, default=False)
|
||||||
is_guest = db.Column(db.Boolean, default=False)
|
is_guest = db.Column(db.Boolean, default=False)
|
||||||
|
|
||||||
|
def as_dict(self):
|
||||||
|
result = {}
|
||||||
|
for p in ['id','name','is_admin','is_approved','is_private','is_guest']:
|
||||||
|
result[p] = getattr(self, p)
|
||||||
|
return result
|
||||||
|
|
||||||
def set_password(self, password):
|
def set_password(self, password):
|
||||||
|
print(f"u:{self.name} > {password}")
|
||||||
self.password_hash = generate_password_hash(password)
|
self.password_hash = generate_password_hash(password)
|
||||||
|
|
||||||
def check_password(self, password):
|
def check_password(self, password):
|
||||||
@@ -34,11 +41,18 @@ class User(UserMixin, db.Model):
|
|||||||
result.append(l)
|
result.append(l)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def all_my_lists_as_dict(self):
|
||||||
|
result = []
|
||||||
|
for l in self.all_my_lists():
|
||||||
|
result.append(l.as_dict())
|
||||||
|
return result
|
||||||
|
|
||||||
def shares_in_list(self, listofitem_id):
|
def shares_in_list(self, listofitem_id):
|
||||||
shared = Shared.query.filter_by(user_id=self.id, listofitem_id=listofitem_id).first()
|
shared = Shared.query.filter_by(user_id=self.id, listofitem_id=listofitem_id).first()
|
||||||
return True if shared else False
|
return True if shared else False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ListOfItems(db.Model):
|
class ListOfItems(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
updated_at = db.Column(db.TIMESTAMP, default=datetime.now(), onupdate=datetime.now(), nullable=False)
|
updated_at = db.Column(db.TIMESTAMP, default=datetime.now(), onupdate=datetime.now(), nullable=False)
|
||||||
@@ -46,6 +60,15 @@ class ListOfItems(db.Model):
|
|||||||
name = db.Column(db.String(40), unique=True, nullable=False)
|
name = db.Column(db.String(40), unique=True, nullable=False)
|
||||||
is_active = db.Column(db.Boolean, default=False)
|
is_active = db.Column(db.Boolean, default=False)
|
||||||
|
|
||||||
|
def as_dict(self):
|
||||||
|
result = {}
|
||||||
|
for p in ['id','name','owner_user_id','is_active']:
|
||||||
|
result[p] = getattr(self, p)
|
||||||
|
result["pending"] = self.items_left()
|
||||||
|
result["total"] = self.items_total()
|
||||||
|
print(result)
|
||||||
|
return result
|
||||||
|
|
||||||
def items_left(self):
|
def items_left(self):
|
||||||
return len(Item.query.filter_by(listofitems_id=self.id, is_checked=False, is_suggestion=False).all())
|
return len(Item.query.filter_by(listofitems_id=self.id, is_checked=False, is_suggestion=False).all())
|
||||||
|
|
||||||
@@ -65,6 +88,12 @@ class Item(db.Model):
|
|||||||
is_suggestion = db.Column(db.Boolean, default=False)
|
is_suggestion = db.Column(db.Boolean, default=False)
|
||||||
category = db.Column(db.String(20))
|
category = db.Column(db.String(20))
|
||||||
|
|
||||||
|
def as_dict(self):
|
||||||
|
result = {}
|
||||||
|
for p in ['id','listofitems_id','label','quantity','unit','is_checked', 'is_suggestion', 'category']:
|
||||||
|
result[p] = getattr(self, p)
|
||||||
|
return result
|
||||||
|
|
||||||
class Shared(db.Model):
|
class Shared(db.Model):
|
||||||
user_id = db.Column(db.Integer, primary_key=True)
|
user_id = db.Column(db.Integer, primary_key=True)
|
||||||
listofitem_id = db.Column(db.Integer, primary_key=True)
|
listofitem_id = db.Column(db.Integer, primary_key=True)
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="16.482828mm"
|
||||||
|
height="14.68959mm"
|
||||||
|
viewBox="0 0 16.482828 14.68959"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8634"
|
||||||
|
inkscape:version="0.92.1 r15371"
|
||||||
|
sodipodi:docname="password reset.svg">
|
||||||
|
<defs
|
||||||
|
id="defs8628" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="5.6568543"
|
||||||
|
inkscape:cx="-3.8835724"
|
||||||
|
inkscape:cy="1.3449005"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1017"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata8631">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-3.0978698,-4.6612284)">
|
||||||
|
<g
|
||||||
|
transform="matrix(1.2809155,0,0,1.2374188,103.99112,-1219.3652)"
|
||||||
|
id="g4667">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4640"
|
||||||
|
d="m -72.695687,1000.9938 c -1.972631,-0.2257 -3.790181,-1.49518 -4.687846,-3.27432 -0.175341,-0.34752 -0.468403,-1.12381 -0.468403,-1.24073 0,-0.0266 0.375731,-0.048 0.834956,-0.0476 l 0.834965,7.2e-4 0.163557,0.4041 c 0.519646,1.28389 1.615359,2.24396 2.994703,2.62398 0.49589,0.13662 1.63139,0.13421 2.144474,-0.005 0.522244,-0.14124 1.250991,-0.50448 1.653619,-0.82424 0.687488,-0.546 1.31115,-1.49718 1.539,-2.34722 0.146128,-0.54518 0.177544,-1.43846 0.07041,-2.00269 -0.178809,-0.94189 -0.54809,-1.63735 -1.241568,-2.33827 -0.844669,-0.85372 -1.728666,-1.24944 -2.900391,-1.29837 -0.550547,-0.023 -0.767325,-0.006 -1.18485,0.0941 -1.415428,0.33867 -2.632012,1.39775 -3.122259,2.71804 l -0.103341,0.27832 h -0.842765 -0.842766 l 0.03834,-0.16113 c 0.08466,-0.35604 0.444253,-1.16597 0.699534,-1.57555 0.354919,-0.56944 1.084444,-1.33426 1.637166,-1.71635 3.344531,-2.31207 7.975171,-0.83571 9.268415,2.95499 1.2168,3.56666 -1.256634,7.33132 -5.098284,7.75962 -0.62535,0.07 -0.754641,0.069 -1.386656,0 z"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.05859375;stroke-opacity:1" />
|
||||||
|
<g
|
||||||
|
id="g4592-5-0"
|
||||||
|
transform="matrix(0.77578717,0,0,0.61178065,-35.477186,373.83106)">
|
||||||
|
<rect
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.04824853"
|
||||||
|
id="rect3717-9-8-2"
|
||||||
|
width="4.4458065"
|
||||||
|
height="1.1666708"
|
||||||
|
x="-55.705212"
|
||||||
|
y="1017.3687" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke-width:0.93749994"
|
||||||
|
id="path3783-0-6"
|
||||||
|
sodipodi:sides="3"
|
||||||
|
sodipodi:cx="-109.71203"
|
||||||
|
sodipodi:cy="1025.7628"
|
||||||
|
sodipodi:r1="8.7855453"
|
||||||
|
sodipodi:r2="6.0797644"
|
||||||
|
sodipodi:arg1="0.43833656"
|
||||||
|
sodipodi:arg2="1.4855341"
|
||||||
|
inkscape:flatsided="true"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="m -101.75708,1029.4917 -15.16173,1.2959 6.45861,-13.7784 z"
|
||||||
|
inkscape:transform-center-x="-0.034756175"
|
||||||
|
inkscape:transform-center-y="0.58213244"
|
||||||
|
transform="matrix(-0.30321012,-0.03553367,0.03028113,-0.35580483,-117.82847,1375.3296)" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g73"
|
||||||
|
transform="matrix(0.83530458,0,0,1,39.090178,0.46999031)">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path68"
|
||||||
|
d="m -34.487464,10.849979 0.0036,-0.730218 0.02775,-0.1337396 c 0.196159,-0.9452109 0.892942,-1.6292248 1.80383,-1.7707713 0.149269,-0.023195 0.448719,-0.023232 0.597434,-8.14e-5 0.850246,0.1323855 1.532973,0.7494915 1.763181,1.5937089 0.07187,0.2635504 0.07468,0.3040374 0.07468,1.0731994 l 3e-6,0.687422 h -0.324033 -0.324031 v -0.678215 c 0,-0.574261 -0.0024,-0.691927 -0.01605,-0.767667 -0.115574,-0.6441436 -0.601574,-1.1513119 -1.21426,-1.2671477 -0.133279,-0.025199 -0.38006,-0.025995 -0.5063,-0.00165 -0.211637,0.040837 -0.407858,0.1240287 -0.588513,0.2495169 -0.118197,0.082103 -0.323993,0.2988632 -0.400284,0.4216091 -0.103814,0.1670294 -0.170352,0.3247404 -0.218926,0.5189097 l -0.02677,0.106992 -0.0035,0.714169 -0.0035,0.714171 h -0.324083 -0.324083 z"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke-width:0.01040864" />
|
||||||
|
<rect
|
||||||
|
ry="0.44648439"
|
||||||
|
y="10.52671"
|
||||||
|
x="-34.942802"
|
||||||
|
height="3.3545389"
|
||||||
|
width="5.1773005"
|
||||||
|
id="rect12"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke-width:0.25095639" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path39"
|
||||||
|
d="m -32.915205,12.470548 c -6.38e-4,-0.291161 -3.71e-4,-0.547149 6.25e-4,-0.568863 0.0065,-0.140261 0.0869,-0.294293 0.198787,-0.380573 0.09138,-0.07046 0.187004,-0.106399 0.298919,-0.112341 0.248028,-0.01317 0.470974,0.149576 0.540122,0.394269 0.01125,0.03982 0.01296,0.108928 0.0153,0.620851 l 0.0026,0.576041 h -0.527593 -0.527593 z"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke-width:0.00735304" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.9 KiB |
+23
-5
@@ -26,6 +26,15 @@
|
|||||||
height: 56px;
|
height: 56px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<script>
|
||||||
|
function toggleButtons() {
|
||||||
|
if ($('#toprightbuttons').hasClass("d-none")){
|
||||||
|
$('#toprightbuttons').removeClass("d-none")
|
||||||
|
} else {
|
||||||
|
$('#toprightbuttons').addClass("d-none")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="bg-light">
|
<body class="bg-light">
|
||||||
@@ -45,13 +54,22 @@
|
|||||||
|
|
||||||
<div class="d-flex align-items-center gap-2">
|
<div class="d-flex align-items-center gap-2">
|
||||||
{% if current_user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
<span class="text-muted small d-none d-sm-inline">
|
|
||||||
{{ current_user.name }}
|
|
||||||
</span>
|
|
||||||
<a href="{{ url_for('auth.logout') }}"
|
<div class="text-muted small d-none d-sm-inline">
|
||||||
>
|
<a class="btn" onclick="toggleButtons()">{{current_user.name }}</a>
|
||||||
|
</div>
|
||||||
|
<div id="toprightbuttons" class="d-none">
|
||||||
|
<a href="{{ url_for('auth.logout') }}">
|
||||||
<img style="width: 2em; height: 2em" src="/static/logout.svg">
|
<img style="width: 2em; height: 2em" src="/static/logout.svg">
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{{ url_for('auth.change_pwd') }}">
|
||||||
|
<img style="width: 2em; height: 2em" src="/static/password.svg">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('auth.login') }}"
|
<a href="{{ url_for('auth.login') }}"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
<h3 class="text-center mb-4"><img style="width: 3em; height: 3em" src="/static/login.svg"></h3>
|
||||||
|
<form method="post">
|
||||||
|
|
||||||
|
<input class="form-control mb-3" name="password_old" type="password" placeholder="Old Password" required>
|
||||||
|
<input class="form-control mb-3" name="password_new1" type="password" placeholder="New Password" required>
|
||||||
|
<input class="form-control mb-3" name="password_new2" type="password" placeholder="Repeat" required>
|
||||||
|
<input
|
||||||
|
type="image"
|
||||||
|
src="/static/save.svg"
|
||||||
|
alt="Save"
|
||||||
|
style="width: 2em; height: 2em;"
|
||||||
|
>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import pickle
|
||||||
|
from cryptography.fernet import Fernet
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# SECRET KEY (only your program has this)
|
||||||
|
# =====================================================
|
||||||
|
# Generate once using: Fernet.generate_key()
|
||||||
|
_SECRET_KEY = b'YFK7QCyTzhyLO4vqrnRxvDAI5uu8mXEYrInEjbRoQgs='
|
||||||
|
|
||||||
|
fernet = Fernet(_SECRET_KEY)
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# ENCRYPT
|
||||||
|
# =====================================================
|
||||||
|
def encrypt_object(obj) -> bytes:
|
||||||
|
"""
|
||||||
|
Encrypt any Python object and return encrypted bytes.
|
||||||
|
"""
|
||||||
|
serialized = pickle.dumps(obj)
|
||||||
|
encrypted = fernet.encrypt(serialized)
|
||||||
|
return encrypted
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# DECRYPT
|
||||||
|
# =====================================================
|
||||||
|
def decrypt_object(encrypted_data: bytes):
|
||||||
|
"""
|
||||||
|
Decrypt bytes back into the original Python object.
|
||||||
|
"""
|
||||||
|
decrypted = fernet.decrypt(encrypted_data)
|
||||||
|
obj = pickle.loads(decrypted)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# EXAMPLE USAGE
|
||||||
|
# =====================================================
|
||||||
|
if __name__ == "__main__":
|
||||||
|
original_object = {
|
||||||
|
"user": "alice",
|
||||||
|
"permissions": ["read", "write"],
|
||||||
|
"balance": 123.45
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypted = encrypt_object(original_object)
|
||||||
|
print("Encrypted:", str(encrypted))
|
||||||
|
|
||||||
|
decrypted = decrypt_object(encrypted)
|
||||||
|
print("Decrypted:", decrypted)
|
||||||
|
|
||||||
|
|
||||||
|
bytes_data = b'I am a bytes string'
|
||||||
|
|
||||||
|
string_data = bytes_data.decode('utf-8')
|
||||||
|
|
||||||
|
print("Type Before :- ",type(bytes_data))
|
||||||
|
|
||||||
|
print(string_data)
|
||||||
|
|
||||||
|
print("Type After :- ",type(string_data))
|
||||||
Reference in New Issue
Block a user