-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add BearerAuthentication to API and improve EoxTenantAPIPermiss…
…ion checks (#149)
- Loading branch information
Showing
12 changed files
with
199 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,66 @@ | ||
""" | ||
Permission for eox_tenant api v1. | ||
""" | ||
from rest_framework import permissions | ||
from django.conf import settings | ||
from django.contrib.auth.models import Permission, User | ||
from django.contrib.contenttypes.models import ContentType | ||
from django.db.utils import ProgrammingError | ||
from rest_framework import exceptions, permissions | ||
|
||
|
||
def load_permissions(): | ||
""" | ||
Helper method to load a custom permission on DB that will be | ||
used to give access to the eox-tenant API. | ||
""" | ||
if settings.EOX_TENANT_LOAD_PERMISSIONS: | ||
try: | ||
content_type = ContentType.objects.get_for_model(User) | ||
Permission.objects.get_or_create( # pylint: disable=unused-variable | ||
codename='can_call_eox_tenant', | ||
name='Can access eox-tenant API', | ||
content_type=content_type, | ||
) | ||
except ProgrammingError: | ||
# This code runs when the app is loaded, if a migration has not been | ||
# done a ProgrammingError exception is raised. | ||
# we are bypassing those cases to let migrations run smoothly. | ||
pass | ||
|
||
|
||
class EoxTenantAPIPermission(permissions.BasePermission): | ||
"""Only allows super user.""" | ||
""" | ||
Defines a custom permissions to access eox-tenant API. | ||
These permissions make sure that a token is created with the client credentials of the same site is | ||
being used on, and the user has a valid SignUp source for the site. | ||
""" | ||
|
||
def has_permission(self, request, view): | ||
if request.user.is_superuser: | ||
""" | ||
To grant access, checks if the requesting user: | ||
1) it's a staff user | ||
3) it's calling the API from a site authorized by the auth application or client | ||
4) has can call eox-tenant API permission | ||
""" | ||
user = request.user | ||
|
||
if user.is_staff: | ||
return True | ||
return False | ||
|
||
try: | ||
application_uri_allowed = request.auth.application.redirect_uri_allowed(request.build_absolute_uri('/')) | ||
except Exception: # pylint: disable=broad-except | ||
application_uri_allowed = False | ||
|
||
try: | ||
client_url_allowed = request.get_host() in request.auth.client.url | ||
except Exception: # pylint: disable=broad-except | ||
client_url_allowed = False | ||
|
||
if client_url_allowed or application_uri_allowed: | ||
return user.has_perm('auth.can_call_eox_tenant') | ||
|
||
# If we get here either someone is using a token created on one site in a different site | ||
# or there was a missconfiguration of the oauth client. | ||
# we return the most basic message to prevent leaking important information . | ||
raise exceptions.NotAuthenticated(detail="Invalid token") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Test module for the permissions class | ||
""" | ||
from django.test import TestCase | ||
from mock import MagicMock | ||
from rest_framework.exceptions import NotAuthenticated | ||
|
||
from eox_tenant.api.v1.permissions import EoxTenantAPIPermission | ||
|
||
|
||
class EoxTenantAPIPermissionTest(TestCase): | ||
""" Test cases for the EoxTenantAPIPermission class.""" | ||
|
||
def test_permissions_for_staff(self): | ||
""" Staff always passes.""" | ||
request = MagicMock() | ||
request.user.is_staff = True | ||
|
||
has_perm = EoxTenantAPIPermission().has_permission(request, MagicMock()) | ||
|
||
self.assertTrue(has_perm) | ||
|
||
def test_read_user_permissions(self): | ||
""" If the auth does not fail, it comes down to check the | ||
domain in the client or application. | ||
Expected behavior: | ||
has_permission method returns False | ||
""" | ||
request = MagicMock() | ||
request.user.is_staff = False | ||
request.user.has_perm.return_value = False | ||
|
||
has_perm = EoxTenantAPIPermission().has_permission(request, MagicMock()) | ||
|
||
self.assertFalse(has_perm) | ||
|
||
def test_permissions_without_auth(self): | ||
""" If anything in the auth fails, the NotAuthenticated exception is raised | ||
Expected behavior: | ||
NotAuthenticated exception is raised | ||
""" | ||
request = MagicMock() | ||
request.user.is_staff = False | ||
request.user.has_perm.return_value = False | ||
request.auth = None | ||
|
||
with self.assertRaises(NotAuthenticated): | ||
EoxTenantAPIPermission().has_permission(request, MagicMock()) | ||
|
||
def test_permissions_auth_dop(self): | ||
""" Authorize the domain via the client.url. | ||
Expected behavior: | ||
has_permission method returns False | ||
""" | ||
request = MagicMock() | ||
request.user.is_staff = False | ||
request.user.has_perm.return_value = True | ||
request.get_host.return_value = "domain.com" | ||
request.auth.client.url = "https://domain.com/" | ||
|
||
has_perm = EoxTenantAPIPermission().has_permission(request, MagicMock()) | ||
|
||
self.assertTrue(has_perm) | ||
|
||
def test_permissions_auth_dot(self): | ||
""" Authorize the domain via the application.allowed_uris | ||
Expected behavior: | ||
has_permission method returns False | ||
""" | ||
request = MagicMock() | ||
request.user.is_staff = False | ||
request.user.has_perm.return_value = True | ||
request.auth.application.redirect_uri_allowed.return_value = True | ||
|
||
has_perm = EoxTenantAPIPermission().has_permission(request, MagicMock()) | ||
|
||
self.assertTrue(has_perm) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
eox_tenant/edxapp_wrapper/backends/bearer_authentication_l_v1.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
"""Backend for authentication. | ||
This file contains all the necessary authentication dependencies from | ||
https://github.com/eduNEXT/edunext-platform/tree/master/openedx/core/lib/api/authentication.py | ||
""" | ||
from openedx.core.lib.api.authentication import BearerAuthentication # pylint: disable=import-error | ||
|
||
|
||
def get_bearer_authentication(): | ||
"""Allow to get the function BearerAuthentication from | ||
https://github.com/eduNEXT/edunext-platform/tree/master/openedx/core/lib/api/authentication.py | ||
Returns: | ||
BearerAuthentication function. | ||
""" | ||
return BearerAuthentication |
15 changes: 15 additions & 0 deletions
15
eox_tenant/edxapp_wrapper/backends/bearer_authentication_test_v1.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
""" Backend test abstraction. """ | ||
|
||
|
||
def get_bearer_authentication(): | ||
"""Allow to get the function BearerAuthentication from | ||
https://github.com/eduNEXT/edunext-platform/tree/master/openedx/core/lib/api/authentication.py | ||
Returns: | ||
BearerAuthentication function. | ||
""" | ||
try: | ||
from openedx.core.lib.api.authentication import BearerAuthentication # pylint: disable=import-outside-toplevel | ||
except ImportError: | ||
BearerAuthentication = object | ||
return BearerAuthentication |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
""" | ||
Bearer Authentication definition. | ||
""" | ||
from importlib import import_module | ||
|
||
from django.conf import settings | ||
|
||
|
||
def get_bearer_authentication(): | ||
""" Gets BearerAuthentication class. """ | ||
backend_function = settings.EOX_TENANT_BEARER_AUTHENTICATION | ||
backend = import_module(backend_function) | ||
|
||
return backend.get_bearer_authentication() | ||
|
||
|
||
BearerAuthentication = get_bearer_authentication() # pylint: disable=invalid-name |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters