From 233802b29e508e6475e8731331ecba3e5e7b8991 Mon Sep 17 00:00:00 2001 From: roberto Date: Tue, 26 Jan 2021 13:06:20 -0500 Subject: [PATCH 1/6] fix: TT-131 remove feature toggle from users endpoint --- .../users/users_namespace_test.py | 51 +------------------ time_tracker_api/users/users_namespace.py | 22 ++++---- 2 files changed, 11 insertions(+), 62 deletions(-) diff --git a/tests/time_tracker_api/users/users_namespace_test.py b/tests/time_tracker_api/users/users_namespace_test.py index 73ea1be4..e6d43e0a 100644 --- a/tests/time_tracker_api/users/users_namespace_test.py +++ b/tests/time_tracker_api/users/users_namespace_test.py @@ -11,31 +11,16 @@ @patch( 'utils.azure_users.AzureConnection.is_test_user', Mock(return_value=True) ) -@patch( - 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.get_azure_app_configuration_client' -) -@patch( - 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.is_toggle_enabled_for_user' -) -@patch('utils.azure_users.AzureConnection.users') @patch('utils.azure_users.AzureConnection.users_v2') -def test_feature_toggle_is_on_then_role_field_is_list( - users_v2_mock, - users_mock, - is_toggle_enabled_for_user_mock, - get_azure_app_configuration_client_mock, - client: FlaskClient, - valid_header: dict, +def test_users_response_contains_expected_props( + users_v2_mock, client: FlaskClient, valid_header: dict, ): - - is_toggle_enabled_for_user_mock.return_value = True users_v2_mock.return_value = [ {'name': 'dummy', 'email': 'dummy', 'roles': ['dummy-role']} ] response = client.get('/users', headers=valid_header) users_v2_mock.assert_called() - users_mock.assert_not_called() assert HTTPStatus.OK == response.status_code assert 'name' in json.loads(response.data)[0] assert 'email' in json.loads(response.data)[0] @@ -43,38 +28,6 @@ def test_feature_toggle_is_on_then_role_field_is_list( assert ['dummy-role'] == json.loads(response.data)[0]['roles'] -@patch( - 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.get_azure_app_configuration_client' -) -@patch( - 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.is_toggle_enabled_for_user' -) -@patch('utils.azure_users.AzureConnection.users') -@patch('utils.azure_users.AzureConnection.users_v2') -def test_feature_toggle_is_off_then_role_field_is_string( - users_v2_mock, - users_mock, - is_toggle_enabled_for_user_mock, - get_azure_app_configuration_client_mock, - client: FlaskClient, - valid_header: dict, -): - is_toggle_enabled_for_user_mock.return_value = False - users_mock.return_value = [ - {'name': 'dummy', 'email': 'dummy', 'role': 'dummy-role'} - ] - - response = client.get('/users', headers=valid_header) - - users_mock.assert_called() - users_v2_mock.assert_not_called() - assert HTTPStatus.OK == response.status_code - assert 'name' in json.loads(response.data)[0] - assert 'email' in json.loads(response.data)[0] - assert 'role' in json.loads(response.data)[0] - assert 'dummy-role' == json.loads(response.data)[0]['role'] - - def test_update_user_role_response_contains_expected_props( client: FlaskClient, valid_header: dict, user_id: str, ): diff --git a/time_tracker_api/users/users_namespace.py b/time_tracker_api/users/users_namespace.py index 241df113..e6ea9c7b 100644 --- a/time_tracker_api/users/users_namespace.py +++ b/time_tracker_api/users/users_namespace.py @@ -6,7 +6,6 @@ from time_tracker_api.security import current_user_id from utils.azure_users import AzureConnection -from commons.feature_toggles.feature_toggle_manager import FeatureToggleManager ns = api.namespace('users', description='Namespace of the API for users') @@ -67,18 +66,15 @@ class Users(Resource): @ns.marshal_list_with(user_response_fields) def get(self): """List all users""" - user_role_field_toggle = FeatureToggleManager('bk-user-role-field') - if user_role_field_toggle.is_toggle_enabled_for_user(): - azure_connection = AzureConnection() - is_current_user_a_tester = azure_connection.is_test_user( - current_user_id() - ) - return ( - azure_connection.users_v2() - if is_current_user_a_tester - else azure_connection.get_non_test_users() - ) - return AzureConnection().users() + azure_connection = AzureConnection() + is_current_user_a_tester = azure_connection.is_test_user( + current_user_id() + ) + return ( + azure_connection.users_v2() + if is_current_user_a_tester + else azure_connection.get_non_test_users() + ) @ns.route('//roles') From ee40e3c08bb2ad33e9f9a3d63a4ef8ac8e79c511 Mon Sep 17 00:00:00 2001 From: roberto Date: Tue, 26 Jan 2021 13:18:06 -0500 Subject: [PATCH 2/6] fix: TT-131 remove old endpoints for grant admin role --- .../users/users_namespace_test.py | 57 ------------------- time_tracker_api/users/users_namespace.py | 29 ---------- utils/azure_users.py | 18 ------ 3 files changed, 104 deletions(-) diff --git a/tests/time_tracker_api/users/users_namespace_test.py b/tests/time_tracker_api/users/users_namespace_test.py index e6d43e0a..d2a995a4 100644 --- a/tests/time_tracker_api/users/users_namespace_test.py +++ b/tests/time_tracker_api/users/users_namespace_test.py @@ -28,26 +28,6 @@ def test_users_response_contains_expected_props( assert ['dummy-role'] == json.loads(response.data)[0]['roles'] -def test_update_user_role_response_contains_expected_props( - client: FlaskClient, valid_header: dict, user_id: str, -): - valid_user_role_data = {'role': 'admin'} - AzureConnection.update_user_role = Mock( - return_value={'name': 'dummy', 'email': 'dummy', 'role': 'dummy'} - ) - - response = client.post( - f'/users/{user_id}/roles', - headers=valid_header, - json=valid_user_role_data, - ) - - assert HTTPStatus.OK == response.status_code - assert 'name' in json.loads(response.data) - assert 'email' in json.loads(response.data) - assert 'role' in json.loads(response.data) - - @patch('utils.azure_users.AzureConnection.update_role') @mark.parametrize( 'role_id,action', [('test', 'grant'), ('admin', 'revoke')], @@ -74,43 +54,6 @@ def test_update_role_response_contains_expected_props( assert 'roles' in json.loads(response.data) -@patch('utils.azure_users.AzureConnection.update_user_role', new_callable=Mock) -def test_on_post_update_user_role_is_being_called_with_valid_arguments( - update_user_role_mock, - client: FlaskClient, - valid_header: dict, - user_id: str, -): - update_user_role_mock.return_value = {} - valid_user_role_data = {'role': 'admin'} - response = client.post( - f'/users/{user_id}/roles', - headers=valid_header, - json=valid_user_role_data, - ) - - assert HTTPStatus.OK == response.status_code - update_user_role_mock.assert_called_once_with( - user_id, valid_user_role_data['role'] - ) - - -@patch('utils.azure_users.AzureConnection.update_user_role', new_callable=Mock) -def test_on_delete_update_user_role_is_being_called_with_valid_arguments( - update_user_role_mock, - client: FlaskClient, - valid_header: dict, - user_id: str, -): - update_user_role_mock.return_value = {} - response = client.delete( - f'/users/{user_id}/roles/time-tracker-admin', headers=valid_header, - ) - - assert HTTPStatus.OK == response.status_code - update_user_role_mock.assert_called_once_with(user_id, role=None) - - @patch('utils.azure_users.AzureConnection.update_role', new_callable=Mock) @mark.parametrize( 'role_id,action,is_grant', diff --git a/time_tracker_api/users/users_namespace.py b/time_tracker_api/users/users_namespace.py index e6ea9c7b..a5710eac 100644 --- a/time_tracker_api/users/users_namespace.py +++ b/time_tracker_api/users/users_namespace.py @@ -77,35 +77,6 @@ def get(self): ) -@ns.route('//roles') -@ns.response(HTTPStatus.NOT_FOUND, 'User not found') -@ns.response(HTTPStatus.UNPROCESSABLE_ENTITY, 'The id has an invalid format') -@ns.param('id', 'The user identifier') -class UserRoles(Resource): - @ns.doc('create_user_role') - @ns.expect(user_role_input_fields) - @ns.response( - HTTPStatus.BAD_REQUEST, 'Invalid format or structure of the user' - ) - @ns.marshal_with(user_response_fields) - def post(self, id): - """Create user's role""" - return AzureConnection().update_user_role(id, ns.payload['role']) - - -@ns.route('//roles/') -@ns.response(HTTPStatus.NOT_FOUND, 'User not found') -@ns.response(HTTPStatus.UNPROCESSABLE_ENTITY, 'The id has an invalid format') -@ns.param('user_id', 'The user identifier') -@ns.param('role_id', 'The role name identifier') -class UserRole(Resource): - @ns.doc('delete_user_role') - @ns.marshal_with(user_response_fields) - def delete(self, user_id, role_id): - """Delete user's role""" - return AzureConnection().update_user_role(user_id, role=None) - - @ns.route('//roles//grant') @ns.param('user_id', 'The user identifier') @ns.param('role_id', 'The role name identifier') diff --git a/utils/azure_users.py b/utils/azure_users.py index 48f24fd2..fab8e74c 100644 --- a/utils/azure_users.py +++ b/utils/azure_users.py @@ -118,24 +118,6 @@ def users_v2(self) -> List[AzureUser]: self.to_azure_user_v2(item) for item in response.json()['value'] ] - def update_user_role(self, id, role): - endpoint = "{endpoint}/users/{user_id}?api-version=1.6".format( - endpoint=self.config.ENDPOINT, user_id=id - ) - data = {self.role_field: role} - response = requests.patch( - endpoint, - auth=BearerAuth(self.access_token), - data=json.dumps(data), - headers=HTTP_PATCH_HEADERS, - ) - assert 204 == response.status_code - - response = requests.get(endpoint, auth=BearerAuth(self.access_token)) - assert 200 == response.status_code - - return self.to_azure_user(response.json()) - def to_azure_user(self, item) -> AzureUser: there_is_email = len(item['otherMails']) > 0 there_is_role = self.role_field in item From e683c86fa6d3c54f016c19082ae175e07f93fab9 Mon Sep 17 00:00:00 2001 From: roberto Date: Tue, 26 Jan 2021 13:30:23 -0500 Subject: [PATCH 3/6] fix: TT-131 replace users by users_v2 method --- .../time_tracker_api/users/users_namespace_test.py | 8 ++++---- tests/utils/azure_users_test.py | 6 +++--- time_tracker_api/users/users_namespace.py | 2 +- utils/azure_users.py | 14 +------------- 4 files changed, 9 insertions(+), 21 deletions(-) diff --git a/tests/time_tracker_api/users/users_namespace_test.py b/tests/time_tracker_api/users/users_namespace_test.py index d2a995a4..4a2015a5 100644 --- a/tests/time_tracker_api/users/users_namespace_test.py +++ b/tests/time_tracker_api/users/users_namespace_test.py @@ -11,16 +11,16 @@ @patch( 'utils.azure_users.AzureConnection.is_test_user', Mock(return_value=True) ) -@patch('utils.azure_users.AzureConnection.users_v2') +@patch('utils.azure_users.AzureConnection.users') def test_users_response_contains_expected_props( - users_v2_mock, client: FlaskClient, valid_header: dict, + users_mock, client: FlaskClient, valid_header: dict, ): - users_v2_mock.return_value = [ + users_mock.return_value = [ {'name': 'dummy', 'email': 'dummy', 'roles': ['dummy-role']} ] response = client.get('/users', headers=valid_header) - users_v2_mock.assert_called() + users_mock.assert_called() assert HTTPStatus.OK == response.status_code assert 'name' in json.loads(response.data)[0] assert 'email' in json.loads(response.data)[0] diff --git a/tests/utils/azure_users_test.py b/tests/utils/azure_users_test.py index fe633946..98087f46 100644 --- a/tests/utils/azure_users_test.py +++ b/tests/utils/azure_users_test.py @@ -45,13 +45,13 @@ def test_azure_connection_get_test_user_ids(get_mock): @patch('msal.ConfidentialClientApplication', Mock()) @patch('utils.azure_users.AzureConnection.get_token', Mock()) @patch('utils.azure_users.AzureConnection.get_test_user_ids') -@patch('utils.azure_users.AzureConnection.users_v2') +@patch('utils.azure_users.AzureConnection.users') def test_azure_connection_get_non_test_users( - users_v2_mock, get_test_user_ids_mock + users_mock, get_test_user_ids_mock ): test_user = AzureUser_v2('ID1', None, None, []) non_test_user = AzureUser_v2('ID2', None, None, []) - users_v2_mock.return_value = [test_user, non_test_user] + users_mock.return_value = [test_user, non_test_user] get_test_user_ids_mock.return_value = ['ID1'] non_test_users = [non_test_user] az_conn = AzureConnection() diff --git a/time_tracker_api/users/users_namespace.py b/time_tracker_api/users/users_namespace.py index a5710eac..99f66fce 100644 --- a/time_tracker_api/users/users_namespace.py +++ b/time_tracker_api/users/users_namespace.py @@ -71,7 +71,7 @@ def get(self): current_user_id() ) return ( - azure_connection.users_v2() + azure_connection.users() if is_current_user_a_tester else azure_connection.get_non_test_users() ) diff --git a/utils/azure_users.py b/utils/azure_users.py index fab8e74c..548aa929 100644 --- a/utils/azure_users.py +++ b/utils/azure_users.py @@ -93,16 +93,6 @@ def get_token(self): raise ValueError(error_info) def users(self) -> List[AzureUser]: - endpoint = "{endpoint}/users?api-version=1.6&$select=displayName,otherMails,objectId,{role_field}".format( - endpoint=self.config.ENDPOINT, role_field=self.role_field, - ) - response = requests.get(endpoint, auth=BearerAuth(self.access_token)) - - assert 200 == response.status_code - assert 'value' in response.json() - return [self.to_azure_user(item) for item in response.json()['value']] - - def users_v2(self) -> List[AzureUser]: role_fields_params = ','.join( [field_name for field_name, _ in ROLE_FIELD_VALUES.values()] ) @@ -162,9 +152,7 @@ def update_role(self, user_id, role_id, is_grant): def get_non_test_users(self) -> List[AzureUser]: test_user_ids = self.get_test_user_ids() - return [ - user for user in self.users_v2() if user.id not in test_user_ids - ] + return [user for user in self.users() if user.id not in test_user_ids] def get_role_data(self, role_id, is_grant=True): assert role_id in ROLE_FIELD_VALUES.keys() From e8a1df41acca0df640dc6b2e87df928f5064145c Mon Sep 17 00:00:00 2001 From: roberto Date: Tue, 26 Jan 2021 13:36:26 -0500 Subject: [PATCH 4/6] fix: TT-131 replace to_azure_user_v2 method --- tests/utils/azure_users_test.py | 6 +++--- utils/azure_users.py | 26 +++----------------------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/tests/utils/azure_users_test.py b/tests/utils/azure_users_test.py index 98087f46..690f557a 100644 --- a/tests/utils/azure_users_test.py +++ b/tests/utils/azure_users_test.py @@ -1,5 +1,5 @@ from unittest.mock import Mock, patch -from utils.azure_users import AzureConnection, ROLE_FIELD_VALUES, AzureUser_v2 +from utils.azure_users import AzureConnection, ROLE_FIELD_VALUES, AzureUser from pytest import mark @@ -49,8 +49,8 @@ def test_azure_connection_get_test_user_ids(get_mock): def test_azure_connection_get_non_test_users( users_mock, get_test_user_ids_mock ): - test_user = AzureUser_v2('ID1', None, None, []) - non_test_user = AzureUser_v2('ID2', None, None, []) + test_user = AzureUser('ID1', None, None, []) + non_test_user = AzureUser('ID2', None, None, []) users_mock.return_value = [test_user, non_test_user] get_test_user_ids_mock.return_value = ['ID1'] non_test_users = [non_test_user] diff --git a/utils/azure_users.py b/utils/azure_users.py index 548aa929..9d9452ba 100644 --- a/utils/azure_users.py +++ b/utils/azure_users.py @@ -38,14 +38,6 @@ def __call__(self, r): class AzureUser: - def __init__(self, id, name, email, role): - self.id = id - self.name = name - self.email = email - self.role = role - - -class AzureUser_v2: def __init__(self, id, name, email, roles): self.id = id self.name = name @@ -104,22 +96,10 @@ def users(self) -> List[AzureUser]: assert 200 == response.status_code assert 'value' in response.json() - return [ - self.to_azure_user_v2(item) for item in response.json()['value'] - ] + return [self.to_azure_user(item) for item in response.json()['value']] def to_azure_user(self, item) -> AzureUser: there_is_email = len(item['otherMails']) > 0 - there_is_role = self.role_field in item - - id = item['objectId'] - name = item['displayName'] - email = item['otherMails'][0] if there_is_email else '' - role = item[self.role_field] if there_is_role else None - return AzureUser(id, name, email, role) - - def to_azure_user_v2(self, item) -> AzureUser_v2: - there_is_email = len(item['otherMails']) > 0 id = item['objectId'] name = item['displayName'] @@ -129,7 +109,7 @@ def to_azure_user_v2(self, item) -> AzureUser_v2: for (field_name, field_value) in ROLE_FIELD_VALUES.values() if field_name in item ] - return AzureUser_v2(id, name, email, roles) + return AzureUser(id, name, email, roles) def update_role(self, user_id, role_id, is_grant): endpoint = "{endpoint}/users/{user_id}?api-version=1.6".format( @@ -148,7 +128,7 @@ def update_role(self, user_id, role_id, is_grant): response = requests.get(endpoint, auth=BearerAuth(self.access_token)) assert 200 == response.status_code - return self.to_azure_user_v2(response.json()) + return self.to_azure_user(response.json()) def get_non_test_users(self) -> List[AzureUser]: test_user_ids = self.get_test_user_ids() From 2fea9be4939bcd5bd3cd021b30fa70c0057cf12d Mon Sep 17 00:00:00 2001 From: roberto Date: Tue, 26 Jan 2021 14:26:04 -0500 Subject: [PATCH 5/6] fix: TT-131 remove role field --- .../users/users_namespace_test.py | 1 - time_tracker_api/users/users_namespace.py | 22 +------------------ utils/azure_users.py | 2 -- 3 files changed, 1 insertion(+), 24 deletions(-) diff --git a/tests/time_tracker_api/users/users_namespace_test.py b/tests/time_tracker_api/users/users_namespace_test.py index 4a2015a5..1783e906 100644 --- a/tests/time_tracker_api/users/users_namespace_test.py +++ b/tests/time_tracker_api/users/users_namespace_test.py @@ -2,7 +2,6 @@ from flask import json from flask.testing import FlaskClient from flask_restplus._http import HTTPStatus -from utils.azure_users import AzureConnection from pytest import mark diff --git a/time_tracker_api/users/users_namespace.py b/time_tracker_api/users/users_namespace.py index 99f66fce..8cf181c2 100644 --- a/time_tracker_api/users/users_namespace.py +++ b/time_tracker_api/users/users_namespace.py @@ -1,8 +1,7 @@ from faker import Faker from flask_restplus import fields, Resource -from flask_restplus._http import HTTPStatus -from time_tracker_api.api import common_fields, api, NullableString +from time_tracker_api.api import common_fields, api from time_tracker_api.security import current_user_id from utils.azure_users import AzureConnection @@ -26,12 +25,6 @@ description='Email of the user that belongs to the tenant', example=Faker().email(), ), - 'role': NullableString( - title="User's Role", - max_length=50, - description='Role assigned to the user by the tenant', - example=Faker().word(['time-tracker-admin']), - ), 'roles': fields.List( fields.String( title='Roles', @@ -46,19 +39,6 @@ user_response_fields.update(common_fields) -user_role_input_fields = ns.model( - 'UserRoleInput', - { - 'role': NullableString( - title="User's Role", - required=True, - max_length=50, - description='Role assigned to the user by the tenant', - example=Faker().word(['time-tracker-admin']), - ), - }, -) - @ns.route('') class Users(Resource): diff --git a/utils/azure_users.py b/utils/azure_users.py index 9d9452ba..dd3a99e7 100644 --- a/utils/azure_users.py +++ b/utils/azure_users.py @@ -72,8 +72,6 @@ def __init__(self, config=MSConfig): self.config = config self.access_token = self.get_token() - self.role_field = 'extension_1d76efa96f604499acc0c0ee116a1453_role' - def get_token(self): response = self.client.acquire_token_for_client( scopes=self.config.SCOPE From 9063e0bc08dfdb3de93d2cbdbaf6bf1b1ae5739a Mon Sep 17 00:00:00 2001 From: roberto Date: Tue, 26 Jan 2021 14:51:36 -0500 Subject: [PATCH 6/6] fix: TT-131 extract MSAL client to method --- .../time_tracker_api/users/users_namespace_test.py | 6 +++++- tests/utils/azure_users_test.py | 6 +++--- utils/azure_users.py | 14 +++++++++----- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/time_tracker_api/users/users_namespace_test.py b/tests/time_tracker_api/users/users_namespace_test.py index 1783e906..de61beb5 100644 --- a/tests/time_tracker_api/users/users_namespace_test.py +++ b/tests/time_tracker_api/users/users_namespace_test.py @@ -5,7 +5,7 @@ from pytest import mark -@patch('msal.ConfidentialClientApplication', Mock()) +@patch('utils.azure_users.AzureConnection.get_msal_client', Mock()) @patch('utils.azure_users.AzureConnection.get_token', Mock()) @patch( 'utils.azure_users.AzureConnection.is_test_user', Mock(return_value=True) @@ -27,6 +27,8 @@ def test_users_response_contains_expected_props( assert ['dummy-role'] == json.loads(response.data)[0]['roles'] +@patch('utils.azure_users.AzureConnection.get_msal_client', Mock()) +@patch('utils.azure_users.AzureConnection.get_token', Mock()) @patch('utils.azure_users.AzureConnection.update_role') @mark.parametrize( 'role_id,action', [('test', 'grant'), ('admin', 'revoke')], @@ -53,6 +55,8 @@ def test_update_role_response_contains_expected_props( assert 'roles' in json.loads(response.data) +@patch('utils.azure_users.AzureConnection.get_msal_client', Mock()) +@patch('utils.azure_users.AzureConnection.get_token', Mock()) @patch('utils.azure_users.AzureConnection.update_role', new_callable=Mock) @mark.parametrize( 'role_id,action,is_grant', diff --git a/tests/utils/azure_users_test.py b/tests/utils/azure_users_test.py index 690f557a..07ed0191 100644 --- a/tests/utils/azure_users_test.py +++ b/tests/utils/azure_users_test.py @@ -3,7 +3,7 @@ from pytest import mark -@patch('msal.ConfidentialClientApplication', Mock()) +@patch('utils.azure_users.AzureConnection.get_msal_client', Mock()) @patch('utils.azure_users.AzureConnection.get_token', Mock()) @patch('requests.get') @mark.parametrize( @@ -26,7 +26,7 @@ def test_azure_connection_is_test_user( assert az_conn.is_test_user(test_user_id) == is_test_user_expected_value -@patch('msal.ConfidentialClientApplication', Mock()) +@patch('utils.azure_users.AzureConnection.get_msal_client', Mock()) @patch('utils.azure_users.AzureConnection.get_token', Mock()) @patch('requests.get') def test_azure_connection_get_test_user_ids(get_mock): @@ -42,7 +42,7 @@ def test_azure_connection_get_test_user_ids(get_mock): assert az_conn.get_test_user_ids() == ids -@patch('msal.ConfidentialClientApplication', Mock()) +@patch('utils.azure_users.AzureConnection.get_msal_client', Mock()) @patch('utils.azure_users.AzureConnection.get_token', Mock()) @patch('utils.azure_users.AzureConnection.get_test_user_ids') @patch('utils.azure_users.AzureConnection.users') diff --git a/utils/azure_users.py b/utils/azure_users.py index dd3a99e7..9d3e4dce 100644 --- a/utils/azure_users.py +++ b/utils/azure_users.py @@ -64,14 +64,18 @@ def __init__(self, id, name, email, roles): class AzureConnection: def __init__(self, config=MSConfig): - self.client = msal.ConfidentialClientApplication( - config.CLIENT_ID, - authority=config.AUTHORITY, - client_credential=config.SECRET, - ) self.config = config + self.client = self.get_msal_client() self.access_token = self.get_token() + def get_msal_client(self): + client = msal.ConfidentialClientApplication( + self.config.CLIENT_ID, + authority=self.config.AUTHORITY, + client_credential=self.config.SECRET, + ) + return client + def get_token(self): response = self.client.acquire_token_for_client( scopes=self.config.SCOPE