pas.plugins.kimug 1.4.2__tar.gz → 1.4.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/CHANGES.md +7 -0
  2. pas_plugins_kimug-1.4.3/CLAUDE.md +89 -0
  3. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/PKG-INFO +8 -1
  4. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/setup.py +1 -1
  5. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/utils.py +10 -2
  6. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas.plugins.kimug.egg-info/PKG-INFO +8 -1
  7. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas.plugins.kimug.egg-info/SOURCES.txt +1 -0
  8. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/utils/test_utils.py +19 -0
  9. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/.editorconfig +0 -0
  10. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/CONTRIBUTORS.md +0 -0
  11. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/LICENSE.GPL +0 -0
  12. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/LICENSE.md +0 -0
  13. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/MANIFEST.in +0 -0
  14. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/README.md +0 -0
  15. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/constraints.txt +0 -0
  16. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/docs/.gitkeep +0 -0
  17. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/news/.changelog_template.jinja +0 -0
  18. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/news/.gitkeep +0 -0
  19. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/pyproject.toml +0 -0
  20. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/requirements.txt +0 -0
  21. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/scripts/create_site.py +0 -0
  22. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/setup.cfg +0 -0
  23. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/__init__.py +0 -0
  24. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/__init__.py +0 -0
  25. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/__init__.py +0 -0
  26. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/browser/configure.zcml +0 -0
  27. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/browser/usergroups_usersoverview.pt +0 -0
  28. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/browser/users.pt +0 -0
  29. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/browser/view.py +0 -0
  30. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/configure.zcml +0 -0
  31. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/content/__init__.py +0 -0
  32. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/controlpanel/__init__.py +0 -0
  33. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/controlpanel/classic.py +0 -0
  34. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/controlpanel/configure.zcml +0 -0
  35. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/controlpanel/controlpanel.pt +0 -0
  36. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/dependencies.zcml +0 -0
  37. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/indexers/__init__.py +0 -0
  38. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/indexers/configure.zcml +0 -0
  39. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/interfaces.py +0 -0
  40. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/locales/__init__.py +0 -0
  41. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/locales/en/LC_MESSAGES/pas.plugins.kimug.po +0 -0
  42. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/locales/pas.plugins.kimug.pot +0 -0
  43. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/locales/update.py +0 -0
  44. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/permissions.zcml +0 -0
  45. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/plugin/__init__.py +0 -0
  46. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/profiles/default/browserlayer.xml +0 -0
  47. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/profiles/default/catalog.xml +0 -0
  48. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/profiles/default/controlpanel.xml +0 -0
  49. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/profiles/default/diff_tool.xml +0 -0
  50. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/profiles/default/metadata.xml +0 -0
  51. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/profiles/default/registry/main.xml +0 -0
  52. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/profiles/default/repositorytool.xml +0 -0
  53. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/profiles/default/rolemap.xml +0 -0
  54. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/profiles/default/types/.gitkeep +0 -0
  55. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/profiles/default/types.xml +0 -0
  56. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/profiles/uninstall/browserlayer.xml +0 -0
  57. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/profiles.zcml +0 -0
  58. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/setuphandlers/__init__.py +0 -0
  59. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/static/logo.svg +0 -0
  60. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/subscribers/configure.zcml +0 -0
  61. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/testing.py +0 -0
  62. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/upgrades/__init__.py +0 -0
  63. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/upgrades/configure.zcml +0 -0
  64. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/vocabularies/__init__.py +0 -0
  65. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas/plugins/kimug/vocabularies/configure.zcml +0 -0
  66. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas.plugins.kimug.egg-info/dependency_links.txt +0 -0
  67. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas.plugins.kimug.egg-info/entry_points.txt +0 -0
  68. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas.plugins.kimug.egg-info/namespace_packages.txt +0 -0
  69. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas.plugins.kimug.egg-info/not-zip-safe +0 -0
  70. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas.plugins.kimug.egg-info/requires.txt +0 -0
  71. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/src/pas.plugins.kimug.egg-info/top_level.txt +0 -0
  72. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/conftest.py +0 -0
  73. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/docker-compose.yml +0 -0
  74. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/keycloak/Dockerfile +0 -0
  75. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/keycloak/import/realm-imio.json +0 -0
  76. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/keycloak/import/realm-plone.json +0 -0
  77. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/plugin/test_plugin.py +0 -0
  78. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/setup/test_setup_install.py +0 -0
  79. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/setup/test_setup_uninstall.py +0 -0
  80. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/view/test_change_password.py +0 -0
  81. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/view/test_migration.py +0 -0
  82. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/view/test_new_user.py +0 -0
  83. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/view/test_personal_information.py +0 -0
  84. {pas_plugins_kimug-1.4.2 → pas_plugins_kimug-1.4.3}/tests/view/test_usergroup_userprefs.py +0 -0
@@ -1,3 +1,10 @@
1
+ ## 1.4.3 (2026-03-24)
2
+
3
+
4
+ - DEVOPS-339 : Fix ConflictError when multiple Zope instances start simultaneously and commit OIDC settings
5
+ [remdub]
6
+
7
+
1
8
  ## 1.4.2 (2025-12-10)
