Eve Authentication with bcrypt driver.db error when sending curl request - python

I use bcrypt for the authentication of my resources and have accounts stored in mydatabase with username and passwords. I have stored the passwords manually as hashes in the database as follows:
I started the python bash and typed in following code:
import bcrypt
password = u'passwordtobehashed'
password_hashed = bcrypt.hashpw(password, bcrypt.gensalt())
print (password_hashed)
Then i copied the output of print and stored it in the account table via a POST Request(w/o authentication):
curl -d '{"username": "someuser", "password": "somehashedpassword", "roles_id": 1}' -H 'Content-Type: application/json' http://127.0.0.1:5000/account
Well I use SQLAlchemy and eve is also up to date (Version: 0.7.1).
Well I request for example the people resource using Bcrypted Authentication as follows:
curl -u username 127.0.0.1:5000/people
Then I enter my password and I get following error:
File "/home/vagrant/erpghost/restapi/oldtrivial.py", line 57, in check_auth
accounts = app.data.driver.db['account']
AttributeError: 'SQLAlchemy' object has no attribute 'db'
For some reason the db attribute is not available. I also tried to use Eve.app.data.driver.db and I tried importing current_app from flask but that all did not work.
Well here is my code:
oldtrivial.py
from eve.auth import BasicAuth
from eve import Eve
from eve_sqlalchemy import SQL
from eve_sqlalchemy.validation import ValidatorSQL
import bcrypt
from connection import connect
from connection import Base
con, meta = connect()
Base.metadata.create_all(con)
class BCryptAuth(BasicAuth):
def check_auth(self, username, password, allowed_roles, resource, method):
accounts = app.data.driver.db['account']
account = accounts.find_one({'username': username})
return account and \
bcrypt.hashpw(password, account['password']) == account['password']
app = Eve(validator=ValidatorSQL, data=SQL, auth=BCryptAuth)
db = app.data.driver
Base.metadata.bind = db.engine
db.Model = Base
db.create_all()
if __name__ == '__main__':
app.run(debug=True, use_reloader=False)
tables.py
from sqlalchemy.orm import column_property
from sqlalchemy import Column, Integer, String, DateTime, func, ForeignKey
from connection import connect
from eve.auth import BasicAuth
from connection import Base
from sqlalchemy.orm import relationship
con, meta = connect()
class CommonColumns(Base):
__abstract__ = True
_created = Column(DateTime, default=func.now())
_updated = Column(DateTime, default=func.now(), onupdate=func.now())
_etag = Column(String(40))
class People(CommonColumns):
__tablename__ = 'people'
_id = Column(Integer, primary_key=True, autoincrement=True)
firstname = Column(String(80))
lastname = Column(String(120))
fullname = column_property(firstname + " " + lastname)
class Roles(CommonColumns):
__tablename__ = 'roles'
_id = Column(Integer, primary_key=True, autoincrement=True)
role = Column(String(80))
class Account(CommonColumns):
__tablename__ = 'account'
_id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(50), nullable=False, unique=True)
password = Column(String(200), nullable=False)
roles = relationship("Roles", backref="account")
roles_id = Column(Integer, ForeignKey('roles._id'))
settings.py
from eve_sqlalchemy.decorators import registerSchema
from eve.utils import config
from tables import People
from tables import Account
from tables import Roles
registerSchema('people')(People)
registerSchema('roles')(Roles)
registerSchema('account')(Account)
DOMAIN = {
'people': People._eve_schema['people'],
'roles': Roles._eve_schema['roles'],
'account': Account._eve_schema['account'],
}
DOMAIN['account'].update({
'additional_lookup': {
'url': 'regex("[\w]+")',
'field': 'username'
},
'cache_control': '',
'cache_expires': 0,
'allowed_roles': ['superuser', 'admin'],
'authentication': None,
})
SQLALCHEMY_DATABASE_URI = 'postgresql://databaseuser:password#localhost:5432/database'
RESOURCE_METHODS = ['GET', 'POST']
ITEM_METHODS = ['GET', 'DELETE', 'PATCH', 'PUT']
DEBUG = True
config.ID_FIELD = config.ITEM_LOOKUP_FIELD = '_id'
DOMAIN['people']['id_field'] = config.ID_FIELD
DOMAIN['roles']['id_field'] = config.ID_FIELD
DOMAIN['account']['id_field'] = config.ID_FIELD
Hope someone can help me out.

