Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Final Django 4.x fixes and improving tox tests #156

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
35 changes: 35 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Read the Docs configuration file for Sphinx projects
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

# Set the OS, Python version and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.12"
# You can also specify other tool versions:
# nodejs: "20"
# rust: "1.70"
# golang: "1.20"

# Build documentation in the "docs/" directory with Sphinx
sphinx:
configuration: docs/conf.py
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
# builder: "dirhtml"
# Fail on all warnings to avoid broken references
# fail_on_warning: true

# Optionally build your docs in additional formats such as PDF and ePub
# formats:
# - pdf
# - epub

# Optional but recommended, declare the Python requirements required
# to build your documentation
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
# python:
# install:
# - requirements: docs/requirements.txt
20 changes: 20 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
ARG PYVERSION=3.9.19-bullseye

FROM python:${PYVERSION} AS dev

WORKDIR /app

COPY requirements.txt /app/

RUN apt-get update \
&& apt-get install -q -y \
jq \
&& apt-get clean

RUN pip install -r requirements.txt

FROM dev as prod

COPY ./ /app/


47 changes: 47 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
pipeline {
agent {
label "python"
}
stages {
stage('Virtualenv'){
steps {
sh '/usr/bin/virtualenv toxtest -p /usr/bin/python3'
sh 'toxtest/bin/pip install tox==3.28.0 pathlib2'
}
}
stage('Test'){
parallel {
stage('Unit Test Django 3.1'){
steps {
sh 'toxtest/bin/tox -e py3.8-django{3.1}'
}
}
stage('Unit Test Django 3.2'){
steps {
sh 'toxtest/bin/tox -e py3.8-django{3.2}'
}
}
stage('Unit Test Django 4.0'){
steps {
sh 'toxtest/bin/tox -e py3.8-django{4.0}'
}
}
stage('Unit Test Django 4.1'){
steps {
sh 'toxtest/bin/tox -e py3.8-django{4.1}'
}
}
stage('Unit Test Django 4.2'){
steps {
sh 'toxtest/bin/tox -e py3.8-django{4.2}'
}
}
}
}
}
post {
cleanup {
cleanWs()
}
}
}
7 changes: 7 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,10 @@ License
=======

*django-oauth2* is a fork of *django-oauth2-provider* which is released under the MIT License. Please see the LICENSE file for details.


Packaging
=========

$ python -m build

68 changes: 68 additions & 0 deletions aws_identity_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import os
import sys
import json

from datetime import datetime
from urllib import request, error
import requests

import boto3
# aws-v4-signature==2.0
from awsv4sign import generate_http11_header

service = 'sts'
region = 'us-west-2'

session = boto3.Session()
creds = session.get_credentials()
access_key = creds.access_key
secret_key = creds.secret_key
session_token = creds.token

print(f"access_key: {access_key[:10]}<redacted...>")
print(f"secret_key: {secret_key[:10]}<redacted...>")
print(f"session_token: {session_token[:20]}<redacted...>")
print(f"profile: {os.environ.get('AWS_PROFILE')}")

url = 'https://sts.{region}.amazonaws.com/'.format(region=region)
httpMethod = 'post'
canonicalHeaders = {
'host': f'sts.{region}.amazonaws.com',
'x-amz-date': datetime.utcnow().strftime('%Y%m%dT%H%M%SZ'),
'content-type': 'application/x-www-form-urlencoded; charset=utf-8',
}
if session_token:
canonicalHeaders['x-amz-security-token'] = session_token

payload_str = "Action=GetCallerIdentity&Version=2011-06-15"

headers = generate_http11_header(
service, region, access_key, secret_key,
url, 'post', canonicalHeaders, {},
'', payload_str
)

token_request_args = {
"grant_type": "aws_identity",
"region": region,
"post_body": payload_str,
"headers_json": json.dumps(headers),
}
print(payload_str)
print(json.dumps(headers, indent=4))

req = request.Request("https://sts.us-west-2.amazonaws.com/", data=payload_str.encode('utf-8'), headers=headers, method='POST')
try:
response = request.urlopen(req)
print(f"Local request test result: {response.read()}")
except error.HTTPError as e:
print(f"HTTPError: {e}: {e.fp.read()}")
sys.exit(1)

print("Attempting access_token grant request with same signed request:\n")

token_response = requests.post("http://localhost:8000/oauth2/access_token",
data=token_request_args)
token_info = token_response.json()

print(token_info)
24 changes: 24 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

