~fnux/meta.sr.ht

e929d461b76fb7b17963c864192a975562bf23b6 — Timothée Floure 10 months ago 1523a34
Make password reset logic more generic
M metasrht/app.py => metasrht/app.py +2 -1
@@ 1,5 1,5 @@
from flask import session
from metasrht.auth import allow_registration, is_external_auth
from metasrht.auth import allow_registration, is_external_auth, allow_password_reset
from metasrht.oauth import MetaOAuthService, MetaOAuthProvider
from metasrht.types import UserType
from srht.config import cfg


@@ 40,6 40,7 @@ class MetaApp(SrhtFlask):
        self.register_blueprint(gql_blueprint)

        self.jinja_env.globals['allow_registration'] = allow_registration
        self.jinja_env.globals['allow_password_reset'] = allow_password_reset
        self.jinja_env.globals['is_external_auth'] = is_external_auth

        if cfg("meta.sr.ht::billing", "enabled") == "yes":

M metasrht/auth/__init__.py => metasrht/auth/__init__.py +7 -3
@@ 21,17 21,21 @@ _auth_method = _auth_method_types[auth_method]()


def allow_registration() -> bool:
    return not is_external_auth() and \
           cfgb("meta.sr.ht::settings", "registration")
    return (not is_external_auth() and
            cfgb("meta.sr.ht::settings", "registration"))


def is_external_auth() -> bool:
    return auth_method != 'builtin'

def allow_password_reset() -> bool:
    return auth_method == 'builtin'

def user_valid(valid: Validation, user: str, password: str) -> bool:
    return _auth_method.user_valid(valid, user, password)


def prepare_user(user: str) -> User:
    return _auth_method.prepare_user(user)

def set_user_password(user: User, password: str) -> None:
    return _auth_method.set_user_password(user, password)

M metasrht/auth/base.py => metasrht/auth/base.py +4 -0
@@ 1,5 1,6 @@
from typing import Optional

from srht.database import db
from srht.validation import Validation

from metasrht.types.user import User


@@ 18,3 19,6 @@ class AuthMethod:

    def prepare_user(self, username: str) -> User:
        raise NotImplementedError()

    def set_user_password(self, user: User, password: str) -> bool:
        raise NotImplementedError()

M metasrht/auth/builtin.py => metasrht/auth/builtin.py +6 -0
@@ 1,4 1,6 @@
import bcrypt

from srht.database import db
from srht.validation import Validation

from metasrht.auth.base import AuthMethod, get_user


@@ 31,3 33,7 @@ def check_password(password: str, hash: str) -> bool:

def hash_password(password: str) -> str:
    return bcrypt.hashpw(password.encode(), salt=bcrypt.gensalt()).decode()

def set_user_password(self, user: User, password: str) -> None:
    user.password = hash_password(password)
    db.session.commit()

M metasrht/blueprints/auth.py => metasrht/blueprints/auth.py +3 -6
@@ 3,7 3,7 @@ from flask import Blueprint, render_template, abort, request, redirect
from flask import url_for
from metasrht.audit import audit_log
from metasrht.auth import allow_registration, user_valid, prepare_user
from metasrht.auth import is_external_auth
from metasrht.auth import is_external_auth, set_user_password
from metasrht.auth.builtin import hash_password, check_password
from metasrht.auth_validation import validate_password
from metasrht.auth_validation import validate_username, validate_email


@@ 216,6 216,7 @@ def confirm_account(token):
        user.email = user.new_email
        user.new_email = None
        db.session.commit()

        UserWebhook.deliver(UserWebhook.Events.profile_update, user.to_dict(),
                UserWebhook.Subscription.user_id == user.id)
        return redirect(url_for("profile.profile_GET"))


@@ 454,9 455,6 @@ def forgot():

@auth.route("/forgot", methods=["POST"])
def forgot_POST():
    if is_external_auth():
        abort(403)

    valid = Validation(request)
    email = valid.require("email", friendly_name="Email")
    if not valid.ok:


@@ 501,11 499,10 @@ def reset_POST(token):
    validate_password(valid, password)
    if not valid.ok:
        return render_template("reset.html", valid=valid)
    user.password = hash_password(password)
    set_user_password(user, password)
    audit_log("password reset", user=user, email=True,
            subject=f"Your {cfg('sr.ht', 'site-name')} password has been reset",
            email_details="Account password reset")
    db.session.commit()
    login_user(user, set_cookie=True)
    print(f"Reset password: {user.username} ({user.email})")
    metrics.meta_pw_resets.inc()

M metasrht/templates/forgot.html => metasrht/templates/forgot.html +12 -11
@@ 5,17 5,10 @@
{% block content %}
<div class="row">
  <div class="col-md-8">
    <h3>Reset password</h3>
    {% if is_external_auth() %}
    Password reset is disabled because {{cfg("sr.ht", "site-name")}}
    authentication is managed by a different service.
    Please contact the system administrator for further information
    on how to reset your password.
    {% elif done %}
    <p>
      An email has been sent to your account with a link to reset your password.
    </p>
    {% else %}
    <h3>
      Reset password
    </h3>
    {% if allow_password_reset() %}
    <form method="POST" action="/forgot">
      {{csrf_token()}}
      <div class="form-group">


@@ 33,6 26,14 @@
        Continue {{icon('caret-right')}}
      </button>
    </form>
    {% elif done %}
    <p>
      An email has been sent to your account with a link to reset your password.
    </p>
    {% else %}
    <p>Password reset is disabled because sr.ht authentication is managed by a
    different service. Please contact the system administrator for further
    information on how to reset your password.</p>
    {% endif %}
  </div>
</div>

M metasrht/templates/security.html => metasrht/templates/security.html +1 -1
@@ 42,7 42,7 @@
      <h3>Change your password</h3>
      <p>A link to complete the process will be sent to the email on file for
      your account ({{current_user.email}}).</p>
      {% if not is_external_auth() %}
      {% if allow_password_reset() %}
      <form method="POST" action="/forgot">
        {{csrf_token()}}
        <input type="hidden" name="email" value="{{current_user.email}}" />