How do check admin creds in flask blueprint - flask

How do you check admin credentials before rendering any templates?
admin = Blueprint('admin', __name__, url_prefix='/Admin' ,static_folder='static', static_url_path='/static/adminUI', template_folder='templates')
#admin.route('/')
def login():
return redirect(url_for('admin.login'))
#admin.route('/login', methods=['POST'])
def login():
admin = Admin.query.filter_by(username='Admin').first()
passwrd = request.form.get('password')
if admin:
if not check_password_hash(admin.password , passwrd):
return render_template('admin_login.html')
else:
return redirect(url_for('admin.dashboard'))
else:
return render_template('admin_login.html')
typing 127.0.0.1:5000/Admin in the browser takes me directly to the dashboard which is not good because anyone can do that, my goal is to have more than one admin and prevent non-admins from accessing the dashboard
models.py
class Admin(db.Model):
__tablename__ = "Administators"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100) , unique=True)
password = db.Column(db.String(200) )

You can use Flask-JWT: https://pythonhosted.org/Flask-JWT/
You must generate the jwt token in the login procedure, then you need to pass it via the header of the http request
from flask import Flask
from flask_jwt import JWT, jwt_required, current_identity
#... see the quickstart section to see how to setup jwt... https://pythonhosted.org/Flask-JWT/
#app.route('/protected')
#jwt_required()
def protected():
return '%s' % current_identity

Related

How do I implement OAuth with Discord using flask dance?

I used the same code for google oauth and I used flask-dance. This worked with google, but didn't work with discord. I get an error message saying that I need to be logged in to reach the home page, meaning that the discord account hasn't been registered in the sqlite database.
Here is my code
from flask import Flask, render_template, redirect, url_for, flash, Blueprint
from flask_login import current_user, login_user, login_required
from flask_dance.contrib.google import make_google_blueprint, google
from flask_dance.contrib.discord import make_discord_blueprint, discord
from flask_dance.consumer import oauth_authorized, oauth_error
from flask_dance.consumer.storage.sqla import SQLAlchemyStorage
from sqlalchemy.orm.exc import NoResultFound
from __init__ import db
from models import User, OAuth
discord_blueprint = make_discord_blueprint(client_id= "1001891783061553233", client_secret="QMvsUwbFGCLgYWgr8GAQ5ae2WibPTeDB", scope=["identify", "email"])
discord_bp = make_discord_blueprint(storage = SQLAlchemyStorage(OAuth, db.session, user = current_user))
#oauth_authorized.connect_via(discord_blueprint)
def discord_logged_in(blueprint, token):
if not token:
flash("Failed to log in", category="error")
return
resp = blueprint.session.get("/users/#me")
if not resp:
msg = "Failed to fetch user info"
flash(msg, category="error")
return
discord_name = resp.json()["name"]
discord_user_id = resp.json() ["id"]
query = OAuth.query.filter_by(
provider = blueprint.name, provider_user_id = discord_user_id
)
try:
oauth = query.one()
except(NoResultFound):
discord_user_login = discord_name
oauth = OAuth(provider = blueprint.name,
provider_user_id = discord_user_id,
provider_user_login = discord_user_login,
token=token,
)
if current_user.is_anonymous:
if oauth.user:
login_user(oauth.user)
else:
user = User(username = discord_name)
oauth.user = user
db.session.add_all([user, oauth])
db.session.commit()
login_user(user)
else:
if oauth.user:
if current_user != oauth.user:
url = url_for("auth.merge", username = oauth.user.username)
return redirect(url)
else:
oauth.user = current_user
db.session.add(oauth)
db.commit()
return redirect(url_for("main.profile"))
The google code is the same as the discord one and my redirect uri is localhost:5000/login/"oauthprovider"/authorized. For some reason the discord user isnt registered in the database?
I took me many hours to figure it out because there's literally 0 guides for this, but this the approach that worked for me in the end. This is an extract from my source, so you can see the full implementation here. I'm still working out some kinks like trying to be able to turn off os.environ['OAUTHLIB_INSECURE_TRANSPORT'] but I can login at least.
#REST_API.route('/register', methods=['GET', 'POST'])
def register():
discord_data = None
if discord.authorized:
discord_info_endpoint = '/api/users/#me'
try:
discord_data = discord.get(discord_info_endpoint).json()
except oauthlib.oauth2.rfc6749.errors.TokenExpiredError:
pass
#REST_API.route('/discord')
def discord_login():
return redirect(url_for('discord.login'))
if __name__ == "__main__":
discord_client_id = os.getenv("DISCORD_CLIENT_ID")
discord_client_secret = os.getenv("DISCORD_CLIENT_SECRET")
REST_API.secret_key = os.getenv("secret_key")
discord_blueprint = make_discord_blueprint(
client_id = discord_client_id,
client_secret = discord_client_secret,
scope = ["identify"],
)
REST_API.register_blueprint(discord_blueprint,url_prefix="/discord")

