Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9b3a17f
Add coverage tests
blagojabozinovski Apr 29, 2025
e73f351
Add tests for cache.py
blagojabozinovski May 16, 2025
0f5ac7d
Additional tests for cache.py
blagojabozinovski May 16, 2025
6f0a361
Tests code cleanup
blagojabozinovski May 16, 2025
384925c
Tests code refactor
blagojabozinovski May 16, 2025
91ffae0
Merge pull request #127 from keitaroinc/coverage-tests
blagojabozinovski May 16, 2025
25ad215
Disable conftests
blagojabozinovski May 17, 2025
4f5fc4c
Add slo unit tests
blagojabozinovski May 19, 2025
8bc6142
Flake8 fixes
blagojabozinovski May 19, 2025
0bf0bca
Remove unit test for CKAN 2.9
blagojabozinovski May 19, 2025
8b84a4e
Revert change to tests
blagojabozinovski May 19, 2025
fe79a00
Flake8 fix
blagojabozinovski May 19, 2025
b0d2f8b
Drop CKAN 2.9 support for test ckan cookie
blagojabozinovski May 19, 2025
838ffcd
Update README.md
blagojabozinovski May 21, 2025
c158223
Update setup
blagojabozinovski May 21, 2025
0cbe81f
Update setup
blagojabozinovski May 21, 2025
62cc216
Remove additional plugins for conftest
blagojabozinovski May 21, 2025
a4cbaf6
Merge pull request #126 from keitaroinc/okfn-updates
blagojabozinovski May 21, 2025
23c56a1
Update badges on readme
blagojabozinovski May 21, 2025
156aa70
Merge pull request #129 from keitaroinc/readme-update
blagojabozinovski May 22, 2025
0222247
Add config declarations
blagojabozinovski May 29, 2025
8743a9f
Merge pull request #130 from keitaroinc/config-declarations-decorator
blagojabozinovski May 29, 2025
49c5d3f
Update MANIFEST.in
blagojabozinovski Oct 24, 2025
5318059
Merge pull request #132 from keitaroinc/manifest
blagojabozinovski Oct 24, 2025
cacc9b6
Merge remote-tracking branch 'upstream/main' into sync-upstream-2026-…
avdata99 Mar 23, 2026
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
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,45 @@ jobs:
run: |
echo "Running SAML2AUTH tests"
pytest --ckan-ini=subdir/test.ini --cov=ckanext.saml2auth --disable-warnings ckanext/saml2auth/tests

- name: Coveralls
uses: AndreMiras/coveralls-python-action@develop
with:
parallel: true
flag-name: Python ${{ matrix.python-version }} Unit Test

publish:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.8'

- name: Install setup requirements
run: |
python -m pip install --upgrade setuptools wheel twine

