How to create admin pages in Flask - python

I currently have a simple Flask application that authenticates registered users with flask_login and the #login_required decorator. So certain pages are accessible only to registered users.
However, I want to create another layer of authentication where only admin users have access to the admin dashboard and admin login pages.
Here is my views.py file.
from flask import (render_template, flash, url_for, redirect)
from models import (db, app, User)
from forms import (RegisterForm, LoginForm)
from flask_login import (LoginManager, logout_user, login_user, login_required,
current_user)
login_manager = LoginManager(app)
login_manager.login_view = "login"
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
#app.route("/")
#app.route("/home")
def home():
return render_template("home.html", title="home")
#app.route("/register", methods = ["GET", "POST"])
def register():
form = RegisterForm()
if form.validate_on_submit():
username = form.username.data
email = form.email.data
password = form.password.data
confirm_password = form.confirm_password.data
user = User(username, email, password, confirm_password)
db.session.add(user)
db.session.commit()
flash("Thanks for registering, {}".format(username.capitalize()))
return redirect(url_for("login"))
return render_template("register.html", title="register",
form=form)
#app.route("/login", methods = ["GET", "POST"])
def login():
form = LoginForm()
users = User.query.all()
if form.validate_on_submit() and users != []:
email = form.email.data
password = form.password.data
user = User.query.filter_by(email=email).first()
if user.checking_password(password) and user is not None:
login_user(user)
flash("Thanks for logging in!")
return redirect(url_for("profile"))
return render_template("login.html", title="login",
form = form)
if __name__ == "__main__":
app.run(debug=True)
I tried reusing flask_login imports by renaming them with an admin_ prefix, and applying them to an admin view function, but it didn't seem to work.
from flask_login import (LoginManager as AdminManager,
logout_user as admin_logout_user,
login_user as admin_login_user,
login_required as admin_login_required,
current_user as admin_user)
admin_manager = AdminManager(app)
admin_manager.login_view = "admin_login"
#admin_manager.user_loader
def load_admin(admin_id):
return Admin.query.get(int(admin_id))
If anyone knows how I can do this, I'd be very grateful.
This is a lot easier to do in Django as an admin user page is generated out of the box, so to speak.

Typically you don’t have a separate login for admin users. Rather, you have roles on each user. So if a user has an admin role, they are able to see certain pages. Then you control access to specific routes using flask-user and the #roles_required decorator.

Related

AttributeError: 'list' object has no attribute 'is_active'