2
9
 
3
10
  - Set administrator role for users in group iA.Smartweb-admin with an imio address.
@@ -0,0 +1,89 @@
1
+ # CLAUDE.md — pas.plugins.kimug
2
+
3
+ Plone PAS plugin that assigns roles to iMio Keycloak users. Extends `pas.plugins.oidc`.
4
+
5
+ ## Build & Run
6
+
7
+ ```bash
8
+ make build # Install Plone (aliases: install, all)
9
+ make config # Create instance configuration
10
+ make create-site # Create a new Plone site
11
+ make start # Start Plone on localhost:8080
12
+ ```
13
+
14
+ ## Testing
15
+
16
+ Tests require a Docker Keycloak instance (managed by pytest-docker via `tests/docker-compose.yml`).
17
+
18
+ ```bash
19
+ make test # Run full test suite (tox -e test)
20
+ tox -e test -- tests/plugin/ # Run a specific test directory
21
+ tox -e test -- -k test_name # Run a single test by name
22
+ tox -e coverage # Tests with coverage report
23
+ ```
24
+
25
+ Test credentials: user `kimug` / password `kimug` / email `kimug@imio.be`
26
+
27
+ Keycloak test fixtures in `tests/conftest.py`:
28
+ - `keycloak` — session fixture (issuer, client_id `plone`, client_secret `12345678910`)
29
+ - `keycloak_api` — admin API fixture (realm `plone-test`, client `plone-admin`, secret `12345678`)
30
+ - `portal` — integration portal with OIDC plugin configured
31
+
32
+ ## Linting & Formatting
33
+
34
+ ```bash
35
+ make check # Lint (runs tox -e lint → pre-commit)
36
+ tox -e format # Auto-format (pyupgrade, isort, black, zpretty)
37
+ ```
38
+
39
+ Pre-commit tools: pyupgrade, isort, black, zpretty, flake8, codespell, check-manifest, pyroma, i18ndude.
40
+
41
+ ## Architecture
42
+
43
+ ### Plugin
44
+
45
+ `KimugPlugin` (in `src/pas/plugins/kimug/plugin/__init__.py`) extends `OIDCPlugin` from `pas.plugins.oidc` and implements:
46
+ - `IExtractionPlugin` — extracts Bearer token from Authorization header
47
+ - `IAuthenticationPlugin` — decodes JWT (currently without signature verification)
48
+ - `IRolesPlugin` — assigns `Member` to all; adds `Manager` if user is in `{application_id}-admin` group AND has `@imio.be` email
49
+ - `IChallengePlugin` — redirects to Keycloak login
50
+
51
+ ### Key Modules
52
+
53
+ | Path | Purpose |
54
+ |------|---------|
55
+ | `plugin/__init__.py` | KimugPlugin PAS plugin class |
56
+ | `utils.py` | Keycloak API integration, JWKS, user migration, settings |
57
+ | `browser/view.py` | Views: Login, Callback, Migration, KeycloakUsers, NewUser, PersonalInformation, ChangePassword |
58
+ | `controlpanel/classic.py` | Control panel adapter and form |
59
+ | `setuphandlers/__init__.py` | GenericSetup post_install handler |
60
+ | `interfaces.py` | IBrowserLayer, IKimugPlugin, IKimugSettings |
61
+ | `testing.py` | INTEGRATION_TESTING, FUNCTIONAL_TESTING, ACCEPTANCE_TESTING layers |
62
+ | `subscribers/configure.zcml` | Calls `set_oidc_settings` on IDatabaseOpenedWithRoot |
63
+
64
+ ### Environment Variables
65
+
66
+ Configuration is read from environment on Zope startup (`set_oidc_settings` subscriber):
67
+
68
+ - `WEBSITE_HOSTNAME` — hostname for redirect_uri
69
+ - `keycloak_url`, `keycloak_realm` (default: `plone`), `keycloak_issuer`
70
+ - `keycloak_client_id` (default: `plone`), `keycloak_client_secret` (default: `12345678910`)
71
+ - `keycloak_admin_user`, `keycloak_admin_password` — for Keycloak admin API
72
+ - `keycloak_allowed_groups` — comma-separated or `[group1, group2]`
73
+ - `keycloak_add_user_url`, `keycloak_personal_information_url`, `keycloak_change_password_url`
74
+ - `application_id` (default: `iA.Smartweb`) — used for admin group matching
75
+
76
+ ### GenericSetup
77
+
78
+ - Default profile at `profiles/default/` (version 1002)
79
+ - Uninstall profile at `profiles/uninstall/`
80
+ - Upgrades in `upgrades/profiles/`
81
+
82
+ ## Key Patterns
83
+
84
+ - **Namespace packages**: `pas` and `pas.plugins` are namespace packages (`setup.py`)
85
+ - **ZCML registration**: `five:registerPackage` with `initialize` function; browser pages, subscribers, controlpanel registered via ZCML includes
86
+ - **z3c.autoinclude**: Plugin auto-discovered by Plone via entry point
87
+ - **Testing layers**: `PloneSandboxLayer` + `PLONE_APP_CONTENTTYPES_FIXTURE`; pytest-plone fixtures; pytest-docker for Keycloak
88
+ - **Dependencies**: `pas.plugins.oidc>=2.0.0b4`, `python-keycloak`, `plone.api`
89
+ - Python ≥ 3.10, Plone 6.0/6.1, GPLv2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pas.plugins.kimug
3
- Version: 1.4.2
3
+ Version: 1.4.3
4
4
  Summary: A PAS plugin to set roles to imio keycloak users