services:
test:
build:
context: .
target: dev
user: ${UID}
volumes:
- ${WORKSPACE:-.}:/app
environment:
- DJANGO_SETTINGS_MODULE=tests.settings

web:
build:
context: .
target: dev
user: ${UID}
volumes:
- ${WORKSPACE:-.}:/app
ports:
- "8000:8000"
environment:
- DJANGO_SETTINGS_MODULE=tests.settings
# entrypoint: [ "python3", "manage.py", "runserver" ]
9 changes: 9 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
v 4.1
-----
* Add aws_identity grant_type
* Update for Django 3.1-4.2

v 4.0
-----
* Update for Django 3.0-4.1

v 2.4
-----
* Add HTTP Authorization Bearer token support to Oauth2UserMiddleware
Expand Down
23 changes: 22 additions & 1 deletion docs/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Add :attr:`provider.oauth2.urls` to your root ``urls.py`` file.

::

url(r'^oauth2/', include('provider.oauth2.urls', namespace = 'oauth2')),
path('oauth2/', include(('provider.oauth2.urls', 'oauth2'))),


.. note:: The namespace argument is required.
Expand Down Expand Up @@ -92,6 +92,27 @@ in :rfc:`4`.
.. note:: Remember that you should always use HTTPS for all your OAuth
2 requests otherwise you won't be secured.

Request an Access Token using AWS credentials
---------------------------------------------

The new aws_identity grant_type uses the parameters for a signed GetCallerIdentity
request to prove the caller's identity.

Your client needs to submit a :attr:`POST` request to
:attr:`/oauth2/access_token` including the following parameters:

* ``region`` - AWS Region
* ``post_body`` - The post body used for signing the request. Usually ``Action=GetCallerIdentity&Version=2011-06-15``
* ``headers_json`` - The headers produced by the AWSv4 signing process

The region value is used to produce the standard https://sts.(region).amazonaws.com/ url used to
make the GetCallerIdentity request. The URL is generated server side to reduce the risk of an
attack based on sending an improperly crafted full URL.

The aws-v4-signature library implements awsv4sign.generate_http11_header(). An example is
presented in the root of the repository in aws_identity_examply.py.


Integrate with Django Authentication
####################################

Expand Down
3 changes: 2 additions & 1 deletion provider/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
__version__ = "3.2"
__version__ = "4.2"
# The major version is expected to follow the current django major version:q
6 changes: 6 additions & 0 deletions provider/oauth2/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,15 @@ class AuthorizedClientAdmin(admin.ModelAdmin):
raw_id_fields = ('user',)


class AwsAccountAdmin(admin.ModelAdmin):
list_display = ('arn', 'client', 'max_token_lifetime')
raw_id_fields = ('acting_user',)


admin.site.register(models.AccessToken, AccessTokenAdmin)
admin.site.register(models.Grant, GrantAdmin)
admin.site.register(models.Client, ClientAdmin)
admin.site.register(models.AuthorizedClient, AuthorizedClientAdmin)
admin.site.register(models.AwsAccount, AwsAccountAdmin)
admin.site.register(models.RefreshToken)
admin.site.register(models.Scope)
3 changes: 3 additions & 0 deletions provider/oauth2/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ class Oauth2(AppConfig):
name = 'provider.oauth2'
label = 'oauth2'
verbose_name = "Provider Oauth2"

def ready(self):
import provider.oauth2.signals
32 changes: 32 additions & 0 deletions provider/oauth2/fixtures/test_oauth2.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@
"model": "auth.user",
"pk": 2
},
{
"fields": {
"date_joined": "2012-01-23 05:53:31",
"email": "",
"first_name": "",
"groups": [],
"is_active": true,
"is_staff": false,
"is_superuser": false,
"last_login": "2012-01-23 05:53:31",
"last_name": "",
"password": "sha1$0cf1b$d66589690edd96b410170fcae5cc2bdfb68821e7",
"user_permissions": [],
"username": "test-user-aws"
},
"model": "auth.user",
"pk": 3
},
{
"fields": {
"name": "basic",
Expand All @@ -88,5 +106,19 @@
},
"model": "oauth2.scope",
"pk": 2
},
{
"fields": {
"arn": "arn:aws:iam::123456789012:role/testrole",
"account_id": "123456789012",
"name": "testrole",
"general_type": "role",
"client": 2,
"autoprovision_user": false,
"acting_user": 3,
"scope": ["basic", "advanced"]
},
"model": "oauth2.awsaccount",
"pk": 1
}
]
Loading