Skip to content

Commit

Permalink
Updating utils file
Browse files Browse the repository at this point in the history
adding in settings import

Intermittent commit

Updating gitignore for tests

Getting tests up for database access check helper

Updating test settings and writing more tests for other utilities

Bumping requirements

Updating based on feedback

Change based on feedback

Fixing a doc string

Saving changes

adding in another util for ENT-1556 and doing a bunch of quality cleanup;

Testing tweaking the tox file to get travis working

another tox tweak

Using the six package for urlparse

Bumping version

Adding logic to only add context string if it exists
  • Loading branch information
christopappas committed Mar 7, 2019
1 parent 33b289a commit 59b9999
Show file tree
Hide file tree
Showing 16 changed files with 513 additions and 16 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ private.py
docs/_build
*.backup
*.log
*.pyc

# Making sure not to add init file for test module
tests/__init__.py

# Visual Studio Code
.vscode
7 changes: 6 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ Change Log
.. There should always be an "Unreleased" section for changes pending release.
[0.1.2] - 2019-03-07
[0.1.4] - 2019-03-07
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* Adding a number of utils for roles in JWTs and the database

[0.1.3] - 2019-03-07
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* Adding get_context to the UserRoleAssignment class.
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ requirements: ## install development environment requirements
pip-sync requirements/dev.txt requirements/private.*

test: clean ## run tests in the current virtualenv
touch tests/__init__.py
pytest
rm tests/__init__.py

diff_cover: test ## find diff lines that need test coverage
diff-cover coverage.xml
Expand Down
2 changes: 1 addition & 1 deletion edx_rbac/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

from __future__ import absolute_import, unicode_literals

__version__ = '0.1.3'
__version__ = '0.1.4'

default_app_config = 'edx_rbac.apps.EdxRbacConfig' # pylint: disable=invalid-name
135 changes: 135 additions & 0 deletions edx_rbac/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-
"""
Utils for 'edx-rbac' module.
"""
from __future__ import absolute_import, unicode_literals

import crum
from django.apps import apps
from django.conf import settings
from django.test.client import RequestFactory
from edx_rest_framework_extensions.auth.jwt.cookies import jwt_cookie_name
from edx_rest_framework_extensions.auth.jwt.decoder import jwt_decode_handler
from six.moves.urllib.parse import urlparse # pylint: disable=import-error


# Taken from edx-platform
def get_request_or_stub():
"""
Return the current request or a stub request.
If called outside the context of a request, construct a fake
request that can be used to build an absolute URI.
This is useful in cases where we need to pass in a request object
but don't have an active request (for example, in tests, celery tasks, and XBlocks).
"""
request = crum.get_current_request()

if request is None:

# The settings SITE_NAME may contain a port number, so we need to
# parse the full URL.
full_url = "http://{site_name}".format(site_name=settings.SITE_NAME)
parsed_url = urlparse(full_url)

# Construct the fake request. This can be used to construct absolute
# URIs to other paths.
return RequestFactory(
SERVER_NAME=parsed_url.hostname,
SERVER_PORT=parsed_url.port or 80,
).get("/")

else:
return request


def get_decoded_jwt_from_request(request):
"""
Grab jwt from request if possible.
Returns a decoded jwt dict if it finds it.
Returns a None if it does not.
"""
jwt_cookie = request.COOKIES.get(jwt_cookie_name(), None)

if not jwt_cookie:
return None
return jwt_decode_handler(jwt_cookie)


def request_user_has_implicit_access_via_jwt(decoded_jwt, role_name):
"""
Check the request's user access by mapping user's roles found in jwt to local feature roles.
decoded_jwt is a dict
role_name is a string
Returns a boolean.
Mapping should be in settings and look like:
SYSTEM_TO_FEATURE_ROLE_MAPPING = {
'enterprise_admin': ['coupon-management', 'data_api_access'],
'enterprise_leaner': [],
'coupon-manager': ['coupon-management']
}
"""
jwt_roles_claim = decoded_jwt.get('roles', [])