5
5
  Home-page: https://github.com/imio/pas.plugins.kimug
6
6
  Author: iMio
@@ -124,6 +124,13 @@ The project is licensed under GPLv2.
124
124
  - iMio [devops@imio.be]
125
125
 
126
126
 
127
+ ## 1.4.3 (2026-03-24)
128
+
129
+
130
+ - DEVOPS-339 : Fix ConflictError when multiple Zope instances start simultaneously and commit OIDC settings
131
+ [remdub]
132
+
133
+
127
134
  ## 1.4.2 (2025-12-10)
128
135
 
129
136
  - Set administrator role for users in group iA.Smartweb-admin with an imio address.
@@ -14,7 +14,7 @@ long_description = f"""
14
14
 
15
15
  setup(
16
16
  name="pas.plugins.kimug",
17
- version="1.4.2",
17
+ version="1.4.3",
18
18
  description="A PAS plugin to set roles to imio keycloak users",
19
19
  long_description=long_description,
20
20
  long_description_content_type="text/markdown",
@@ -3,6 +3,7 @@ from collections import defaultdict
3
3
  from plone import api
4
4
  from Products.PluggableAuthService.interfaces.plugins import IAuthenticationPlugin
5
5
  from urllib.parse import urlparse
6
+ from ZODB.POSException import ConflictError
6
7
  from zope.annotation.interfaces import IAnnotations
7
8
  from zope.component.hooks import setSite
8
9
 
@@ -74,8 +75,15 @@ def set_oidc_settings(context):
74
75
 
75
76
  _set_allowed_groups(oidc)
76
77
 
77
- transaction.commit()
78
- logger.info("OIDC settings set with set_oidc_settings()")
78
+ try:
79
+ transaction.commit()
80
+ logger.info("OIDC settings set with set_oidc_settings()")
81
+ except ConflictError:
82
+ transaction.abort()
83
+ logger.warning(
84
+ "ConflictError committing OIDC settings: another instance already "
85
+ "committed the same values. Settings are correct; skipping."
86
+ )
79
87
  else:
80
88
  logger.warning("Could not find OIDC plugin, not setting OIDC settings")
81
89
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pas.plugins.kimug
3
- Version: 1.4.2
3
+ Version: 1.4.3
4
4
  Summary: A PAS plugin to set roles to imio keycloak users
5
5
  Home-page: https://github.com/imio/pas.plugins.kimug
6
6
  Author: iMio
@@ -124,6 +124,13 @@ The project is licensed under GPLv2.
124
124
  - iMio [devops@imio.be]
125
125
 
126
126
 
127
+ ## 1.4.3 (2026-03-24)
128
+
129
+
130
+ - DEVOPS-339 : Fix ConflictError when multiple Zope instances start simultaneously and commit OIDC settings
131
+ [remdub]
132
+
133
+
127
134
  ## 1.4.2 (2025-12-10)
128
135
 
129
136
  - Set administrator role for users in group iA.Smartweb-admin with an imio address.
@@ -1,5 +1,6 @@
1
1
  .editorconfig
2
2
  CHANGES.md
3
+ CLAUDE.md
3
4
  CONTRIBUTORS.md
4
5
  LICENSE.GPL
5
6
  LICENSE.md
@@ -1,5 +1,7 @@
1
1
  from pas.plugins.kimug import utils
2
2
  from plone import api
3
+ from unittest.mock import patch
4
+ from ZODB.POSException import ConflictError
3
5
  from zope.annotation.interfaces import IAnnotations
4
6
 
5
7
  import os
@@ -122,3 +124,20 @@ class TestUtils:
122
124
  utils._set_allowed_groups(oidc)
123
125
 
124
126
  assert oidc.allowed_groups == ("group.1 is - the first!",)
127
+
128
+
129
+ class TestSetOidcSettings:
130
+ def test_conflict_error_is_handled(self, portal):
131
+ """ConflictError on commit must be caught and transaction aborted — no exception raised."""
132
+ with patch("pas.plugins.kimug.utils.transaction") as mock_txn:
133
+ mock_txn.commit.side_effect = ConflictError()
134
+ utils.set_oidc_settings(None)
135
+ mock_txn.abort.assert_called_once()
136
+
137
+ def test_settings_are_applied(self, portal):
138
+ """set_oidc_settings should apply environment values to the OIDC plugin."""
139
+ with patch.dict(os.environ, {"keycloak_client_id": "my-client"}):
140
+ with patch("pas.plugins.kimug.utils.transaction"):
141
+ utils.set_oidc_settings(None)
142
+ oidc = utils.get_plugin()
143
+ assert oidc.client_id == "my-client"