Skip to content

Commit

Permalink
Merge pull request #1 from pennlabs/feature/settings
Browse files Browse the repository at this point in the history
Feature/settings
  • Loading branch information
ArmaanT authored Mar 25, 2019
2 parents 7648421 + 197bc78 commit 5a25182
Show file tree
Hide file tree
Showing 15 changed files with 270 additions and 50 deletions.
23 changes: 23 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
version: 2
jobs:
build:
working_directory: ~/django-labs-accounts
docker:
- image: themattrix/tox
steps:
- checkout
- run:
name: Install dependencies
command: |
pip install tox coveralls
- run:
name: Run tests
command: |
tox
coveralls
workflows:
version: 2
build:
jobs:
- build
2 changes: 1 addition & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[run]
source = labs_accounts
source = accounts
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Changelog
=========

0.2.0 (2019-03-24)
------------------
* New feature: Provide an easier way to access settings through a new `accounts_settings` object

0.1.0 (2019-03-17)
------------------
* Initial Release
42 changes: 37 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
Django Labs Accounts
====================
.. image:: https://img.shields.io/circleci/project/github/pennlabs/django-labs-accounts/master.svg
:target: https://circleci.com/gh/pennlabs/django-labs-accounts
.. image:: https://coveralls.io/repos/github/pennlabs/django-labs-accounts/badge.svg?branch=feature%2Fsettings
:target: https://coveralls.io/github/pennlabs/django-labs-accounts?branch=feature%2Fsettings
.. image:: https://img.shields.io/pypi/v/django-labs-accounts.svg
:target: https://pypi.org/project/django-labs-accounts/

Requirements
------------
* Python 3.4+
* Django 2.0+

Installation
------------
Install with pip
pip install django-labs-accounts
Install with pipenv
pipenv install django-labs-accounts

Add ``accounts`` to ``INSTALLED_APPS``

Add `accounts` to `INSTALLED_APPS`
.. code-block:: python
INSTALLED_APPS = (
...
'accounts.apps.AccountsConfig',
)
Add the following to `urls.py`
Add the following to ``urls.py``

.. code-block:: python
urlpatterns = [
Expand All @@ -27,7 +37,29 @@ Add the following to `urls.py`
Documentation
-------------
TODO
All settings are handled with a ``PLATFORM_ACCOUNTS`` dictionary.

Example:

.. code-block:: python
PLATFORM_ACCOUNTS = {
'CLIENT_ID': 'id',
'CLIENT_SECRET': 'secret',
'REDIRECT_URI': 'example',
}
The available settings are:

``CLIENT_ID`` the client ID to connect to platform with. Defaults to ``LABS_CLIENT_ID`` environment variable.

``CLIENT_SECRET`` the client secret to connect to platform with. Defaults to ``LABS_CLIENT_SECRET`` environment variable.

``REDIRECT_URI`` the redirect uri to send to platform. Defaults to ``LABS_REDIRECT_URI`` environment variable.

``SCOPE`` the scope for this applications tokens. Must include ``introspection``. Defaults to ``['read', 'introspection']``.

``PLATFORM_URL`` URL of platform server to connect to. Should be ``https://platform(-dev).pennlabs.org`` (no trailing slash)

Changelog
---------
Expand Down
38 changes: 32 additions & 6 deletions accounts/settings.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
import os
from django.conf import settings

CLIENT_ID = getattr(settings, "OAUTH2_CLIENT_ID", os.environ.get('OAUTH2_CLIENT_ID'))
CLIENT_SECRET = getattr(settings, "OAUTH2_CLIENT_SECRET", os.environ.get('OAUTH2_CLIENT_ID'))
REDIRECT_URI = getattr(settings, "OAUTH2_REDIRECT_URI", os.environ.get('OAUTH2_REDIRECT_URI'))
SCOPE = getattr(settings, "OAUTH2_SCOPES", ['read', 'introspection'])
PLATFORM_URL = getattr(settings, "OAUTH2_PLATFORM_URL", 'https://platform.pennlabs.org')

USER_SETTINGS = getattr(settings, "PLATFORM_ACCOUNTS", {})

DEFAULTS = {
'CLIENT_ID': os.environ.get('LABS_CLIENT_ID'),
'CLIENT_SECRET': os.environ.get('LABS_CLIENT_SECRET'),
'REDIRECT_URI': os.environ.get('LABS_REDIRECT_URI'),
'SCOPE': ['read', 'introspection'],
'PLATFORM_URL': 'https://platform.pennlabs.org',
}


class AccountsSettings(object):
"""
Based on https://github.com/encode/django-rest-framework/blob/master/rest_framework/settings.py
"""
def __init__(self, settings=None, defaults=None):
self.settings = settings or {}
self.defaults = defaults or {}

def __getattr__(self, attr):
if attr not in self.defaults.keys():
raise AttributeError("Invalid Penn Labs accounts setting: %s" % attr)

try:
val = self.settings[attr]
except KeyError:
val = self.defaults[attr]

setattr(self, attr, val)
return val


accounts_settings = AccountsSettings(USER_SETTINGS, DEFAULTS)
30 changes: 20 additions & 10 deletions accounts/views.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
from requests_oauthlib import OAuth2Session
from django.contrib import auth
from django.http import HttpResponseServerError
from django.http.response import HttpResponseBadRequest
from django.shortcuts import redirect
from django.views import View
from accounts.settings import CLIENT_ID, CLIENT_SECRET, PLATFORM_URL, REDIRECT_URI, SCOPE
from accounts.settings import accounts_settings


class LoginView(View):
def get(self, request):
request.session.__setitem__('next', request.GET.get('next'))
return_to = request.GET.get('next')
if not return_to:
return HttpResponseBadRequest("Invalid next parameter")
request.session.__setitem__('next', return_to)
if not request.user.is_authenticated:
platform = OAuth2Session(CLIENT_ID, scope=SCOPE, redirect_uri=REDIRECT_URI)
authorization_url, state = platform.authorization_url(PLATFORM_URL + '/accounts/authorize/')
platform = OAuth2Session(
accounts_settings.CLIENT_ID,
scope=accounts_settings.SCOPE,
redirect_uri=accounts_settings.REDIRECT_URI
)
authorization_url, state = platform.authorization_url(
accounts_settings.PLATFORM_URL + '/accounts/authorize/'
)
response = redirect(authorization_url)
request.session.__setitem__('state', state)
return response
Expand All @@ -20,17 +30,17 @@ def get(self, request):

class CallbackView(View):
def get(self, request):
code = request.GET.get('code')
state = request.session.pop('state')
platform = OAuth2Session(CLIENT_ID, state=state)
platform = OAuth2Session(accounts_settings.CLIENT_ID, state=state)
token = platform.fetch_token(
PLATFORM_URL + '/accounts/token/',
client_secret=CLIENT_SECRET,
accounts_settings.PLATFORM_URL + '/accounts/token/',
client_secret=accounts_settings.CLIENT_SECRET,
authorization_response=request.get_full_path()
)
platform = OAuth2Session(CLIENT_ID, token=token)
platform = OAuth2Session(accounts_settings.CLIENT_ID, token=token)
access_token = token['access_token']
uuid = platform.get(PLATFORM_URL + '/accounts/introspect/?token=' + access_token).json()['uuid']
introspect_url = accounts_settings.PLATFORM_URL + '/accounts/introspect/?token=' + access_token
uuid = platform.get(introspect_url).json()['uuid']
user = auth.authenticate(remote_user=uuid)
if user:
auth.login(request, user)
Expand Down
10 changes: 10 additions & 0 deletions runtests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import sys
import django
from django.test.runner import DiscoverRunner

django.setup()
test_runner = DiscoverRunner(verbosity=1)

failures = test_runner.run_tests(['tests'])
if failures:
sys.exit(failures)
79 changes: 79 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
SECRET_KEY = 'supersecret'

ALLOWED_HOSTS = []

INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'django.contrib.messages',
'accounts',
'tests'
)

MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
]

ROOT_URLCONF = 'tests.urls'

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'debug': True,
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.contrib.messages.context_processors.messages',
],
},
},
]

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'example.sqlite',
}
}


LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

SITE_ID = 1

USE_I18N = True

USE_L10N = True

USE_TZ = True

STATIC_URL = '/static/'

AUTH_USER_MODEL = 'accounts.User'

AUTHENTICATION_BACKENDS = (
'accounts.backends.LabsUserBackend',
'django.contrib.auth.backends.ModelBackend',
)

PLATFORM_ACCOUNTS = {
'CLIENT_ID': 'id',
'CLIENT_SECRET': 'secret',
'REDIRECT_URI': 'example',
}
7 changes: 7 additions & 0 deletions tests/test_apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.test import TestCase
from accounts.apps import AccountsConfig


class AppsTestCase(TestCase):
def test_apps(self):
self.assertEqual(AccountsConfig.name, 'accounts')
2 changes: 1 addition & 1 deletion tests/test_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def test_invalid_remote_user(self):
self.assertIsNone(user)

def test_create_user(self):
user = auth.authenticate(remote_user='00000000000000000000000000000001')
auth.authenticate(remote_user='00000000000000000000000000000001')
self.assertEqual(len(self.User.objects.all()), 1)
self.assertEqual(str(self.User.objects.all()[0].uuid), '00000000-0000-0000-0000-000000000001')
self.assertEqual(str(self.User.objects.all()[0]), '00000000000000000000000000000001')
Expand Down
13 changes: 0 additions & 13 deletions tests/test_models.py

This file was deleted.

15 changes: 15 additions & 0 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.test import TestCase
from tests.settings import PLATFORM_ACCOUNTS
from accounts.settings import accounts_settings, DEFAULTS


class SettingsTestCase(TestCase):
def test_invalid_setting(self):
with self.assertRaises(AttributeError):
accounts_settings.INVALID_SETTING

def test_defined_setting(self):
self.assertEqual(accounts_settings.CLIENT_ID, PLATFORM_ACCOUNTS['CLIENT_ID'])

def test_default_setting(self):
self.assertEqual(accounts_settings.SCOPE, DEFAULTS['SCOPE'])
Loading

0 comments on commit 5a25182

Please sign in to comment.