introducing auth blueprints, refactoring app init, setting up forms and sqlalchemy, starting user registration process

This commit is contained in:
lza_menace 2020-08-25 12:48:50 -07:00
parent f245272796
commit 218500c20c
20 changed files with 225 additions and 100 deletions

View file

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
source .venv/bin/activate source .venv/bin/activate
export FLASK_APP=wowstash/run.py export FLASK_APP=wowstash/app.py
export FLASK_SECRETS=config.py export FLASK_SECRETS=config.py
export FLASK_DEBUG=1 export FLASK_DEBUG=1
flask run flask run

View file

@ -3,3 +3,6 @@ psycopg2-binary
redis redis
flask_session flask_session
requests requests
Flask-WTF
flask_sqlalchemy
flask-bcrypt

6
wowstash/app.py Normal file
View file

@ -0,0 +1,6 @@
from wowstash.factory import create_app
if __name__ == '__main__':
app = create_app()
app.run()

View file

@ -1,2 +1,3 @@
from .account import account_bp from .account import account_bp
from .authentication import authentication_bp from .authentication import authentication_bp
from .meta import meta_bp

View file

@ -1,8 +1,7 @@
from flask import request, render_template, session from flask import request, render_template, session
from flask import redirect, url_for, current_app from flask import redirect, url_for, current_app
from wallet.blueprints.account import account_bp from wowstash.blueprints.account import account_bp
from wallet.library.daemon import daemon
from wallet.library.wallet import wallet
@account_bp.route("/account") @account_bp.route("/account")
def overview(): def overview():

View file

@ -1,42 +1,25 @@
from flask import request, render_template, session, redirect, url_for from flask import request, render_template, session, redirect, url_for
from wallet.blueprints.authentication import authentication_bp from wowstash.blueprints.authentication import authentication_bp
from wallet.library.daemon import daemon from wowstash.forms import Register
from wallet.library.wallet import wallet from wowstash.models import User
from monero.seed import Seed
from binascii import hexlify
from datetime import datetime @authentication_bp.route("/register", methods=["GET", "POST"])
from os import urandom def register():
form = Register()
if form.validate_on_submit():
print(dir(User))
# user = User.query
user = User.objects.filter(email=form.email.data)
print(user)
return "ok"
else:
print(form)
return render_template("authentication/register.html", form=form)
@authentication_bp.route("/login", methods=["GET", "POST"]) @authentication_bp.route("/login", methods=["GET", "POST"])
def login(): def login():
error = None return render_template("authentication/login.html")
if request.method == "POST":
if request.form.get('seed'):
try:
seed = Seed(str(request.form['seed']))
session['seed'] = seed.phrase
session['start_time'] = datetime.utcnow()
session['public_address'] = seed.public_address()
session['private_spend_key'] = seed.secret_spend_key()
session['public_spend_key'] = seed.public_spend_key()
session['private_view_key'] = seed.secret_view_key()
session['public_view_key'] = seed.public_view_key()
session['wallet_password'] = hexlify(urandom(64))
if request.form.get('persistence'):
session['wallet_persistence'] = "Enabled"
else:
session['wallet_persistence'] = "Disabled"
return redirect(url_for('account.overview'))
except AssertionError:
error = "Invalid seed checksum"
except Exception as e:
error = "Invalid seed {0}".format(e)
else:
error = "Must provide a seed"
return render_template("login.html", error=error)
@authentication_bp.route("/logout") @authentication_bp.route("/logout")
def logout(): def logout():

View file

@ -0,0 +1,5 @@
from flask import Blueprint
meta_bp = Blueprint("meta", __name__)
from . import routes

View file

@ -0,0 +1,23 @@
from flask import request, render_template, session, redirect, url_for, make_response, jsonify
from wowstash.blueprints.meta import meta_bp
from wowstash.library.jsonrpc import daemon
from wowstash.library.info import info
from wowstash.library.db import Database
@meta_bp.route('/')
def index():
return render_template('index.html', node=daemon.info(), info=info.get_info())
@meta_bp.route('/health')
def health():
return make_response(jsonify({
'cache': info.redis.ping(),
'db': Database().connected
}), 200)
# @app.errorhandler(404)
# def not_found(error):
# return make_response(jsonify({
# 'error': 'Page not found'
# }), 404)

12
wowstash/cli.py Normal file
View file

@ -0,0 +1,12 @@
@app.errorhandler(404)
def not_found(error):
return make_response(jsonify({
'error': 'Page not found'
}), 404)
@app.cli.command('initdb')
def initdb():
from wowstash.models import *
db.create_all()

61
wowstash/factory.py Normal file
View file

