user handling done
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Torma Kristóf 2020-11-24 20:26:02 +01:00
parent f54d752657
commit e02bf1aa62
9 changed files with 207 additions and 7 deletions

View File

@ -2,10 +2,10 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: input-service
name: backend
labels:
app: input-service
namespace: birbnetes
app: backend
namespace: videon
data:
SENTRY_DSN: https://58bd309272c642d884ff2332c336b977@sentry.kmlabz.com/21
RELEASE_ID: kmlabz-k8s

View File

@ -1,7 +1,6 @@
flask
sentry-sdk[flask]
flask-restful
flask-security
kubernetes
gunicorn
psycopg2-binary
@ -10,4 +9,6 @@ flask_sqlalchemy
marshmallow
marshmallow-sqlalchemy
flask-marshmallow
py-healthcheck
py-healthcheck
flask-jwt-extended
flask-bcrypt

View File

@ -9,8 +9,11 @@ from healthcheck import HealthCheck
from config import *
from db import db
from jwtman import jwtman
from fbcrypt import bcrypt
from marshm import ma
from healthchecks import health_database_status
from resources import SignupApi, LoginApi
"""
Main Flask RESTful API
@ -35,15 +38,19 @@ if SENTRY_DSN:
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] =\
f"postgresql://{POSTGRES_USERNAME}:{POSTGRES_PASSWORD}@{POSTGRES_HOSTNAME}:5432/{POSTGRES_DB}"
app.config['JWT_SECRET_KEY'] = JWT_SECRET_KEY
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
api = Api(app)
health = HealthCheck(app, "/healthz")
db.init_app(app)
ma.init_app(app)
bcrypt.init_app(app)
jwtman.init_app(app)
with app.app_context():
db.create_all()
""
formatter = logging.Formatter(
fmt="%(asctime)s - %(levelname)s - %(module)s - %(message)s"
)
@ -56,9 +63,17 @@ logger.setLevel(logging.DEBUG)
logger.addHandler(handler)
# api.add_resource(SampleResource, "/sample")
api.add_resource(SignupApi, '/api/auth/signup')
api.add_resource(LoginApi, '/api/auth/login')
health.add_check(health_database_status)
@app.errorhandler(404)
def page_not_found(e):
return {'status': 'error', 'message': 'page not found'}, 404
if __name__ == "__main__":
app.run(
debug=bool(DEBUG),

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3
import os
from uuid import uuid4
"""
Configuration
@ -25,3 +25,5 @@ POSTGRES_HOSTNAME = os.getenv("VIDEON_POSTGRES_HOSTNAME", "localhost")
POSTGRES_USERNAME = os.getenv("VIDEON_POSTGRES_USERNAME", "videon")
POSTGRES_PASSWORD = os.getenv("VIDEON_POSTGRES_PASSWORD", "videon")
POSTGRES_DB = os.getenv("VIDEON_POSTGRES_DB", "videon")
JWT_SECRET_KEY = os.getenv("VIDEON_POSTGRES_DB", str(uuid4()))

13
src/fbcrypt.py Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
from flask_bcrypt import Bcrypt
"""
Bcrypt object
"""
__author__ = '@tormakris'
__copyright__ = "Copyright 2020, videON Team"
__module_name__ = "fbrypt"
__version__text__ = "1"
bcrypt = Bcrypt()

14
src/jwtman.py Normal file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env python3
from flask_jwt_extended import JWTManager
"""
JWTManager
"""
__author__ = '@tormakris'
__copyright__ = "Copyright 2020, videON Team"
__module_name__ = "jwtman"
__version__text__ = "1"
jwtman = JWTManager()

31
src/models.py Normal file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env python3
from sqlalchemy.sql import func
from flask_bcrypt import generate_password_hash, check_password_hash
from db import db
"""
Database models
"""
__author__ = '@tormakris'
__copyright__ = "Copyright 2020, videON Team"
__module_name__ = "models"
__version__text__ = "1"
class User(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String, nullable=False, unique=True)
password = db.Column(db.String, nullable=False)
last_logon = db.Column(db.TIMESTAMP, nullable=False, server_default=func.now())
timestamp = db.Column(db.TIMESTAMP, nullable=False, server_default=func.now())
def hash_password(self):
self.password = generate_password_hash(self.password).decode('utf8')
def check_password(self, password):
return check_password_hash(self.password, password)

86
src/resources.py Normal file
View File

@ -0,0 +1,86 @@
#!/usr/bin/env python3
import datetime
from flask_jwt_extended import create_access_token, jwt_required
from flask_restful import Resource
from flask import request, current_app
from db import db
from models import User
from schemas import UserSchema, UserMetadataSchema
"""
Flask Restful endpoints
"""
__author__ = '@tormakris'
__copyright__ = "Copyright 2020, videON Team"
__module_name__ = "resources"
__version__text__ = "1"
class SignupApi(Resource):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/createuser
"""
userschema = UserSchema(many=False)
usermetadataschema = UserMetadataSchema(many=False)
def post(self):
body = request.get_json()
try:
userobj = self.userschema.load(body)
except Exception as e:
current_app.logger.exception(e)
return {'status': 'error', 'message': 'Input JSON schema invalid'}, 417
user = User(name=userobj['name'], password=userobj['password'])
try:
user.hash_password()
db.session.add(user)
db.session.commit()
except Exception as e:
db.session.rollback()
current_app.logger.exception(e)
return {'status': 'error', 'message': 'db transaction error'}, 503
return self.usermetadataschema.dump(user), 200
class LoginApi(Resource):
"""
See: https://swagger.kmlabz.com/?urls.primaryName=videON%20Backend#/backend/logon
"""
userschema = UserSchema(many=False)
usermetadataschema = UserMetadataSchema(many=False)
def post(self):
body = request.get_json()
try:
userobj = self.userschema.load(body)
except Exception as e:
current_app.logger.exception(e)
return {'status': 'error', 'message': 'Input JSON schema invalid'}, 417
user = User.query.filter_by(name=userobj['name']).first()
authorized = user.check_password(userobj['password'])
if not authorized:
return {'status': 'error', 'message': 'username or password invalid'}, 401
try:
user.last_logon = datetime.datetime.now()
db.session.add(user)
db.session.commit()
except Exception as e:
db.session.rollback()
current_app.logger.exception(e)
return {'status': 'error', 'message': 'db transaction error'}, 503
expires = datetime.timedelta(days=7)
access_token = create_access_token(identity=str(user.name), expires_delta=expires)
return {'token': access_token}, 200

38
src/schemas.py Normal file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env python3
from flask_marshmallow.sqla import auto_field
from models import User
from marshm import ma
from marshmallow import fields
"""
Marshmallow schemas
"""
__author__ = "@tormakris"
__copyright__ = "Copyright 2020, videON Team"
__module_name__ = "schemas"
__version__text__ = "1"
class UserSchema(ma.Schema):
"""
Parameters:
- name (string)
- passowrd (string)
"""
name = fields.String(required=True)
password = fields.String(required=True)
class UserMetadataSchema(ma.SQLAlchemyAutoSchema):
"""
Marshmallow schema generated
"""
class Meta:
model = User
exclude = ('timestamp', 'password',)
creation_date = auto_field("timestamp", dump_only=False)