Add token validation logic and protected routes
parent
6c856965a1
commit
f506e3dfaa
|
@ -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)
|
||||
|
|
|
@ -77,7 +77,6 @@ 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),
|
||||
|
@ -89,8 +88,20 @@ class Account(db.Model):
|
|||
app.config.get('SECRET_KEY'),
|
||||
algorithm='HS256'
|
||||
).decode('utf-8')
|
||||
except Exception as e:
|
||||
return e
|
||||
|
||||
@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
|
||||
|
|
|
@ -1,18 +1,47 @@
|
|||
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
|
||||
|
||||
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)
|
||||
@api_bp.errorhandler(422)
|
||||
def handle_validation_error(e):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -19,6 +19,7 @@ parameters:
|
|||
- password
|
||||
- email
|
||||
$ref: '#/definitions/User'
|
||||
security: []
|
||||
responses:
|
||||
201:
|
||||
description: Successful creation
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue