From 0bb0b3e688c7a1ac474139caf11024a68623eceb Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Mon, 13 Feb 2023 10:16:33 +0100 Subject: [PATCH] all: drop support for user invites --- api/graph/schema.graphqls | 3 +- api/graph/schema.resolvers.go | 38 ++----------- config.example.ini | 4 -- metasrht-manageuser | 1 - .../versions/2c272378490d_drop_invites.py | 36 ++++++++++++ metasrht/app.py | 2 - metasrht/auth/pam.py | 1 - metasrht/blueprints/auth.py | 36 +++--------- metasrht/blueprints/invites.py | 40 ------------- metasrht/templates/invite-link-generated.html | 22 ------- metasrht/templates/invite.html | 57 ------------------- metasrht/templates/register-step2.html | 9 +-- metasrht/templates/register.html | 5 +- metasrht/templates/tabs.html | 5 -- metasrht/types/__init__.py | 1 - metasrht/types/invite.py | 28 --------- metasrht/types/user.py | 2 - schema.sql | 10 ---- 18 files changed, 51 insertions(+), 249 deletions(-) create mode 100644 metasrht/alembic/versions/2c272378490d_drop_invites.py delete mode 100644 metasrht/blueprints/invites.py delete mode 100644 metasrht/templates/invite-link-generated.html delete mode 100644 metasrht/templates/invite.html delete mode 100644 metasrht/types/invite.py diff --git a/api/graph/schema.graphqls b/api/graph/schema.graphqls index 9e65c37..8a5de68 100644 --- a/api/graph/schema.graphqls +++ b/api/graph/schema.graphqls @@ -476,8 +476,7 @@ type Mutation { registerAccount(email: String!, username: String!, password: String!, - pgpKey: String, - invite: String): User @anoninternal + pgpKey: String): User @anoninternal """ Registers an OAuth client. Only OAuth 2.0 confidental clients are diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go index 13ad024..34e5cfe 100644 --- a/api/graph/schema.resolvers.go +++ b/api/graph/schema.resolvers.go @@ -17,7 +17,6 @@ import ( "log" "net/url" "sort" - "strconv" "strings" "time" @@ -614,7 +613,7 @@ func (r *mutationResolver) DeleteWebhook(ctx context.Context, id int) (model.Web } // RegisterAccount is the resolver for the registerAccount field. -func (r *mutationResolver) RegisterAccount(ctx context.Context, email string, username string, password string, pgpKey *string, invite *string) (*model.User, error) { +func (r *mutationResolver) RegisterAccount(ctx context.Context, email string, username string, password string, pgpKey *string) (*model.User, error) { // Note: this resolver is used with anonymous internal auth, so most of the // fields in auth.ForContext(ctx) are invalid. valid := valid.New(ctx) @@ -688,16 +687,6 @@ func (r *mutationResolver) RegisterAccount(ctx context.Context, email string, us return nil, nil } - invites := 0 - inv, ok := conf.Get("meta.sr.ht::settings", "user-invites") - if ok { - var err error - invites, err = strconv.Atoi(inv) - if err != nil { - panic(err) - } - } - pwhash, err := bcrypt.GenerateFromPassword( []byte(password), bcrypt.DefaultCost) if err != nil { @@ -729,14 +718,14 @@ func (r *mutationResolver) RegisterAccount(ctx context.Context, email string, us row = tx.QueryRowContext(ctx, ` INSERT INTO "user" ( created, updated, username, email, user_type, password, - confirmation_hash, invites + confirmation_hash ) VALUES ( NOW() at time zone 'utc', NOW() at time zone 'utc', - $1, $2, 'unconfirmed', $3, $4, $5 + $1, $2, 'unconfirmed', $3, $4 ) RETURNING id, created, updated, username, email, user_type; - `, username, email, string(pwhash), confirmation, invites) + `, username, email, string(pwhash), confirmation) if err := row.Scan(&user.ID, &user.Created, &user.Updated, &user.Username, &user.Email, &user.UserTypeRaw); err != nil { @@ -757,25 +746,6 @@ func (r *mutationResolver) RegisterAccount(ctx context.Context, email string, us return err } - if invite != nil { - row = tx.QueryRowContext(ctx, ` - UPDATE invite - SET recipient_id = $1 - WHERE invite_hash = $2 AND recipient_id IS NULL - RETURNING id; - `, user.ID, *invite) - - var id int - if err := row.Scan(&id); err != nil { - if err == sql.ErrNoRows { - valid.Error("The invite code you've used is invalid or expired."). - WithField("invite") - return errors.New("placeholder") - } - return err - } - } - addr := server.RemoteAddr(ctx) _, err = tx.ExecContext(ctx, ` INSERT INTO audit_log_entry ( diff --git a/config.example.ini b/config.example.ini index 30f5d52..5a12fdc 100644 --- a/config.example.ini +++ b/config.example.ini @@ -157,10 +157,6 @@ registration=no # # Where to redirect new users upon registration onboarding-redirect=http://example.org -# -# How many invites each user is issued upon registration (only applicable if -# open registration is disabled) -user-invites=5 [meta.sr.ht::aliases] # diff --git a/metasrht-manageuser b/metasrht-manageuser index 9fde257..7487d8b 100755 --- a/metasrht-manageuser +++ b/metasrht-manageuser @@ -130,7 +130,6 @@ if __name__ == '__main__': else: validate_user(username, email) user = User(username) - user.invites = cfg("meta.sr.ht::settings", "user-invites", default=0) db.session.add(user) if set_password: diff --git a/metasrht/alembic/versions/2c272378490d_drop_invites.py b/metasrht/alembic/versions/2c272378490d_drop_invites.py new file mode 100644 index 0000000..6c032a9 --- /dev/null +++ b/metasrht/alembic/versions/2c272378490d_drop_invites.py @@ -0,0 +1,36 @@ +"""Drop invites + +Revision ID: 2c272378490d +Revises: 20ca7d8cb982 +Create Date: 2023-02-13 10:09:52.930567 + +""" + +# revision identifiers, used by Alembic. +revision = '2c272378490d' +down_revision = '20ca7d8cb982' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.execute(""" + ALTER TABLE "user" DROP COLUMN invites; + DROP TABLE invite; + """) + + +def downgrade(): + op.execute(""" + ALTER TABLE "user" ADD COLUMN invites integer DEFAULT 0; + + CREATE TABLE invite ( + id serial PRIMARY KEY, + created timestamp without time zone NOT NULL, + updated timestamp without time zone NOT NULL, + invite_hash character varying(128), + sender_id integer, + recipient_id integer + ); + """) diff --git a/metasrht/app.py b/metasrht/app.py index b190875..4f019b7 100644 --- a/metasrht/app.py +++ b/metasrht/app.py @@ -17,7 +17,6 @@ class MetaApp(SrhtFlask): from metasrht.blueprints.api import register_api from metasrht.blueprints.auth import auth - from metasrht.blueprints.invites import invites from metasrht.blueprints.keys import keys from metasrht.blueprints.oauth_exchange import oauth_exchange from metasrht.blueprints.oauth_web import oauth_web @@ -29,7 +28,6 @@ class MetaApp(SrhtFlask): from srht.graphql import gql_blueprint self.register_blueprint(auth) - self.register_blueprint(invites) self.register_blueprint(keys) self.register_blueprint(oauth_exchange) self.register_blueprint(oauth_web) diff --git a/metasrht/auth/pam.py b/metasrht/auth/pam.py index be044bb..132d53c 100644 --- a/metasrht/auth/pam.py +++ b/metasrht/auth/pam.py @@ -104,7 +104,6 @@ class PamAuthMethod(AuthMethod): user = User(username) user.email = f'{username}@{self.domain}' user.password = '' - user.invites = cfg("meta.sr.ht::settings", "user-invites", default=0) user.confirmation_hash = None user.user_type = UserType.active_non_paying diff --git a/metasrht/blueprints/auth.py b/metasrht/blueprints/auth.py index 3c34de3..392ccd6 100644 --- a/metasrht/blueprints/auth.py +++ b/metasrht/blueprints/auth.py @@ -10,7 +10,7 @@ from metasrht.auth_validation import validate_password from metasrht.blueprints.security import metrics as security_metrics from metasrht.email import send_email_notification from metasrht.totp import totp -from metasrht.types import User, UserType, Invite +from metasrht.types import User, UserType from metasrht.types import UserAuthFactor, FactorType, PGPKey from metasrht.webhooks import UserWebhook from prometheus_client import Counter @@ -117,62 +117,44 @@ def register(): return redirect(url_for("auth.register_step2_GET")) return render_template("register.html", site_key=site_key_id) -@auth.route("/register/") -def register_invite(invite): - if current_user: - return redirect("/") - if is_external_auth(): - return render_template("register.html") - return render_template("register.html", site_key=site_key_id, invite=invite) - @auth.route("/register", methods=["POST"]) def register_POST(): is_open = allow_registration() valid = Validation(request) payment = valid.require("payment") - invite = valid.optional("invite") if not valid.ok: abort(400) payment = payment == "yes" - - if invite: - session["invite"] = invite session["payment"] = payment return redirect(url_for("auth.register_step2_GET")) @auth.route("/register/step2") def register_step2_GET(): - invite = session.get("invite") payment = session.get("payment", "no") if current_user: return redirect("/") return render_template("register-step2.html", - site_key=site_key_id, invite=invite, payment=payment) + site_key=site_key_id, payment=payment) @auth.route("/register/step2", methods=["POST"]) def register_step2_POST(): if current_user: abort(400) is_open = allow_registration() - session.pop("invite", None) payment = session.get("payment", False) valid = Validation(request) username = valid.require("username", friendly_name="Username") email = valid.require("email", friendly_name="Email address") password = valid.require("password", friendly_name="Password") - invite = valid.optional("invite", default=None) pgpKey = valid.optional("pgpKey", default=None) - if not invite: - invite = None if not pgpKey: pgpKey = None if not valid.ok: - return render_template("register-step2.html", - is_open=(is_open or invite is not None), + return render_template("register-step2.html", is_open=is_open, site_key=site_key_id, payment=payment, **valid.kwargs), 400 if is_abuse(valid): @@ -180,23 +162,21 @@ def register_step2_POST(): allow_plus_in_email = valid.optional("allow-plus-in-email") if "+" in email and allow_plus_in_email != "yes": - return render_template("register-step2.html", - is_open=(is_open or invite is not None), + return render_template("register-step2.html", is_open=is_open, site_key=site_key_id, payment=payment, **valid.kwargs), 400 resp = exec_gql("meta.sr.ht", """ mutation RegisterAccount($email: String!, $username: String!, - $password: String!, $pgpKey: String, $invite: String) { + $password: String!, $pgpKey: String) { registerAccount(email: $email, username: $username, - password: $password, pgpKey: $pgpKey, invite: $invite) { + password: $password, pgpKey: $pgpKey) { id } } """, valid=valid, user=internal_anon, username=username, - email=email, password=password, pgpKey=pgpKey, invite=invite) + email=email, password=password, pgpKey=pgpKey) if not valid.ok: - return render_template("register-step2.html", - is_open=(is_open or invite is not None), + return render_template("register-step2.html", is_open=is_open, site_key=site_key_id, payment=payment, **valid.kwargs), 400 metrics.meta_registrations.inc() diff --git a/metasrht/blueprints/invites.py b/metasrht/blueprints/invites.py deleted file mode 100644 index d3c1b02..0000000 --- a/metasrht/blueprints/invites.py +++ /dev/null @@ -1,40 +0,0 @@ -from flask import Blueprint, render_template, redirect, abort -from metasrht.types import Invite, UserType -from srht.config import cfg -from srht.database import db -from srht.flask import session -from srht.oauth import current_user, loginrequired - -invites = Blueprint('invites', __name__) - -site_name = cfg("sr.ht", "site-name") -site_root = cfg("meta.sr.ht", "origin") - -@invites.route("/invites") -@loginrequired -def index(): - return render_template("invite.html") - -@invites.route("/invites/gen-invite", methods=["POST"]) -@loginrequired -def gen_invite(): - if current_user.invites == 0 and current_user.user_type != UserType.admin: - abort(401) - invite = Invite() - invite.sender_id = current_user.id - if current_user.invites > 0: - current_user.invites -= 1 - db.session.add(invite) - db.session.commit() - session["invite_link"] = "{}/register/{}".format( - site_root, invite.invite_hash) - return redirect("/invites/generated") - -@invites.route("/invites/generated") -@loginrequired -def view_invite(): - invite_link = session.get("invite_link") - if not invite_link: - return redirect("/invites") - del session["invite_link"] - return render_template("invite-link-generated.html", link=invite_link) diff --git a/metasrht/templates/invite-link-generated.html b/metasrht/templates/invite-link-generated.html deleted file mode 100644 index 7a07c39..0000000 --- a/metasrht/templates/invite-link-generated.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "meta.html" %} -{% block title %} -Invite generated - {{cfg("sr.ht", "site-name")}} meta -{% endblock %} -{% block content %} -
-
-

- This is a one-time use link that can be used to create an account on this - site. -

-
- - {{ link }} - -
- - Continue {{icon("caret-right")}} - -
-
-{% endblock %} diff --git a/metasrht/templates/invite.html b/metasrht/templates/invite.html deleted file mode 100644 index d110e3b..0000000 --- a/metasrht/templates/invite.html +++ /dev/null @@ -1,57 +0,0 @@ -{% extends "meta.html" %} -{% block title %} -Invitations - {{cfg("sr.ht", "site-name")}} meta -{% endblock %} -{% block content %} -
-
-

- You have {{ current_user.invites }} - invite{{ 's' if current_user.invites != 1 else '' }} remaining with which - to invite people to {{ cfg("sr.ht", "site-name") }}. If you need more, - reach out to - {{cfg("sr.ht", "owner-name")}} - <{{cfg("sr.ht", "owner-email")}}> - . -

- {% if current_user.user_type == UserType.admin %} -
- Admins have unlimited invites. -
- {% else %} -
- You may be held accountable for the actions of users you invite. Please - exercise due care when giving them out. -
- {% endif %} - {% if current_user.invites != 0 or current_user.user_type == UserType.admin %} -
- {{csrf_token()}} - -
- {% endif %} - {% if current_user.invites_sent %} -

Invites sent

-
    - {% for invite in current_user.invites_sent %} -
  • - {% if invite.recipient_id %} - {{invite.invite_hash}} - (claimed by ~{{ invite.recipient.username}}) - {% else %} - - {{invite.invite_hash}} - - (unclaimed) - {% endif %} -
  • - {% endfor %} -
- {% endif %} -
-
-{% endblock %} diff --git a/metasrht/templates/register-step2.html b/metasrht/templates/register-step2.html index 77ea28e..dca3e61 100644 --- a/metasrht/templates/register-step2.html +++ b/metasrht/templates/register-step2.html @@ -17,7 +17,7 @@

Registration is disabled because {{cfg("sr.ht", "site-name")}} authentication is managed by a different service. Please contact the system administrator for further information.

-{% elif allow_registration() or invite %} +{% elif allow_registration() %} {% if cfg("meta.sr.ht::billing", "enabled") == "yes" %}
@@ -42,13 +42,6 @@
{{csrf_token()}} - {% if invite %} - -
- You have received a special invitation to join {{cfg("sr.ht", - "site-name")}}. Sign up here! -
- {% endif %}
Registration is disabled because {{cfg("sr.ht", "site-name")}} authentication is managed by a different service. Please contact the system administrator for further information.

-{% elif allow_registration() or invite %} +{% elif allow_registration() %} {{csrf_token()}} - {% if invite %} - - {% endif %}

Register as a contributor

diff --git a/metasrht/templates/tabs.html b/metasrht/templates/tabs.html index 7546cca..2358b7d 100644 --- a/metasrht/templates/tabs.html +++ b/metasrht/templates/tabs.html @@ -28,11 +28,6 @@ {% endif %} {% endif %} -{% if not allow_registration() %} - -{% endif %} {% if current_user.user_type == UserType.admin %}