log in
This commit is contained in:
		@@ -1,8 +1,12 @@
 | 
			
		||||
from flask import Flask
 | 
			
		||||
from flask import Flask, session
 | 
			
		||||
from dotenv import load_dotenv
 | 
			
		||||
from .models import Avatars, Users
 | 
			
		||||
from .auth import digest
 | 
			
		||||
from .constants import PermissionLevel
 | 
			
		||||
from .routes.users import is_logged_in, get_active_user
 | 
			
		||||
from .constants import (
 | 
			
		||||
    PermissionLevel,
 | 
			
		||||
    InfoboxKind, InfoboxIcons, InfoboxHTMLClass
 | 
			
		||||
    )
 | 
			
		||||
import os
 | 
			
		||||
import time
 | 
			
		||||
import secrets
 | 
			
		||||
@@ -64,6 +68,22 @@ def create_app():
 | 
			
		||||
        create_deleted_user()
 | 
			
		||||
 | 
			
		||||
    from app.routes.app import bp as app_bp
 | 
			
		||||
    from app.routes.topics import bp as topics_bp
 | 
			
		||||
    from app.routes.users import bp as users_bp
 | 
			
		||||
    app.register_blueprint(app_bp)
 | 
			
		||||
    app.register_blueprint(topics_bp)
 | 
			
		||||
    app.register_blueprint(users_bp)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @app.context_processor
 | 
			
		||||
    def inject_constants():
 | 
			
		||||
        return {
 | 
			
		||||
            "InfoboxIcons": InfoboxIcons,
 | 
			
		||||
            "InfoboxHTMLClass": InfoboxHTMLClass,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    @app.context_processor
 | 
			
		||||
    def inject_auth():
 | 
			
		||||
        return {"is_logged_in": is_logged_in, "get_active_user": get_active_user}
 | 
			
		||||
 | 
			
		||||
    return app
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
from enum import Enum
 | 
			
		||||
from enum import Enum, IntEnum
 | 
			
		||||
 | 
			
		||||
class PermissionLevel(Enum):
 | 
			
		||||
    GUEST = 0
 | 
			
		||||
@@ -6,3 +6,23 @@ class PermissionLevel(Enum):
 | 
			
		||||
    MODERATOR = 2
 | 
			
		||||
    SYSTEM = 3
 | 
			
		||||
    ADMIN = 4
 | 
			
		||||
 | 
			
		||||
class InfoboxKind(IntEnum):
 | 
			
		||||
    INFO = 0
 | 
			
		||||
    LOCK = 1
 | 
			
		||||
    WARN = 2
 | 
			
		||||
    ERROR = 3
 | 
			
		||||
 | 
			
		||||
InfoboxIcons = {
 | 
			
		||||
    InfoboxKind.INFO: "/static/misc/info.svg",
 | 
			
		||||
    InfoboxKind.LOCK: "/static/misc/lock.svg",
 | 
			
		||||
    InfoboxKind.WARN: "/static/misc/warn.svg",
 | 
			
		||||
    InfoboxKind.ERROR: "/static/misc/error.svg",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
InfoboxHTMLClass = {
 | 
			
		||||
    InfoboxKind.INFO: "",
 | 
			
		||||
    InfoboxKind.LOCK: "warn",
 | 
			
		||||
    InfoboxKind.WARN: "warn",
 | 
			
		||||
    InfoboxKind.ERROR: "critical",
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,24 @@
 | 
			
		||||
from .db import Model
 | 
			
		||||
from .constants import PermissionLevel
 | 
			
		||||
 | 
			
		||||
class Users(Model):
 | 
			
		||||
    table = "users"
 | 
			
		||||
 | 
			
		||||
    def is_guest(self):
 | 
			
		||||
        return self.permission == PermissionLevel.GUEST.value
 | 
			
		||||
 | 
			
		||||
    def is_mod(self):
 | 
			
		||||
        return self.permission >= PermissionLevel.MODERATOR.value
 | 
			
		||||
 | 
			
		||||
    def is_admin(self):
 | 
			
		||||
        return self.permission == PermissionLevel.ADMIN.value
 | 
			
		||||
 | 
			
		||||
    def is_system(self):
 | 
			
		||||
        return self.permission == PermissionLevel.SYSTEM.value
 | 
			
		||||
 | 
			
		||||
    def is_default_avatar(self):
 | 
			
		||||
        return self.avatar_id == 1
 | 
			
		||||
 | 
			
		||||
class Topics(Model):
 | 
			
		||||
    table = "topics"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								app/routes/topics.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/routes/topics.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
from flask import Blueprint, render_template
 | 
			
		||||
from ..models import Users
 | 
			
		||||
 | 
			
		||||
bp = Blueprint("topics", __name__, url_prefix = "/topics/")
 | 
			
		||||
 | 
			
		||||
@bp.get("/")
 | 
			
		||||
def all_topics():
 | 
			
		||||
    admin = Users.find({"id": 1})
 | 
			
		||||
    return render_template("topics/topics.html", admin = admin)
 | 
			
		||||
							
								
								
									
										77
									
								
								app/routes/users.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								app/routes/users.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
from flask import (
 | 
			
		||||
    Blueprint, render_template, request, redirect, url_for, flash, session
 | 
			
		||||
    )
 | 
			
		||||
from ..models import Users, Sessions
 | 
			
		||||
from ..constants import InfoboxKind
 | 
			
		||||
from ..auth import digest, verify
 | 
			
		||||
import secrets
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
bp = Blueprint("users", __name__, url_prefix = "/users/")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_logged_in():
 | 
			
		||||
    return "pyrom_session_key" in session
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_active_user():
 | 
			
		||||
    if not is_logged_in():
 | 
			
		||||
        return None
 | 
			
		||||
    sess = Sessions.find({"key": session["pyrom_session_key"]})
 | 
			
		||||
    if not sess:
 | 
			
		||||
        return None
 | 
			
		||||
    return Users.find({"id": sess.user_id})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.get("/log_in")
 | 
			
		||||
def log_in():
 | 
			
		||||
    return render_template("users/log_in.html")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.post("/log_in")
 | 
			
		||||
def log_in_post():
 | 
			
		||||
    target_user = Users.find({
 | 
			
		||||
        "username": request.form['username']
 | 
			
		||||
    })
 | 
			
		||||
    if not target_user:
 | 
			
		||||
        flash("Incorrect username or password.", InfoboxKind.ERROR)
 | 
			
		||||
        return redirect(url_for("users.log_in"))
 | 
			
		||||
 | 
			
		||||
    if not verify(target_user.password_hash, request.form['password']):
 | 
			
		||||
        flash("Incorrect username or password.", InfoboxKind.ERROR)
 | 
			
		||||
        return redirect(url_for("users.log_in"))
 | 
			
		||||
 | 
			
		||||
    session_obj = Sessions.create({
 | 
			
		||||
        "key": secrets.token_hex(16),
 | 
			
		||||
        "user_id": target_user.id,
 | 
			
		||||
        "expires_at": int(time.time()) + 30 * 24 * 60 * 60,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    session['pyrom_session_key'] = session_obj.key
 | 
			
		||||
    flash("Logged in!", InfoboxKind.INFO)
 | 
			
		||||
    return redirect(url_for("users.log_in"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.get("/sign_up")
 | 
			
		||||
def sign_up():
 | 
			
		||||
    return "not yet"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.get("/<username>")
 | 
			
		||||
def page(username):
 | 
			
		||||
    return "stub"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.get("/<username>/setings")
 | 
			
		||||
def settings(username):
 | 
			
		||||
    return "stub"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.get("/<username>/inbox")
 | 
			
		||||
def inbox(username):
 | 
			
		||||
    return "stub"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.get("/list")
 | 
			
		||||
def user_list():
 | 
			
		||||
    return "stub"
 | 
			
		||||
							
								
								
									
										28
									
								
								app/templates/base.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/templates/base.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
{% from 'common/infobox.html' import infobox with context %}
 | 
			
		||||
<!DOCTYPE HTML>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
  <meta charset="UTF-8">
 | 
			
		||||
  {% if title %}
 | 
			
		||||
    <title>Porom - {% block title %}{% endblock %}</title>
 | 
			
		||||
  {% else %}
 | 
			
		||||
    <title>Porom</title>
 | 
			
		||||
  {% endif %}
 | 
			
		||||
  <link rel="stylesheet" href="/static/style.css">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
  {% include 'common/topnav.html' %}
 | 
			
		||||
  {% with messages = get_flashed_messages(with_categories=true) %}
 | 
			
		||||
    {% if messages %}
 | 
			
		||||
      {% for category, message in messages %}
 | 
			
		||||
        {{ infobox(message, category) }}
 | 
			
		||||
      {% endfor %}
 | 
			
		||||
    {% endif %}
 | 
			
		||||
  {% endwith %}
 | 
			
		||||
  {% block content %}{% endblock %}
 | 
			
		||||
  <footer class="darkbg">
 | 
			
		||||
    <span>Porom commit</span>
 | 
			
		||||
  </footer>
 | 
			
		||||
  <script src="/static/js/copy-code.js"></script>
 | 
			
		||||
  <script src="/static/js/ui.js"></script>
 | 
			
		||||
</body>
 | 
			
		||||
							
								
								
									
										10
									
								
								app/templates/common/infobox.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/templates/common/infobox.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
{% macro infobox(message, kind=InfoboxKind.INFO) %}
 | 
			
		||||
<div class="{{ "infobox " + InfoboxHTMLClass[kind] }}">
 | 
			
		||||
  <span>
 | 
			
		||||
  <div class="infobox-icon-container">
 | 
			
		||||
    <img src="{{ InfoboxIcons[kind] }}">
 | 
			
		||||
  </div>
 | 
			
		||||
  {{ message }}
 | 
			
		||||
  </span>
 | 
			
		||||
</div>
 | 
			
		||||
{% endmacro %}
 | 
			
		||||
							
								
								
									
										22
									
								
								app/templates/common/topnav.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/templates/common/topnav.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
<nav id="topnav">
 | 
			
		||||
  <span>
 | 
			
		||||
    <a class="site-title" href="{{url_for('topics.all_topics')}}">Porom</a>
 | 
			
		||||
  </span>
 | 
			
		||||
  <span>
 | 
			
		||||
  {% if not is_logged_in() %}
 | 
			
		||||
    Welcome, guest. Please <a href="{{url_for('users.sign_up')}}">sign up</a> or <a href="{{url_for('users.log_in')}}">log in</a>
 | 
			
		||||
  {% else %}
 | 
			
		||||
    {% with user = get_active_user() %}
 | 
			
		||||
      Welcome, <a href="{{ url_for("users.page", username = user.username) }}">{{user.username}}</a>
 | 
			
		||||
      •
 | 
			
		||||
      <a href="{{ url_for("users.settings", username = user.username) }}">Settings</a>
 | 
			
		||||
      •
 | 
			
		||||
      <a href="{{ url_for("users.inbox", username = user.username) }}">Inbox</a>
 | 
			
		||||
      {% if user.is_mod() %}
 | 
			
		||||
        •
 | 
			
		||||
        <a href="{{ url_for("users.user_list") }}">User list</a>
 | 
			
		||||
      {% endif %}
 | 
			
		||||
    {% endwith %}
 | 
			
		||||
  {% endif %}
 | 
			
		||||
  </span>
 | 
			
		||||
</nav>
 | 
			
		||||
							
								
								
									
										7
									
								
								app/templates/topics/topics.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								app/templates/topics/topics.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
{% extends "base.html" %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
<nav class="darkbg">
 | 
			
		||||
  <h1 class="thread-title">All topics</h1>
 | 
			
		||||
  <span>{{admin.is_default_avatar()}}</span>
 | 
			
		||||
</nav>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										14
									
								
								app/templates/users/log_in.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/templates/users/log_in.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% block title %}Log in{% endblock %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
<div class="darkbg login-container">
 | 
			
		||||
  <h1>Log In</h1>
 | 
			
		||||
  <form method="post">
 | 
			
		||||
    <label for="username">Username</label><br>
 | 
			
		||||
    <input type="text" id="username" name="username" required autocomplete="username"><br>
 | 
			
		||||
    <label for="password">Password</label><br>
 | 
			
		||||
    <input type="password" id="password" name="password" required autocomplete="current-password"><br>
 | 
			
		||||
    <input type="submit" value="Log in">
 | 
			
		||||
  </form>
 | 
			
		||||
</div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										0
									
								
								app/templates/users/sign_up.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								app/templates/users/sign_up.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										5
									
								
								data/static/misc/error.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								data/static/misc/error.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<svg width="60px" height="60px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path d="M18.364 5.63604C19.9926 7.26472 21 9.51472 21 12C21 16.9706 16.9706 21 12 21C9.51472 21 7.26472 19.9926 5.63604 18.364M18.364 5.63604C16.7353 4.00736 14.4853 3 12 3C7.02944 3 3 7.02944 3 12C3 14.4853 4.00736 16.7353 5.63604 18.364M18.364 5.63604L5.63604 18.364" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
 | 
			
		||||
</svg>
 | 
			
		||||
<!-- https://www.figma.com/community/file/1136337054881623512/iconcino-v2-0-0-free-icons-cc0-1-0-license -->
 | 
			
		||||
| 
		 After Width: | Height: | Size: 609 B  | 
							
								
								
									
										5
									
								
								data/static/misc/image.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								data/static/misc/image.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<svg width="24px" height="24px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path d="M4 17L7.58959 13.7694C8.38025 13.0578 9.58958 13.0896 10.3417 13.8417L11.5 15L15.0858 11.4142C15.8668 10.6332 17.1332 10.6332 17.9142 11.4142L20 13.5M11 9C11 9.55228 10.5523 10 10 10C9.44772 10 9 9.55228 9 9C9 8.44772 9.44772 8 10 8C10.5523 8 11 8.44772 11 9ZM6 20H18C19.1046 20 20 19.1046 20 18V6C20 4.89543 19.1046 4 18 4H6C4.89543 4 4 4.89543 4 6V18C4 19.1046 4.89543 20 6 20Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
 | 
			
		||||
</svg>
 | 
			
		||||
<!-- https://www.figma.com/community/file/1136337054881623512/iconcino-v2-0-0-free-icons-cc0-1-0-license -->
 | 
			
		||||
| 
		 After Width: | Height: | Size: 728 B  | 
							
								
								
									
										5
									
								
								data/static/misc/info.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								data/static/misc/info.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<svg width="60px" height="60px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path d="M12 8V8.5M12 12V16M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
 | 
			
		||||
</svg>
 | 
			
		||||
<!-- https://www.figma.com/community/file/1136337054881623512/iconcino-v2-0-0-free-icons-cc0-1-0-license -->
 | 
			
		||||
| 
		 After Width: | Height: | Size: 480 B  | 
							
								
								
									
										5
									
								
								data/static/misc/lock.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								data/static/misc/lock.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<svg width="60px" height="60px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path d="M12 14V16M8 9V6C8 3.79086 9.79086 2 12 2C14.2091 2 16 3.79086 16 6V9M7 21H17C18.1046 21 19 20.1046 19 19V11C19 9.89543 18.1046 9 17 9H7C5.89543 9 5 9.89543 5 11V19C5 20.1046 5.89543 21 7 21Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
 | 
			
		||||
</svg>
 | 
			
		||||
<!-- https://www.figma.com/community/file/1136337054881623512/iconcino-v2-0-0-free-icons-cc0-1-0-license -->
 | 
			
		||||
| 
		 After Width: | Height: | Size: 539 B  | 
							
								
								
									
										5
									
								
								data/static/misc/sticky.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								data/static/misc/sticky.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<svg width="24px" height="24px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path d="M13 20H6C4.89543 20 4 19.1046 4 18V6C4 4.89543 4.89543 4 6 4H18C19.1046 4 20 4.89543 20 6V13M13 20L20 13M13 20V14C13 13.4477 13.4477 13 14 13H20" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
 | 
			
		||||
</svg>
 | 
			
		||||
<!-- https://www.figma.com/community/file/1136337054881623512/iconcino-v2-0-0-free-icons-cc0-1-0-license -->
 | 
			
		||||
| 
		 After Width: | Height: | Size: 498 B  | 
							
								
								
									
										5
									
								
								data/static/misc/warn.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								data/static/misc/warn.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<svg width="60px" height="60px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path d="M12 15H12.01M12 12V9M4.98207 19H19.0179C20.5615 19 21.5233 17.3256 20.7455 15.9923L13.7276 3.96153C12.9558 2.63852 11.0442 2.63852 10.2724 3.96153L3.25452 15.9923C2.47675 17.3256 3.43849 19 4.98207 19Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
 | 
			
		||||
</svg>
 | 
			
		||||
<!-- https://www.figma.com/community/file/1136337054881623512/iconcino-v2-0-0-free-icons-cc0-1-0-license -->
 | 
			
		||||
| 
		 After Width: | Height: | Size: 550 B  | 
		Reference in New Issue
	
	Block a user