Something along these lines should work:
from flask import current_app
from tables import Account
# ...
def check_auth(...):
session = current_app.data.driver.session
return session.query(Account) \
.filter(Account.username == username,
Account.password == hashed_password) \
.count() > 0
I guess you've tried to mimic the code in http://python-eve.org/authentication.html#basic-authentication-with-bcrypt ? This is for using Mongo-DB instead of SQLAlchemy.

Related

Flask-Marshmallow returning empty json and saying my schema is invalid

I am trying to use Marshmallow to serialize my Message Model class yet it is returning an empty json object whenever I try to get the data and after I debugged the program with
print(messagesSchema.validate(messages))
It said: 0: {'_schema': ['Invalid input type.']}
This is my Chat model, Message Model and my MessageSchema model
class Chat(db.Model):
id = db.Column(db.Integer, primary_key=True)
user1 = db.Column(db.String(150), unique=True)
user2 = db.Column(db.String(150), unique=True)
room = db.Column(db.String(300), unique=True)
messages = db.relationship("Message", backref="chat")
class Message(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String(300))
username = db.Column(db.String(150))
chat_id = db.Column(db.Integer, db.ForeignKey("chat.id"))
class MessageSchema(ma.SQLAlchemySchema):
class Meta:
model = Message
This is my socketHandler file
messages = Chat.query.filter_by(room=room).first().messages
messagesSchema = MessageSchema(many=True)
print(messagesSchema.validate(messages))
output = messagesSchema.dump(messages)
print(output)
print(jsonify(output))
and the init.py file if anybody is wondering
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from os import path
from flask_login import LoginManager
db = SQLAlchemy()
DB_NAME = "database.db"
ma = Marshmallow()
def create_app():
app = Flask(__name__)
app.config["SECRET_KEY"] = "secret"
app.config["SQLALCHEMY_DATABASE_URI"] = f'sqlite:///{DB_NAME}'
db.init_app(app)
from .views import views
from .auth import auth
from .slashUrl import slashUrl
app.register_blueprint(slashUrl, url_prefix="/")
app.register_blueprint(views, url_prefix="/home/")
app.register_blueprint(auth, url_prefix="/auth/")
from .models import User
create_database(app)
login_manager = LoginManager()
login_manager.login_view = "auth.login"
login_manager.login_message_category = "dangerAlert"
login_manager.init_app(app)
#login_manager.user_loader
def load_user(id):
return User.query.get(int(id))
return app
def create_database(app):
if not path.exists("website/" + DB_NAME):
db.create_all(app=app)
print("Database created")
From my print statements I get
{0: {'_schema': ['Invalid input type.']}}
[{}]
<Response 9 bytes [200 OK]>
If anybody knows how to fix this your help would be greatly appreciated!

Sqlalchemy ProgrammingError exception: Unknown column

