From 953b0e728bda73a6a8aa6bcf470e50a3c4344d3e Mon Sep 17 00:00:00 2001
From: Manjinder Singh <49171515+jinder1s@users.noreply.github.com>
Date: Thu, 27 Feb 2020 15:42:09 -0500
Subject: [PATCH] Remove ThirdPartyAuthProviderApiPermission (#23195)

* Remove ThirdPartyAuthProviderApiPermission

Also removed ProviderApiPermissions and ApiPermissionsAdminForm and removal of DOP for third_party_auth

* Removing model

* Replaced long_token with default_token_generator

* Adding skip to test_migrations_are_in_sync
---
 common/djangoapps/third_party_auth/admin.py   | 28 +---------
 .../third_party_auth/api/permissions.py       | 30 +----------
 .../api/tests/test_permissions.py             | 44 +---------------
 .../third_party_auth/api/tests/test_views.py  | 52 +++++++------------
 common/djangoapps/third_party_auth/models.py  | 27 +---------
 .../third_party_auth/tests/testutil.py        | 16 ++----
 .../third_party_auth/tests/utils.py           |  7 ++-
 common/djangoapps/util/tests/test_db.py       |  5 +-
 8 files changed, 36 insertions(+), 173 deletions(-)

diff --git a/common/djangoapps/third_party_auth/admin.py b/common/djangoapps/third_party_auth/admin.py
index 6ec3ce73b04..bbb4c5ea16d 100644
--- a/common/djangoapps/third_party_auth/admin.py
+++ b/common/djangoapps/third_party_auth/admin.py
@@ -7,19 +7,17 @@ Admin site configuration for third party authentication
 from config_models.admin import KeyedConfigurationModelAdmin
 from django import forms
 from django.contrib import admin
-from django.db import DatabaseError, transaction
+from django.db import transaction
 from django.urls import reverse
 from django.utils.translation import ugettext_lazy as _
 
 from openedx.core.djangolib.markup import HTML
-from third_party_auth.provider import Registry
 
 from .models import (
     _PSA_OAUTH2_BACKENDS,
     _PSA_SAML_BACKENDS,
     LTIProviderConfig,
     OAuth2ProviderConfig,
-    ProviderApiPermissions,
     SAMLConfiguration,
     SAMLProviderConfig,
     SAMLProviderData
@@ -199,27 +197,3 @@ class LTIProviderConfigAdmin(KeyedConfigurationModelAdmin):
         )
 
 admin.site.register(LTIProviderConfig, LTIProviderConfigAdmin)
-
-
-class ApiPermissionsAdminForm(forms.ModelForm):
-    """ Django admin form for ApiPermissions model """
-    class Meta(object):
-        model = ProviderApiPermissions
-        fields = ['client', 'provider_id']
-
-    provider_id = forms.ChoiceField(choices=[], required=True)
-
-    def __init__(self, *args, **kwargs):
-        super(ApiPermissionsAdminForm, self).__init__(*args, **kwargs)
-        self.fields['provider_id'].choices = (
-            (provider.provider_id, u"{} ({})".format(provider.name, provider.provider_id))
-            for provider in Registry.enabled()
-        )
-
-
-class ApiPermissionsAdmin(admin.ModelAdmin):
-    """ Django Admin class for ApiPermissions """
-    list_display = ('client', 'provider_id')
-    form = ApiPermissionsAdminForm
-
-admin.site.register(ProviderApiPermissions, ApiPermissionsAdmin)
diff --git a/common/djangoapps/third_party_auth/api/permissions.py b/common/djangoapps/third_party_auth/api/permissions.py
index 0178fa0a550..91f5e9fa710 100644
--- a/common/djangoapps/third_party_auth/api/permissions.py
+++ b/common/djangoapps/third_party_auth/api/permissions.py
@@ -4,7 +4,6 @@ Third party auth API related permissions
 
 import logging
 
-from edx_django_utils.monitoring import set_custom_metric
 from edx_rest_framework_extensions.auth.jwt.decoder import decode_jwt_filters
 from edx_rest_framework_extensions.permissions import (
     IsSuperuser,
@@ -14,39 +13,12 @@ from edx_rest_framework_extensions.permissions import (
 )
 from rest_condition import C
 from rest_framework.permissions import BasePermission
-from third_party_auth.models import ProviderApiPermissions
 
 from openedx.core.lib.api.permissions import ApiKeyHeaderPermission
 
 log = logging.getLogger(__name__)
 
 
-class ThirdPartyAuthProviderApiPermission(BasePermission):
-    """
-    Allow someone to access the view if they have valid OAuth client credential.
-
-    Deprecated: Only works for DOP oauth applications. To be removed as part of DOPrecation.
-
-    """
-    def has_permission(self, request, view):
-        """
-        Check if the OAuth client associated with auth token in current request has permission to access
-        the information for provider
-        """
-        provider_id = view.kwargs.get('provider_id')
-        if not request.auth or not provider_id:
-            # doesn't have access token or no provider_id specified
-            return False
-
-        try:
-            ProviderApiPermissions.objects.get(client__pk=request.auth.client_id, provider_id=provider_id)
-        except ProviderApiPermissions.DoesNotExist:
-            return False
-
-        set_custom_metric('deprecated_ThirdPartyAuthProviderApiPermission', True)
-        return True
-
-
 class JwtHasTpaProviderFilterForRequestedProvider(BasePermission):
     """
     Ensures the JWT used to authenticate contains the appropriate tpa_provider
@@ -79,7 +51,7 @@ class JwtHasTpaProviderFilterForRequestedProvider(BasePermission):
 # TODO: Remove ApiKeyHeaderPermission. Check deprecated_api_key_header custom metric for active usage.
 _NOT_JWT_RESTRICTED_TPA_PERMISSIONS = (
     C(NotJwtRestrictedApplication) &
-    (C(IsSuperuser) | ApiKeyHeaderPermission | ThirdPartyAuthProviderApiPermission)
+    (C(IsSuperuser) | ApiKeyHeaderPermission)
 )
 _JWT_RESTRICTED_TPA_PERMISSIONS = (
     C(JwtRestrictedApplication) &
diff --git a/common/djangoapps/third_party_auth/api/tests/test_permissions.py b/common/djangoapps/third_party_auth/api/tests/test_permissions.py
index 8011589c8a9..8dccb48aed4 100644
--- a/common/djangoapps/third_party_auth/api/tests/test_permissions.py
+++ b/common/djangoapps/third_party_auth/api/tests/test_permissions.py
@@ -10,58 +10,18 @@ from django.conf import settings
 from django.test import RequestFactory, TestCase
 from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
 from edx_rest_framework_extensions.auth.jwt.tests.utils import generate_jwt
-from mock import Mock, patch
+from mock import patch
 from rest_framework.authentication import SessionAuthentication
 from rest_framework.response import Response
-from rest_framework.test import APITestCase
 from rest_framework.views import APIView
 from student.tests.factories import UserFactory
 
-from third_party_auth.api.permissions import ThirdPartyAuthProviderApiPermission, TPA_PERMISSIONS
-from third_party_auth.tests.testutil import ThirdPartyAuthTestMixin
+from third_party_auth.api.permissions import TPA_PERMISSIONS
 
 IDP_SLUG_TESTSHIB = 'testshib'
 PROVIDER_ID_TESTSHIB = 'saml-' + IDP_SLUG_TESTSHIB
 
 
-@ddt.ddt
-@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
-class ThirdPartyAuthApiPermissionTest(ThirdPartyAuthTestMixin, APITestCase):
-    """ Tests for third party auth API permission """
-
-    @ddt.data(
-        (1, PROVIDER_ID_TESTSHIB, True),
-        (1, 'invalid-provider-id', False),
-        (999, PROVIDER_ID_TESTSHIB, False),
-        (999, 'invalid-provider-id', False),
-        (1, None, False),
-    )
-    @ddt.unpack
-    def test_api_permission(self, client_pk, provider_id, expect):
-        dop_client = self.configure_oauth_dop_client()
-        self.configure_api_permission(dop_client, PROVIDER_ID_TESTSHIB)
-
-        request = Mock()
-        request.auth = Mock()
-        request.auth.client_id = client_pk
-        view = Mock(kwargs={'provider_id': provider_id})
-
-        result = ThirdPartyAuthProviderApiPermission().has_permission(request, view)
-        self.assertEqual(result, expect)
-
-    def test_api_permission_unauthorized_client(self):
-        dop_client = self.configure_oauth_dop_client()
-        self.configure_api_permission(dop_client, 'saml-anotherprovider')
-
-        request = Mock()
-        request.auth = Mock()
-        request.auth.client_id = dop_client.pk
-        view = Mock(kwargs={'provider_id': PROVIDER_ID_TESTSHIB})
-
-        result = ThirdPartyAuthProviderApiPermission().has_permission(request, view)
-        self.assertEqual(result, False)
-
-
 @ddt.ddt
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
 class ThirdPartyAuthPermissionTest(TestCase):
diff --git a/common/djangoapps/third_party_auth/api/tests/test_views.py b/common/djangoapps/third_party_auth/api/tests/test_views.py
index d79b7eac6e2..820946c75be 100644
--- a/common/djangoapps/third_party_auth/api/tests/test_views.py
+++ b/common/djangoapps/third_party_auth/api/tests/test_views.py
@@ -12,20 +12,16 @@ from django.http import QueryDict
 from django.test.utils import override_settings
 from django.urls import reverse
 from mock import patch
-from provider.constants import CONFIDENTIAL
-from provider.oauth2.models import AccessToken, Client
 from rest_framework.test import APITestCase
 from six.moves import range
 from social_django.models import UserSocialAuth
 
-from openedx.core.lib.api.permissions import ApiKeyHeaderPermission
 from student.tests.factories import UserFactory
-from third_party_auth.api.permissions import ThirdPartyAuthProviderApiPermission
-from third_party_auth.models import ProviderApiPermissions
 from third_party_auth.tests.testutil import ThirdPartyAuthTestMixin
 from third_party_auth.api.permissions import (JwtRestrictedApplication,
                                               JwtHasScope,
                                               JwtHasTpaProviderFilterForRequestedProvider)
+from edx_rest_framework_extensions.auth.jwt.tests.utils import generate_jwt
 
 VALID_API_KEY = "i am a key"
 IDP_SLUG_TESTSHIB = 'testshib'
@@ -247,25 +243,29 @@ class UserMappingViewAPITests(TpaAPITestCase):
         response = self.client.get(url, HTTP_X_EDX_API_KEY=api_key)
         self._verify_response(response, expect_code, expect_data)
 
+    def _create_jwt_header(self, user, is_restricted=False, scopes=None, filters=None):
+        token = generate_jwt(user, is_restricted=is_restricted, scopes=scopes, filters=filters)
+        return "JWT {}".format(token)
+
     @ddt.data(
-        (PROVIDER_ID_TESTSHIB, 'valid-token', 200, get_mapping_data_by_usernames(LINKED_USERS)),
-        ('non-existing-id', 'valid-token', 404, []),
-        (PROVIDER_ID_TESTSHIB, 'invalid-token', 401, []),
+        (True, 200, get_mapping_data_by_usernames(LINKED_USERS)),
+        (False, 401, []),
     )
     @ddt.unpack
-    def test_list_all_user_mappings_oauth2(self, provider_id, access_token, expect_code, expect_data):
-        url = reverse('third_party_auth_user_mapping_api', kwargs={'provider_id': provider_id})
+    def test_list_all_user_mappings_oauth2(self, valid_call, expect_code, expect_data):
+        url = reverse('third_party_auth_user_mapping_api', kwargs={'provider_id': PROVIDER_ID_TESTSHIB})
+        provider_filter = 'tpa_provider:' + PROVIDER_ID_TESTSHIB
+        filters = [provider_filter, 'tpa_provider:another_tpa_provider']
         # create oauth2 auth data
         user = UserFactory.create(username='api_user')
-        client = Client.objects.create(name='oauth2_client', client_type=CONFIDENTIAL)
-        token = AccessToken.objects.create(user=user, client=client)
-        ProviderApiPermissions.objects.create(client=client, provider_id=provider_id)
-
-        if access_token == 'valid-token':
-            access_token = token.token
-
-        response = self.client.get(url, HTTP_AUTHORIZATION=u'Bearer {}'.format(access_token))
-        self._verify_response(response, expect_code, expect_data)
+        if valid_call:
+            auth_header = self._create_jwt_header(user, is_restricted=True, scopes=['tpa:read'], filters=filters)
+        else:
+            auth_header = ''
+        with patch('edx_rest_framework_extensions.permissions.waffle.switch_is_active') as mock_toggle:
+            mock_toggle.return_value = True
+            response = self.client.get(url, HTTP_AUTHORIZATION=auth_header)
+            self._verify_response(response, expect_code, expect_data)
 
     @ddt.data(
         ({'username': [ALICE_USERNAME, STAFF_USERNAME]}, 200,
@@ -335,20 +335,6 @@ class UserMappingViewAPITests(TpaAPITestCase):
         self.assertEqual(response.status_code, 200)
         self._verify_response(response, 200, get_mapping_data_by_usernames(LINKED_USERS))
 
-    @ddt.data(
-        (True, True, 200),
-        (False, True, 200),
-        (True, False, 200),
-        (False, False, 401)
-    )
-    @ddt.unpack
-    def test_user_mapping_permission_logic(self, api_key_permission, token_permission, expect):
-        url = reverse('third_party_auth_user_mapping_api', kwargs={'provider_id': PROVIDER_ID_TESTSHIB})
-        with patch.object(ApiKeyHeaderPermission, 'has_permission', return_value=api_key_permission):
-            with patch.object(ThirdPartyAuthProviderApiPermission, 'has_permission', return_value=token_permission):
-                response = self.client.get(url)
-                self.assertEqual(response.status_code, expect)
-
     @ddt.data(
         (True, 200),
         (False, 401),
diff --git a/common/djangoapps/third_party_auth/models.py b/common/djangoapps/third_party_auth/models.py
index 08fb3ec3b62..d8bc94dfb85 100644
--- a/common/djangoapps/third_party_auth/models.py
+++ b/common/djangoapps/third_party_auth/models.py
@@ -17,8 +17,7 @@ from django.db import models
 from django.utils import timezone
 from django.utils.translation import ugettext_lazy as _
 from organizations.models import Organization
-from provider.oauth2.models import Client
-from provider.utils import long_token
+from django.contrib.auth.tokens import default_token_generator
 from social_core.backends.base import BaseAuth
 from social_core.backends.oauth import OAuthAuth
 from social_core.backends.saml import SAMLAuth
@@ -826,7 +825,7 @@ class LTIProviderConfig(ProviderConfig):
     )
 
     lti_consumer_secret = models.CharField(
-        default=long_token,
+        default=default_token_generator,
         max_length=255,
         help_text=(
             u'The shared secret that the LTI Tool Consumer will use to '
@@ -878,25 +877,3 @@ class LTIProviderConfig(ProviderConfig):
         app_label = "third_party_auth"
         verbose_name = u"Provider Configuration (LTI)"
         verbose_name_plural = verbose_name
-
-
-class ProviderApiPermissions(models.Model):
-    """
-    This model links OAuth2 client with provider Id.
-
-    It gives permission for a OAuth2 client to access the information under certain IdPs.
-
-    .. no_pii:
-    """
-    client = models.ForeignKey(Client, on_delete=models.CASCADE)
-    provider_id = models.CharField(
-        max_length=255,
-        help_text=(
-            u'Uniquely identify a provider. This is different from backend_name.'
-        )
-    )
-
-    class Meta(object):
-        app_label = "third_party_auth"
-        verbose_name = u"Provider API Permission"
-        verbose_name_plural = verbose_name + 's'
diff --git a/common/djangoapps/third_party_auth/tests/testutil.py b/common/djangoapps/third_party_auth/tests/testutil.py
index 763194b6f6e..dbe8082cc56 100644
--- a/common/djangoapps/third_party_auth/tests/testutil.py
+++ b/common/djangoapps/third_party_auth/tests/testutil.py
@@ -15,15 +15,13 @@ from django.conf import settings
 from django.contrib.auth.models import User
 from django.contrib.sites.models import Site
 from mako.template import Template
-from provider import constants
-from provider.oauth2.models import Client as OAuth2Client
+from oauth2_provider.models import Application
 from openedx.core.djangolib.testing.utils import CacheIsolationMixin
 from openedx.core.storage import OverwriteStorage
 
 from third_party_auth.models import (
     LTIProviderConfig,
     OAuth2ProviderConfig,
-    ProviderApiPermissions,
     SAMLConfiguration,
     SAMLProviderConfig
 )
@@ -173,14 +171,9 @@ class ThirdPartyAuthTestMixin(object):
         user.save()
 
     @staticmethod
-    def configure_oauth_dop_client():
+    def configure_oauth_dot_client():
         """ Configure an oauth DOP client for testing """
-        return OAuth2Client.objects.create(client_type=constants.CONFIDENTIAL)
-
-    @staticmethod
-    def configure_api_permission(client, provider_id):
-        """ Configure the client and provider_id pair. This will give the access to a client for that provider. """
-        return ProviderApiPermissions.objects.create(client=client, provider_id=provider_id)
+        return Application.objects.create(client_type=Application.CLIENT_CONFIDENTIAL)
 
     @staticmethod
     def read_data_file(filename):
@@ -191,7 +184,8 @@ class ThirdPartyAuthTestMixin(object):
 
 class TestCase(ThirdPartyAuthTestMixin, CacheIsolationMixin, django.test.TestCase):
     """Base class for auth test cases."""
-    def setUp(self):
+
+    def setUp(self):  # pylint: disable=arguments-differ
         super(TestCase, self).setUp()
         # Explicitly set a server name that is compatible with all our providers:
         # (The SAML lib we use doesn't like the default 'testserver' as a domain)
diff --git a/common/djangoapps/third_party_auth/tests/utils.py b/common/djangoapps/third_party_auth/tests/utils.py
index 0fe7f779456..2360074f1ae 100644
--- a/common/djangoapps/third_party_auth/tests/utils.py
+++ b/common/djangoapps/third_party_auth/tests/utils.py
@@ -6,8 +6,7 @@ from base64 import b64encode
 
 import httpretty
 from onelogin.saml2.utils import OneLogin_Saml2_Utils
-from provider.constants import PUBLIC
-from provider.oauth2.models import Client
+from oauth2_provider.models import Application
 from social_core.backends.facebook import API_VERSION as FACEBOOK_API_VERSION
 from social_core.backends.facebook import FacebookOAuth2
 from social_django.models import Partial, UserSocialAuth
@@ -52,9 +51,9 @@ class ThirdPartyOAuthTestMixin(ThirdPartyAuthTestMixin):
         """
         Create an OAuth2 client application
         """
-        return Client.objects.create(
+        return Application.objects.create(
             client_id=self.client_id,
-            client_type=PUBLIC,
+            client_type=Application.CLIENT_PUBLIC,
         )
 
     def _setup_provider_response(self, success=False, email=''):
diff --git a/common/djangoapps/util/tests/test_db.py b/common/djangoapps/util/tests/test_db.py
index 8fdcf40b9f0..a20896fa618 100644
--- a/common/djangoapps/util/tests/test_db.py
+++ b/common/djangoapps/util/tests/test_db.py
@@ -221,8 +221,9 @@ class MigrationTests(TestCase):
     """
     Tests for migrations.
     """
-
-    @unittest.skip("Need to skip as part of renaming a field in schedules app. This will be unskipped in DE-1825")
+    @unittest.skip(
+        "Need to skip as part of renaming a field in schedules app. This will be unskipped in DE-1825. Also need to skip as part of removal of ProviderApiPermissions.This will be unskipped in BOM-1350"
+    )
     @override_settings(MIGRATION_MODULES={})
     def test_migrations_are_in_sync(self):
         """
-- 
GitLab