# Copyright (c) 2020 CloudZero - ALL RIGHTS RESERVED - PROPRIETARY AND CONFIDENTIAL
# Unauthorized copying of this file and/or project, via any medium is strictly prohibited.
# Direct all questions to legal@cloudzero.com

import os
from copy import deepcopy

import pytest

from czc import json
from czc.authorization import (
    PathAuthorizationException,
    authorize_organizer,
    authorize_path,
    organization_path_authorizer,
    path_authorizer,
    unpack_acls,
)
from czc.string import vacuum_pack

_TEST_CLOUD_ACCOUNT_IDS = ['123456789012', '123456789013', '123456789014']
_VACUUMED_ACLS = vacuum_pack({'cloud_account_ids': _TEST_CLOUD_ACCOUNT_IDS}).decode()
_EMPTY_VACUUMED_ACLS = vacuum_pack({'cloud_account_ids': []})
_TEST_ORG_ID = '1'
_TEST_USER_ID = '0'
_PATH_PARAMS = ['organizationId', 'userId']
_TEST_EVENT = {
    'headers': {'origin': 'https://test.cloudzero.com'},
    'requestContext': {
        'authorizer': {
            'domain': 'cloudzero.com',
            'acls': _VACUUMED_ACLS,
            'organization_id': _TEST_ORG_ID,
            'user_id': _TEST_USER_ID,
        },
        'role_arn': 'external_role',
        'external_id': 'role_external_id',
    },
    'pathParameters': {'organizationId': _TEST_ORG_ID, 'userId': _TEST_USER_ID},
}


def identity_handler(event, context, **kwargs):
    return event, context, kwargs


def load_test_data(data_directory, filename):
    path = os.path.join(data_directory, filename)
    data_file = open(path)
    return json.load(data_file)


@pytest.mark.unit
def test_authorize_organizer_succeeds():
    handler = authorize_organizer(identity_handler)
    event = deepcopy(_TEST_EVENT)
    event['requestContext']['authorizer']['acls'] = vacuum_pack({'cloud_account_ids': ['*']})
    returned_event, _, _ = handler(event, None)
    assert returned_event == event


@pytest.mark.unit
def test_authorize_organizer_fails(caplog):
    handler = authorize_organizer(identity_handler)
    response = handler(_TEST_EVENT, None)
    assert response['statusCode'] == 403
    assert response['body']['error'] == 'User not authorized.'
    assert 'User attempting to access an organizer-only endpoint' in caplog.text


@pytest.mark.unit
def test_path_authorizer_succeeds():
    handler = path_authorizer(_PATH_PARAMS)(identity_handler)
    _, _, kwargs = handler(_TEST_EVENT, None)

    assert kwargs['user_id'] == _TEST_USER_ID
    assert kwargs['organization_id'] == _TEST_ORG_ID


@pytest.mark.unit
def test_path_authorizer_unauthorized():
    event = deepcopy(_TEST_EVENT)
    event['pathParameters']['userId'] = '99'  # path is wrong
    handler = path_authorizer(_PATH_PARAMS)(identity_handler)
    result = handler(event, None)
    assert result['statusCode'] == 403
    assert result['body']['error']['message'] == 'User not authorized'

    event = deepcopy(_TEST_EVENT)
    event['requestContext']['authorizer']['user_id'] = '99'  # token is wrong
    result = handler(event, None)
    assert result['statusCode'] == 403
    assert result['body']['error']['message'] == 'User not authorized'


@pytest.mark.unit
def test_organization_path_authorizer_succeeds():
    handler = organization_path_authorizer(identity_handler)
    _, _, kwargs = handler(_TEST_EVENT, None)

    assert kwargs['organization_id'] == _TEST_ORG_ID


@pytest.mark.unit
def test_authorize_path_succeeds(caplog):
    result = authorize_path(_TEST_EVENT, _PATH_PARAMS)
    assert result['user_id'] == _TEST_USER_ID
    assert result['organization_id'] == _TEST_ORG_ID


@pytest.mark.unit
def test_authorize_path_with_options_succeeds(caplog):
    event = deepcopy(_TEST_EVENT)
    event['requestContext']['authorizer']['userId'] = event['requestContext']['authorizer']['user_id']
    event['requestContext']['authorizer']['organizationId'] = event['requestContext']['authorizer']['organization_id']
    result = authorize_path(event, _PATH_PARAMS, snake_auth_param=False, snake_out_param=False)
    assert result['userId'] == _TEST_USER_ID
    assert result['organizationId'] == _TEST_ORG_ID


@pytest.mark.unit
def test_authorize_path_succeeds_no_params(caplog):
    result = authorize_path(_TEST_EVENT, [])
    assert not result


@pytest.mark.unit
def test_authorize_path_user_fails(caplog):
    event = deepcopy(_TEST_EVENT)
    event['pathParameters']['userId'] = '99'
    with pytest.raises(PathAuthorizationException):
        authorize_path(event, _PATH_PARAMS)

    assert f'userId {_TEST_USER_ID} attempting to access data for userId 99' in caplog.text

    event = deepcopy(_TEST_EVENT)
    event['requestContext']['authorizer']['user_id'] = '99'
    with pytest.raises(PathAuthorizationException):
        authorize_path(event, _PATH_PARAMS)

    assert f'userId 99 attempting to access data for userId {_TEST_USER_ID}' in caplog.text


@pytest.mark.unit
def test_authorize_path_organization_fails(caplog):
    event = deepcopy(_TEST_EVENT)
    event['pathParameters']['organizationId'] = '99'
    with pytest.raises(PathAuthorizationException):
        authorize_path(event, _PATH_PARAMS)

    assert f'organizationId {_TEST_ORG_ID} attempting to access data for organizationId 99' in caplog.text

    event = deepcopy(_TEST_EVENT)
    event['requestContext']['authorizer']['organization_id'] = '99'
    with pytest.raises(PathAuthorizationException):
        authorize_path(event, _PATH_PARAMS)

    assert f'organizationId 99 attempting to access data for organizationId {_TEST_ORG_ID}' in caplog.text


@pytest.mark.unit
def test_unpack_acls():
    assert unpack_acls(_TEST_EVENT) == {'cloud_account_ids': _TEST_CLOUD_ACCOUNT_IDS, 'tags': []}