I am building a website using Flask and when I try to log in and signup I get this error AttributeError: 'list' object has no attribute 'is_active'
an error like this appears when I have used login_user in the flask_login library
here I use firestore database
I have tried various solutions on StackOverflow but none of them worked, I'm very confused now
this is my code
__ini__.py
from flask import Flask
from flask_login import UserMixin
from flask_login import LoginManager
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = 'adjadlkahd'
db = connect_database()
from .views import views
from .auth import auth
from .models import User
#check user login
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
#create user model
#login_manager.user_loader
def load_user(id):
return User.get_user_id(id)
#register blueprints
app.register_blueprint(views, url_prefix='/')
app.register_blueprint(auth, url_prefix='/')
return app
cred = credentials.Certificate("websites\serviceAccountKey.json")
firebase_admin.initialize_app(cred)
def connect_database():
db = firestore.client()
return db
auth.py
from flask import Blueprint, render_template, request, redirect, url_for
from .models import User
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import login_user, login_required, logout_user, current_user
auth = Blueprint('auth', __name__)
#auth.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
email = request.form.get('email')
password = request.form.get('password')
user = User.log_in(email)
if user:
if check_password_hash(user[0].to_dict()['password'], password):
login_user(user, remember=True)
redirect(url_for('views.home'))
else:
return "<h1>paass salah</h1>"
else:
return "<h1>user tidak ada</h1>"
return render_template('login.html')
#auth.route('/logout')
#login_required
def logout():
logout_user()
return redirect(url_for('auth.login'))
#auth.route('/signup', methods=['GET', 'POST'])
def signup():
if request.method == 'POST':
username = request.form.get('username')
email = request.form.get('email')
password = request.form.get('password')
if User.check_user(email):
return "<h1>email already exist</h1>"
new_user = User(username=username, email=email,
password=generate_password_hash(password)).create_user()
login_user(new_user, remember=True)
return redirect(url_for('views.home'))
return render_template('signup.html')
models.py
from flask_login import UserMixin
from . import connect_database
import uuid
db = connect_database()
class User(UserMixin):
def __init__(self, username, email, password):
self.username = username
self.email = email
self.password = password
def create_user(self):
db.collection('user').document(self.username).set({
'id' : str(uuid.uuid4()),
'username': self.username,
'email': self.email,
'password': self.password
})
def log_in(email):
user = db.collection('user').where('email', '==', email).get()
return user
def check_user(email):
user = db.collection('user').where('email', '==', email).get()
return bool(user)
def get_user_id(id):
return db.collection('user').document(id).get()
views.py
from flask import Blueprint
from flask_login import login_required, current_user
views = Blueprint('views', __name__)
#views.route('/')
#login_required
def home():
return "<h1>logged in</h1>"
For flask login to work you need to have a user class that implements some properties and methods. When you use login_user method the argument should be an instance of that user class.
make user that your method def log_in(email) returns a user object not a list.
def log_in(email):
user = db.collection('user').where('email', '==', email).get()
return user # <-- most probably is a list.
Read the items form the user collection
Create an instance of the user class
fill the properties of the user class from the collection
return the instance

Flask session redirects to login page

I am currently in the process of building a flask based LAN chatting app (using sqlite3 to store usernames and socketio for messaging) and am having trouble implementing sessions correctly.
I have followed both this guide:
https://www.techwithtim.net/tutorials/flask/sessions/
and read the documentation here https://flask-session.readthedocs.io/en/latest/ but am somehow still not getting my code to work:
In the login page, when the username is posted, I want users to be redirected to the chat-page.html, but this does not occur. Instead they are redirected to the login page, and I cannot figure out why:
from flask import Flask, render_template, request, flash, session, redirect, url_for
#creating the routes
#app.route('/login', methods=["POST", "GET"])
def login_form():
if request.method == "POST":
username = request.form.get("user_name")
session["user"] = username
return redirect(url_for('chat_page'))
else:
if "user" in session:
return redirect(url_for('chat_page'))
return render_template('login.html')
#app.route('/chat-page')
def chat_page():
if "user" in session:
username = session["user"]
return render_template('chat-page.html', Uname=username)
return redirect(url_for('login_form'))
#app.route("/logout")
def logout():
session.pop("user", None)
flash("You have been logged out!")
return redirect(url_for('login_form'))
from flask_session import Session
app = Flask(__name__)
Session(app)
When I tried to debug your code, I ran into issues with the secret key. I don't know how or where you set it or call your app, but here is my complete code that worked. They key might be to set app.config['SESSION_TYPE'] = 'filesystem'. I used this answer to solve it.
from flask import Flask, render_template, request, flash, session, redirect, url_for
from flask_session import Session
app = Flask(__name__)
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SECRET_KEY'] = 'secret key'
Session(app)
# creating the routes
#app.route('/login', methods=["POST", "GET"])
def login_form():
if request.method == "POST":
username = request.form.get("user_name")
session["user"] = username
return redirect(url_for('chat_page'))
else:
if "user" in session:
return redirect(url_for('chat_page'))
return render_template('login.html')
#app.route('/chat-page')
def chat_page():
if "user" in session:
return '<div>chat page!</div>'
return redirect(url_for('login_form'))
#app.route("/logout")
def logout():
session.pop("user", None)
flash("You have been logged out!")
return redirect(url_for('login_form'))
app.run(debug=True)
What I'm saying is, your redirect logic is completely fine. The issue must be with the session.

Flask - MongoDB - Protect routes

