fine-tuning login
This commit is contained in:
		
							
								
								
									
										25
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								README.md
									
									
									
									
									
								
							@ -2,16 +2,6 @@
 | 
			
		||||
 | 
			
		||||
Python Flask web application for capsul.org
 | 
			
		||||
 | 
			
		||||
## postgres database schema management
 | 
			
		||||
 | 
			
		||||
capsulflask has a concept of a schema version. When the application starts, it will query the database for a table named
 | 
			
		||||
`schemaversion` that has one row and one column (`version`). If the `version` it finds is not equal to the `desiredSchemaVersion` variable set in `db.py`, it will run  migration scripts from the `schema_migrations` folder one by one until the `schemaversion` table shows the correct version.
 | 
			
		||||
 | 
			
		||||
For example, the script named `02_up_xyz.sql` should contain code that migrates the database from schema version 1 to schema version 2. Likewise, the script `02_down_xyz.sql` should contain code that migrates from schema version 2 back to schema version 1.
 | 
			
		||||
 | 
			
		||||
**IMPORTANT: if you need to make changes to the schema, make a NEW schema version. DO NOT EDIT the existing schema versions.**
 | 
			
		||||
 | 
			
		||||
In general, for safety, schema version upgrades should not delete data. Schema version downgrades will simply throw an error and exit for now. 
 | 
			
		||||
 | 
			
		||||
## how to run locally
 | 
			
		||||
 | 
			
		||||
@ -33,11 +23,22 @@ pip install -r requirements.txt
 | 
			
		||||
Run an instance of Postgres (I used docker for this, you can use whatever you want, point is its listening on localhost:5432)
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
docker run -it -e POSTGRES_PASSWORD=dev -p 5432:5432 postgres
 | 
			
		||||
docker run --rm -it -e POSTGRES_PASSWORD=dev -p 5432:5432 postgres
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Run the app 
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
FLASK_APP=capsulflask flask run
 | 
			
		||||
