Adicionar endpoint: POST /trainer/authenticate

This commit is contained in:
Augusto Gunsch 2021-10-17 21:14:56 -03:00
parent fffcb5aa45
commit 7f7dbd68d0
No known key found for this signature in database
GPG Key ID: F7EEFE29825C72DC
5 changed files with 86 additions and 20 deletions

View File

@ -1,2 +1,7 @@
import string
import random
random_str = string.ascii_letters + string.digits + string.ascii_uppercase
SECRET_KEY = ''.join(random.choice(random_str) for i in range(12))
SQLALCHEMY_DATABASE_URI = 'sqlite:///database.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False

View File

@ -1,6 +1,5 @@
from api.app import db, ma
from werkzeug.security import generate_password_hash
from enum import Enum
teams = (
"Team Valor",
@ -16,10 +15,10 @@ class Trainer(db.Model):
__tablename__ = "trainers"
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
nickname = db.Column(db.String(20), nullable=False, index=True)
nickname = db.Column(db.String(20), unique=True, nullable=False, index=True)
first_name = db.Column(db.String(30), nullable=False)
last_name = db.Column(db.String(30), nullable=False)
email = db.Column(db.String(60), unique=True, nullable=False)
email = db.Column(db.String(60), unique=True, index=True, nullable=False)
password = db.Column(db.String(200), nullable=False)
team = db.Column(db.String(10), nullable=False)
pokemons_owned = db.Column(db.Integer, default=0)

View File

@ -14,3 +14,7 @@ def route_get_trainers():
@app.route('/trainer', methods=['POST'])
def route_create_trainer():
return trainer.post_trainer()
@app.route("/trainer/authenticate", methods=['POST'])
def route_auth_trainer():
return trainer.auth_trainer()

18
api/views/errors.py Normal file
View File

@ -0,0 +1,18 @@
def error(code, type, message, http_code=400):
return ({
"code": code,
"type": type,
"message": message
}, http_code)
def ParsingError(message):
return error(1, "ParsingError", message)
def ConflictingParameters(message):
return error(2, "ConflictingParameters", message)
def ConflictingResources(message):
return error(3, "ConflictingResources", message, http_code=409)
def AuthenticationFailure(message):
return error(4, "AuthenticationFailure", message, http_code=401)

View File

@ -1,18 +1,18 @@
from sqlalchemy.exc import NoResultFound, IntegrityError
from api.models.trainer import Trainer, trainer_schema, trainer_schemas, InvalidTeam
from api.app import db
from api.app import app
from flask import request, jsonify
# função auxiliar
def error(code, type, message, http_code=400):
return ({
"code": code,
"type": type,
"message": message
}, http_code)
from .errors import ParsingError, ConflictingParameters, ConflictingResources, AuthenticationFailure
from werkzeug.security import check_password_hash
import datetime
import jwt
def get_trainer(id):
return trainer_schema.dump(Trainer.query.get(id))
try:
return trainer_schema.dump(Trainer.query.get(id))
except:
return jsonify({})
def get_trainers():
args = request.args
@ -21,10 +21,10 @@ def get_trainers():
limit = int(args.get("limit", -1))
offset = int(args.get("offset", 0))
except ValueError:
return error(0, "ParsingError", "Couldn't parse parameter as integer")
return ParsingError("Couldn't parse parameter as integer")
if limit < -1 or offset < 0:
return error(1, "ParsingError", "Expected positive integer as parameter")
return ParsingError("Expected positive integer as parameter")
nickname = args.get("nickname", "")
nickname_contains = args.get("nickname_contains", "")
@ -32,7 +32,7 @@ def get_trainers():
try:
if nickname:
if nickname_contains:
return error(2, "ConflictingParameters", "nickname and nickname_contains are mutually exclusive")
return ConflictingParameters("nickname and nickname_contains are mutually exclusive")
query = Trainer.query.filter_by(nickname=nickname)
return trainer_schemas.dumps(query.all())
@ -45,14 +45,20 @@ def get_trainers():
except NoResultFound:
return jsonify([])
def get_trainer_by_email(email):
try:
return Trainer.query.filter_by(email=email).one()
except:
return None
def post_trainer():
try:
json = request.get_json()
except:
return error(3, "ParsingError", "Failed to parse JSON content")
return ParsingError("Failed to parse JSON body")
if type(json) is not dict:
return error(4, "ParsingError", "Expected JSON object as content")
return ParsingError("Expected JSON object as body")
try:
nickname = json["nickname"]
@ -76,8 +82,42 @@ def post_trainer():
return trainer_schema.dump(trainer)
except InvalidTeam:
return error(5, "ParsingError", "Field team is invalid")
return ParsingError("Field team is invalid")
except KeyError:
return error(6, "ParsingError", "Missing JSON object fields")
return ParsingError("Missing JSON object fields")
except IntegrityError:
return error(7, "ConflictingResource", "Trainer with the same nickname or email already exists", http_code=500)
return ConflictingResources("Trainer with the same nickname or email already exists", http_code=500)
def auth_trainer():
try:
auth = request.get_json()
except:
return ParsingError("Failed to parse JSON body")
if type(auth) is not dict:
return ParsingError("Expected JSON object as body")
try:
email = auth["email"]
password = auth["password"]
except KeyError:
return AuthenticationFailure("Login required")
trainer = get_trainer_by_email(email)
if not trainer:
return AuthenticationFailure("Trainer not found")
if trainer and check_password_hash(trainer.password, password):
token = jwt.encode(
{
"username": trainer.nickname,
"exp": datetime.datetime.now() + datetime.timedelta(hours=12)
},
app.config["SECRET_KEY"])
return jsonify(
{
"id": trainer.id,
"token": token
})
return AuthenticationFailure("Invalid login")