Skip to content

Commit

Permalink
more fixes after testing
Browse files Browse the repository at this point in the history
  • Loading branch information
amCap1712 committed Sep 28, 2023
1 parent b5f7bd9 commit d7f0a25
Show file tree
Hide file tree
Showing 12 changed files with 96 additions and 25 deletions.
5 changes: 5 additions & 0 deletions admin/sql/create_foreign_keys.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ ALTER TABLE token
REFERENCES supporter (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE SET NULL;

ALTER TABLE supporter
ADD CONSTRAINT supporter_user_id_fkey FOREIGN KEY (user_id)
REFERENCES "user" (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE SET NULL;

ALTER TABLE supporter
ADD CONSTRAINT supporter_tier_id_fkey FOREIGN KEY (tier_id)
REFERENCES tier (id) MATCH SIMPLE
Expand Down
6 changes: 3 additions & 3 deletions admin/sql/create_tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ CREATE TABLE "user" (
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
name TEXT NOT NULL,
password TEXT NOT NULL,
email TEXT,
unconfirmed_email TEXT,
email TEXT NOT NULL,
email_confirmed_at TIMESTAMP WITH TIME ZONE,
member_since TIMESTAMP WITH TIME ZONE,
last_login_at TIMESTAMP WITH TIME ZONE,
last_updated TIMESTAMP WITH TIME ZONE,
Expand All @@ -31,8 +31,8 @@ CREATE TABLE dataset (

CREATE TABLE supporter (
id SERIAL NOT NULL, -- PK
user_id INTEGER NOT NULL,
is_commercial BOOLEAN NOT NULL,
musicbrainz_id CHARACTER VARYING UNIQUE,
created TIMESTAMP WITH TIME ZONE,
state state_types NOT NULL,
contact_name CHARACTER VARYING NOT NULL,
Expand Down
5 changes: 4 additions & 1 deletion config.py.example
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
from datetime import timedelta
# CUSTOM CONFIGURATION

DEBUG = True # set to False in production mode

SECRET_KEY = "CHANGE_THIS"

EMAIL_VERIFICATION_SECRET_KEY = "CHANGE THIS"
EMAIL_VERIFICATION_EXPIRY = timedelta(hours=24)
EMAIL_RESET_PASSWORD_EXPIRY = timedelta(hours=24)

# Bcrypt
BCRYPT_HASH_PREFIX = "2a"
Expand Down Expand Up @@ -48,6 +50,7 @@ STRIPE_KEYS = {

# if developing payment integration locally, change this to your localhost url
SERVER_BASE_URL = "https://metabrainz.org"
SERVER_NAME = "metabrainz.org"


# REDIS
Expand Down
6 changes: 6 additions & 0 deletions consul_config.py.ctmpl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@
{{- end -}}
{{- end -}}

from datetime import timedelta

SECRET_KEY = '''{{template "KEY" "secret_key"}}'''
EMAIL_VERIFICATION_SECRET_KEY = '''{{template "KEY" "email_verification_secret_key"}}'''
EMAIL_VERIFICATION_EXPIRY = timedelta(hours=24)
EMAIL_RESET_PASSWORD_EXPIRY = timedelta(hours=24)
DEBUG = False

{{if service "pgbouncer-master"}}
Expand Down Expand Up @@ -79,6 +84,7 @@ MAIL_FROM_DOMAIN = '''{{template "KEY" "mail_from_domain"}}'''
PREFERRED_URL_SCHEME = '''{{template "KEY" "preferred_url_scheme"}}'''

SERVER_BASE_URL = f"{PREFERRED_URL_SCHEME}://{MAIL_FROM_DOMAIN}"
SERVER_NAME = MAIL_FROM_DOMAIN

# List of email addresses
NOTIFICATION_RECIPIENTS = [
Expand Down
3 changes: 1 addition & 2 deletions metabrainz/model/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ class User(db.Model, UserMixin):
password = Column(Text, nullable=False) # TODO: add a constraint to ensure password is cleared when deleted field is set

email = Column(Text)
unconfirmed_email = Column(Text)

email_confirmed_at = Column(DateTime(timezone=True))
member_since = Column(DateTime(timezone=True), default=func.now())
last_login_at = Column(DateTime(timezone=True), default=func.now())
last_updated = Column(DateTime(timezone=True), default=func.now())
Expand Down
4 changes: 3 additions & 1 deletion metabrainz/supporter/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from wtforms.validators import DataRequired, Length
from wtforms.widgets import ListWidget, CheckboxInput

from metabrainz.user.forms import UserSignupForm


class DatasetsField(SelectMultipleField):
widget = ListWidget(prefix_label=False)
Expand Down Expand Up @@ -33,7 +35,7 @@ def post_validate(self, form, validation_stopped):
self.data = [datasets_dict.get(x) for x in self.data]


class SupporterSignUpForm(FlaskForm):
class SupporterSignUpForm(UserSignupForm):
"""Base sign up form for new supporters.
Contains common fields required from both commercial and non-commercial
Expand Down
1 change: 0 additions & 1 deletion metabrainz/supporter/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from metabrainz.supporter import musicbrainz_login, login_forbidden
from metabrainz.supporter.forms import CommercialSignUpForm, NonCommercialSignUpForm, CommercialSupporterEditForm, \
NonCommercialSupporterEditForm
from metabrainz.user.forms import UserLoginForm, UserSignupForm

supporters_bp = Blueprint('supporters', __name__)

Expand Down
52 changes: 52 additions & 0 deletions metabrainz/templates/index/signup-options.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{% extends 'users/base.html' %}

{% block title %}{{ _('Sign up') }} - MetaBrainz Foundation{% endblock %}

{% block auth_form %}
<div class="auth-card-container">

<div class="auth-card">
<h1 class="page-title text-center">{{ _('Choose account type') }}</h1>
<div class="h4 text-center">access all MetaBrainz projects</div>

<div id="tiers">
<div id="primary-tiers">
<div class="row">
<div class="tier shiny col-md-4">
<div class="thumbnail">
<div class="caption">
<h3>Supporter</h3>
<div class="description">
Do you rely on data from MetaBrainz? We rely on contributions to keep providing it. To access our
datasets or the <a href="https://musicbrainz.org/register">MusicBrainz Live Data Feed</a>, please
choose between non-commercial and commercial use on the next page.
</div>
<div class="buttons">
<p>
<a href="{{ url_for('account_type') }}" class="btn btn-primary btn-block" role="button">{{ _('Sign up') }}</a>
</p>
</div>
</div>
</div>
</div>
<div class="tier shiny col-md-4">
<div class="thumbnail">
<div class="caption">
<h3>User</h3>
<div class="description">
You want to use one or more of MetaBrainz projects.
</div>
<div class="buttons">
<p>
<a href="{{ url_for('account_type') }}" class="btn btn-primary btn-block" role="button">{{ _('Sign up') }}</a>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
2 changes: 1 addition & 1 deletion metabrainz/templates/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
{% if not error %}
<ul class="nav navbar-nav navbar-right">
{% if not current_user or current_user.is_anonymous %}
<li class="support-link"><a href="{{ url_for('users.signup') }}">{{ _('Register') }}</a></li>
<li class="support-link"><a href="{{ url_for('index.signup_options') }}">{{ _('Register') }}</a></li>
<li><a href="{{ url_for('users.login') }}">{{ _('Sign in') }}</a></li>
{% else %}
<li class="dropdown">
Expand Down
2 changes: 1 addition & 1 deletion metabrainz/user/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def send_forgot_password_email(user: User):
""" Send email for resetting the user's password. """
timestamp = int(datetime.now().timestamp())
checksum = create_email_link_checksum(RESET_PASSWORD, user, timestamp)
reset_password_link = url_for("users.reset_password", user_id=user.id, timestamp=timestamp, checksum=checksum)
reset_password_link = url_for("users.reset_password", user_id=user.id, timestamp=timestamp, checksum=checksum, _external=True)
content = render_template(
"email/user-password-reset.txt",
reset_password_link=reset_password_link,
Expand Down
30 changes: 15 additions & 15 deletions metabrainz/user/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ def signup():
form.form_errors.append(f"Another user with email '{form.username.data}' exists.")
return render_template("users/signup.html", form=form)

password_hash = bcrypt.generate_password_hash(form.password.data).decode()
password_hash = bcrypt.generate_password_hash(form.password.data).decode("utf-8")
user = User.add(name=form.username.data, email=form.email.data, password_hash=password_hash)

send_verification_email(user)
flash.success("Account created. Please check your inbox to complete verification.")
return render_template("index/index.html")
return redirect(url_for("index.home"))

return render_template("users/signup.html", form=form)

Expand Down Expand Up @@ -70,27 +70,27 @@ def verify_email():
user_id = request.args.get("user_id")

timestamp = int(request.args.get("timestamp"))
if datetime.fromtimestamp(timestamp) + current_app.config["EMAIL_VERIFICATION_EXPIRY"] > datetime.now():
if datetime.fromtimestamp(timestamp) + current_app.config["EMAIL_VERIFICATION_EXPIRY"] <= datetime.now():
flash.error("Email verification link expired.")
return render_template("index/index.html")
return redirect(url_for("index.home"))

received_checksum = request.args.get("checksum")

user = User.get(id=user_id)
if user is None:
flash.error("User not found.")
return render_template("index/index.html")
return redirect(url_for("index.home"))

checksum = create_email_link_checksum(VERIFY_EMAIL, user, timestamp)
if checksum != received_checksum:
flash.error("Unable to verify email.")
return render_template("index/index.html")
return redirect(url_for("index.home"))

user.email_confirmed_at = datetime.now(tz=timezone.utc)
db.session.commit()

flash.success("Email verified!")
return render_template("index/index.html")
return redirect(url_for("index.home"))


# TODO: Add email verification resend
Expand All @@ -107,7 +107,7 @@ def lost_username():

send_forgot_username_email(user)
flash.success("Username recovery link sent!")
return render_template("index/index.html")
return redirect(url_for("index.home"))

return render_template("users/lost_username.html", form=form)

Expand All @@ -125,7 +125,7 @@ def lost_password():

send_forgot_password_email(user)
flash.success("Password reset link sent!")
return render_template("index/index.html")
return redirect(url_for("index.home"))

return render_template("users/lost_password.html", form=form)

Expand All @@ -137,29 +137,29 @@ def reset_password():
user_id = request.args.get("user_id")

timestamp = int(request.args.get("timestamp"))
if datetime.fromtimestamp(timestamp) + current_app.config["EMAIL_RESET_PASSWORD_EXPIRY"] > datetime.now():
if datetime.fromtimestamp(timestamp) + current_app.config["EMAIL_RESET_PASSWORD_EXPIRY"] <= datetime.now():
flash.error("Email verification link expired.")
return render_template("index/index.html")
return redirect(url_for("index.home"))

received_checksum = request.args.get("checksum")

user = User.get(id=user_id)
if user is None:
flash.error("User not found.")
return render_template("index/index.html")
return redirect(url_for("index.home"))

checksum = create_email_link_checksum(RESET_PASSWORD, user, timestamp)
if checksum != received_checksum:
flash.error("Unable to reset password.")
return render_template("index/index.html")
return redirect(url_for("index.home"))

form = ResetPasswordForm()
if form.validate_on_submit():
user.password = form.password.data
user.password = bcrypt.generate_password_hash(form.password.data).decode("utf-8")
db.session.commit()

flash.success("Password reset!")
return render_template("index/index.html")
return redirect(url_for("index.home"))

return render_template("users/reset_password.html", form=form)

Expand Down
5 changes: 5 additions & 0 deletions metabrainz/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ def gdpr_statement():
return render_template('index/gdpr.html')


@index_bp.route("/signup-options")
def signup_options():
return render_template('index/signup-options.html')


@index_bp.route('/about/customers.html')
def about_customers_redirect():
return redirect(url_for('supporters.supporters_list'), 301)
Expand Down

0 comments on commit d7f0a25

Please sign in to comment.