Added sessions support.

This commit is contained in:
Daniele Maglie 2024-01-21 14:18:57 +01:00
parent 183fa3e27a
commit 0fb6d08a7f
9 changed files with 115 additions and 60 deletions

View file

@ -23,17 +23,19 @@ class ErrorCode:
TOKEN_FORMAT_NON_VALID = '1008'
NON_VALID_PARAMETERS = '1009'
TOKEN_EXPIRED = '1009'
MISSING_PARAMETERS = '1010'
NON_VALID_PARAMETERS = '1010'
TOO_MANY_PARAMETERS = '1011'
MISSING_PARAMETERS = '1011'
MISSING_FILE = '1012'
TOO_MANY_PARAMETERS = '1012'
CHANGE_PASSWORD = '1013'
MISSING_FILE = '1013'
IP_ADDRESS_BLACKLISTED = '1014'
CHANGE_PASSWORD = '1014'
IP_ADDRESS_BLACKLISTED = '1015'
class ErrorMessage:
@ -63,6 +65,8 @@ class ErrorMessage:
TOKEN_FORMAT_NON_VALID = 'Token format non valid.'
TOKEN_EXPIRED = 'This token is expired.'
# json POST parameters
NON_VALID_PARAMETERS = 'Expecting JSON data.'
@ -77,6 +81,6 @@ class ErrorMessage:
# account settings
CHANGE_PASSWORD = 'A password change has been activated for your account. You must first authenticate via web app and follow the instructions.'
CHANGE_PASSWORD = 'A password change has been activated for your account. You must authenticate via web interface and follow the instructions. You should now discard this tokens pairs.'
IP_ADDRESS_BLACKLISTED = 'Your IP address is temporary blacklisted.'

View file

@ -2,6 +2,8 @@ from .base import BaseHandler
from piracyshield_component.exception import ApplicationException
from piracyshield_service.security.blacklist.exists_by_access_token import SecurityBlacklistExistsByAccessTokenService
from piracyshield_service.authentication.verify_access_token import AuthenticationVerifyAccessTokenService
from piracyshield_service.permission.service import PermissionService
@ -28,6 +30,8 @@ class ProtectedHandler(BaseHandler):
account_data = {}
async def prepare(self):
self.security_blacklist_exists_by_access_token_service = SecurityBlacklistExistsByAccessTokenService()
self.authentication_verify_access_token_service = AuthenticationVerifyAccessTokenService()
await super().prepare()
@ -52,12 +56,19 @@ class ProtectedHandler(BaseHandler):
return False
# get the token only
token = authorization_header[7:]
self.current_access_token = authorization_header[7:]
try:
if self.security_blacklist_exists_by_access_token_service.execute(
access_token = self.current_access_token
) == True:
self.error(status_code = 401, error_code = ErrorCode.TOKEN_EXPIRED, message = ErrorMessage.TOKEN_EXPIRED)
return False
# set the current account data
# TODO: absolutely need to validate the payload.
self.account_data = self.authentication_verify_access_token_service.execute(token)
self.account_data = self.authentication_verify_access_token_service.execute(self.current_access_token)
if not self.account_data.get('email'):
self.error(status_code = 401, error_code = ErrorCode.TOKEN_FORMAT_NON_VALID, message = ErrorMessage.TOKEN_FORMAT_NON_VALID)

View file

