Write basic authentication and log API
This commit is contained in:
commit
41a301b273
153
api/app.py
Normal file
153
api/app.py
Normal file
@ -0,0 +1,153 @@
|
||||
import json
|
||||
from datetime import timedelta, datetime, timezone
|
||||
|
||||
import bcrypt
|
||||
from flask import Flask, request, Response, jsonify, session
|
||||
|
||||
from database.models import Flight, User, AuthLevel
|
||||
from mongoengine import connect, ValidationError, DoesNotExist
|
||||
from flask_jwt_extended import create_access_token, get_jwt , get_jwt_identity, unset_jwt_cookies, jwt_required, JWTManager
|
||||
|
||||
api = Flask(__name__)
|
||||
|
||||
api.config["JWT_SECRET_KEY"] = "please-remember-to-change-me"
|
||||
api.config["JWT_ACCESS_TOKEN_EXPORES"] = timedelta(hours=1)
|
||||
jwt = JWTManager(api)
|
||||
|
||||
|
||||
connect('tailfin')
|
||||
|
||||
|
||||
@api.after_request
|
||||
def refresh_expiring_jwts(response):
|
||||
try:
|
||||
exp_timestamp = get_jwt()["exp"]
|
||||
now = datetime.now(timezone.utc)
|
||||
target_timestamp = datetime.timestamp(now + timedelta(minutes=30))
|
||||
if target_timestamp > exp_timestamp:
|
||||
access_token = create_access_token(identity=get_jwt_identity())
|
||||
data = response.get_json()
|
||||
if type(data) is dict:
|
||||
data["access_token"] = access_token
|
||||
response.data = json.dumps(data)
|
||||
return response
|
||||
except (RuntimeError, KeyError):
|
||||
# No valid JWT, return original response
|
||||
return response
|
||||
|
||||
|
||||
@api.route('/add_user', methods=["POST"])
|
||||
@jwt_required()
|
||||
def add_user():
|
||||
user = User.objects.get(username=get_jwt_identity())
|
||||
if user.level != AuthLevel.ADMIN:
|
||||
return '', 401
|
||||
|
||||
username = request.json.get("username", None)
|
||||
password = request.json.get("password", None)
|
||||
auth_level = request.json.get("auth_level", None)
|
||||
|
||||
try:
|
||||
existing_user = User.objects.get(username=username)
|
||||
print(existing_user.to_json())
|
||||
return jsonify({"msg": "Username already exists"})
|
||||
except DoesNotExist:
|
||||
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
|
||||
user = User(username=username, password=hashed_password, level=auth_level).save()
|
||||
return jsonify({"id": user.id}), 200
|
||||
|
||||
|
||||
@api.route('/users', methods=["GET"])
|
||||
@jwt_required()
|
||||
def get_users():
|
||||
user = User.objects.get(username=get_jwt_identity())
|
||||
if user.level != AuthLevel.ADMIN:
|
||||
return '', 401
|
||||
|
||||
users = User.objects.to_json()
|
||||
return users, 200
|
||||
|
||||
|
||||
@api.route('/login', methods=["POST"])
|
||||
def create_token():
|
||||
username = request.json.get("username", None)
|
||||
password = request.json.get("password", None)
|
||||
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
except DoesNotExist:
|
||||
return jsonify({"msg": "Invalid username or password"}), 401
|
||||
else:
|
||||
if bcrypt.checkpw(password.encode('utf-8'), user.password.encode('utf-8')):
|
||||
access_token = create_access_token(identity=username)
|
||||
response = {"access_token": access_token}
|
||||
return jsonify(response), 200
|
||||
return jsonify({"msg": "Invalid username or password"}), 401
|
||||
|
||||
|
||||
@api.route('/logout', methods=["POST"])
|
||||
def logout():
|
||||
response = jsonify({"msg": "logout successful"})
|
||||
unset_jwt_cookies(response)
|
||||
return response
|
||||
|
||||
|
||||
@api.route('/profile', methods=["GET"])
|
||||
@jwt_required()
|
||||
def get_profile():
|
||||
user = User.objects.get(username=get_jwt_identity())
|
||||
print(user.to_json())
|
||||
return jsonify({"username": user.username, "auth_level:": str(user.level)}), 200
|
||||
|
||||
|
||||
@api.route('/flights', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_flights():
|
||||
user = User.objects.get(username=get_jwt_identity()).id
|
||||
flights = Flight.objects(user=user).to_json()
|
||||
return flights, 200
|
||||
|
||||
|
||||
@api.route('/flights/<flight_id>', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_flight(flight_id):
|
||||
user = User.objects.get(username=get_jwt_identity()).id
|
||||
flight = Flight.objects(id=flight_id).to_json()
|
||||
if flight.user != user:
|
||||
return '', 401
|
||||
return flight, 200
|
||||
|
||||
|
||||
@api.route('/flights', methods=['POST'])
|
||||
@jwt_required()
|
||||
def add_flight():
|
||||
user = User.objects(username=get_jwt_identity())
|
||||
|
||||
body = request.get_json()
|
||||
try:
|
||||
flight = Flight(user=user, **body).save()
|
||||
except ValidationError:
|
||||
return jsonify({"msg": "Invalid request"})
|
||||
id = flight.id
|
||||
return jsonify({'id': str(id)}), 201
|
||||
|
||||
|
||||
@api.route('/flights/<flight_id>', methods=['PUT'])
|
||||
def update_flight(flight_id):
|
||||
body = request.get_json()
|
||||
Flight.objects(id=flight_id).update(**body)
|
||||
return '', 200
|
||||
|
||||
|
||||
@api.route('/flights/<int:index>', methods=['DELETE'])
|
||||
def delete_flight(index):
|
||||
Flight.objects(id=id).delete()
|
||||
return '', 200
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if User.objects(level=AuthLevel.ADMIN).count() == 0:
|
||||
hashed_password = bcrypt.hashpw("admin".encode('utf-8'), bcrypt.gensalt())
|
||||
User(username="admin", password=hashed_password, level=AuthLevel.ADMIN).save()
|
||||
|
||||
api.run()
|
68
api/database/models.py
Normal file
68
api/database/models.py
Normal file
@ -0,0 +1,68 @@
|
||||
from enum import Enum
|
||||
|
||||
from mongoengine import *
|
||||
|
||||
|
||||
class AuthLevel(Enum):
|
||||
GUEST = 0
|
||||
USER = 1
|
||||
ADMIN = 2
|
||||
|
||||
|
||||
class User(Document):
|
||||
username = StringField(required=True, unique=True)
|
||||
password = StringField(required=True)
|
||||
level = EnumField(AuthLevel, default=AuthLevel.USER)
|
||||
|
||||
|
||||
class Flight(Document):
|
||||
user = ObjectIdField(required=True)
|
||||
|
||||
date = DateField(required=True, unique=False)
|
||||
aircraft = StringField(default="")
|
||||
waypoint_from = StringField(default="")
|
||||
waypoint_to = StringField(default="")
|
||||
route = StringField(default="")
|
||||
|
||||
hobbs_start = DecimalField()
|
||||
hobbs_end = DecimalField()
|
||||
tach_start = DecimalField()
|
||||
tach_end = DecimalField()
|
||||
|
||||
time_start = DateTimeField()
|
||||
time_off = DateTimeField()
|
||||
time_down = DateTimeField()
|
||||
time_stop = DateTimeField()
|
||||
|
||||
time_total = DecimalField(default=0)
|
||||
time_pic = DecimalField(default=0)
|
||||
time_sic = DecimalField(default=0)
|
||||
time_night = DecimalField(default=0)
|
||||
time_solo = DecimalField()
|
||||
|
||||
time_xc = DecimalField()
|
||||
dist_xc = DecimalField()
|
||||
|
||||
takeoffs_day = IntField()
|
||||
landings_day = IntField()
|
||||
takeoffs_night = IntField()
|
||||
landings_night = IntField()
|
||||
landings_all = IntField()
|
||||
|
||||
time_instrument = DecimalField()
|
||||
time_sim_instrument = DecimalField()
|
||||
holds_instrument = DecimalField()
|
||||
|
||||
dual_given = DecimalField()
|
||||
dual_recvd = DecimalField()
|
||||
time_sim = DecimalField()
|
||||
time_ground = DecimalField()
|
||||
|
||||
tags = ListField(StringField())
|
||||
|
||||
pax = ListField(StringField())
|
||||
crew = ListField(StringField())
|
||||
|
||||
comments = StringField()
|
||||
|
||||
photos = ListField(ImageField())
|
3
api/requirements.txt
Normal file
3
api/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
||||
bcrypt~=4.1.2
|
||||
flask~=3.0.0
|
||||
mongoengine~=0.27.0
|
Loading…
x
Reference in New Issue
Block a user