[Flask]-Customising Flask-user login

Have been working on Flask-user for the user management system, But I couldn't see a way to customise the login page and registration page. I installed flask-user using pip
pip install flask-user
Any help would be appreciated
In templates folder, create a new folder named flask_user and you can copy the login.html and register.html pages used by flask-user to the newly created folder. Then you can modify them as per your requirements. These files will override the pages used by flask-user.
From official flask-user documentation
You can get a boilerplate (an app example) at https://github.com/lingthio/Flask-User-starter-app
Other resources to learn
1. Flask-mega tutorial
Here are a few resources online that can really help you customizing your user-login and also other topics:
https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-v-user-logins
This tutorial uses openid but explains everything with code examples i.e.
app/views.py
from flask import render_template, flash, redirect, session, url_for, request, g
from flask_login import login_user, logout_user, current_user, login_required
from app import app, db, lm, oid
from .forms import LoginForm
from .models import User
#app.route('/login', methods=['GET', 'POST'])
#oid.loginhandler
def login():
if g.user is not None and g.user.is_authenticated:
return redirect(url_for('index'))
form = LoginForm()
if form.validate_on_submit():
session['remember_me'] = form.remember_me.data
return oid.try_login(form.openid.data, ask_for=['nickname', 'email'])
return render_template('login.html',
title='Sign In',
form=form,
providers=app.config['OPENID_PROVIDERS'])
2. Exploreflask.com
Complete free step by step guide. Also comes with a full chapter for your problem in the 'users' chapter.
Follow the official link
Default flask-user BasicApp has email based authorization.
We can change into user based authorization
Change the model
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
active = db.Column('is_active', db.Boolean(), nullable=False, server_default='1')
username = db.Column(db.String(100, collation='NOCASE'), nullable=False, unique=True)
password = db.Column(db.String(255), nullable=False, server_default='')
first_name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
last_name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
# Define the relationship to Role via UserRoles
roles = db.relationship('Role', secondary='user_roles')
In config change the following lines:
USER_ENABLE_EMAIL = False # Enable email authentication
USER_ENABLE_USERNAME = True # Disable username authentication
Insert the Default two users by Username. Replace Existing code by following code.
if not User.query.filter(User.username == 'member').first():
user = User(
username='member',
password=user_manager.hash_password('Password1'),
)
db.session.add(user)
db.session.commit()
# Create 'admin#example.com' user with 'Admin' and 'Agent' roles
if not User.query.filter(User.username == 'admin').first():
user = User(
username='admin',
password=user_manager.hash_password('Password1'),
)
user.roles.append(Role(name='Admin'))
user.roles.append(Role(name='Agent'))
db.session.add(user)
db.session.commit()

flask-dance: multiple auth-providers