@ -15,7 +15,7 @@ class TestReporterCreateTicket:
ticket_wait_time = 76
ticket_parameters = {
'dda_id': '2326485749e94573bf5724ff5006f30c',
'dda_id': '002ad48ea02a43db9003b4f15f1da9b3',
'description': '__MOCK_TICKET__',
'forensic_evidence': {
'hash': {}
@ -44,6 +44,8 @@ class TestReporterCreateTicket:
create_response = authenticated_post_request('/api/v1/ticket/create', self.access_token, self.ticket_parameters)
print(" RES -> ", create_response.json())
assert create_response.status_code == 200
assert create_response.json()['status'] == 'success'

View file

@ -4,9 +4,10 @@ import pytest
import os
import requests
application_config = Config('api').get('general')
application_config = Config('application').get('general')
api_config = Config('application').get('api')
URL = f"http://127.0.0.1:{application_config['port']}"
URL = f"{application_config.get('domain')}:{api_config.get('port')}"
def get_request(endpoint: str):
return requests.get(f'{URL}{endpoint}')

View file

@ -0,0 +1,41 @@
import sys
import os
# I hate python imports
current = os.path.dirname(os.path.realpath(__file__))
parent = os.path.dirname(current)
sys.path.append(parent)
import tornado
from ioutils.protected import ProtectedHandler
from piracyshield_service.permission.service import PermissionService
from piracyshield_service.account.session.get_all_by_account_ordered import AccountSessionGetAllByAccountOrderedService
from piracyshield_component.exception import ApplicationException
class GetAllSessionAccountHandler(ProtectedHandler):
"""
Handles getting multiple active sessions.
"""
async def get(self):
if self.initialize_account() == False:
return
try:
account_session_get_all_by_account_ordered_service = AccountSessionGetAllByAccountOrderedService()
response = await tornado.ioloop.IOLoop.current().run_in_executor(
None,
account_session_get_all_by_account_ordered_service.execute,
self.account_data.get('account_id'),
self.current_access_token
)
self.success(data = response)
except ApplicationException as e:
self.error(status_code = 400, error_code = e.code, message = e.message)

View file

@ -11,8 +11,6 @@ from ioutils.base import BaseHandler
from ioutils.errors import ErrorCode, ErrorMessage
from piracyshield_service.authentication.authenticate import AuthenticationAuthenticateService
from piracyshield_service.authentication.generate_access_token import AuthenticationGenerateAccessTokenService
from piracyshield_service.authentication.generate_refresh_token import AuthenticationGenerateRefreshTokenService
from piracyshield_component.exception import ApplicationException
@ -36,14 +34,19 @@ class AuthenticationLoginHandler(BaseHandler):
return
try:
authentication_authenticate_service = AuthenticationAuthenticateService()
access_token, refresh_token = await tornado.ioloop.IOLoop.current().run_in_executor(
None,
self.process,
authentication_authenticate_service.execute,
self.request_data.get('email'),
self.request_data.get('password'),
self.request.remote_ip
)
# store the refresh token in a http-only secure cookie
self.set_refresh_cookie(value = refresh_token)
self.success(data = {
'access_token': access_token,
'refresh_token': refresh_token
@ -51,25 +54,3 @@ class AuthenticationLoginHandler(BaseHandler):
except ApplicationException as e:
self.error(status_code = 400, error_code = e.code, message = e.message)
def process(self, email: str, password: str, ip_address: str) -> tuple:
authentication_authenticate_service = AuthenticationAuthenticateService()
# try to authenticate
payload = authentication_authenticate_service.execute(
email = email,
password = password,
ip_address = ip_address
)
authentication_generate_access_token_service = AuthenticationGenerateAccessTokenService()
authentication_generate_refresh_token_service = AuthenticationGenerateRefreshTokenService()
# generate token pairs
access_token = authentication_generate_access_token_service.execute(payload)
refresh_token = authentication_generate_refresh_token_service.execute(payload)
# store the refresh_token in a http-only cookie
self.set_refresh_cookie(value = refresh_token)
return access_token, refresh_token

View file

@ -7,19 +7,31 @@ sys.path.append(parent)
from ioutils.protected import ProtectedHandler
from piracyshield_service.account.session.destroy_current_sessions import AccountSessionDestroyCurrentSessionsService
from piracyshield_component.exception import ApplicationException
class AuthenticationLogoutHandler(ProtectedHandler):
"""
Removes the authentication refresh token.
The effective logout remains on the access token expiration time, this is why it should be set to a short time.
Removes the authentication refresh token and blacklists both the tokens.
"""
def get(self):
if self.initialize_account() == False:
return
self.clear_cookie('refresh_token')
try:
account_session_destroy_current_sessions_service = AccountSessionDestroyCurrentSessionsService()
self.success(data = 'Goodbye!')
account_session_destroy_current_sessions_service.execute(
self.account_data.get('account_id'),
self.current_access_token
)
self.clear_cookie('refresh_token')
self.success(data = 'Goodbye!')
except ApplicationException as e:
self.error(status_code = 400, error_code = e.code, message = e.message)

View file

@ -10,8 +10,9 @@ import tornado
from ioutils.base import BaseHandler
from ioutils.errors import ErrorCode, ErrorMessage
from piracyshield_service.authentication.verify_refresh_token import AuthenticationVerifyRefreshTokenService
from piracyshield_service.authentication.generate_access_token import AuthenticationGenerateAccessTokenService
from piracyshield_service.security.blacklist.exists_by_refresh_token import SecurityBlacklistExistsByRefreshTokenService
from piracyshield_service.authentication.refresh_access_token import AuthenticationRefreshAccessTokenService
from piracyshield_component.exception import ApplicationException
@ -41,10 +42,22 @@ class AuthenticationRefreshHandler(BaseHandler):
refresh_token = self.request_data.get('refresh_token')
try:
security_blacklist_exists_by_refresh_token_service = SecurityBlacklistExistsByRefreshTokenService()
if security_blacklist_exists_by_refresh_token_service.execute(
refresh_token = refresh_token
) == True:
self.error(status_code = 401, error_code = ErrorCode.TOKEN_EXPIRED, message = ErrorMessage.TOKEN_EXPIRED)
return False
authentication_refresh_access_token_service = AuthenticationRefreshAccessTokenService()
access_token = await tornado.ioloop.IOLoop.current().run_in_executor(
None,
self.process,
refresh_token
authentication_refresh_access_token_service.execute,
refresh_token,
self.request.remote_ip
)
# return the access_token
@ -54,18 +67,3 @@ class AuthenticationRefreshHandler(BaseHandler):
except ApplicationException as e:
self.error(status_code = 400, error_code = e.code, message = e.message)
def process(self, refresh_token: str) -> str:
authentication_verify_refresh_token_service = AuthenticationVerifyRefreshTokenService()
# verify the token and unwrap the payload
payload = authentication_verify_refresh_token_service.execute(
token = refresh_token
)
authentication_generate_access_token_service = AuthenticationGenerateAccessTokenService()
# generate a new access token
access_token = authentication_generate_access_token_service.execute(payload)
return access_token

View file

@ -31,6 +31,8 @@ from .handlers.account.provider.set_status import SetStatusActiveProviderAccount
from .handlers.account.provider.change_password import ChangePasswordProviderAccountHandler
from .handlers.account.provider.remove import RemoveProviderAccountHandler
from .handlers.account.session.get_all import GetAllSessionAccountHandler
from .handlers.ticket.create import CreateTicketHandler
from .handlers.ticket.get import GetTicketHandler
from .handlers.ticket.get_all import GetAllTicketHandler
@ -139,6 +141,9 @@ class APIv1:
(r"/account/provider/change_password", ChangePasswordProviderAccountHandler),
(r"/account/provider/remove", RemoveProviderAccountHandler),
# sessions
(r"/account/session/get/all", GetAllSessionAccountHandler),
# blocking ticket management
(r"/ticket/create", CreateTicketHandler),
(r"/ticket/get", GetTicketHandler),