- name: Build and package
run: |
python setup.py sdist bdist_wheel
twine check dist/*

- name: Publish package
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}

coveralls_finish:
needs: test
runs-on: ubuntu-latest
steps:
- name: Coveralls Finished
uses: AndreMiras/coveralls-python-action@develop
with:
parallel-finished: true
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
include README.rst
include LICENSE
recursive-include ckanext/saml2auth *.html *.json *.js *.less *.css *.mo
recursive-include ckanext/saml2auth .html *.json *.js *.less *.css *.mo *.yml *.config *.png *.jpeg *.svg *.po *.pot *.jpg *.map *.eot *.svg *.ttf *.woff *.woff2 *.yaml *.ico

10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
[![CI][]][1] [![Coverage][]][2] [![Gitter][]][3] [![Pypi][]][4] [![Python][]][5] [![CKAN][]][6]

# Temporary fork

**This is a temporary fork from OKFN** to work with CKAN 2.10 waiting for upstream repo at https://github.com/keitaroinc/ckanext-saml2auth to be updated.

# ckanext-saml2auth

A [CKAN](https://ckan.org) extension to enable Single Sign-On (SSO) for CKAN data portals via SAML2 Authentication.

## Requirements

This extension works with CKAN 2.9+.
This extension works with CKAN 2.10+
Note: For CKAN 2.9 or older use v1.3.0 or older versions.

## Installation

Expand Down Expand Up @@ -234,7 +232,7 @@ to PyPI follow these steps:
[3]: https://gitter.im/keitaroinc/ckan?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
[Pypi]: https://img.shields.io/pypi/v/ckanext-saml2auth
[4]: https://pypi.org/project/ckanext-saml2auth
[Python]: https://img.shields.io/badge/python-3.7%20%7C%203.8%20%7C%203.9-blue
[Python]: https://img.shields.io/badge/python-3.9%20|%203.10%20|%203.11-blue
[5]: https://www.python.org
[CKAN]: https://img.shields.io/badge/ckan-2.9%20%7C%202.10-yellow
[CKAN]: https://img.shields.io/badge/ckan-2.10%20|%202.11-yellow
[6]: https://www.ckan.org
171 changes: 171 additions & 0 deletions ckanext/saml2auth/config_declaration.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
version: 1
groups:
- annotation: ckanext-saml2auth settings
options:
- key: ckanext.saml2auth.idp_metadata.location
example: local
default: remote
description: |
Specifies the metadata location type.
Options: local or remote
required: true
- key: ckanext.saml2auth.idp_metadata.local_path
example: /opt/metadata/idp.xml
default:
description: |
Path to a local file accessible on the server the service runs on.
Ignore this config if the idp metadata location is set to: remote
required: false
- key: ckanext.saml2auth.idp_metadata.remote_url
example: https://kalmar2.org/simplesaml/module.php/aggregator/?id=kalmarcentral2&set=saml2
default:
description: |
A remote URL serving aggregate metadata
Ignore this config if the idp metadata location is set to: local
required: false
- key: ckanext.saml2auth.idp_metadata.remote_cert
example: /opt/metadata/kalmar2.cert
default:
description: |
Path to a local file accessible on the server the service runs on
Ignore this config if the idp metadata location is set to: local and metadata is public
required: false
- key: ckanext.saml2auth.user_firstname
example: name
default: firstname
description: |
Corresponding SAML user field for firstname
required: false
- key: ckanext.saml2auth.user_lastname
example: surname
default: lastname
description: |
Corresponding SAML user field for lastname
required: false
- key: ckanext.saml2auth.user_email
example: emailadress
default: email
description: |
Corresponding SAML user field for email
required: true
- key: ckanext.saml2auth.user_fullname
example: fullname
default: fullname
description: |
Corresponding SAML user field for fullname
(Optional: Can be used as an alternative to firstname + lastname)
required: false
- key: ckanext.saml2auth.enable_ckan_internal_login
example: True
default: False
description: |
Configuration setting that enables CKAN's internal register/login functionality as well
required: false
type: bool
- key: ckanext.saml2auth.sysadmins_list
example: mail@domain.com mail2@domain.com mail3@domain.com
default:
description: |
List of email addresses from users that should be created as sysadmins (system administrators)
Note that this means that CKAN sysadmins will _only_ be managed based on this config option and will override existing user permissions in the CKAN database
If not set then it is ignored and CKAN sysadmins are managed through normal means
required: false
- key: ckanext.saml2auth.allow_unknown_attributes
example: False
default: True
description: |
Indicates that attributes that are not recognized (they are not configured in attribute-mapping),
will not be discarded.
required: false
type: bool
- key: ckanext.saml2auth.sp.name_id_format
example: urn:oasis:names:tc:SAML:2.0:nameid-format:transient
default: urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
description: |
A list of string values that will be used to set the <NameIDFormat> element of the metadata of an entity.
required: false
- key: ckanext.saml2auth.sp.name_id_policy_format
example: urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
default:
description: |
A string value that will be used to set the Format attribute of the <NameIDPolicy> element of the metadata of an entity.
required: false
- key: ckanext.saml2auth.entity_id
example: urn:gov:gsa:SAML:2.0.profiles:sp:sso:gsa:catalog-dev
default: urn:mace:umu.se:saml:ckan:sp
description: |
Entity ID (also know as Issuer)
Define the entity ID.
required: false
- key: ckanext.saml2auth.want_response_signed
example: False
default: True
description: |
Indicates if SAML responses must be signed.
required: false
type: bool
- key: ckanext.saml2auth.want_assertions_signed
example: True
default: False
description: |
Indicates if SAML assertions must be signed.
required: false
type: bool
- key: ckanext.saml2auth.want_assertions_or_response_signed
example: True
default: False
description: |
If set to true, either the SAML response or the assertion must be signed.
required: false
type: bool
- key: ckanext.saml2auth.key_file_path
example: /path/to/mykey.pem
default:
description: |
Path to the private key file used to sign SAML requests or decrypt assertions.
required: false
- key: ckanext.saml2auth.cert_file_path
example: /path/to/mycert.pem
default:
description: |
Path to the public certificate file used to verify signatures or encrypt assertions.
required: false
- key: ckanext.saml2auth.attribute_map_dir
example: /path/to/dir/attributemaps
default:
description: |
Directory path containing SAML attribute mapping configuration files.
required: false
- key: ckanext.saml2auth.requested_authn_context
example: http://idmanagement.gov/ns/assurance/aal/3?hspd12=true
default:
description: |
Specifies authentication context class references requested during login.
You can provide multiple values separated by spaces.
required: false
- key: ckanext.saml2auth.requested_authn_context_comparison
example: exact
default: exact
description: |
Comparison method for RequestedAuthnContext.
Options: exact, minimum, maximum, better
required: false
- key: ckanext.saml2auth.logout_requests_signed
example: True
default: False
description: |
Indicates whether this service provider will sign logout requests.
required: false
type: bool
- key: ckanext.saml2auth.logout_expected_binding
example: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
default: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
description: |
The expected SAML binding method for logout requests.
required: false
- key: ckanext.saml2auth.default_fallback_endpoint
example: home.index
default: user.me
description: |
The default endpoint to redirect to if no RelayState is provided in the SAML response.
required: false
1 change: 1 addition & 0 deletions ckanext/saml2auth/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
log = logging.getLogger(__name__)


@toolkit.blanket.config_declarations
class Saml2AuthPlugin(plugins.SingletonPlugin):
plugins.implements(plugins.IConfigurer)
plugins.implements(plugins.IBlueprint)
Expand Down
40 changes: 1 addition & 39 deletions ckanext/saml2auth/tests/test_blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,50 +61,12 @@ def test_came_from_sent_as_relay_state(self, app):
response = app.get(url=url, follow_redirects=False)
assert 'RelayState=%2Fdataset%2Fmy-dataset' in response.headers['Location']

@pytest.mark.ckan_config(u'ckanext.saml2auth.idp_metadata.location', u'local')
@pytest.mark.ckan_config(u'ckanext.saml2auth.idp_metadata.local_path',
os.path.join(extras_folder, 'provider2', 'idp.xml'))
@pytest.mark.usefixtures('with_request_context')
def test_cookies_cleared_on_slo(self, app):
if toolkit.check_ckan_version(min_version='2.10'):
# Remove when dropping support for 2.9
pytest.skip("auth_tkt cookie has been deprecated in 2.10")
url = url_for('user.logout')

import datetime
from unittest import mock
from http.cookies import SimpleCookie
from flask import make_response
from dateutil.parser import parse as date_parse

with mock.patch(
'ckanext.saml2auth.plugin._perform_slo',
return_value=make_response('')):
response = app.get(url=url, follow_redirects=False)

cookie_headers = [
h[1] for h in response.headers
if h[0].lower() == 'set-cookie']

assert len(cookie_headers) == 2

for cookie_header in cookie_headers:
cookie = SimpleCookie()
cookie.load(cookie_header)
cookie_name = [name for name in cookie.keys()][0]
assert cookie_name in ['auth_tkt', 'ckan']
assert cookie[cookie_name]['domain'] == 'test.ckan.net'
cookie_date = date_parse(cookie[cookie_name]['expires'], ignoretz=True)
assert cookie_date < datetime.datetime.now()

@pytest.mark.ckan_config(u'ckanext.saml2auth.idp_metadata.location', u'local')
@pytest.mark.ckan_config(u'ckanext.saml2auth.idp_metadata.local_path',
os.path.join(extras_folder, 'provider2', 'idp.xml'))
@pytest.mark.usefixtures('with_request_context')
def test_ckan_cookie_cleared_on_slo(self, app):
if not toolkit.check_ckan_version(min_version='2.10'):
# Remove when dropping support for 2.9
pytest.skip("This test logic introduced in CKAN 2.10")

url = url_for('user.logout')

import datetime
Expand Down
Loading
Loading