here are the files which contain my code:
this is models.py file
from . import db
from flask_login import UserMixin
from sqlalchemy import func
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(150), unique=True)
password = db.Column(db.String(150))
first_name = db.Column(db.String(150))
notes = db.relationship('Note')
class Note(db.Model):
time = func.now()
id = db.Column(db.Integer, primary_key=True)
data = db.Column(db.String(1000))
date = db.Column(db.DateTime(timezone=True), default=time)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
and this is views.py
from flask import Blueprint, render_template, request
from flask_login import login_required, current_user
from .models import Note
from . import db
views = Blueprint('views', __name__)
#views.route('/notes', methods=['GET', 'POST'])
#login_required
def notes():
if request.method == 'POST':
note = request.form.get('note')
new_note = Note(data=note, user_id=current_user.id)
db.session.add(new_note)
db.session.commit()
return render_template('notes.html', user=current_user)
I got the error
sqlalchemy.exc.ProgrammingError:
(mysql.connector.errors.ProgrammingError) 1054 (42S22): Unknown column
'user_id' in 'field list' [SQL: INSERT INTO note (id, data, date,
user_id) VALUES (%(id)s, %(data)s, now(), %(user_id)s)] [parameters:
{'id': 1, 'data': 'get a ', 'user_id': None}]
Any idea on where I messed
up and how to fix it ?
I just fixed a problem similar to this. My code was trying to insert one of the Column values into one of the Column names. Like the others have suggested, examine your code within the database. Here are some examples of my code before and after:
incorrect variable: self.all_output_data
self.user_tables = f"""
INSERT INTO voicechannel_{self.my_name_and_id}
(my_input, msg_from_input, self.all_output_data, my_output)
VALUES
('{self.my_input_widget}','{self.all_input_data[0]}',
'{self.all_output_data}','{self.output_to_other_nodes[0]}')
"""
correct variable: msg_to_output
self.user_tables = f"""
INSERT INTO voicechannel_{self.my_name_and_id}
(my_input, msg_from_input, msg_to_output, my_output)
VALUES
('{self.my_input_widget}','{self.all_input_data[0]}',
'{self.all_output_data}','{self.output_to_other_nodes[0]}')
"""
Change this line of code from
new_note = Note(data=note, id=current_user.id)
to
new_note = Note(data=note, user_id=current_user.id)

Flask-sqlalchemy AttributeError: 'function' object has no attribute 'query'

I've been using flask with flask-sqlalchemy successfully for a number of weeks, but all of a sudden my code is coming up with this error: AttributeError: 'function' object has no attribute 'query', relating to this line of code:
project_choices = [(str(c.id) + ': ' + c.project_name) for c in Projects.query.all()]
Seems like the flask-sqalchemy Projects class is not being successfully created but I can't work out why.
Main routes file:
from flask import Flask, request, flash, url_for, redirect, render_template, session
from flask_sqlalchemy import SQLAlchemy
from app import db
from app import app
from app.forms import Basic_data_Form, Which_project#, Areas_form
from flask_bootstrap import Bootstrap
from app.models import Projects
#app.route('/Index', methods = ['GET', 'POST'])
def Index():
if 'project_name' not in session:
session['project_name'] = "0: No project selected"
project_name = session['project_name'].split(':')[1]
project_choices = [(str(c.id) + ': ' + c.project_name) for c in Projects.query.all()]
form2 = Which_project()
form2.project_choice.choices = project_choices
return render_template('Index.html', form2=form2, projects = Projects.query.filter_by(id=session['project_name'].split(':')[0]).all(), project_name=project_name)
init file:
from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_bootstrap import Bootstrap
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
bootstrap = Bootstrap(app)
from app import routes
models file:
from app import db
class Projects(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
project_name = db.Column(db.String(100))
user_id = db.Column(db.String(100))
address = db.Column(db.String(100))
region = db.Column(db.String(100))
postcode = db.Column(db.String(20))
building_year = db.Column(db.Integer)
climate_file = db.Column(db.String(100))
building_TFA = db.Column(db.Float)
thermal_mass = db.Column(db.String(100))
winter_int_temp = db.Column(db.Float)
summer_int_temp = db.Column(db.Float)
height_above_sea = db.Column(db.Float)
occupany = db.Column(db.Float)
def __repr__(self):
return '<Project {}>'.format(self.project_name)
I had a similar issue with that same error. It was because there was a function with the same name. So that is probably why it was saying a function had no attribute query even though the table used with sqlalchemy is a class
Consider either implementing a class function which renders the object into the current string representation you desire or implementing a hybrid property.
String method:
class Projects(db.Model):
...
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
project_name = db.Column(db.String(100))
...
def __str__(self):
return f'{self.id}: {self.project_name}'
Use string method as follows:
project_choices = Projects.query.all()
form2 = Which_project()
# probably str(x) would work, but if it doesn't the code below def works
form2.project_choice.choices = [x.__str__() for x in project_choices]
Hybrid property method:
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method
class Projects(db.Model):
...
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
project_name = db.Column(db.String(100))
...
#hybrid_property
def project_choice(self):
return f'{self.id}: {self.project_name}'
Use hybrid property as follows:
# Access only the hybrid property
project_choices = Projects.query(Projects.project_choice).all()
form2 = Which_project()
form2.project_choice.choices = project_choices

<Object id> is not JSON serializable

i'm creating an API in python + Flask + marshmallow.
Here's the class + the first function (i don't already use the others )
import datetime
from marshmallow import Schema, fields, ValidationError, pre_load
from flask import Flask, jsonify
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy
from flask import request
import os
app = Flask(__name__)
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'User.sqlite')
# Order matters: Initialize SQLAlchemy before Marshmallow
db = SQLAlchemy(app)
ma = Marshmallow(app)
class User(db.Model):
userid = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30))
badgenum = db.Column(db.String(30))
mail = db.Column(db.String(30))
status = db.Column(db.String(30))
admin = db.Column(db.String(30))
def __init__(self, userid, name, badgenum, mail, status, admin):
self.userid = userid
self.name = name
self.badgenum = badgenum
self.mail = mail
self.status = status
self.admin = admin
class UserSchema(ma.Schema):
class Meta:
fields = ('userid', 'name', 'badgenum', 'mail', 'status', 'admin')
user_schema = UserSchema()
users_schema = UserSchema(many=True)
#app.route("/User", methods=["POST"])
def add_User():
userid = request.json['userid']
name = request.json['name']
badgenum = request.json['badgenum']
mail = request.json['mail']
status = request.json['status']
admin = request.json['admin']
new_user = User(userid, name, badgenum, mail, status, admin)
db.session.add(new_user)
db.session.commit()
return jsonify(new_user)
I tested the function add_User using Postman with this json request :
{
"userid" : 1,
"name" : "name1",
"badgenum" : "66897",
"mail" : "ghh#orange.fr",
"status" : "on",
"admin" : "f"
}
and i got this error:
TypeError: <User 145> is not JSON serializable
User has its own autogenerated id user.id which is ObjectId.
You need custom json encoder to encode ObjectId field.
import json, bson
def json_response(obj, cls=None):
response = make_response(json.dumps(obj, cls=cls))
response.content_type = 'application/json'
return response
class MongoJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, bson.ObjectId):
return str(obj)
return json.JSONEncoder.default(self, obj)
Now try to call return json_response(new_user, cls=MongoJsonEncoder)

