diff --git a/common/djangoapps/third_party_auth/api/views.py b/common/djangoapps/third_party_auth/api/views.py index 9f3d799bcf..8f83908c55 100644 --- a/common/djangoapps/third_party_auth/api/views.py +++ b/common/djangoapps/third_party_auth/api/views.py @@ -16,10 +16,9 @@ from rest_framework import exceptions, permissions, status, throttling from rest_framework.generics import ListAPIView from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework_oauth.authentication import OAuth2Authentication from social_django.models import UserSocialAuth -from openedx.core.lib.api.authentication import OAuth2AuthenticationAllowInactiveUser +from openedx.core.lib.api.authentication import OAuth2AuthenticationDeprecated, OAuth2AuthenticationAllowInactiveUser from openedx.core.lib.api.permissions import ApiKeyHeaderPermission from third_party_auth import pipeline from third_party_auth.api import serializers @@ -333,7 +332,7 @@ class UserMappingView(ListAPIView): * remote_id: The Id from third party auth provider """ authentication_classes = ( - OAuth2Authentication, + OAuth2AuthenticationDeprecated, ) serializer_class = serializers.UserMappingSerializer diff --git a/lms/djangoapps/bulk_enroll/views.py b/lms/djangoapps/bulk_enroll/views.py index ee4ea64f00..072f92c2ab 100644 --- a/lms/djangoapps/bulk_enroll/views.py +++ b/lms/djangoapps/bulk_enroll/views.py @@ -18,7 +18,7 @@ from lms.djangoapps.instructor.views.api import students_update_enrollment from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, get_cohort_by_name from openedx.core.djangoapps.course_groups.models import CourseUserGroup from openedx.core.djangoapps.enrollments.views import EnrollmentUserThrottle -from openedx.core.lib.api.authentication import OAuth2Authentication +from openedx.core.lib.api.authentication import OAuth2AuthenticationDeprecated from openedx.core.lib.api.permissions import IsStaff from util.disable_rate_limit import can_disable_rate_limit @@ -68,7 +68,7 @@ class BulkEnrollView(APIView): to the 'before' and 'after' states. """ - authentication_classes = JwtAuthentication, OAuth2Authentication + authentication_classes = (JwtAuthentication, OAuth2AuthenticationDeprecated,) permission_classes = (IsStaff,) throttle_classes = (EnrollmentUserThrottle,) diff --git a/lms/djangoapps/commerce/api/v1/views.py b/lms/djangoapps/commerce/api/v1/views.py index 39a5c6e06b..7986854565 100644 --- a/lms/djangoapps/commerce/api/v1/views.py +++ b/lms/djangoapps/commerce/api/v1/views.py @@ -13,7 +13,7 @@ from rest_framework.authentication import SessionAuthentication from rest_framework.generics import ListAPIView, RetrieveUpdateAPIView from rest_framework.permissions import IsAuthenticated from rest_framework.views import APIView -from rest_framework_oauth.authentication import OAuth2Authentication +from openedx.core.lib.api.authentication import OAuth2AuthenticationDeprecated from course_modes.models import CourseMode from openedx.core.djangoapps.commerce.utils import ecommerce_api_client @@ -30,7 +30,7 @@ log = logging.getLogger(__name__) class CourseListView(ListAPIView): """ List courses and modes. """ - authentication_classes = (JwtAuthentication, OAuth2Authentication, SessionAuthentication,) + authentication_classes = (JwtAuthentication, OAuth2AuthenticationDeprecated, SessionAuthentication,) permission_classes = (IsAuthenticated,) serializer_class = CourseSerializer pagination_class = None @@ -44,7 +44,7 @@ class CourseRetrieveUpdateView(PutAsCreateMixin, RetrieveUpdateAPIView): lookup_field = 'id' lookup_url_kwarg = 'course_id' model = CourseMode - authentication_classes = (JwtAuthentication, OAuth2Authentication, SessionAuthentication,) + authentication_classes = (JwtAuthentication, OAuth2AuthenticationDeprecated, SessionAuthentication,) permission_classes = (ApiKeyOrModelPermission,) serializer_class = CourseSerializer diff --git a/lms/djangoapps/teams/views.py b/lms/djangoapps/teams/views.py index 9b6b83eb80..9a1b8827bb 100644 --- a/lms/djangoapps/teams/views.py +++ b/lms/djangoapps/teams/views.py @@ -27,7 +27,7 @@ from rest_framework.generics import GenericAPIView from rest_framework.response import Response from rest_framework.reverse import reverse from rest_framework.views import APIView -from rest_framework_oauth.authentication import OAuth2Authentication +from openedx.core.lib.api.authentication import OAuth2AuthenticationDeprecated from lms.djangoapps.courseware.courses import get_course_with_access, has_access from lms.djangoapps.discussion.django_comment_client.utils import has_discussion_privileges @@ -368,7 +368,7 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView): """ # OAuth2Authentication must come first to return a 401 for unauthenticated users - authentication_classes = (OAuth2Authentication, SessionAuthentication) + authentication_classes = (OAuth2AuthenticationDeprecated, SessionAuthentication) permission_classes = (permissions.IsAuthenticated,) serializer_class = CourseTeamSerializer @@ -696,7 +696,7 @@ class TeamsDetailView(ExpandableFieldViewMixin, RetrievePatchAPIView): If the user is logged in and the team does not exist, a 404 is returned. """ - authentication_classes = (OAuth2Authentication, SessionAuthentication) + authentication_classes = (OAuth2AuthenticationDeprecated, SessionAuthentication) permission_classes = (permissions.IsAuthenticated, IsStaffOrPrivilegedOrReadOnly, IsEnrolledOrIsStaff,) lookup_field = 'team_id' serializer_class = CourseTeamSerializer @@ -791,7 +791,7 @@ class TopicListView(GenericAPIView): those teams whose members are outside of institutions affliation. """ - authentication_classes = (OAuth2Authentication, SessionAuthentication) + authentication_classes = (OAuth2AuthenticationDeprecated, SessionAuthentication) permission_classes = (permissions.IsAuthenticated,) pagination_class = TopicsPagination @@ -922,7 +922,7 @@ class TopicDetailView(APIView): those teams whose members are outside of institutions affliation. """ - authentication_classes = (OAuth2Authentication, SessionAuthentication) + authentication_classes = (OAuth2AuthenticationDeprecated, SessionAuthentication) permission_classes = (permissions.IsAuthenticated,) def get(self, request, topic_id, course_id): @@ -1082,7 +1082,7 @@ class MembershipListView(ExpandableFieldViewMixin, GenericAPIView): another user to a team. """ - authentication_classes = (OAuth2Authentication, SessionAuthentication) + authentication_classes = (OAuth2AuthenticationDeprecated, SessionAuthentication) permission_classes = (permissions.IsAuthenticated,) serializer_class = MembershipSerializer @@ -1295,7 +1295,7 @@ class MembershipDetailView(ExpandableFieldViewMixin, GenericAPIView): If the membership does not exist, a 404 error is returned. """ - authentication_classes = (OAuth2Authentication, SessionAuthentication) + authentication_classes = (OAuth2AuthenticationDeprecated, SessionAuthentication) permission_classes = (permissions.IsAuthenticated,) serializer_class = MembershipSerializer diff --git a/openedx/core/djangoapps/api_admin/api/v1/views.py b/openedx/core/djangoapps/api_admin/api/v1/views.py index ff476ee650..75ea98f232 100644 --- a/openedx/core/djangoapps/api_admin/api/v1/views.py +++ b/openedx/core/djangoapps/api_admin/api/v1/views.py @@ -8,7 +8,7 @@ from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthenticat from rest_framework.authentication import SessionAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.generics import ListAPIView -from rest_framework_oauth.authentication import OAuth2Authentication +from openedx.core.lib.api.authentication import OAuth2AuthenticationDeprecated from openedx.core.djangoapps.api_admin.api.v1 import serializers as api_access_serializers from openedx.core.djangoapps.api_admin.models import ApiAccessRequest @@ -50,7 +50,7 @@ class ApiAccessRequestView(ListAPIView): "previous": null } """ - authentication_classes = (JwtAuthentication, OAuth2Authentication, SessionAuthentication,) + authentication_classes = (JwtAuthentication, OAuth2AuthenticationDeprecated, SessionAuthentication,) permission_classes = (IsAuthenticated, ) serializer_class = api_access_serializers.ApiAccessRequestSerializer filter_backends = (IsOwnerOrStaffFilterBackend, DjangoFilterBackend) diff --git a/openedx/core/djangoapps/bookmarks/views.py b/openedx/core/djangoapps/bookmarks/views.py index 8f4fb3978c..490f1c4ad4 100644 --- a/openedx/core/djangoapps/bookmarks/views.py +++ b/openedx/core/djangoapps/bookmarks/views.py @@ -22,7 +22,7 @@ from rest_framework.authentication import SessionAuthentication from rest_framework.generics import ListCreateAPIView from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework_oauth.authentication import OAuth2Authentication +from openedx.core.lib.api.authentication import OAuth2AuthenticationDeprecated from openedx.core.djangoapps.bookmarks.api import BookmarksLimitReachedError from openedx.core.lib.api.permissions import IsUserInUrl @@ -99,7 +99,7 @@ class BookmarksViewMixin(object): class BookmarksListView(ListCreateAPIView, BookmarksViewMixin): """REST endpoints for lists of bookmarks.""" - authentication_classes = (OAuth2Authentication, SessionAuthentication) + authentication_classes = (OAuth2AuthenticationDeprecated, SessionAuthentication) pagination_class = BookmarksPagination permission_classes = (permissions.IsAuthenticated,) serializer_class = BookmarkSerializer @@ -290,7 +290,7 @@ class BookmarksDetailView(APIView, BookmarksViewMixin): to a requesting user's bookmark a 404 is returned. 404 will also be returned if the bookmark does not exist. """ - authentication_classes = (OAuth2Authentication, SessionAuthentication) + authentication_classes = (OAuth2AuthenticationDeprecated, SessionAuthentication) permission_classes = (permissions.IsAuthenticated, IsUserInUrl) serializer_class = BookmarkSerializer diff --git a/openedx/core/djangoapps/credit/views.py b/openedx/core/djangoapps/credit/views.py index b3b295280f..dd273df265 100644 --- a/openedx/core/djangoapps/credit/views.py +++ b/openedx/core/djangoapps/credit/views.py @@ -18,7 +18,7 @@ from rest_framework import generics, mixins, permissions, views, viewsets from rest_framework.authentication import SessionAuthentication from rest_framework.exceptions import ValidationError from rest_framework.response import Response -from rest_framework_oauth.authentication import OAuth2Authentication +from openedx.core.lib.api.authentication import OAuth2AuthenticationDeprecated from six import text_type from openedx.core.djangoapps.credit.api import create_credit_request @@ -45,7 +45,7 @@ from openedx.core.lib.api.mixins import PutAsCreateMixin from openedx.core.lib.api.permissions import IsStaffOrOwner log = logging.getLogger(__name__) -AUTHENTICATION_CLASSES = (JwtAuthentication, OAuth2Authentication, SessionAuthentication,) +AUTHENTICATION_CLASSES = (JwtAuthentication, OAuth2AuthenticationDeprecated, SessionAuthentication,) class CreditProviderViewSet(viewsets.ReadOnlyModelViewSet): diff --git a/openedx/core/djangoapps/user_api/verification_api/views.py b/openedx/core/djangoapps/user_api/verification_api/views.py index 0a9810593b..d943b0c49a 100644 --- a/openedx/core/djangoapps/user_api/verification_api/views.py +++ b/openedx/core/djangoapps/user_api/verification_api/views.py @@ -5,7 +5,7 @@ from django.http import Http404 from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from rest_framework.authentication import SessionAuthentication from rest_framework.generics import RetrieveAPIView -from rest_framework_oauth.authentication import OAuth2Authentication +from openedx.core.lib.api.authentication import OAuth2AuthenticationDeprecated from lms.djangoapps.verify_student.models import ManualVerification, SoftwareSecurePhotoVerification, SSOVerification from lms.djangoapps.verify_student.utils import most_recent_verification @@ -19,7 +19,7 @@ from openedx.core.lib.api.permissions import IsStaffOrOwner class IDVerificationStatusView(RetrieveAPIView): """ IDVerificationStatus detail endpoint. """ - authentication_classes = (JwtAuthentication, OAuth2Authentication, SessionAuthentication,) + authentication_classes = (JwtAuthentication, OAuth2AuthenticationDeprecated, SessionAuthentication,) permission_classes = (IsStaffOrOwner,) def get_serializer(self, *args, **kwargs): diff --git a/openedx/core/lib/api/authentication.py b/openedx/core/lib/api/authentication.py index 50ed39f661..81c192ffa8 100644 --- a/openedx/core/lib/api/authentication.py +++ b/openedx/core/lib/api/authentication.py @@ -8,6 +8,7 @@ from oauth2_provider import models as dot_models from provider.oauth2 import models as dop_models from rest_framework.exceptions import AuthenticationFailed from rest_framework_oauth.authentication import OAuth2Authentication +from edx_django_utils.monitoring import set_custom_metric OAUTH2_TOKEN_ERROR = u'token_error' @@ -20,22 +21,41 @@ OAUTH2_TOKEN_ERROR_NOT_PROVIDED = u'token_not_provided' log = logging.getLogger(__name__) -class OAuth2AuthenticationAllowInactiveUser(OAuth2Authentication): +class OAuth2AuthenticationDeprecated(OAuth2Authentication): + """ + This child class was added to add new_relic metrics to OAuth2Authentication. This should be very temporary. + """ + + def authenticate(self, request): + """ + Returns two-tuple of (user, token) if access token authentication + succeeds, None if the user did not try to authenticate using an access + token, or raises an AuthenticationFailed (HTTP 401) if authentication + fails. + """ + set_custom_metric("OAuth2AuthenticationDeprecated", "Failed") + output = super(OAuth2AuthenticationDeprecated, self).authenticate(request) + if output is None: + set_custom_metric("OAuth2AuthenticationDeprecated", "None") + else: + set_custom_metric("OAuth2AuthenticationDeprecated", "Success") + return output + + +class OAuth2AuthenticationAllowInactiveUser(OAuth2AuthenticationDeprecated): """ This is a temporary workaround while the is_active field on the user is coupled with whether or not the user has verified ownership of their claimed email address. Once is_active is decoupled from verified_email, we will no longer need this class override. - But until then, this authentication class ensures that the user is logged in, but does not require that their account "is_active". - This class can be used for an OAuth2-accessible endpoint that allows users to access that endpoint without having their email verified. For example, this is used for mobile endpoints. """ - def authenticate(self, *args, **kwargs): + def authenticate(self, request): """ Returns two-tuple of (user, token) if access token authentication succeeds, raises an AuthenticationFailed (HTTP 401) if authentication @@ -44,7 +64,7 @@ class OAuth2AuthenticationAllowInactiveUser(OAuth2Authentication): """ try: - return super(OAuth2AuthenticationAllowInactiveUser, self).authenticate(*args, **kwargs) + return super(OAuth2AuthenticationAllowInactiveUser, self).authenticate(request) except AuthenticationFailed as exc: if isinstance(exc.detail, dict): developer_message = exc.detail['developer_message'] @@ -65,7 +85,6 @@ class OAuth2AuthenticationAllowInactiveUser(OAuth2Authentication): def authenticate_credentials(self, request, access_token): """ Authenticate the request, given the access token. - Overrides base class implementation to discard failure if user is inactive. """