API - init OAuth server and oauth clients creation
This commit is contained in:
@ -10,6 +10,7 @@ os.environ['DATABASE_URL'] = os.environ['DATABASE_TEST_URL']
|
||||
TEMP_FOLDER = '/tmp/FitTrackee'
|
||||
os.environ['UPLOAD_FOLDER'] = TEMP_FOLDER
|
||||
os.environ['APP_LOG'] = TEMP_FOLDER + '/fittrackee.log'
|
||||
os.environ['AUTHLIB_INSECURE_TRANSPORT'] = '1'
|
||||
|
||||
pytest_plugins = [
|
||||
'fittrackee.tests.fixtures.fixtures_app',
|
||||
|
@ -1,4 +1,5 @@
|
||||
import json
|
||||
from random import randint
|
||||
from typing import Any, Dict, Optional, Tuple
|
||||
|
||||
from flask import Flask
|
||||
@ -18,10 +19,18 @@ class RandomMixin:
|
||||
) -> str:
|
||||
return random_string(length, prefix, suffix)
|
||||
|
||||
@staticmethod
|
||||
def random_domain() -> str:
|
||||
return random_string(prefix='https://', suffix='com')
|
||||
|
||||
@staticmethod
|
||||
def random_email() -> str:
|
||||
return random_email()
|
||||
|
||||
@staticmethod
|
||||
def random_int(min_val: int = 0, max_val: int = 999999) -> int:
|
||||
return randint(min_val, max_val)
|
||||
|
||||
|
||||
class ApiTestCaseMixin(RandomMixin):
|
||||
@staticmethod
|
||||
|
0
fittrackee/tests/oauth2/__init__.py
Normal file
0
fittrackee/tests/oauth2/__init__.py
Normal file
137
fittrackee/tests/oauth2/test_oauth2_client.py
Normal file
137
fittrackee/tests/oauth2/test_oauth2_client.py
Normal file
@ -0,0 +1,137 @@
|
||||
from time import time
|
||||
from typing import Dict
|
||||
from unittest.mock import patch
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.oauth2.client import create_oauth_client
|
||||
from fittrackee.oauth2.models import OAuth2Client
|
||||
from fittrackee.users.models import User
|
||||
|
||||
from ..utils import random_domain, random_string
|
||||
|
||||
TEST_METADATA = {
|
||||
'client_name': random_string(),
|
||||
'client_uri': random_string(),
|
||||
'redirect_uris': [random_domain()],
|
||||
'scope': 'read write',
|
||||
}
|
||||
|
||||
|
||||
class TestCreateOAuth2Client:
|
||||
def test_it_creates_oauth_client(self, app: Flask, user_1: User) -> None:
|
||||
oauth_client = create_oauth_client(TEST_METADATA, user_1)
|
||||
|
||||
assert isinstance(oauth_client, OAuth2Client)
|
||||
|
||||
def test_oauth_client_id_is_generated_with_gen_salt(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
client_id = random_string()
|
||||
with patch(
|
||||
'fittrackee.oauth2.client.gen_salt', return_value=client_id
|
||||
):
|
||||
|
||||
oauth_client = create_oauth_client(TEST_METADATA, user_1)
|
||||
|
||||
assert oauth_client.client_id == client_id
|
||||
|
||||
def test_oauth_client_client_id_issued_at_is_initialized(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
client_id_issued_at = int(time())
|
||||
with patch(
|
||||
'fittrackee.oauth2.client.time', return_value=client_id_issued_at
|
||||
):
|
||||
oauth_client = create_oauth_client(TEST_METADATA, user_1)
|
||||
|
||||
assert oauth_client.client_id_issued_at == client_id_issued_at
|
||||
|
||||
def test_oauth_client_has_expected_name(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
client_name = random_string()
|
||||
client_metadata: Dict = {**TEST_METADATA, 'client_name': client_name}
|
||||
|
||||
oauth_client = create_oauth_client(client_metadata, user_1)
|
||||
|
||||
assert oauth_client.client_name == client_name
|
||||
|
||||
def test_oauth_client_has_expected_client_uri(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
client_uri = random_domain()
|
||||
client_metadata: Dict = {**TEST_METADATA, 'client_uri': client_uri}
|
||||
|
||||
oauth_client = create_oauth_client(client_metadata, user_1)
|
||||
|
||||
assert oauth_client.client_uri == client_uri
|
||||
|
||||
def test_oauth_client_has_expected_grant_types(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
oauth_client = create_oauth_client(TEST_METADATA, user_1)
|
||||
|
||||
assert oauth_client.grant_types == ['authorization_code']
|
||||
|
||||
def test_oauth_client_has_expected_redirect_uris(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
redirect_uris = [random_domain()]
|
||||
client_metadata: Dict = {
|
||||
**TEST_METADATA,
|
||||
'redirect_uris': redirect_uris,
|
||||
}
|
||||
|
||||
oauth_client = create_oauth_client(client_metadata, user_1)
|
||||
|
||||
assert oauth_client.redirect_uris == redirect_uris
|
||||
|
||||
def test_oauth_client_has_expected_response_types(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
response_types = ['code']
|
||||
client_metadata: Dict = {
|
||||
**TEST_METADATA,
|
||||
'response_types': response_types,
|
||||
}
|
||||
|
||||
oauth_client = create_oauth_client(client_metadata, user_1)
|
||||
|
||||
assert oauth_client.response_types == ['code']
|
||||
|
||||
def test_oauth_client_has_expected_scope(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
scope = 'profile'
|
||||
client_metadata: Dict = {**TEST_METADATA, 'scope': scope}
|
||||
|
||||
oauth_client = create_oauth_client(client_metadata, user_1)
|
||||
|
||||
assert oauth_client.scope == scope
|
||||
|
||||
def test_oauth_client_has_expected_token_endpoint_auth_method(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
oauth_client = create_oauth_client(TEST_METADATA, user_1)
|
||||
|
||||
assert oauth_client.token_endpoint_auth_method == 'client_secret_post'
|
||||
|
||||
def test_when_auth_method_is_not_none_oauth_client_secret_is_generated(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
client_secret = random_string()
|
||||
with patch(
|
||||
'fittrackee.oauth2.client.gen_salt', return_value=client_secret
|
||||
):
|
||||
|
||||
oauth_client = create_oauth_client(TEST_METADATA, user_1)
|
||||
|
||||
assert oauth_client.client_secret == client_secret
|
||||
|
||||
def test_it_creates_oauth_client_for_given_user(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
oauth_client = create_oauth_client(TEST_METADATA, user_1)
|
||||
|
||||
assert oauth_client.user_id == user_1.id
|
36
fittrackee/tests/oauth2/test_oauth2_models.py
Normal file
36
fittrackee/tests/oauth2/test_oauth2_models.py
Normal file
@ -0,0 +1,36 @@
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.oauth2.models import OAuth2Client
|
||||
|
||||
from ..mixins import RandomMixin
|
||||
|
||||
|
||||
class TestOAuthClientSerialize(RandomMixin):
|
||||
def test_it_returns_oauth_client(self, app: Flask) -> None:
|
||||
oauth_client = OAuth2Client(
|
||||
id=self.random_int(),
|
||||
client_id=self.random_string(),
|
||||
client_id_issued_at=self.random_int(),
|
||||
)
|
||||
oauth_client.set_client_metadata(
|
||||
{
|
||||
'client_name': self.random_string(),
|
||||
'redirect_uris': [self.random_string()],
|
||||
'client_uri': self.random_domain(),
|
||||
}
|
||||
)
|
||||
|
||||
serialized_oauth_client = oauth_client.serialize()
|
||||
|
||||
assert serialized_oauth_client['client_id'] == oauth_client.client_id
|
||||
assert (
|
||||
serialized_oauth_client['client_secret']
|
||||
== oauth_client.client_secret
|
||||
)
|
||||
assert serialized_oauth_client['id'] == oauth_client.id
|
||||
assert serialized_oauth_client['name'] == oauth_client.client_name
|
||||
assert (
|
||||
serialized_oauth_client['redirect_uris']
|
||||
== oauth_client.redirect_uris
|
||||
)
|
||||
assert serialized_oauth_client['website'] == oauth_client.client_uri
|
163
fittrackee/tests/oauth2/test_oauth2_routes.py
Normal file
163
fittrackee/tests/oauth2/test_oauth2_routes.py
Normal file
@ -0,0 +1,163 @@
|
||||
import json
|
||||
from typing import List, Union
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.oauth2.models import OAuth2Client
|
||||
from fittrackee.users.models import User
|
||||
|
||||
from ..mixins import ApiTestCaseMixin
|
||||
from ..utils import random_domain, random_string
|
||||
|
||||
TEST_METADATA = {
|
||||
'client_name': random_string(),
|
||||
'client_uri': random_domain(),
|
||||
'redirect_uris': [random_domain()],
|
||||
'scope': 'read write',
|
||||
}
|
||||
|
||||
|
||||
class TestOAuthClientCreation(ApiTestCaseMixin):
|
||||
route = '/api/oauth/apps'
|
||||
|
||||
def test_it_returns_error_when_no_user_authenticated(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_1.email
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
self.route,
|
||||
data=json.dumps(TEST_METADATA),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
self.assert_401(response)
|
||||
|
||||
def test_it_returns_error_when_no_metadata_provided(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_1.email
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
self.route,
|
||||
data=json.dumps(dict()),
|
||||
content_type='application/json',
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
self.assert_400(
|
||||
response, error_message='OAuth client metadata missing'
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'missing_key',
|
||||
[
|
||||
'client_name',
|
||||
'client_uri',
|
||||
'redirect_uris',
|
||||
'scope',
|
||||
],
|
||||
)
|
||||
def test_it_returns_error_when_metadata_key_is_missing(
|
||||
self, app: Flask, user_1: User, missing_key: str
|
||||
) -> None:
|
||||
metadata = TEST_METADATA.copy()
|
||||
del metadata[missing_key]
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_1.email
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
self.route,
|
||||
data=json.dumps(metadata),
|
||||
content_type='application/json',
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
self.assert_400(
|
||||
response,
|
||||
error_message=f'OAuth client metadata missing keys: {missing_key}',
|
||||
)
|
||||
|
||||
def test_it_creates_oauth_client(self, app: Flask, user_1: User) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_1.email
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
self.route,
|
||||
data=json.dumps(TEST_METADATA),
|
||||
content_type='application/json',
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
oauth_client = OAuth2Client.query.first()
|
||||
assert oauth_client is not None
|
||||
|
||||
def test_it_returns_serialized_oauth_client(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_1.email
|
||||
)
|
||||
client_id = self.random_string()
|
||||
client_secret = self.random_string()
|
||||
with patch(
|
||||
'fittrackee.oauth2.client.gen_salt',
|
||||
side_effect=[client_id, client_secret],
|
||||
):
|
||||
|
||||
response = client.post(
|
||||
self.route,
|
||||
data=json.dumps(TEST_METADATA),
|
||||
content_type='application/json',
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
data = json.loads(response.data.decode())
|
||||
assert data['data']['client']['client_id'] == client_id
|
||||
assert data['data']['client']['client_secret'] == client_secret
|
||||
assert data['data']['client']['name'] == TEST_METADATA['client_name']
|
||||
assert (
|
||||
data['data']['client']['redirect_uris']
|
||||
== TEST_METADATA['redirect_uris']
|
||||
)
|
||||
assert data['data']['client']['website'] == TEST_METADATA['client_uri']
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'input_key,expected_value',
|
||||
[
|
||||
('grant_types', ['authorization_code']),
|
||||
('response_types', ['code']),
|
||||
('token_endpoint_auth_method', 'client_secret_post'),
|
||||
],
|
||||
)
|
||||
def test_it_always_create_oauth_client_with_authorization_grant(
|
||||
self,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
input_key: str,
|
||||
expected_value: Union[List, str],
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_1.email
|
||||
)
|
||||
|
||||
client.post(
|
||||
self.route,
|
||||
data=json.dumps(
|
||||
{**TEST_METADATA, input_key: self.random_string()}
|
||||
),
|
||||
content_type='application/json',
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
oauth_client = OAuth2Client.query.first()
|
||||
assert getattr(oauth_client, input_key) == expected_value
|
@ -24,6 +24,10 @@ def random_string(
|
||||
)
|
||||
|
||||
|
||||
def random_domain() -> str:
|
||||
return random_string(prefix='https://', suffix='.com')
|
||||
|
||||
|
||||
def random_email() -> str:
|
||||
return random_string(suffix='@example.com')
|
||||
|
||||
|
Reference in New Issue
Block a user