@ -0,0 +1,61 @@
import wowstash.models
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_session import Session
from flask_bcrypt import Bcrypt
from redis import Redis
from wowstash.blueprints.authentication import authentication_bp
from wowstash.blueprints.account import account_bp
from wowstash.blueprints.meta import meta_bp
from wowstash import config
app = None
db = None
bcrypt = None
def _setup_db(app: Flask):
global db
uri = 'postgresql+psycopg2://{user}:{pw}@{url}/{db}'.format(
user=config.DB_USER,
pw=config.DB_PASS,
url=config.DB_HOST,
db=config.DB_NAME
)
app.config['SQLALCHEMY_DATABASE_URI'] = uri
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
db.create_all()
def _setup_session(app: Flask):
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_COOKIE_NAME'] = app.config.get('SESSION_COOKIE_NAME', 'wowstash')
app.config['SESSION_REDIS'] = Redis(
host=app.config['REDIS_HOST'],
port=app.config['REDIS_PORT']
)
Session(app)
def _setup_bcrypt(app: Flask):
bcrypt = Bcrypt(app)
def create_app():
global app
global db
app = Flask(__name__)
app.config.from_envvar('FLASK_SECRETS')
app.secret_key = app.config['SECRET_KEY']
# Setup backends
_setup_db(app)
_setup_session(app)
_setup_bcrypt(app)
# Routes
app.register_blueprint(meta_bp)
app.register_blueprint(authentication_bp)
app.register_blueprint(account_bp)
app.app_context().push()
return app

8
wowstash/forms.py Normal file
View file

@ -0,0 +1,8 @@
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired
class Register(FlaskForm):
email = StringField('Email Address:', validators=[DataRequired()], render_kw={"placeholder": "Email", "class": "form-control", "type": "email"})
password = StringField('Password:', validators=[DataRequired()], render_kw={"placeholder": "Password", "class": "form-control", "type": "password"})

View file

@ -68,7 +68,7 @@ class Wallet(object):
_account = self.make_wallet_rpc('create_account', {'label': label}) _account = self.make_wallet_rpc('create_account', {'label': label})
return _account['account_index'] return _account['account_index']
def addresses(self, account, addr_indices=None): def addresses(self, account=0, addr_indices=None):
qdata = {'account_index': account} qdata = {'account_index': account}
if addr_indices: if addr_indices:
qdata['address_index'] = addr_indices qdata['address_index'] = addr_indices
@ -78,12 +78,12 @@ class Wallet(object):
addresses[_addr['address_index']] = _addr['address'] addresses[_addr['address_index']] = _addr['address']
return addresses return addresses
def new_address(self, account, label=None): def new_address(self, account=0, label=None):
data = {'account_index': account, 'label': label} data = {'account_index': account, 'label': label}
_address = self.make_wallet_rpc('create_address', data) _address = self.make_wallet_rpc('create_address', data)
return (_address['address_index'], _address['address']) return (_address['address_index'], _address['address'])
def balances(self, account): def balances(self, account=0):
data = {'account_index': account} data = {'account_index': account}
_balance = self.make_wallet_rpc('get_balance', data) _balance = self.make_wallet_rpc('get_balance', data)
return (from_atomic(_balance['balance']), from_atomic(_balance['unlocked_balance'])) return (from_atomic(_balance['balance']), from_atomic(_balance['unlocked_balance']))

23
wowstash/models.py Normal file
View file

@ -0,0 +1,23 @@
from sqlalchemy import Column, Integer, DateTime, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import func
from flask_bcrypt import generate_password_hash, check_password_hash
from wowstash.factory import db
Base = declarative_base()
class User(db.Model):
__tablename__ = 'users'
id = db.Column('user_id', db.Integer, primary_key=True)
password = db.Column(db.String(120))
email = db.Column(db.String(50), unique=True, index=True)
subaddress_index = db.Column(db.Integer)
registered_on = db.Column(db.DateTime, server_default=func.now())
def hash_password(self):
self.password = generate_password_hash(self.password).decode('utf8')
def check_password(self, password):
return check_password_hash(self.password, password)

View file

@ -1,51 +0,0 @@
from flask import Flask, jsonify, request, make_response, render_template, session, redirect, url_for, escape
from flask_session import Session
from datetime import timedelta, datetime
from redis import Redis
from wowstash.library.jsonrpc import daemon
from wowstash.library.info import info
from wowstash.library.db import Database
from wowstash import config
# from wowstash.blueprints.account import account_bp
# from wowstash.blueprints.authentication import authentication_bp
# Setup app
app = Flask(__name__)
app.config.from_envvar('FLASK_SECRETS')
app.secret_key = app.config['SECRET_KEY']
# Setup sessions
app.config['SESSION_REDIS'] = Redis(
host=app.config['REDIS_HOST'],
port=app.config['REDIS_PORT']
)
sess = Session()
sess.init_app(app)
# app.register_blueprint(account_bp)
# app.register_blueprint(authentication_bp)
@app.route('/')
def index():
return render_template('home.html', node=daemon.info(), info=info.get_info())
@app.route('/health')
def health():
return make_response(jsonify({
'cache': info.redis.ping(),
'db': Database().connected
}), 200)
@app.errorhandler(404)
def not_found(error):
return make_response(jsonify({
'error': 'Page not found'
}), 404)
@app.cli.command('eviscerate')
def eviscerate():
print('Eviscerate the proletariat')
if __name__ == '__main__':
app.run()

View file

