Skip to content

v1.3.0 #291

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

Merged
merged 4 commits into from
Jun 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 47 additions & 10 deletions djangosaml2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
UnsolicitedResponse)
from saml2.s_utils import UnsupportedBinding
from saml2.saml import SCM_BEARER
from saml2.saml import AuthnContextClassRef
from saml2.samlp import RequestedAuthnContext
from saml2.samlp import AuthnRequest, IDPEntry, IDPList, Scoping
from saml2.sigver import MissingKey
from saml2.validate import ResponseLifetimeExceed, ToEarly
Expand Down Expand Up @@ -133,6 +135,41 @@ def unknown_idp(self, request, idp):
msg.format('Please contact technical support.'), status=403
)

def load_sso_kwargs_scoping(self, sso_kwargs):
""" Performs IdP Scoping if scoping param is present. """
idp_scoping_param = self.request.GET.get('scoping', None)
if idp_scoping_param:
idp_scoping = Scoping()
idp_scoping.idp_list = IDPList()
idp_scoping.idp_list.idp_entry.append(
IDPEntry(provider_id = idp_scoping_param)
)
sso_kwargs['scoping'] = idp_scoping

def load_sso_kwargs_authn_context(self, sso_kwargs):
# this would work when https://github.com/IdentityPython/pysaml2/pull/807
ac = getattr(self.conf, '_sp_requested_authn_context', {})

# this works even without https://github.com/IdentityPython/pysaml2/pull/807
# hopefully to be removed soon !
if not ac:
scs = getattr(
settings, 'SAML_CONFIG', {}
).get('service', {}).get('sp', {})
ac = scs.get('requested_authn_context', {})
# end transitional things to be removed soon !

if ac:
sso_kwargs["requested_authn_context"] = RequestedAuthnContext(
authn_context_class_ref=[
AuthnContextClassRef(ac['authn_context_class_ref']),
],
comparison = ac.get('comparison', "minimum"),
)

def load_sso_kwargs(self, sso_kwargs):
""" Inherit me if you want to put your desidered things in sso_kwargs """

def get(self, request, *args, **kwargs):
logger.debug('Login process started')
next_path = self.get_next_path(request)
Expand Down Expand Up @@ -166,6 +203,7 @@ def get(self, request, *args, **kwargs):
configured_idps = available_idps(conf)
selected_idp = request.GET.get('idp', None)

self.conf = conf
sso_kwargs = {}

# Do we have a Discovery Service?
Expand Down Expand Up @@ -200,16 +238,6 @@ def get(self, request, *args, **kwargs):
if selected_idp is None:
selected_idp = list(configured_idps.keys())[0]

# perform IdP Scoping if scoping param is present
idp_scoping_param = request.GET.get('scoping', None)
if idp_scoping_param:
idp_scoping = Scoping()
idp_scoping.idp_list = IDPList()
idp_scoping.idp_list.idp_entry.append(
IDPEntry(provider_id = idp_scoping_param)
)
sso_kwargs['scoping'] = idp_scoping

# choose a binding to try first
binding = getattr(settings, 'SAML_DEFAULT_BINDING',
saml2.BINDING_HTTP_POST)
Expand Down Expand Up @@ -267,6 +295,15 @@ def get(self, request, *args, **kwargs):
# custom nsprefixes
sso_kwargs['nsprefix'] = get_namespace_prefixes()


# Enrich sso_kwargs ...
# idp scoping
self.load_sso_kwargs_scoping(sso_kwargs)
# authn context
self.load_sso_kwargs_authn_context(sso_kwargs)
# other customization to be inherited
self.load_sso_kwargs(sso_kwargs)

logger.debug(f'Redirecting user to the IdP via {binding} binding.')
_msg = 'Unable to know which IdP to use'
http_response = None
Expand Down
17 changes: 8 additions & 9 deletions docs/source/contents/developer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,23 @@ a link to do a global logout.
Unit tests
==========

You can also run the unit tests as follows::
Djangosaml2 have a legacy way to do tests, using an example project in `tests` directory.
This means that to run tests you have to clone the repository, then install djangosaml2, then run tests using the example project.

example::

pip install -r requirements-dev.txt
# or
pip install djangosaml2[test]
python3 tests/manage.py migrate

then::

python tests/run_tests.py

or::

cd tests/
then::
cd tests
./manage.py migrate
./manage.py test djangosaml2


If you have `tox`_ installed you can simply call tox inside the root directory
If you have `tox`_ installed you can simply call `tox` inside the root directory
and it will run the tests in multiple versions of Python.

.. _`tox`: http://pypi.python.org/pypi/tox
Expand Down
19 changes: 11 additions & 8 deletions docs/source/contents/setup.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,6 @@ installed apps::
'djangosaml2', # new application
)

.. Note::

When you finish the configuration you can run the djangosaml2 test suite as
you run any other Django application test suite. Just type ``python manage.py
test djangosaml2``.

Python users need to ``pip install djangosaml2[test]`` in order to run the
tests.

SameSite cookie
===============
Expand Down Expand Up @@ -221,6 +213,17 @@ This parameter can be combined with the IdP parameter if multiple IdPs are prese
Currently there is support for a single IDPEntry in the IDPList.


Authn Context
=============

We can define the authentication context in settings.SAML_CONFIG['service']['sp'] as follows::

'requested_authn_context': {
'authn_context_class_ref': saml2.saml.AUTHN_PASSWORD_PROTECTED,
'comparison': "exact"
}


Custom and dynamic configuration loading
========================================

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def read(*rnames):

setup(
name='djangosaml2',
version='1.2.2',
version='1.3.0',
description='pysaml2 integration for Django',
long_description=read('README.md'),
long_description_content_type='text/markdown',
Expand Down
2 changes: 1 addition & 1 deletion tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'NAME': os.path.join(BASE_DIR, 'tests/db.sqlite3'),
}
}

Expand Down