Add token validation logic and protected routes

master
esensar 2018-05-07 17:17:19 +02:00
parent 6c856965a1
commit f506e3dfaa
8 changed files with 117 additions and 22 deletions

View File

@ -49,3 +49,15 @@ def create_token(username, password):
raise ValueError("Invalid credentials")
return account.create_auth_token()
def validate_token(token):
"""
Validates token and returns associated account
:param token: auth token to validate
:type token: string
:returns created token
:rtype Account
"""
return Account.validate_token(token)

View File

@ -77,20 +77,31 @@ class Account(db.Model):
Generates the Auth Token
:return: string
"""
try:
current_time = datetime.datetime.utcnow()
payload = {
'exp': current_time + datetime.timedelta(days=0, hours=1),
'iat': current_time,
'sub': self.id
}
return jwt.encode(
payload,
app.config.get('SECRET_KEY'),
algorithm='HS256'
).decode('utf-8')
except Exception as e:
return e
current_time = datetime.datetime.utcnow()
payload = {
'exp': current_time + datetime.timedelta(days=0, hours=1),
'iat': current_time,
'sub': self.id
}
return jwt.encode(
payload,
app.config.get('SECRET_KEY'),
algorithm='HS256'
).decode('utf-8')
@staticmethod
def validate_token(token):
"""
Validates given Auth token
:rtype: Account
:return: Account associated with token
"""
payload = jwt.decode(
token,
app.config.get('SECRET_KEY'),
algorithms=['HS256']
)
return Account.get(id=payload['sub'])
def __repr__(self):
return '<Account (name=%s, role=%s)>' % self.username, self.role

View File

@ -1,16 +1,45 @@
from flask import Blueprint
from flask_restful import Api
from .resources.account import AccountResource
from .resources.token import TokenResource
from flask import Blueprint, request, g
from flask_restful import Api, Resource, abort
from functools import wraps
from marshmallow import ValidationError
from app.accounts import validate_token
api_bp = Blueprint('api', __name__)
api = Api(api_bp)
# Add resources
api.add_resource(AccountResource, '/v1/accounts')
api.add_resource(TokenResource, '/v1/token')
def protected(func):
@wraps(func)
def protected_function(*args, **kwargs):
try:
token = request.headers['Authorization'] or None
if not token:
abort(401, message='Unauthorized', status='error')
g.current_account = validate_token(token.replace("Bearer ", ""))
if not g.current_account:
abort(401, message='Unauthorized', status='error')
except Exception:
abort(401, message='Unauthorized', status='error')
return protected_function
class ProtectedResource(Resource):
method_decorators = [protected]
def add_resources():
from .resources.account import AccountResource
from .resources.token import TokenResource
api.add_resource(AccountResource, '/v1/accounts')
api.add_resource(TokenResource, '/v1/token')
add_resources()
@api_bp.errorhandler(ValidationError)

View File

@ -3,6 +3,7 @@ from webargs import fields
from webargs.flaskparser import use_args
from flasgger import swag_from
import app.accounts as accounts
from app.api import protected
class AccountResource(Resource):
@ -28,6 +29,7 @@ class AccountResource(Resource):
except ValueError:
abort(422, message='Account already exists', status='error')
@protected
@swag_from('swagger/get_account_spec.yaml')
def get(self):
return '', 200

View File

@ -19,6 +19,7 @@ parameters:
- password
- email
$ref: '#/definitions/User'
security: []
responses:
201:
description: Successful creation

View File

@ -16,6 +16,7 @@ parameters:
properties:
user:
$ref: '#/definitions/Credentials'
security: []
responses:
200:
description: Successful creation
@ -33,4 +34,4 @@ responses:
401:
description: Bad credentials
schema:
$ref: '#/definitions/Error'
$ref: '#/definitions/UnauthorizedError'

View File

@ -57,6 +57,19 @@ definitions:
email:
$ref: '#/definitions/email'
UnauthorizedError:
type: object
required:
- status
- message
properties:
status:
$ref: '#/definitions/status'
default: error
message:
$ref: '#/definitions/message'
default: Unauthorized
Error:
type: object
required:
@ -70,6 +83,27 @@ definitions:
$ref: '#/definitions/message'
default: Error message
securityDefinitions:
Bearer:
type: apiKey
name: Authorization
in: header
description: |
For accessing the API a valid JWT token must be passed in all the queries in
the 'Authorization' header as Bearer token.
A valid JWT token is generated by the API and returned as answer of a call
to the route /login giving a valid user & password.
The following syntax must be used in the 'Authorization' header :
Bearer xxxxxx.yyyyyyy.zzzzzz
security:
- Bearer: []
info:
description: Python (Flask) backend for IoT sysyem made for master's degree final project
title: IoT Backend

View File

@ -33,3 +33,8 @@ MQTT_BROKER_PORT = 1883
MQTT_USERNAME = 'user'
MQTT_PASSWORD = 'secret'
MQTT_REFRESH_TIME = 1.0 # refresh time in seconds
# Flassger config
SWAGGER = {
'uiversion': 3
}