Setting DateTime in Flask App

I am working on a flask app and I was wondering if someone can help me figure out why I am getting an error as I am trying to set the DateTime when a new user is registering an account. I just want it to save the exact time that the user was registered. The following is the route that I created to add and commit the user to the database.
from flask import Flask, render_template, request, redirect, url_for, flash
from flask import escape, session
from datetime import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from database import Users, Base
app.route('/registered', methods=['Get', 'POST'])
def Register():
if request.method == 'POST':
password = request.form['password']
verify = request.form['verify']
if password == verify:
newUser = Users(Username = request.form['username'],
Password = request.form['password'],
Email = request.form['email'],
DateTime = datetime.now())
session.add(newUser)
session.commit()
return redirect('HomePage')
else:
flash('Passwords do not match')
return render_template('Register.html')
And here is the database structure that I currently have setup:
class Users(Base):
__tablename__ = 'users'
Id = Column(Integer, primary_key = True)
Username = Column(String(22), unique = True, nullable = False)
Password = Column(String(22), nullable = False)
Email = Column(String(55), unique = True, nullable = False)
Registered = Column(DateTime, nullable = False)
I am not sure if the html file would have anything with setting up the datetime property.
If anyone can help. it would be much appreciated.
In the request handler, you pass a keyword argument DateTime to the Users constructor:
app.route('/registered', methods=['Get', 'POST'])
def Register():
if request.method == 'POST':
password = request.form['password']
verify = request.form['verify']
if password == verify:
newUser = Users(Username = request.form['username'],
Password = request.form['password'],
Email = request.form['email'],
DateTime = datetime.now())
But the Users model does not have a DateTime column, the correct name is Registered:
class Users(Base):
__tablename__ = 'users'
Id = Column(Integer, primary_key = True)
Username = Column(String(22), unique = True, nullable = False)
Password = Column(String(22), nullable = False)
Email = Column(String(55), unique = True, nullable = False)
Registered = Column(DateTime, nullable = False)

Categories

Resources