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

mm-logging
lza_menace 4 years ago
parent f245272796
commit 218500c20c

@ -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

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

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

@ -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

@ -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():

@ -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
from os import urandom
@authentication_bp.route("/login", methods=["GET", "POST"])
def login():
error = None
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: @authentication_bp.route("/register", methods=["GET", "POST"])
error = "Must provide a seed" 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)
return render_template("login.html", error=error) @authentication_bp.route("/login", methods=["GET", "POST"])
def login():
return render_template("authentication/login.html")
@authentication_bp.route("/logout") @authentication_bp.route("/logout")
def logout(): def logout():

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

@ -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)

@ -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()

@ -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

@ -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"})

@ -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']))

@ -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)

@ -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()

@ -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;

@ -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

@ -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>

@ -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>

@ -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">

@ -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>