I have successfully followed the examples in the documentation of Flask-Dance to add Oauth from GitHub, and manage the users with SQLAlchemy. However, I am unable to add more providers. I tried to simply add another blueprint for Twitter and registering it, but I get various errors when trying to login with Twitter. Also, I end up with lots of duplicated code, so this is not a good approach.
Is there a better way to add another provider?
import sys
from flask import Flask, redirect, url_for, flash, render_template
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm.exc import NoResultFound
from flask_dance.contrib.github import make_github_blueprint, github
from flask_dance.consumer.backend.sqla import OAuthConsumerMixin, SQLAlchemyBackend
from flask_dance.consumer import oauth_authorized, oauth_error
from flask_login import (
LoginManager, UserMixin, current_user,
login_required, login_user, logout_user
)
# setup Flask application
app = Flask(__name__)
app.secret_key = "supersekrit"
blueprint = make_github_blueprint(
client_id="my-key-here",
client_secret="my-secret-here",
)
app.register_blueprint(blueprint, url_prefix="/login")
# setup database models
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///multi.db"
db = SQLAlchemy()
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(256), unique=True)
# ... other columns as needed
class OAuth(db.Model, OAuthConsumerMixin):
user_id = db.Column(db.Integer, db.ForeignKey(User.id))
user = db.relationship(User)
# setup login manager
login_manager = LoginManager()
login_manager.login_view = 'github.login'
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
# setup SQLAlchemy backend
blueprint.backend = SQLAlchemyBackend(OAuth, db.session, user=current_user)
# create/login local user on successful OAuth login
#oauth_authorized.connect_via(blueprint)
def github_logged_in(blueprint, token):
if not token:
flash("Failed to log in with {name}".format(name=blueprint.name))
return
# figure out who the user is
resp = blueprint.session.get("/user")
if resp.ok:
username = resp.json()["login"]
query = User.query.filter_by(username=username)
try:
user = query.one()
except NoResultFound:
# create a user
user = User(username=username)
db.session.add(user)
db.session.commit()
login_user(user)
flash("Successfully signed in with GitHub")
else:
msg = "Failed to fetch user info from {name}".format(name=blueprint.name)
flash(msg, category="error")
# notify on OAuth provider error
#oauth_error.connect_via(blueprint)
def github_error(blueprint, error, error_description=None, error_uri=None):
msg = (
"OAuth error from {name}! "
"error={error} description={description} uri={uri}"
).format(
name=blueprint.name,
error=error,
description=error_description,
uri=error_uri,
)
flash(msg, category="error")
#app.route("/logout")
#login_required
def logout():
logout_user()
flash("You have logged out")
return redirect(url_for("index"))
#app.route("/")
def index():
return render_template("home.html")
# hook up extensions to app
db.init_app(app)
login_manager.init_app(app)
if __name__ == "__main__":
if "--setup" in sys.argv:
with app.app_context():
db.create_all()
db.session.commit()
print("Database tables created")
else:
app.run(debug=True)
Here is a sample project that uses multiple providers simultaneously: flask-dance-multi-provider. You can read through the code to see how it works. Does that help?

How to secure Flask-Admin if my only user is going to be the Admin?

I have seen plenty of solutions online, however all of them addressed more complex apps which allow external users to create accounts. In my case the only user will be the admin. How do I secure the /admin routes created by Flask-Admin in an efficient way?
You can use Flask-Login for that. I usually add a route to the AdminIndexView class that handles the login if the user isn't logged in, yet. Otherwise the default admin page will be shown.
from flask import Flask
from flask_login import LoginManager
from flask_admin import Admin
app = Flask(__name__)
login_manager = LoginManager(app)
login_manager.session_protection = 'strong'
login_manager.login_view = 'admin.login'
admin = Admin(app, index_view=MyIndexView())
The definition of MyAdminView can look like this:
from flask_admin import AdminIndexView, expose, helpers
class FlaskyAdminIndexView(AdminIndexView):
#expose('/')
def index(self):
if not login.current_user.is_authenticated:
return redirect(url_for('.login'))
return super(MyAdminIndexView, self).index()
#expose('/login', methods=['GET', 'POST'])
def login(self):
form = LoginForm(request.form)
if helpers.validate_form_on_submit(form):
user = form.get_user()
if user is not None and user.verify_password(form.password.data):
login.login_user(user)
else:
flash('Invalid username or password.')
if login.current_user.is_authenticated:
return redirect(url_for('.index'))
self._template_args['form'] = form
return super(MyAdminIndexView, self).index()
#expose('/logout')
#login_required
def logout(self):
login.logout_user()
return redirect(url_for('.login'))
This integrates Flask-Login unobtrusively in the Flask-Admin interface. You will still need to implement the user and password verification like described in the Flask-Login documentation.
EDIT
To prevent unauthorized access to your admin routes create a ModelView class for each view and add a function is_accessible() with the following code:
def is_accessible(self):
if (not login.current_user.is_active or not
login.current_user.is_authenticated):
return False
return True

How can I create a django url specific to a user?

I am interested in making my urls customised for each user, something like
username.mysite.com/home but I am not sure how to do this with django.
I am also curious if this might work in development (so as to have username.localhost:8000/home) or not.
Thank you.
There is another way as well. What you can do is have Middleware that gets the url, parses the subdomain and then renders a user profile page.
This is assuming you are using a custom profile page and not the default profile page.
#in yourapp.middleware
from django.contrib.auth.models import User
import logging
import yourapp.views as yourappviews
logger = logging.getLogger(__name__)
class AccountMiddleware(object):
def process_request(self, request):
path = request.META['PATH_INFO']
domain = request.META['HTTP_HOST']
pieces = domain.split('.')
username = pieces[0]
try:
user = User.objects.get(username=username)
if path in ["/home","/home/"]:
return yourappviews.user_profile(request, user.id)
#In yourapp.views.py
def user_profile(request,id):
user = User.objects.get(id=id)
return render(request, "user_profile.html", {"user": user})
#In settings.py
MIDDLEWARE_CLASSES = (
#... other imports here
'yourapp.middleware.AccountMiddleware'
)