Add Github login

This commit is contained in:
rubenwardy 2018-03-18 18:05:53 +00:00
parent 7f4faf21fa
commit 7d20c49ebb
9 changed files with 196 additions and 62 deletions

@ -1,7 +1,14 @@
from flask import * from flask import *
from flask_user import * from flask_user import *
import flask_menu as menu
from flask.ext import markdown
from flask_github import GitHub
app = Flask(__name__) app = Flask(__name__)
app.config.from_pyfile('../config.cfg') app.config.from_pyfile('../config.cfg')
menu.Menu(app=app)
markdown.Markdown(app, extensions=['fenced_code'])
github = GitHub(app)
import models, views import models, views

@ -1,5 +1,5 @@
from flask import Flask, url_for from flask import Flask, url_for
from flask.ext.sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from app import app from app import app
from datetime import datetime from datetime import datetime
from sqlalchemy.orm import validates from sqlalchemy.orm import validates
@ -22,6 +22,10 @@ class User(db.Model, UserMixin):
password = db.Column(db.String(255), nullable=False, server_default='') password = db.Column(db.String(255), nullable=False, server_default='')
reset_password_token = db.Column(db.String(100), nullable=False, server_default='') reset_password_token = db.Column(db.String(100), nullable=False, server_default='')
# Account linking
github_username = db.Column(db.String(50), nullable=True, unique=True)
forums_username = db.Column(db.String(50), nullable=True, unique=True)
# User email information # User email information
email = db.Column(db.String(255), nullable=True, unique=True) email = db.Column(db.String(255), nullable=True, unique=True)
confirmed_at = db.Column(db.DateTime()) confirmed_at = db.Column(db.DateTime())
@ -38,6 +42,7 @@ class User(db.Model, UserMixin):
self.username = username self.username = username
self.confirmed_at = datetime.datetime.now() - datetime.timedelta(days=6000) self.confirmed_at = datetime.datetime.now() - datetime.timedelta(days=6000)
self.display_name = username
def isClaimed(self): def isClaimed(self):
return self.password is not None and self.password != "" return self.password is not None and self.password != ""

@ -68,9 +68,16 @@ Sign in
<h2>New here?</h2> <h2>New here?</h2>
{% if user_manager.enable_register and not user_manager.require_invitation %} {% if user_manager.enable_register and not user_manager.require_invitation %}
<a href="">{%trans%}Create an account{%endtrans%}</a> <a href="{{ url_for('github_signin_page') }}">{%trans%}Create an account{%endtrans%}</a>
{% endif %} {% endif %}
</aside> </aside>
<aside class="box box_grey">
<h2>OAUTH</h2>
<a href="{{ url_for('github_signin_page') }}">GitHub</a>
</aside>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

@ -3,72 +3,20 @@ from flask import *
from flask_user import * from flask_user import *
from flask_login import login_user, logout_user from flask_login import login_user, logout_user
from app.models import * from app.models import *
from flask.ext import menu, markdown import flask_menu as menu
from flask.ext import markdown
from sqlalchemy import func from sqlalchemy import func
from werkzeug.contrib.cache import SimpleCache from werkzeug.contrib.cache import SimpleCache
cache = SimpleCache() cache = SimpleCache()
menu.Menu(app=app)
markdown.Markdown(app, extensions=['fenced_code'])
# TODO: remove on production! # TODO: remove on production!
@app.route('/static/<path:path>') @app.route('/static/<path:path>')
def send_static(path): def send_static(path):
return send_from_directory('static', path) return send_from_directory('static', path)
import users, githublogin
@app.route('/') @app.route('/')
@menu.register_menu(app, '.', 'Home') @menu.register_menu(app, '.', 'Home')
def home_page(): def home_page():
return render_template('index.html') return render_template('index.html')
# Define the User registration form
# It augments the Flask-User RegisterForm with additional fields
from flask_user.forms import RegisterForm
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, validators
class MyRegisterForm(RegisterForm):
first_name = StringField('First name', validators=[
validators.DataRequired('First name is required')])
last_name = StringField('Last name', validators=[
validators.DataRequired('Last name is required')])
# Define the User profile form
class UserProfileForm(FlaskForm):
first_name = StringField('First name', validators=[
validators.DataRequired('First name is required')])
last_name = StringField('Last name', validators=[
validators.DataRequired('Last name is required')])
submit = SubmitField('Save')
@app.route('/user/', methods=['GET', 'POST'])
@app.route('/user/<username>/', methods=['GET'])
def user_profile_page(username=None):
user = None
form = None
if username is None:
if not current_user.is_authenticated:
return current_app.login_manager.unauthorized()
user = current_user
else:
user = User.query.filter_by(username=username).first()
if not user:
abort(404)
if user == current_user:
# Initialize form
form = UserProfileForm(request.form, current_user)
# Process valid POST
if request.method=='POST' and form.validate():
# Copy form fields to user_profile fields
form.populate_obj(current_user)
# Save user_profile
db.session.commit()
# Redirect to home page
return redirect(url_for('home_page'))
# Process GET or invalid POST
return render_template('users/user_profile_page.html',
user=user, form=form)

100
app/views/githublogin.py Normal file