I am new to Flask and I built a basic web app using Flask. This app will be used by a single user. The user must be connected in order to access any routes. What would be the easiest and most secure way to create new routes on my app and make sure that the user is logged in before they are able to access the page?
I added this route and I am able to access the page even if I am logged in.
#login_required
#app.route('/secret')
def secret():
return "hello world"
app.py
from flask import Flask, render_template, url_for, request, session, redirect
from flask_pymongo import PyMongo
import bcrypt
app = Flask(__name__)
app.config['MONGO_DBNAME'] = xxx'
app.config['MONGO_URI'] = 'xxxx'
mongo = PyMongo(app)
#app.route('/')
def index():
if 'username' in session:
return 'You are logged in as ' + session['username']
return render_template('index.html')
#app.route('/login', methods=['POST'])
def login():
users = mongo.db.users
login_user = users.find_one({'name' : request.form['username']})
if login_user:
if bcrypt.hashpw(request.form['pass'].encode('utf-8'), login_user['password']) == login_user['password']:
session['username'] = request.form['username']
return redirect(url_for('index'))
return 'Invalid username/password combination'
#app.route('/register', methods=['POST', 'GET'])
def register():
if request.method == 'POST':
users = mongo.db.users
existing_user = users.find_one({'name' : request.form['username']})
if existing_user is None:
hashpass = bcrypt.hashpw(request.form['pass'].encode('utf-8'), bcrypt.gensalt())
users.insert({'name' : request.form['username'], 'password' : hashpass})
session['username'] = request.form['username']
return redirect(url_for('index'))
return 'That username already exists!'
return render_template('register.html')
#app.route('/logout')
def logout():
session.pop('username', None)
return render_template('index.html')
if __name__ == '__main__':
app.secret_key = 'mysecret'
app.run(debug=True, port='3500')
You can use the flask-login module for this. It handles user authentication, allowing routes to be protected with the #login_required decorator.
I recommend reading the documentation, as you need to provide a class with certain properties to represent your users and you need to implement a method that returns a user based on a given identifier.
When a user is logged in, the templates can access the current_user variable and it's properties (as defined by you; name, email etc).
Here is a simple example of the python code (I have not included the static files or templates).
from flask import Flask, render_template, request, url_for, request, redirect, abort
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
from flask_pymongo import PyMongo
from flask_bcrypt import Bcrypt
from urllib.parse import urlparse, urljoin
import sys
# Import User classes
from user import User, Anonymous
# Create app
app = Flask(__name__)
# Configuration
app.config['MONGO_DBNAME'] = 'database_name'
app.config['MONGO_URI'] = 'mongo_database_uri'
app.secret_key = 'change this before production'
# Create Pymongo
mongo = PyMongo(app)
# Create Bcrypt
bc = Bcrypt(app)
# Create login manager
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.anonymous_user = Anonymous
login_manager.login_view = "login"
# ROUTES
#app.route('/')
def index():
return render_template('index.html')
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
if current_user.is_authenticated:
return redirect(url_for('/index'))
return render_template('login.html')
users = mongo.db.users
user_data = users.find_one({'email': request.form['email']}, {'_id' : 0 })
if user_data:
if bc.check_password_hash(user_data['password'], request.form['pass']):
user = User(user_data['title'], user_data['first_name'], user_data['last_name'], user_data['email'], user_data['password'], user_data['id'])
login_user(user)
#Check for next argument (direct user to protected page they wanted)
next = request.args.get('next')
if not is_safe_url(next):
return abort(400)
return redirect(next or url_for('profile'))
return 'Invalid email or password'
#app.route('/register', methods=['POST', 'GET'])
def register():
if request.method == 'POST':
users = mongo.db.users
existing_user = users.find_one({'email' : request.form['email']}, {'_id' : 0 })
if existing_user is None:
logout_user()
hashpass = bc.generate_password_hash(request.form['pass']).decode('utf-8')
new_user = User(request.form['title'], request.form['first_name'], request.form['last_name'], request.form['email'], hashpass)
login_user(new_user)
users.insert_one(new_user.dict())
return redirect(url_for('profile'))
return 'That email already exists!'
return render_template('register.html')
#app.route('/profile', methods=['GET'])
#login_required
def profile():
return render_template('profile.html')
#app.route('/list', methods=['GET'])
#login_required
def list():
#if current_user.id:
log(current_user.is_authenticated)
all_users = mongo.db.users.find({}, {'_id' : 0 })
return render_template('list.html', users = all_users)
#app.route('/logout', methods=['GET'])
#login_required
def logout():
logout_user()
return redirect(url_for('index'))
# Login Manager requirements
#login_manager.user_loader
def load_user(userid):
# Return user object or none
users = mongo.db.users
user = users.find_one({'id': userid}, {'_id' : 0 })
if user:
log(user)
return User(user['title'], user['first_name'], user['last_name'], user['email'], user['password'], user['id'])
return None
# Safe URL
def is_safe_url(target):
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
return test_url.scheme in ('http', 'https') and \
ref_url.netloc == test_url.netloc
# Run app
if __name__ == '__main__':
app.run(debug=True)
I have a functioning basic template app using flask, flask-login and flask-pymongo at https://github.com/chriswilson1982/flask-mongo-app

