Skip to content

Commit 89ad9e2

Browse files
committed
[IMP] auth_saml: download the provider metadata
On Office365, what you get when configuring an application for SAML authentication is the URL of the federation metadata document. This URL is stable, but the content of the document is not. I suspect some of the encryption keys can be updated / renewed over time. The result is that the configured provider in Odoo suddenly stops working, because the messages sent by the Office365 provider can no longer be validated by Odoo (because the federation document is out of date). Downloading the new version and updating the auth.saml.provider record fixes the issue. This PR adds a new field to store the URL of the metadata document. When this field is set on a provider, you get a button next to it in the form view to download the document from the URL. The button will not update the document if it has not changed. Additionally, when a SignatureError happens, we check if downloading the document again fixes the issue.
1 parent bf2852e commit 89ad9e2

File tree

4 files changed

+66
-7
lines changed

4 files changed

+66
-7
lines changed

auth_oidc/README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Authentication OpenID Connect
77
!! This file is generated by oca-gen-addon-readme !!
88
!! changes will be overwritten. !!
99
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10-
!! source digest: sha256:71510d7bf0aa7f001922c23a7610ad556deef38538d265989fb70ddc010547d6
10+
!! source digest: sha256:c0b511a2aa2ce3715f6c903369015852d573c98a028d2d9919c6b789578e2d21
1111
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1212
1313
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png

auth_oidc/static/description/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ <h1 class="title">Authentication OpenID Connect</h1>
366366
!! This file is generated by oca-gen-addon-readme !!
367367
!! changes will be overwritten. !!
368368
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
369-
!! source digest: sha256:71510d7bf0aa7f001922c23a7610ad556deef38538d265989fb70ddc010547d6
369+
!! source digest: sha256:c0b511a2aa2ce3715f6c903369015852d573c98a028d2d9919c6b789578e2d21
370370
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
371371
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/server-auth/tree/15.0/auth_oidc"><img alt="OCA/server-auth" src="https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/server-auth-15-0/server-auth-15-0-auth_oidc"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/server-auth&amp;target_branch=15.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
372372
<p>This module allows users to login through an OpenID Connect provider using the

auth_saml/models/auth_saml_provider.py

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,17 @@
99
import tempfile
1010
import urllib.parse
1111

12+
import requests
13+
1214
# dependency name is pysaml2 # pylint: disable=W7936
1315
import saml2
1416
import saml2.xmldsig as ds
1517
from saml2.client import Saml2Client
1618
from saml2.config import Config as Saml2Config
19+
from saml2.sigver import SignatureError
1720

1821
from odoo import api, fields, models
22+
from odoo.exceptions import UserError
1923

2024
_logger = logging.getLogger(__name__)
2125

@@ -42,6 +46,14 @@ class AuthSamlProvider(models.Model):
4246
),
4347
required=True,
4448
)
49+
idp_metadata_url = fields.Char(
50+
string="Identity Provider Metadata URL",
51+
help="Some SAML providers, notably Office365 can have a metadata "
52+
"document which changes over time, and they provide a URL to the "
53+
"document instead. When this field is set, the metadata can be "
54+
"fetched from the provided URL.",
55+
)
56+
4557
sp_baseurl = fields.Text(
4658
string="Override Base URL",
4759
help="""Base URL sent to Odoo with this, rather than automatically
@@ -282,11 +294,24 @@ def _validate_auth_response(self, token: str, base_url: str = None):
282294
self.ensure_one()
283295

284296
client = self._get_client_for_provider(base_url)
285-
response = client.parse_authn_request_response(
286-
token,
287-
saml2.entity.BINDING_HTTP_POST,
288-
self._get_outstanding_requests_dict(),
289-
)
297+
try:
298+
response = client.parse_authn_request_response(
299+
token,
300+
saml2.entity.BINDING_HTTP_POST,
301+
self._get_outstanding_requests_dict(),
302+
)
303+
except SignatureError:
304+
# we have a metadata url: try to refresh the metadata document
305+
if self.idp_metadata_url:
306+
self.action_refresh_metadata_from_url()
307+
# retry: if it fails again, we let the exception flow
308+
response = client.parse_authn_request_response(
309+
token,
310+
saml2.entity.BINDING_HTTP_POST,
311+
self._get_outstanding_requests_dict(),
312+
)
313+
else:
314+
raise
290315
matching_value = None
291316

292317
if self.matching_attribute == "subject.nameId":
@@ -370,3 +395,28 @@ def _hook_validate_auth_response(self, response, matching_value):
370395
vals[attribute.field_name] = attribute_value
371396

372397
return {"mapped_attrs": vals}
398+
399+
def action_refresh_metadata_from_url(self):
400+
providers = self.search(
401+
[("idp_metadata_url", "ilike", "http%"), ("id", "in", self.ids)]
402+
)
403+
if not providers:
404+
return False
405+
# lock the records we might update, so that multiple simultaneous login
406+
# attempts will not cause concurrent updates
407+
self.env.cr.execute(
408+
"SELECT id FROM auth_saml_provider WHERE id in %s FOR UPDATE",
409+
(list(providers.ids),),
410+
)
411+
updated = False
412+
for provider in providers:
413+
document = requests.get(provider.idp_metadata_url)
414+
if document.status_code != 200:
415+
raise UserError(
416+
f"Unable to download the metadata for {provider.name}: {document.reason}"
417+
)
418+
if document.text != provider.idp_metadata:
419+
provider.idp_metadata = document.text
420+
_logger.info("Updated provider metadata for %s", provider.name)
421+
updated = True
422+
return updated

auth_saml/views/auth_saml.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@
7676
</div>
7777
<group name="idp_settings">
7878
<group string="Identity Provider Settings">
79+
<div>
80+
<field name="idp_metadata_url" />
81+
<button
82+
type="object"
83+
string="Refresh"
84+
name="action_refresh_metadata_from_url"
85+
attrs="{'invisible': [('idp_metadata_url', '=', False)]}"
86+
/>
87+
</div>
7988
<label for="idp_metadata" />
8089
<div>
8190
<field

0 commit comments

Comments
 (0)