Browse Source

Refactor: Moved token validation, made token list a page, documentation

merge-requests/1/head
Drew Short 5 years ago
parent
commit
1450fa596d
  1. 40
      server/corvus/api/authentication_api.py
  2. 2
      server/corvus/middleware/authentication_middleware.py
  3. 21
      server/corvus/service/authentication_service.py
  4. 34
      server/corvus/service/user_token_service.py

40
server/corvus/api/authentication_api.py

@ -2,7 +2,7 @@
from flask import Blueprint, g, abort, request
from corvus.api.decorators import return_json
from corvus.api.model import APIMessage, APIResponse
from corvus.api.model import APIMessage, APIResponse, APIPage
from corvus.middleware import authentication_middleware
from corvus.service import (
user_token_service,
@ -13,6 +13,7 @@ from corvus.service import (
from corvus.middleware.authentication_middleware import Auth
from corvus.service.role_service import Role
from corvus.model import UserToken
from corvus.utility.pagination_utility import get_pagination_params
AUTH_BLUEPRINT = Blueprint(
name='auth', import_name=__name__, url_prefix='/auth')
@ -61,17 +62,34 @@ def logout() -> APIResponse:
@return_json
@authentication_middleware.require(required_auth=Auth.BASIC, required_role=Role.USER)
def get_tokens() -> APIResponse:
user_tokens = user_token_service.find_by_user(g.user)
return APIResponse(user_tokens, 200)
"""
Get a list of all tokens for the current user
:return: a paginated list of user tokens
"""
page, per_page = get_pagination_params(request.args)
user_token_page = user_token_service.find_by_user(g.user, page, per_page)
if user_token_page is not None:
return APIResponse(APIPage.from_page(user_token_page), 200)
return abort(404)
@AUTH_BLUEPRINT.route('/token', methods=['POST'])
@return_json
@authentication_middleware.require(required_auth=Auth.BASIC, required_role=Role.USER)
def create_token():
"""
Create a new token with optional parameters
note: String
enabled: Boolean
expirationTime: DateTime
:return: The new token with the optional parameters
"""
requested_token: UserToken = transformation_service.deserialize_model(
UserToken, request.json, options=['note', 'enabled', 'expirationTime'])
user_token = user_token_service.create(g.user, requested_token.note, requested_token.enabled, requested_token.expiration_time)
user_token = user_token_service.create(
g.user, requested_token.note, requested_token.enabled, requested_token.expiration_time)
return APIResponse(user_token, 200)
@ -79,6 +97,12 @@ def create_token():
@return_json
@authentication_middleware.require(required_auth=Auth.BASIC, required_role=Role.USER)
def get_token(token: str):
"""
Retrieve a specific token for this user
:param token: The token to retrieve for this user
:return: The token if it exists
"""
user_token = user_token_service.find_by_user_and_token(g.user, token)
if user_token is None:
return abort(404)
@ -89,8 +113,14 @@ def get_token(token: str):
@return_json
@authentication_middleware.require(required_auth=Auth.BASIC, required_role=Role.USER)
def delete_token(token: str):
"""
Delete a specific token for this user
:param token: The token to delete for this user
:return: Nothing on success
"""
user_token = user_token_service.find_by_user_and_token(g.user, token)
if user_token is None:
return abort(404)
user_token_service.delete(user_token)
return APIResponse(None, 200)
return APIResponse(APIMessage(True, None), 200)

2
server/corvus/middleware/authentication_middleware.py

@ -54,7 +54,7 @@ def authenticate_with_token(name: str, token: str) -> bool:
if user is not None:
user_token = user_token_service.find_by_user_and_token(user, token)
if user is not None \
and authentication_service.is_valid_token(user_token):
and user_token_service.is_valid_token(user_token):
g.user = user
g.user_token = user_token
return True

21
server/corvus/service/authentication_service.py

@ -1,6 +1,5 @@
"""Service to handle authentication."""
import re
from datetime import datetime
from typing import Optional
from nacl import pwhash
@ -53,26 +52,6 @@ def is_valid_password(user: User, password: str) -> bool:
return False
def is_valid_token(user_token: Optional[UserToken]) -> bool:
"""
Validate a token.
Token must be enabled and if it has an expiration, it must be greater
than now.
:param user_token:
:return:
"""
if user_token is None:
return False
if not user_token.enabled:
return False
if (user_token.expiration_time is not None
and user_token.expiration_time < datetime.utcnow()):
return False
return True
def logout(user_token: Optional[UserToken] = None) -> None:
"""
Remove a user_token associated with a client session.

34
server/corvus/service/user_token_service.py

@ -3,6 +3,7 @@ import uuid
from datetime import datetime
from typing import Optional, Dict, Callable, Any, List
from flask_sqlalchemy import Pagination
from iso8601 import iso8601, ParseError
from corvus.db import db
@ -42,6 +43,7 @@ class UserTokenTransformer(BaseTransformer):
'expirationTime': self.serialize_expiration_time,
'creationTime': self.serialize_creation_time,
'lastUsageTime': self.serialize_last_usage_time,
'isValid': self.serialize_is_valid,
'version': self.serialize_version
}
@ -108,6 +110,9 @@ class UserTokenTransformer(BaseTransformer):
"""User token last usage time."""
model.last_usage_time = iso8601.parse_date(last_usage_time)
def serialize_is_valid(self):
return is_valid_token(self.model)
def serialize_version(self) -> int:
"""User token version."""
return self.model.version
@ -161,6 +166,26 @@ def create(
return user_token
def is_valid_token(user_token: Optional[UserToken]) -> bool:
"""
Validate a token.
Token must be enabled and if it has an expiration, it must be greater
than now.
:param user_token:
:return:
"""
if user_token is None:
return False
if not user_token.enabled:
return False
if (user_token.expiration_time is not None
and user_token.expiration_time < datetime.utcnow()):
return False
return True
def delete(user_token: UserToken) -> bool:
"""
Delete a user_token.
@ -186,11 +211,14 @@ def find_by_user_and_token(user: User, token: str) -> Optional[UserToken]:
return UserToken.query.filter_by(user_id=user.id, token=token).first()
def find_by_user(user: User) -> List[UserToken]:
def find_by_user(user: User, page: int, per_page: int = 20, max_per_page: int = 100) -> Pagination:
"""
Find all tokens for a user
:param user:
:param user: The user to find tokens for
:param page: The page to request
:param per_page: The number to get per page
:param max_per_page:
:return:
"""
return UserToken.query.filter_by(user_id=user.id)
return UserToken.query.filter_by(user_id=user.id).paginate(page, per_page, True, max_per_page)
Loading…
Cancel
Save