Implementing a login page with Flask

Does Flask have any built-in support for user login/logout functionality? I've found this add-on project, but it only seems to provide the pieces to build your own login system. It doesn't seem to be a complete system.
I'm new to Flask, and I'm coming from Django where this is all built-in, so I'm finding it a little baffling that this basic functionality is missing.
Using some incomplete examples I've found, I'm trying to implement an index page that redirects to a login page for anonymous users, and after a successful login, redirects page to the index page. This is what I currently have implemented as a "hello world" login/logout example:
#!/usr/bin/env python
import flask
from flask import Flask, Response, render_template
from flask.ext.login import LoginManager, UserMixin, login_required, login_user, logout_user
app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
class User(UserMixin):
# proxy for a database of users
user_database = {
"JohnDoe": ("JohnDoe", "John"),
"JaneDoe": ("JaneDoe", "Jane"),
}
def __init__(self, username, password):
self.id = username
self.password = password
#property
def name(self):
return self.id
#classmethod
def get(cls, id):
ret = cls.user_database.get(id)
if ret is not None:
return cls(*ret)
#login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
#login_manager.request_loader
def load_user(request):
token = request.headers.get('Authorization')
if token is None:
token = request.args.get('token')
if token is not None:
username,password = token.split(":") # naive token
user_entry = User.get(username)
if (user_entry is not None):
user = User(user_entry[0],user_entry[1])
if (user.password == password):
return user
return None
#app.route("/",methods=["GET"])
#login_manager.request_loader
def index():
if load_user(flask.request):
return render_template('index.html')
else:
return flask.redirect(flask.url_for('login'))
#return Response(response="Hello World!",status=200)
#app.route('/login', methods=['GET', 'POST'])
def login():
if flask.request.method == 'GET':
return '''
<form action='login' method='POST'>
<input type='text' name='email' id='email' placeholder='email'></input>
<input type='password' name='pw' id='pw' placeholder='password'></input>
<input type='submit' name='submit'></input>
</form>
'''
email = flask.request.form['email']
user = User.get(email)
if user and flask.request.form['pw'] == user.password:
login_user(user)
return flask.redirect(flask.url_for('index'))
return 'Bad login'
#app.route("/logout")
#login_required
def logout():
logout_user()
return flask.redirect(flask.url_for('index'))
if __name__ == '__main__':
app.config["SECRET_KEY"] = "ITSASECRET"
app.run(port=5000, debug=True)
However, it doesn't work, because even though it seems to login successfully, when it redirects to the index page, it can't lookup the user from the session and redirects back to the login page. What am I doing wrong?
Flask-Login is a very basic login manager that is built upon with a few other user management frameworks. I have been using Flask-User in production for over 1.5 years with about 30K users and have not had any problems with it (which uses flask-login under the hood). The maintainer is active and has responded to my issues in a timely manner.
It handles user login, registration, lost password, and even email confirmations if so desired. It comes with some pre-built forms if you don't want to mess with that but is easily customizable if you do.
Sounds like maybe when login_user gets called the login is not being persisted. I would make sure however you're doing your sessions is actually storing the login.

