diff --git a/hiboo/account/login.py b/hiboo/account/login.py index 316189eb545682dca2bf8e06b88bec27b8e01179..590ade9fc4dae9d21f67bb0b814221cca3906dda 100644 --- a/hiboo/account/login.py +++ b/hiboo/account/login.py @@ -2,6 +2,7 @@ from hiboo import models, utils, security from hiboo.account import blueprint, forms from flask_babel import lazy_gettext as _ from flask import session +from authlib.jose import JsonWebToken import datetime import flask_login @@ -32,6 +33,20 @@ def signout(): @blueprint.route("/signup", methods=["GET", "POST"]) def signup(): + if not flask.current_app.config['OPEN_SIGNUP']: + token = flask.request.args.get('token') or flask.abort(403) + key = flask.current_app.config["SECRET_KEY"] + jwt = JsonWebToken(['HS512']) + claims_options = { + 'exp': {'essential': True, 'value': datetime.datetime.now().timestamp()}, + 'aud': {'essential': True, 'value': flask.url_for('.signup')} + } + try: + claims = jwt.decode(token, key, claims_options=claims_options) + claims.validate() + except Exception as e: + flask.flash(_("Invalid or expired signup link"), "danger") + return flask.redirect(flask.url_for(".signin")) form = forms.SignupForm() if form.validate_on_submit(): conflict = models.User.query.filter_by(username=form.username.data).first() @@ -72,4 +87,4 @@ def reset(token_uuid): models.db.session.commit() flask.flash(_("Successfully reset your password"), "success") return flask.redirect(flask.url_for(".signin")) - return flask.render_template("account_reset.html", form=form) \ No newline at end of file + return flask.render_template("account_reset.html", form=form) diff --git a/hiboo/configuration.py b/hiboo/configuration.py index e36dbe777df23e60bcdd034fe57bc4df10f93e3f..a2005a9e3ba66818fc4abea8068d6d15e8436be8 100644 --- a/hiboo/configuration.py +++ b/hiboo/configuration.py @@ -14,6 +14,7 @@ DEFAULT_CONFIG = { 'TEMPLATES_AUTO_RELOAD': False, 'MAIL_DOMAIN': 'tedomum.net', 'WEBSITE_NAME': 'Hiboo', + 'OPEN_SIGNUP': True, 'API_TOKEN': 'changeMe' } diff --git a/hiboo/user/templates/user_list.html b/hiboo/user/templates/user_list.html index 1414649c8f3933b9eae9fbf2be8f1c0a9ababb95..78ac14af3a96e966cb2004a3cee62d9d0f80b479 100644 --- a/hiboo/user/templates/user_list.html +++ b/hiboo/user/templates/user_list.html @@ -24,3 +24,7 @@ </div> </div> {% endblock %} + +{% block actions %} +<a href="{{ url_for(".invite") }}" class="btn btn-primary">{% trans %}Sign-up link{% endtrans %}</a> +{% endblock %} diff --git a/hiboo/user/views.py b/hiboo/user/views.py index 7a814435e2a8578f03e7f8f74fe18185a097691c..5ea587490d8442d3f83cb124c20ea31aeb297c44 100644 --- a/hiboo/user/views.py +++ b/hiboo/user/views.py @@ -1,6 +1,7 @@ from hiboo.user import blueprint, forms from hiboo import models, utils, security from flask_babel import lazy_gettext as _ +from authlib.jose import jwt import datetime import flask @@ -39,4 +40,21 @@ def password_reset(user_uuid): models.db.session.commit() reset_link = flask.url_for("account.reset", token_uuid=token.uuid, _external=True) flask.flash(_("Reset link: {}").format(reset_link), "success") - return flask.redirect(flask.url_for(".details", user_uuid=user.uuid)) \ No newline at end of file + return flask.redirect(flask.url_for(".details", user_uuid=user.uuid)) + + +@blueprint.route("/invite", methods=["GET", "POST"]) +@security.admin_required() +@security.confirmation_required("generate a signup link") +def invite(): + expired = datetime.datetime.now() + datetime.timedelta(days=1) + payload = { + "exp": int(expired.timestamp()), + "aud": flask.url_for('account.signup') + } + header = {"alg": "HS512"} + key = flask.current_app.config["SECRET_KEY"] + token = jwt.encode(header, payload, key) + signup_link = flask.url_for("account.signup", token=token, _external=True) + flask.flash(_("Signup link: {}").format(signup_link), "success") + return flask.redirect(flask.url_for("user.list"))