@ -0,0 +1,100 @@
from flask import *
from flask_user import *
from flask_login import login_user, logout_user
import flask_menu as menu
from flask_github import GitHub
from app import app, github
from app.models import *
@app.route('/user/github/start/')
def github_signin_page():
return github.authorize("public_repo,repo")
def _do_login_user(user, remember_me=False):
def _call_or_get(v):
if callable(v):
return v()
else:
return v
# User must have been authenticated
if not user:
return False
user.active = True
db.session.commit()
# Check if user account has been disabled
if not _call_or_get(user.is_active):
flash('Your account has not been enabled.', 'error')
return False
# Check if user has a confirmed email address
user_manager = current_app.user_manager
if user_manager.enable_email and user_manager.enable_confirm_email \
and not current_app.user_manager.enable_login_without_confirm_email \
and not user.has_confirmed_email():
url = url_for('user.resend_confirm_email')
flash("Your email address has not yet been confirmed", 'error')
return False
# Use Flask-Login to sign in user
login_user(user, remember=remember_me)
signals.user_logged_in.send(current_app._get_current_object(), user=user)
flash('You have signed in successfully.', 'success')
return True
def _login_user(user):
user_mixin = None
if user_manager.enable_username:
user_mixin = user_manager.find_user_by_username(user.username)
return _do_login_user(user_mixin, False)
@app.route('/user/github/callback/')
@github.authorized_handler
def github_authorized(oauth_token):
next_url = request.args.get('next')
if oauth_token is None:
flash("Authorization failed [err=gh-oauth-login-failed]", "danger")
return redirect(url_for("user.login"))
import requests
# Get Github username
url = "https://api.github.com/user"
r = requests.get(url, headers={"Authorization": "token " + oauth_token})
username = r.json()["login"]
# Get user by github username
userByGithub = User.query.filter_by(github_username=username).first()
# If logged in, connect
if current_user and current_user.is_authenticated:
if userByGithub is None:
current_user.github_username = username
db.session.add(auth)
db.session.commit()
return redirect(url_for("gitAccount", id=auth.id))
else:
flash("Github account is already associated with another user", "danger")
return redirect(url_for("home_page"))
# If not logged in, log in
else:
if userByGithub is None:
flash("Authorization failed [err=gh-no-such-account]", "danger")
return redirect(url_for("user.login"))
elif _login_user(userByGithub):
return redirect(next_url or url_for("home_page"))
else:
flash("Authorization failed [err=gh-login-failed]", "danger")
return redirect(url_for("user.login"))

60
app/views/users.py Normal file

@ -0,0 +1,60 @@
from flask import *
from flask_user import *
from flask_login import login_user, logout_user
from flask.ext import menu
from app import app
from app.models import *
# Define the User registration form
# It augments the Flask-User RegisterForm with additional fields
from flask_user.forms import RegisterForm
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, validators
class MyRegisterForm(RegisterForm):
first_name = StringField('First name', validators=[
validators.DataRequired('First name is required')])
last_name = StringField('Last name', validators=[
validators.DataRequired('Last name is required')])
# Define the User profile form
class UserProfileForm(FlaskForm):
first_name = StringField('First name', validators=[
validators.DataRequired('First name is required')])
last_name = StringField('Last name', validators=[
validators.DataRequired('Last name is required')])
submit = SubmitField('Save')
@app.route('/user/', methods=['GET', 'POST'])
@app.route('/user/<username>/', methods=['GET'])
def user_profile_page(username=None):
user = None
form = None
if username is None:
if not current_user.is_authenticated:
return current_app.login_manager.unauthorized()
user = current_user
else:
user = User.query.filter_by(username=username).first()
if not user:
abort(404)
if user == current_user:
# Initialize form
form = UserProfileForm(request.form, current_user)
# Process valid POST
if request.method=='POST' and form.validate():
# Copy form fields to user_profile fields
form.populate_obj(current_user)
# Save user_profile
db.session.commit()
# Redirect to home page
return redirect(url_for('home_page'))
# Process GET or invalid POST
return render_template('users/user_profile_page.html',
user=user, form=form)

@ -4,3 +4,6 @@ SECRET_KEY=""
WTF_CSRF_SECRET_KEY="" WTF_CSRF_SECRET_KEY=""
SQLALCHEMY_DATABASE_URI = "sqlite:///../db.sqlite" SQLALCHEMY_DATABASE_URI = "sqlite:///../db.sqlite"
GITHUB_CLIENT_ID = ""
GITHUB_CLIENT_SECRET = ""

@ -3,3 +3,4 @@ Flask-SQLAlchemy>=2.3
Flask-User>=0.6.19 Flask-User>=0.6.19
Flask-Menu>=0.7.0 Flask-Menu>=0.7.0
Flask-Markdown>=0.3 Flask-Markdown>=0.3
GitHub-Flask>=3.2.0

@ -6,12 +6,15 @@ if delete_db and os.path.isfile("app/data.sqlite"):
os.remove("app/data.sqlite") os.remove("app/data.sqlite")
if not os.path.isfile("app/data.sqlite"): if not os.path.isfile("app/data.sqlite"):
from app import models from app.models import *
print("Creating database tables...") print("Creating database tables...")
models.db.create_all() db.create_all()
print("Filling database...") print("Filling database...")
models.db.session.commit()
ruben = User("rubenwardy")
ruben.github_username = "rubenwardy"
db.session.add(ruben)
db.session.commit()
else: else:
print("Database already exists") print("Database already exists")