feature_roles = []
for role_data in jwt_roles_claim:
role_in_jwt = role_data.split(':')[0] # split should be more robust because of our cousekeys having colons
mapped_roles = settings.SYSTEM_TO_FEATURE_ROLE_MAPPING.get(role_in_jwt, [])
feature_roles.extend(mapped_roles)

if role_name in feature_roles:
return True
return False


def user_has_access_via_database(user, role_name, role_assignment_class):
"""
Check if there is a role assignment for a given user and role.
The role object itself is found via the role_name
"""
try:
role_assignment_class.objects.get(user=user, role__name=role_name)
except role_assignment_class.DoesNotExist:
return False
return True


def create_role_auth_claim_for_user(user):
"""
Create role auth claim for a given user.
Takes a user, and for each RoleAssignment class specified in config as a
system wide jwt role associated with that user, creates a list of strings
denoting the role and context.
Returns a list.
This setting is a list of classes whose roles should be added to the
jwt. The setting should look something like this:
SYSTEM_WIDE_ROLE_CLASSES = [
SystemWideConcreteUserRoleAssignment
]
"""
role_auth_claim = []
for system_role_class in settings.SYSTEM_WIDE_ROLE_CLASSES:
app_name, model_name = system_role_class.split('.')
model_class = apps.get_model(app_name, model_name)

role_assignments = model_class.objects.filter(
user=user
).select_related('role')

for role_assignment in role_assignments:
role_string = role_assignment.role.name
context = role_assignment.get_context()
if context:
role_string += ':{}'.format(context)
role_auth_claim.append(role_string)
return role_auth_claim
4 changes: 3 additions & 1 deletion requirements/base.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@