flask-sqlalchemy - User Query 'BaseQuery' object has no attribute 'password'

I'm new to Python and Flask.
I'm following this tutorial http://douglasstarnes.com/index.php/2015/05/27/easy-authentication-with-flask-login/ to have registration and login pages and have slightly modified it to hash the passwords on registration and to verify the password against the hash on login.
Initial password registration hashing works, but verifying the hashed password stored in the database against the one given in plain text via a login form does not.
The error I am receiving is against the following line on /login page and for the following reason:
if user.count() == 1 and check_password_hash(user.password, password) == True:
AttributeError: 'BaseQuery' object has no attribute 'password'
I cannot work out why I'm receiving this error. The user gets successfully queried from the database, and the user has it's hashed password within the password column.
The query I'm using and method of returning data from the password column are similar to that included in the documentation http://flask-sqlalchemy.pocoo.org/2.1/queries/#querying-records
This is my views.py (/login and the erroring line are towards the bottom)
from flask import Flask, render_template, request, abort, redirect, url_for, flash
from flask.ext.login import LoginManager, UserMixin, login_user, logout_user, login_required
from flask.ext.sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from app import app
import os
login_manager = LoginManager(app)
login_manager.init_app(app)
login_manager.login_view = 'login'
login_manager.session_protection = "strong"
db = SQLAlchemy(app)
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String)
password = db.Column(db.String)
#login_manager.user_loader
def user_loader(user_id):
user = User.query.filter_by(id=user_id)
if user.count() == 1:
return user.one()
return None
#app.before_first_request
def init_request():
db.create_all()
#app.route('/secret')
#login_required
def secret():
return render_template('secret.html')
#app.route('/logout')
def logout():
logout_user()
return redirect(url_for('index'))
#app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
return render_template('register.html')
elif request.method == 'POST':
username = request.form['txtUsername']
password = request.form['txtPassword']
user = User.query.filter_by(username=username)
if user.count() == 0:
hashed_password = generate_password_hash(password)
user = User(username=username, password=hashed_password)
db.session.add(user)
db.session.commit()
flash('You have registered the username {0}. Please login'.format(username))
return redirect(url_for('login'))
else:
flash('The username {0} is already in use. Please try a new username.'.format(username))
return redirect(url_for('register'))
else:
abort(405)
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html', next=request.args.get('next'))
elif request.method == 'POST':
username = request.form['txtUsername']
password = request.form['txtPassword']
user = User.query.filter_by(username=username)
if user.count() == 1 and check_password_hash(user.password, password) == True:
login_user(user.one(), remember=True)
flash('Welcome back {0}'.format(username))
try:
next = request.form['next']
return redirect(next)
except:
return redirect(url_for('index'))
else:
flash('Invalid login')
return redirect(url_for('login'))
else:
return abort(405)
#app.route('/')
def index():
return render_template('index.html')
Does anyone know why I cannot access the password column for the user, or in fact any column? I've tried all 3 within the database, ID, username and password.
#login_manager.user_loader
def user_loader(user_id):
user = User.query.filter_by(id=user_id).first()
if user:
return user
return None
Applying the .first() executes the query, instead of storing the Query object
it will return only the first result.
.all() returns all
edit:
or you could use user = User.query.get(user_id) assuming user_id is defined as PK
In your Login Function it should be
user = User.query.filter_by(username=username).first()
if user and check_password_hash(user.password, password) == True:
login_user(user)
Now user references a real User object, as opposed to a stored query. you cannot access a User Object attribute (password) from a Query Object
Don't miss .first() or .all() after the SQLQLCHAMY ORM query.
This is cause of such error always.

Categories