```
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## postgres database schema management
 | 
			
		||||
 | 
			
		||||
capsulflask has a concept of a schema version. When the application starts, it will query the database for a table named
 | 
			
		||||
`schemaversion` that has one row and one column (`version`). If the `version` it finds is not equal to the `desiredSchemaVersion` variable set in `db.py`, it will run  migration scripts from the `schema_migrations` folder one by one until the `schemaversion` table shows the correct version.
 | 
			
		||||
 | 
			
		||||
For example, the script named `02_up_xyz.sql` should contain code that migrates the database from schema version 1 to schema version 2. Likewise, the script `02_down_xyz.sql` should contain code that migrates from schema version 2 back to schema version 1.
 | 
			
		||||
 | 
			
		||||
**IMPORTANT: if you need to make changes to the schema, make a NEW schema version. DO NOT EDIT the existing schema versions.**
 | 
			
		||||
 | 
			
		||||
In general, for safety, schema version upgrades should not delete data. Schema version downgrades will simply throw an error and exit for now. 
 | 
			
		||||
@ -18,7 +18,7 @@ from capsulflask.db import get_model
 | 
			
		||||
bp = Blueprint("auth", __name__, url_prefix="/auth")
 | 
			
		||||
 | 
			
		||||
def account_required(view):
 | 
			
		||||
    """View decorator that redirects anonymous users to the login page."""
 | 
			
		||||
    """View decorator that redirects non-logged-in users to the login page."""
 | 
			
		||||
 | 
			
		||||
    @functools.wraps(view)
 | 
			
		||||
    def wrapped_view(**kwargs):
 | 
			
		||||
@ -36,31 +36,34 @@ def login():
 | 
			
		||||
        error = None
 | 
			
		||||
 | 
			
		||||
        if not email:
 | 
			
		||||
            error = "Email is required."
 | 
			
		||||
            error = "email is required"
 | 
			
		||||
        elif not re.match(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", email):
 | 
			
		||||
	        error = "Enter a valid email address."
 | 
			
		||||
 | 
			
		||||
	        error = "enter a valid email address"
 | 
			
		||||
 | 
			
		||||
        if error is None:
 | 
			
		||||
            token = get_model().login(email)
 | 
			
		||||
            if token is None:
 | 
			
		||||
                error = "too many logins. please use one of the existing login links that have been emailed to you"
 | 
			
		||||
            else:
 | 
			
		||||
                link = f"{current_app.config['BASE_URL']}/auth/magic/{token}"
 | 
			
		||||
 | 
			
		||||
            link = f"{current_app.config['BASE_URL']}/auth/magic/{token}"
 | 
			
		||||
                current_app.config["FLASK_MAIL_INSTANCE"].send(
 | 
			
		||||
                    Message(
 | 
			
		||||
                    "Click This Link to Login to Capsul",
 | 
			
		||||
                    body=f"""
 | 
			
		||||
                        Navigate to {link} to log into capsul.
 | 
			
		||||
 | 
			
		||||
            current_app.config["FLASK_MAIL_INSTANCE"].send(
 | 
			
		||||
                Message(
 | 
			
		||||
                  "Click This Link to Login to Capsul",
 | 
			
		||||
                  body=f"""
 | 
			
		||||
                    Navigate to {link} to log into capsul.
 | 
			
		||||
                  """,
 | 
			
		||||
                  html=f"""
 | 
			
		||||
                    Navigate to <a href="{link}">{link}</a> to log into capsul.
 | 
			
		||||
                  """,
 | 
			
		||||
                  sender=current_app.config['MAIL_DEFAULT_SENDER'],
 | 
			
		||||
                  recipients=[email]
 | 
			
		||||
                        If you didn't request this, ignore this message.
 | 
			
		||||
                    """,
 | 
			
		||||
                    html=f"""
 | 
			
		||||
                        <p>Navigate to <a href="{link}">{link}</a> to log into capsul.</p>
 | 
			
		||||
                        <p>If you didn't request this, ignore this message.</p>
 | 
			
		||||
                    """,
 | 
			
		||||
                    recipients=[email]
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            return render_template("check-your-email.html")
 | 
			
		||||
                return render_template("login-landing.html", email=email)
 | 
			
		||||
 | 
			
		||||
        flash(error)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,10 @@ class Model:
 | 
			
		||||
    if len(self.cursor.fetchall()) == 0:
 | 
			
		||||
      self.cursor.execute("INSERT INTO accounts (email) VALUES (%s)", (email, ))
 | 
			
		||||
 | 
			
		||||
    self.cursor.execute("SELECT token FROM logintokens WHERE email = %s", (email, ))
 | 
			
		||||
    if len(self.cursor.fetchall()) > 2:
 | 
			
		||||
      return None
 | 
			
		||||
 | 
			
		||||
    token = generate()
 | 
			
		||||
    self.cursor.execute("INSERT INTO logintokens (email, token) VALUES (%s, %s)", (email, token))
 | 
			
		||||
    self.connection.commit()
 | 
			
		||||
@ -22,7 +26,8 @@ class Model:
 | 
			
		||||
    self.cursor.execute("SELECT email FROM logintokens WHERE token = %s", (token, ))
 | 
			
		||||
    rows = self.cursor.fetchall()
 | 
			
		||||
    if len(rows) > 0:
 | 
			
		||||
      self.cursor.execute("DELETE FROM logintokens WHERE token = %s", (token, ))
 | 
			
		||||
      email = rows[0][0]
 | 
			
		||||
      self.cursor.execute("DELETE FROM logintokens WHERE email = %s", (email, ))
 | 
			
		||||
      self.connection.commit()
 | 
			
		||||
      return rows[0][0]
 | 
			
		||||
      return email
 | 
			
		||||
    return None
 | 
			
		||||
@ -15,7 +15,7 @@ CREATE TABLE vms (
 | 
			
		||||
CREATE TABLE payments (
 | 
			
		||||
  email      TEXT REFERENCES accounts(email) ON DELETE RESTRICT,
 | 
			
		||||
  created    TIMESTAMP NOT NULL DEFAULT NOW(),
 | 
			
		||||
  dollars    INTEGER NOT NULL,
 | 
			
		||||
  dollars    NUMERIC(8, 2) NOT NULL,
 | 
			
		||||
  PRIMARY KEY (email, created)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@
 | 
			
		||||
<head>
 | 
			
		||||
<meta charset="utf-8">
 | 
			
		||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
			
		||||
<title>{% block title %}{% endblock %}</title>
 | 
			
		||||
<title>{% block title %}{% endblock %}{% if self.title() %} - {% endif %}capsul</title>
 | 
			
		||||
<meta name="viewport" content="width=device-width, initial-scale=1">
 | 
			
		||||
<link rel="shortcut icon" href="/favicon.ico" />
 | 
			
		||||
<link rel="apple-touch-icon" href="/icon.png" />
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
 | 
			
		||||
    <div class="float-right">
 | 
			
		||||
      {% if session["account"] %}
 | 
			
		||||
        <span>{{ session["account"] }}</span>
 | 
			
		||||
        <span>Logged in as {{ session["account"] }}</span>
 | 
			
		||||
        <a href="{{ url_for('auth.logout') }}">Log Out</a>
 | 
			
		||||
      {% else %}
 | 
			
		||||
        <a href="{{ url_for('auth.login') }}">Log In</a>
 | 
			
		||||
 | 
			
		||||
@ -1,6 +0,0 @@
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
  Check Your Email My Dude
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										7
									
								
								capsulflask/templates/login-landing.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								capsulflask/templates/login-landing.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
 | 
			
		||||
{% block title %}check your email{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
  check your email. a login link has been sent to {{ email }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
 | 
			
		||||
{% block title %}login{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
  <form method="post">
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user