Django>=1.8,<2.0 # Web application framework
django-model-utils # Provides TimeStampedModel abstract base class
six
six
django-crum
edx-drf-extensions
24 changes: 24 additions & 0 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,31 @@
#
# make upgrade
#
certifi==2018.11.29 # via requests
chardet==3.0.4 # via requests
django-crum==0.7.3
django-model-utils==3.1.2
django-waffle==0.15.1 # via edx-django-utils, edx-drf-extensions
django==1.11.20
djangorestframework-jwt==1.11.0 # via edx-drf-extensions
djangorestframework==3.9.2 # via edx-drf-extensions, rest-condition
edx-django-utils==1.0.3 # via edx-drf-extensions
edx-drf-extensions==2.0.1
edx-opaque-keys==0.4.4 # via edx-drf-extensions
future==0.17.1 # via pyjwkest
idna==2.8 # via requests
newrelic==4.14.0.115 # via edx-django-utils
pbr==5.1.3 # via stevedore
psutil==1.2.1 # via edx-django-utils, edx-drf-extensions
pycryptodomex==3.7.3 # via pyjwkest
pyjwkest==1.3.2 # via edx-drf-extensions
pyjwt==1.7.1 # via djangorestframework-jwt
pymongo==3.7.2 # via edx-opaque-keys
python-dateutil==2.8.0 # via edx-drf-extensions
pytz==2018.9 # via django
requests==2.21.0 # via edx-drf-extensions, pyjwkest
rest-condition==1.0.3 # via edx-drf-extensions
semantic-version==2.6.0 # via edx-drf-extensions
six==1.12.0
stevedore==1.30.1 # via edx-opaque-keys
urllib3==1.24.1 # via requests
18 changes: 18 additions & 0 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,21 @@ contextlib2==0.5.5 # via importlib-metadata
coverage==4.5.2
diff-cover==1.0.6
distlib==0.2.8
django-crum==0.7.3
django-model-utils==3.1.2
django-waffle==0.15.1
django==1.11.20
djangorestframework-jwt==1.11.0
djangorestframework==3.9.2
edx-django-utils==1.0.3
edx-drf-extensions==2.0.1
edx-i18n-tools==0.4.8
edx-lint==1.1.1
edx-opaque-keys==0.4.4
enum34==1.1.6
filelock==3.0.10
funcsigs==1.0.2
future==0.17.1
futures==3.1.1
idna==2.8
importlib-metadata==0.8 # via path.py
Expand All @@ -38,31 +46,41 @@ jinja2==2.10
lazy-object-proxy==1.3.1
markupsafe==1.1.1
mccabe==0.6.1
mock==2.0.0
more-itertools==5.0.0
newrelic==4.14.0.115
packaging==19.0
path.py==11.5.0 # via edx-i18n-tools
pathlib2==2.3.3
pbr==5.1.3
pip-tools==3.4.0
pluggy==0.9.0
polib==1.1.0 # via edx-i18n-tools
psutil==1.2.1
py==1.8.0
pycodestyle==2.5.0
pycryptodomex==3.7.3
pydocstyle==3.0.0
pygments==2.3.1 # via diff-cover
pyjwkest==1.3.2
pyjwt==1.7.1
pylint-celery==0.3
pylint-django==0.7.2
pylint-plugin-utils==0.5
pylint==1.7.6
pymongo==3.7.2
pyparsing==2.3.1
pytest-cov==2.6.1
pytest-django==3.4.8
pytest==4.3.0
python-dateutil==2.8.0
python-slugify==3.0.0
pytz==2018.9
pyyaml==3.13
requests==2.21.0
rest-condition==1.0.3
scandir==1.9.0
semantic-version==2.6.0
singledispatch==3.4.0.3
six==1.12.0
snowballstemmer==1.2.1
Expand Down
28 changes: 23 additions & 5 deletions requirements/doc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,45 +9,63 @@ atomicwrites==1.3.0
attrs==19.1.0
babel==2.6.0 # via sphinx
bleach==3.1.0 # via readme-renderer
certifi==2018.11.29 # via requests
chardet==3.0.4 # via doc8, requests
certifi==2018.11.29
chardet==3.0.4
click==7.0
code-annotations==0.3
coverage==4.5.2
django-crum==0.7.3
django-model-utils==3.1.2
django-waffle==0.15.1
django==1.11.20
djangorestframework-jwt==1.11.0
djangorestframework==3.9.2
doc8==0.8.0
docutils==0.14 # via doc8, readme-renderer, restructuredtext-lint, sphinx
edx-django-utils==1.0.3
edx-drf-extensions==2.0.1
edx-opaque-keys==0.4.4
edx-sphinx-theme==1.4.0
funcsigs==1.0.2
idna==2.8 # via requests
future==0.17.1
idna==2.8
imagesize==1.1.0 # via sphinx
jinja2==2.10
markupsafe==1.1.1
mock==2.0.0
more-itertools==5.0.0
newrelic==4.14.0.115
packaging==19.0 # via sphinx
pathlib2==2.3.3
pbr==5.1.3
pluggy==0.9.0
psutil==1.2.1
py==1.8.0
pycryptodomex==3.7.3
pygments==2.3.1 # via readme-renderer, sphinx
pyjwkest==1.3.2
pyjwt==1.7.1
pymongo==3.7.2
pyparsing==2.3.1 # via packaging
pytest-cov==2.6.1
pytest-django==3.4.8
pytest==4.3.0
python-dateutil==2.8.0
python-slugify==3.0.0
pytz==2018.9
pyyaml==3.13
readme-renderer==24.0
requests==2.21.0 # via sphinx
requests==2.21.0
rest-condition==1.0.3
restructuredtext-lint==1.2.2 # via doc8
scandir==1.9.0
semantic-version==2.6.0
six==1.12.0
snowballstemmer==1.2.1 # via sphinx
sphinx==1.8.4
sphinxcontrib-websupport==1.1.0 # via sphinx
stevedore==1.30.1
text-unidecode==1.2
typing==3.6.6 # via sphinx
urllib3==1.24.1 # via requests
urllib3==1.24.1
webencodings==0.5.1 # via bleach
Loading

0 comments on commit 59b9999

Please sign in to comment.