@ -78,7 +78,6 @@ section h2 {
} }
#mainNav .navbar-brand { #mainNav .navbar-brand {
color: white;
font-family: 'Catamaran', 'Helvetica', 'Arial', 'sans-serif'; font-family: 'Catamaran', 'Helvetica', 'Arial', 'sans-serif';
font-weight: 200; font-weight: 200;
letter-spacing: 1px; letter-spacing: 1px;
@ -126,7 +125,7 @@ section h2 {
background-color: transparent; background-color: transparent;
} }
#mainNav .navbar-brand { #mainNav .navbar-brand {
color: fade(white, 70%); color: white;
} }
#mainNav .navbar-brand:hover, #mainNav .navbar-brand:focus { #mainNav .navbar-brand:hover, #mainNav .navbar-brand:focus {
color: white; color: white;

View file

@ -30,8 +30,10 @@
var navbarCollapse = function() { var navbarCollapse = function() {
if ($("#mainNav").offset().top > 100) { if ($("#mainNav").offset().top > 100) {
$("#mainNav").addClass("navbar-shrink"); $("#mainNav").addClass("navbar-shrink");
$("#navbar-brand").addClass("navbar-brand");
} else { } else {
$("#mainNav").removeClass("navbar-shrink"); $("#mainNav").removeClass("navbar-shrink");
// $("#navbar-brand").removeClass("navbar-brand");
} }
}; };
// Collapse now if page is not at top // Collapse now if page is not at top

View file

@ -27,10 +27,11 @@
<br> <br>
<span> <span>
<button type="submit" class="btn btn-link btn-outline btn-xl">Submit</button> <button type="submit" class="btn btn-link btn-outline btn-xl">Submit</button>
<a href="/new" class="btn btn-outline btn-xl">Create Account</a>
</span> </span>
</form> </form>
<hr><br>
<p>Need an account? Register below:</p>
<a href="{{ url_for('authentication.register') }}" class="btn btn-outline btn-xl">Register</a>
</div> </div>
</div> </div>
</div> </div>

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
{% include 'head.html' %}
<body id="page-top">
{% include 'navbar.html' %}
<header class="masthead">
<div class="container h-100">
<div class="row h-100">
<div class="col-lg-12 my-auto">
<div class="header-content mx-auto">
<form method="POST" action="{{ url_for('authentication.register') }}">
{{ form.csrf_token }}
<div class="form-group">
{{ form.email.label }}
{{ form.email }}
</div>
<div class="form-group">
{{ form.password.label }}
{{ form.password }}
</div>
<ul>
{% for field, errors in form.errors.items() %}
<li>{{ form[field].label }}: {{ ', '.join(errors) }}</li>
{% endfor %}
</ul>
<input type="submit" value="Register" class="btn btn-link btn-outline btn-xl">
</form>
</div>
</div>
</div>
</div>
</header>
{% include 'footer.html' %}
{% include 'scripts.html' %}
</body>
</html>

View file

@ -13,8 +13,8 @@
<div class="col-lg-7 my-auto"> <div class="col-lg-7 my-auto">
<div class="header-content mx-auto"> <div class="header-content mx-auto">
<h1 class="mb-5">Manage your Wownero funds securely and anonymously.</h1> <h1 class="mb-5">Manage your Wownero funds securely and anonymously.</h1>
<a href="/register" class="btn btn-outline btn-xl">Register</a> <a href="{{ url_for('authentication.register') }}" class="btn btn-outline btn-xl">Register</a>
<a href="/login" class="btn btn-outline btn-xl">Login</a> <a href="{{ url_for('authentication.login') }}" class="btn btn-outline btn-xl">Login</a>
</div> </div>
</div> </div>
<div class="col-lg-5 my-auto"> <div class="col-lg-5 my-auto">

View file

@ -1,6 +1,6 @@
<nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav"> <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
<div class="container"> <div class="container">
<a class="navbar-brand js-scroll-trigger" href="#page-top">{{ config.SITE_NAME }}</a> <a id="navbar-brand" class="navbar-brand js-scroll-trigger" href="#page-top">{{ config.SITE_NAME }}</a>
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
Menu Menu
<i class="fa fa-bars"></i> <i class="fa fa-bars"></i>
@ -13,6 +13,12 @@
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#statistics">Statistics</a></li> <li class="nav-item"><a class="nav-link js-scroll-trigger" href="#statistics">Statistics</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#contact">Contact</a></li> <li class="nav-item"><a class="nav-link js-scroll-trigger" href="#contact">Contact</a></li>
<li class="nav-item"><a class="nav-link" href="/login">Login</a></li> <li class="nav-item"><a class="nav-link" href="/login">Login</a></li>
{% else %}
<li class="nav-item"><a class="nav-link" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link" href="/#about">About</a></li>
<li class="nav-item"><a class="nav-link" href="/#statistics">Statistics</a></li>
<li class="nav-item"><a class="nav-link" href="/#contact">Contact</a></li>
<li class="nav-item"><a class="nav-link" href="/login">Login</a></li>
{% endif %} {% endif %}
</ul> </ul>
</div> </div>