Python3 Flask & Sqlite3 - Can't insert into database - python-3.x

I can't make my code work. I've been looking around for tutorials and guides but to no avail. If anyone see the mistake I'm doing, it would be very helpful.
Here is the folder structure:
/
application.py
database.db
/routes
__init__.py
signup.py
/templates
signup.html
Here is my signup.py:
import sqlite3
from flask import Blueprint, render_template, redirect, session, request, flash
from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
from werkzeug.security import check_password_hash, generate_password_hash
# Set Blueprints
signup = Blueprint('signup', __name__,)
#signup.route("/signup", methods=["GET", "POST"])
def signupFunction():
# Forget any user_id
session.clear()
# User reached route via POST (as by submitting a form via POST)
if request.method == "POST":
try:
email = request.form.get("email")
username = request.form.get("username")
password = request.form.get("password")
confirmPassword = request.form.get("confirm-password")
with sqlite3.connect("./database") as connection:
print("Opened database successfully")
current = connection.cursor()
# Ensure email was submitted
if not email:
return flash("must provide email")
# Ensure username was submitted
if not username:
return flash("must provide username")
# Ensure password was submitted
if not password:
return flash("must provide password")
# Ensure confirm password is correct
if password != confirmPassword:
return flash("The passwords don't match")
# Query database for username if already exists
current.execute("SELECT * FROM users WHERE username = :username", username=username)
if current.fetchall() == username:
return flash("Username already taken")
# Insert user and hash of the password into the table
current.execute("INSERT INTO users(username, hash) VALUES (:username, :hash)", username=username, hash=generate_password_hash(password))
current.commit()
# Query database for username
current.execute("SELECT * FROM users WHERE username = :username", username=username)
rows = current.fetchall()
# Remember which user has logged in
session["user_id"] = rows[0]["id"]
# Commit to databse
connection.commit()
print("Database operation succesful")
except:
connection.rollback()
print("Error in sign up operation")
finally:
# Close database connection
connection.close()
# Redirect user to home page
return redirect("/")
# User reached route via GET (as by clicking a link or via redirect)
else:
return render_template("signup.html")
here is my signup.html
{% extends "layout.html" %}
{% block title %}
Sign Up
{% endblock %}
{% block main %}
<p>Register or <a href='/signin'>login</a></p><br>
<form action="/signup" method="post">
<div>
<p>Username (6 characters min).</p>
<input id="username" autocomplete="off" autofocus class="form-control" name="username" placeholder="Username" type="text">
</div>
<br>
<div>
<p>Email.</p>
<input id="email" autocomplete="off" autofocus class="form-control" name="email" placeholder="Email" type="text">
</div>
<br>
<div>
<p>Password (7 to 15 characters, 1 numeric, 1 special).</p>
<input id="password" name="password" placeholder="Password" type="password">
</div>
<div>
<input id="confirm-pass" name="confirm-password" placeholder="Confirm Password" type="password">
</div>
<br>
<button id="submit" type="submit">Sign Up</button>
<br>
</form>
<script>
// Password must be between 7 to 15 characters which contain at least one numeric digit and a special character
document.querySelector("#submit").disabled = true;
document.querySelector("#confirm-pass").onkeyup = function() {
let password = document.querySelector("#password").value;
let username = document.querySelector("#username").value;
let email = document.querySelector("#email").value;
let confirmation = document.querySelector("#confirm-pass").value;
if (password.match(/^^(?=.*[0-9])(?=.*[!##$%^&*])[a-zA-Z0-9!##$%^&*]{7,15}$/i)) {
document.querySelector("#submit").disabled = true;
if (email.match(/^(([^<>()\[\]\.,;:\s#\"]+(\.[^<>()\[\]\.,;:\s#\"]+)*)|(\".+\"))#(([^<>()[\]\.,;:\s#\"]+\.)+[^<>()[\]\.,;:\s#\"]{2,})$/i)) {
document.querySelector("#submit").disabled = true;
if (username.length > 5) {
document.querySelector("#submit").disabled = true;
if (confirmation === password) {
document.querySelector("#submit").disabled = false;
}
}
}
}
else {
document.querySelector('#submit').disabled = true;
}
}
</script>
{% endblock %}
Here is the output of my console:
* Serving Flask app "application.py"
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [13/Dec/2020 19:23:44] "GET /signup HTTP/1.1" 200 -
127.0.0.1 - - [13/Dec/2020 19:23:44] "GET /static/styles.css HTTP/1.1" 200 -
Opened database successfully
Error in sign up operation
So the problem lies in signup.py

The try/except block is not doing you any favors. It can error on any line and all you'll know is "Error in signup operation". There will be problems because of invalid execute syntax. execute takes at most 2 arguments, the first is the sql to be executed. If it contains placeholders, the second argument is a set. Since these sqls are using named style, the second argument should be a dictionary, as per this example from the doc.
# And this is the named style:
cur.execute("select * from people where name_last=:who and age=:age", {"who": who, "age": age})
You might consider "catching" the exception and adding more info to the message, something like:
except Exception as e:
print("Error in sign up operation: ",e)

Related

Get a string variable from views to urls in Django

I am a beginner at Python and Django, I am trying to solve a problem where when a user searches if that search is valid, I want to get that search = var_X, in my URL like:
www.website.com/search/var_X
or something like,
www.website.com/search/<df.item>
Views.py
def views_search(request):
temp_dict = {}
if request.method == 'POST':
var_X = request.POST['var_X']
try:
df = wsc.Summary(temp_dict, var_X)
except Exception as e:
df = "ERROR"
return render(request,'search.html', {'df' : df})
else:
return render(request,'home.html', {'var_X' : "Not Found"})
Urls
from django.urls import path
from . import views
from requests import *
urlpatterns = [
path('search/<var_X>', views.views_search, name="url-search"),
]
HTML
<form action="{% url 'url-search' var_X %}"class="d-flex" method="POST">
{% csrf_token %}
<input class="form-control me-2" type="search" placeholder="Enter String" aria-label="Search" name="var_X">
<button class="btn btn-outline-secondary" type="submit">Search</button>
</form>
When Django renders a template it converts all template tags into html.
You won't be able to set the value of var_X in {% url 'url-search' var_X %} using python code on the client side.
My suggestion is to use javascript to set the form's action using the onclick html attribute.
<button ... onclick="this.form.action = {% url 'url-search' %} + document.getElementById('id-of-input-element').value;>"
This also means removing the action attribute from the input element.
From
<form action="{% url 'url-search' var_X %}"class="d-flex" method="POST">
to
<form class="d-flex" method="POST">
Because I don't know what your code does (because I don't know what df is. Use the following as only an example of what you could achieve.
(In my code snippets entering a search goes back to the same page to display results.)
urls.py
urlpatterns = [
path('search/', views.views_search, name="url-search"),
path('search/<str:var_X>', views.views_search)
]
The route 'search/' is needed in order to make {% url 'url-search' %} work on first page load.
The route 'search/<str:var_X>' matches any value after search/ into a variable called var_X as a string in the view function.
views.py
def views_search(request, var_X=None):
if request.method == 'POST':
pass
# do the stuff you need to do with the form..
return render(
request,
"views_search.html",
{
'var_X': var_X
}
)
The parameter var_X is None by default if the url doesn't have anything after search/ else it will have the search string.
views_search.html (wtv your html template is called)
<form class="d-flex" method="POST">
{% csrf_token %}
<input id="search_input" class="form-control me-2" type="search" placeholder="Enter String" aria-label="Search" name="var_X" value="">
<button class="btn btn-outline-secondary" type="submit" onclick="this.form.action = {% url 'url-search' %} + document.getElementById('search_input').value;">Search</button>
</form>
{% if var_X %}
<p>Showing results for '{{ var_X }}'.</p>
{% endif %}
with the above html you'll get this output:
Before pressing search button
After pressing search button
This also has the benefit that if you type something directly into the url it will search that too.
Hopefully this helps steer you in the right direction for what you're looking for.

current_user.is_authenticated always False (TACACS and flask_login)

Summary
I'm trying to implement a TACACS+ based flask_login system and I'm stuck because the current_user.is_authenticated is always false. I am not using any local databases - the username and password that is submitted via my login form are directly passed to TACACS.
My custom User class implements the 3 attributes and 1 method described in flask_login's documentation here:
Below, you'll find the relevant code for each file so you can set this up in your own environment.
There is a lot of information below, so I wanted to share my actual question as clearly as possible:
How can I connect my custom User class to the current_user proxy?
The underlying issue with my implementation is that when I go to any flask routes with the #login_required decorator, the app thinks the user is not logged in and redirects them to the login page. I've determined it to be because the current_user.is_authenticated attribute is never True.
app.py
from flask import Flask, request, render_template, url_for, flash, redirect, session, Markup
from forms import LoginForm
from flask_login import LoginManager, login_user, logout_user, login_required, current_user, UserMixin
import user_auth as auth
# TODO: Save the user_dict to a pickle file so that users persist between service restarts
user_dict = {}
class User():
def __init__(self, username, password):
self.username = username
self.password = password
# Send TACACS authentication and authorization requests
priv = auth.login(self.username, self.password)
if priv:
self.is_authenticated = True
self.is_active = True
self.is_anonymous = False
if priv == 'admin':
self.priv_lvl = 15
elif priv == 'user':
self.priv_lvl = 10
elif priv == 'employee':
self.priv_lvl = 5
else:
self.is_authenticated = False
self.is_anonymous = True
self.priv_lvl = -1
def get_id(self):
return self.username
app = Flask(__name__)
app.secret_key = 'mysecretkey!'
app.static_url_path = 'static/'
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'index'
#login_manager.user_loader
def load_user(user):
global user_dict
if user in user_dict.keys():
return user_dict[user]
#app.route('/', methods=['GET', 'POST'])
def index():
form = LoginForm()
try:
if user.is_authenticated:
return render_template('test.html')
except UnboundLocalError:
user = None
pass
if form.validate_on_submit():
username = form.username.data
password = form.password.data
user = User(username, password)
username = password = None
print(f"User {user.username} logged in. User authenticated: {user.is_authenticated}")
print(f"Is current_user authenticated? {current_user.is_authenticated}")
if user.priv_lvl >= 0:
# SOLUTION -> The following was missing
login_user(user, remember=form.remember.data)
user_dict.update({
user.username : user
})
# END SOLUTION
print("User is authorized to view test.html.")
return render_template('test.html', current_user=current_user, user=user)
else:
flash(f'Invalid login', 'error')
return render_template('index.html', title='Login Required', form=form, user=user)
return render_template('index.html', title='Login Required', form=form, user=user)
#app.route("/home", methods=['GET'])
#login_required
def home():
return render_template('test.html')
user_auth.py
from tacacs_plus.client import TACACSClient
from tacacs_plus.flags import TAC_PLUS_ACCT_FLAG_START, TAC_PLUS_ACCT_FLAG_WATCHDOG, TAC_PLUS_ACCT_FLAG_STOP
import socket
ISE = 'my.ip.add.rr'
auth_key = 'password'
def login(username, password):
cli = TACACSClient(ISE, 49, auth_key, timeout=10, family=socket.AF_INET)
authen = cli.authenticate(username, password)
if authen.valid:
author = cli.authorize(username, arguments=[b"service=", b"protocol="])
if author.valid:
role = author.arguments[0].decode('utf-8')
if 'user' in role.lower():
priv = 'user'
elif 'admin' in role.lower():
priv = 'admin'
else:
print("User has authenticated successfully, but failed authorization.")
priv = 'employee'
else:
print("User failed authentication.")
priv = None
return priv
forms.py
from flask_wtf import FlaskForm
from wtforms import Form, StringField, SubmitField, BooleanField
from wtforms.fields import PasswordField
from wtforms.validators import DataRequired
class LoginForm(FlaskForm):
username = StringField("Username", validators=[DataRequired()])
password = PasswordField("Password", validators=[DataRequired()])
remember = BooleanField("Remember Me")
submit = SubmitField("Login")
index.html
<div id='login-container' class='container'>
<form method="POST" action="">
{{ form.csrf_token }}
{{ form.hidden_tag() }}
<fieldset>
<legend class="border-bottom mb-4">Login</legend>
<p>Use your TACACS credentials.</p>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alerts">
{% for message in messages %}
<div class="alert alert-warning" role="alert">{{ message }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<div class='form-group'>
{{ form.username.label(class="form-control-label") }}
{% if form.username.errors %}
{{ form.username(class="form-control form-control-lg is-invalid") }}
<div class='custom-invalid-feedback'>
{% for error in form.username.errors %}
<span>
{{ error }}
</span>
{% endfor %}
</div>
{% else %}
{{ form.username(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class='form-group'>
{{ form.password.label(class="form-control-label") }}
{% if form.password.errors %}
{{ form.password(class="form-control form-control-lg is-invalid") }}
<div class='custom-invalid-feedback'>
{% for error in form.password.errors %}
<span>
{{ error }}
</span>
{% endfor %}
</div>
{% else %}
{{ form.password(class="form-control form-control-lg") }}
{% endif %}
</div>
{{form.remember.label}}
{{form.remember}}
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</fieldset>
</form>
</div>
test.html
{% if user.is_authenticated %}
<h2>My custom user class is authenticated</h2>
{% else %}
<h2> My custom user class is NOT authenticated</h2>
{% endif %}
{% if current_user.is_authenticated %}
<h2> current_user is authenticated</h2>
{% else %}
<h2>current_user is NOT authenticated</h2>
{% endif %}
requirements.txt (in case you want to test in your own environment)
Jinja2==2.10
dominate==2.5.1
Flask==1.1.1
flask_login==0.5.0
flask_wtf==0.14.3
plotly==4.5.3
tacacs_plus==2.6
WTForms==2.2.1
Note: If you want to test in your own application and don't want to bother with TACACS, manually set priv equal to admin or user
When I login with valid credentials, here is what I see from my console:
User LetMeIn logged in. User authenticated: True
Is current_user authenticated? False
User is authorized to view test.html.
... And on the test.html page in my browser, I see
I figured it out after dissecting my code for the quadrillionth time.
Simply put, I was missing the login_user() function call. This is what associates the custom User class to the flask_login current_user association.
I also needed to create a simple local username storage method so that the required load_user function could work properly. I did this by adding a globally accessible dictionary that stores my custom User object as a value associated to a key that holds the username.
I will update the code snippets in my original post with these changes in hopes that my efforts will be useful for someone in the future. I couldn't find much online about integrating TACACS with flask_login.

Why is my function not printing anything in the CLI?

At the moment I am just trying to get the function to print what it has found in
the database, however I am not getting anything in the CLI when submitting
eywords via a form which has this functions URL as an action.
import os
from flask import Flask, render_template, redirect, request, url_for
from flask_pymongo import PyMongo
from bson.objectid import ObjectId
app = Flask(__name__)
app.config["MONGO_DBNAME"] = 'recipe_site'
app.config["MONGO_URI"] = os.getenv("MONGO_URI")
mongo = PyMongo(app)
#function for search bar.
#app.route('/search', methods=['GET', 'POST'])
def search():
mongo.db.recipes.create_index( { "name": "text" } )
if request.method == "POST":
q = request.form['search']
else:
q = ""
query = ( { "$text": { "$search": q } } )
results = mongo.db.recipes.find(query)
return render_template('searchresults.html', recipes = results)
The form that calls search function and captures key search word:
<form action="{{ url_for('search', text=text) }}" method="POST">
<div>
<div class="input-field">
<input id="search" name="search" type="text" class="validate">
<span class="helper-text" data-error="Try again" data-success="Done"></span>
</div>
</div>
<button type="submit">Submit </button>
</form>
Error code
Debugger PIN: 272-267-243
10.240.1.220 - - [25/May/2019 19:24:14] "POST /search/ HTTP/1.1" 404 -
As the code stands now all I expect is that it should print any collections in my database which have the keyword searched using the form.
You need to read the key words received from the post request using request.form['search']
like below:
#function for search bar.
#app.route('/search', methods=['GET', 'POST'])
def search():
# index for field name
mongo.db.recipes.create_index('name')
# index for field name and example
#mongo.db.recipes.create_index([("name", pymongo.DESCENDING),
#("example", pymongo.ASCENDING)])
if request.method == "POST":
q=request.form['search']
else:
q = ""
query = ( { "$text": { "$search": q } } )
results = mongo.db.recipes.find(query)
print (results)
return render_template('searchresults.html', recipes = results)
In the form, you need to replace
<form action="{{ url_for('search', text=text) }}" method="POST">
By
<form action="{{ url_for('search') }}" method="POST">
Edit:
I suggest to use GET method instead of POST for search feature because when the client refresh the browser, it ask him to confirm Form submission. So we need to replace:
if request.method == "POST":
q=request.form['search']
else:
q = ""
By
q = request.args.get('search', "")
And
<form action="{{ url_for('search') }}" method="POST">
by
<form action="{{ url_for('search') }}" method="GET">
Using the official documentation for PyMongo
Here is a tested code that might help.
from flask import Flask
from flask_pymongo import PyMongo
import pymongo
app = Flask(__name__)
app.config["MONGO_DBNAME"] = 'test'
app.config["MONGO_URI"] = "mongodb+srv://<username>:<password>#cluster0-vhd1j.gcp.mongodb.net/test?retryWrites=true"
mongo = PyMongo(app)
# mongo.db.collection.create_index([("name", pymongo.ASCENDING),("text", pymongo.ASCENDING)])
mongo.db.collection.insert_one({"name":"myname","text":"abcd"})
query = { "text": "abcd"}
results = mongo.db.collection.find(query)
for result in results:
print(result)
you can manipulate it as per your requirement. :)
I hope it helps.
Thank you for your attempts to support but I have now rectified the issue. The reason why nothing was printing in the cli was due the there being an issue with the create_index line. There was an existing index within that collection i was unaware of which was preventing this line from running.

How to redirect users to the required html page only after successful login (Flask Python App)?

I am trying to set up login page before accessing any other page. So as of now the code is working fine but if I access '/home' its taking directly to that page without login required page.
I tried to understand online solutions like login-required with SQLAlchemy but nothing seems working and I got totally lost. This does not require login-required I guess.
Here's my code:
from dbscriptdeploy import dbupdate
from flask.templating import render_template
from flask import Flask, request, url_for, redirect
DBupdate = dbupdate()
app = Flask(__name__) #create the Flask app
#app.route('/success')
def deploy_success():
return render_template("success.html")
def deploy_script(sql):
res = DBupdate.dbvalues_get(sql)
return res
#app.route('/', methods=['GET', 'POST'])
def login_page():
error = None
if request.method == 'POST':
if request.form['username'] != 'admin' or request.form['password'] != 'admin':
error = 'Invalid Credentials. Please try again.'
else:
return redirect(url_for('form_example'))
return render_template('login.html', error=error)
#app.route('/home', methods=['GET', 'POST'])
def form_example():
res = None
error = ''
try:
if request.method == 'POST':
sql = request.form.get('sql')
res = deploy_script(sql)
if res:
return redirect(url_for('deploy_success'))
else:
error = "*Invalid Value. Please try again."
return render_template('home.html', error=error)
except Exception as e:
return render_template('home.html', error=error)
if __name__ == '__main__':
app.run(debug=True) #run app in debug mode on port 5000
Login.html Code:
<html>
<head>
<title>Flask Intro - login page</title>
</head>
<body>
<div class="container">
<h1>Please login</h1>
<br>
<form action="" method="post">
<input type="text" placeholder="Username" name="username" value="{{
request.form.username }}">
<input type="password" placeholder="Password" name="password" value="{{
request.form.password }}">
<input class="btn btn-default" type="submit" value="Login">
</form>
{% if error %}
<p class="error"><strong>Error:</strong> {{ error }}
{% endif %}
</div>
</body>
</html>
So what I expect is after successful login only then they should be directed to \home page else they should be directed again to login page.
If I were you, I'd use flask-login. However, if you want to develop a simple solution by your own, you should save some var in the user's session.
Also in your login method there is a bug. I'd do:
from flask import session
#app.route('/', methods=['GET', 'POST'])
def login_page():
error = None
if request.method == 'POST':
if request.form['username'] == 'admin' and request.form['password'] == 'admin':
session['admin'] = request.form['username']
return redirect(url_for('form_example'))
else:
error = 'Invalid Credentials. Please try again.'
return render_template('login.html', error=error)
Then, in your form_example view you should check that admin is in session, else you can redirect to your login view:
#app.route('/home', methods=['GET', 'POST'])
def form_example():
if 'admin' not in session:
return redirect(url_for('login_page'))
else:
# Your code here
If you really want to go down the path of developing your own login/authentication solution, check out the documentation for flash_message
http://flask.pocoo.org/docs/1.0/patterns/flashing/. This tutorial has the exact problem you're tackling.
The re-routing of a successful login attempt occurs with the redirect call
return redirect(url_for('homepage'))

jwt python can´t add token to user

i´m doing a simple project where I want to add jwt authentication.
When I log in I try to create a new token but when i´m trying to see who´s the user using the token it says that the token is missing.
I´m using Flask and SQLAlchemy with Postgresql
app.py
app.config['JWT_TOKEN_LOCATION'] = ['cookies']
#app.config['JWT_COOKIE_SECURE'] = False
app.config['JWT_ACCESS_COOKIE_PATH'] = '/api/'
app.config['JWT_REFRESH_COOKIE_PATH'] = '/token/refresh'
app.config['JWT_COOKIE_CSRF_PROTECT'] = False
app.config['JWT_SECRET_KEY'] = 'abva'
jwt = JWTManager(app)
#app.route('/token/auth', methods=['POST'])
def login():
email = request.form['email']
password = request.form['password']
user = User.query.all()
for user in user:
if user.email == email and user.password == password:
access_token = create_access_token(identity=user.email)
refresh_token = create_refresh_token(identity=user.email)
# Set the JWTs and the CSRF double submit protection cookies
# in this response
resp = jsonify({'login': True})
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp, 200
return jsonify({'login': False}), 401
#app.route('/protected', methods=['GET'])
#jwt_required
def protected():
ret = {
'current_identity': get_jwt_identity(), # test
}
return jsonify(ret), 200
#app.route('/token/remove', methods=['POST'])
def logout():
resp = jsonify({'logout': True})
unset_jwt_cookies(resp)
return resp, 200
#jwt.user_identity_loader
def user_identity_lookup(user):
return user
add_user.html
<!DOCTYPE html>
<html>
<body>
<form method="POST" action="/token/auth">
<label> Email: </label>
<input id="email" name ="email" type="text" />
<label> Password: </label>
<input id="password" name ="password" type="password" />
<input type="submit" />
</form>
<form method="POST" action="/token/remove">
<input type="submit" value="LogOut" />
</form>
</body>
</html>
Your route is /protected but your JWT_ACCESS_COOKIE_PATH is /api/. That will prevent the cookie from being sent to that endpoint. Either change the endpoint to /api/protected, or change the cookie path to just /

Resources