Initial commit: OCA Server Auth packages (29 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:06 +02:00
commit 3ed80311c4
1325 changed files with 127292 additions and 0 deletions

View file

@ -0,0 +1,178 @@
====================
SAML2 Authentication
====================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:5aa0ecfdde2bcc32865c5da17331096cb58254161938b36003e6f0baf825107c
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github
:target: https://github.com/OCA/server-auth/tree/16.0/auth_saml
:alt: OCA/server-auth
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-auth-16-0/server-auth-16-0-auth_saml
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-auth&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
Let users log into Odoo via an SAML2 identity provider.
This module allows to deport the management of users and passwords in an
external authentication system to provide SSO functionality (Single Sign On)
between Odoo and other applications of your ecosystem.
**Benefits**:
* Reducing the time spent typing different passwords for different accounts.
* Reducing the time spent in IT support for password oversights.
* Centralizing authentication systems.
* Securing all input levels / exit / access to multiple systems without
prompting users.
* The centralization of access control information for compliance testing to
different standards.
**Table of contents**
.. contents::
:local:
Installation
============
This addon requires the python module ``pysaml2``.
``pysaml2`` requires the binary ``xmlsec1`` (on Debian or Ubuntu you can install it with ``apt-get install xmlsec1``)
Configuration
=============
To use this module, you need an IDP server, properly set up.
#. Configure the module according to your IdPs instructions
(Settings > Users & Companies > SAML Providers).
#. Pre-create your users and set the SAML information against the user.
By default, the module let users have both a password and SAML ids.
To increase security, disable passwords by using the option in Settings.
Note that the admin account can still have a password, even if the option is activated.
Setting the option immediately remove all password from users with a configured SAML ids.
If all the users have a SAML id in a single provider, you can set automatic redirection
in the provider settings. The autoredirection will only be done on the active provider
with the highest priority. It is still possible to access the login without redirection
by using the query parameter ``disable_autoredirect``, as in
``https://example.com/web/login?disable_autoredirect=`` The login is also displayed if
there is an error with SAML login, in order to display any error message.
If you are using Office365 as identity provider, set up the federation metadata document
rather than the document itself. This will allow the module to refresh the document when
needed.
Usage
=====
Users can login with the configured SAML IdP with buttons added in the login screen.
Known issues / Roadmap
======================
* clean up ``auth_saml.request``
Changelog
=========
16.0.1.2.1 (2025-05-13)
~~~~~~~~~~~~~~~~~~~~~~~
**Bugfixes**
- Avoid redirecting when there is a SAML error. ()
16.0.1.0.0
~~~~~~~~~~
Initial migration for 16.0.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-auth/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/server-auth/issues/new?body=module:%20auth_saml%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* XCG Consulting
Contributors
~~~~~~~~~~~~
* `XCG Consulting <https://xcg-consulting.fr/>`__:
* Florent Aide <florent.aide@xcg-consulting.fr>
* Vincent Hatakeyama <vincent.hatakeyama@xcg-consulting.fr>
* Alexandre Brun
* Houzéfa Abbasbhay <houzefa.abba@xcg-consulting.fr>
* Szeka Wong <szeka.wong@xcg-consulting.fr>
* Jeremy Co Kim Len <jeremy.cokimlen@vinci-concessions.com>
* Jeffery Chen Fan <jeffery9@gmail.com>
* Bhavesh Odedra <bodedra@opensourceintegrators.com>
* `Tecnativa <https://www.tecnativa.com/>`__:
* Jairo Llopis
* `GlodoUK <https://www.glodo.uk/>`__:
* Karl Southern
* `TAKOBI <https://takobi.online/>`__:
* Lorenzo Battistini
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
.. |maintainer-vincent-hatakeyama| image:: https://github.com/vincent-hatakeyama.png?size=40px
:target: https://github.com/vincent-hatakeyama
:alt: vincent-hatakeyama
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-vincent-hatakeyama|
This module is part of the `OCA/server-auth <https://github.com/OCA/server-auth/tree/16.0/auth_saml>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

@ -0,0 +1 @@
from . import controllers, models

View file

@ -0,0 +1,31 @@
# Copyright (C) 2020 GlodoUK <https://www.glodo.uk/>
# Copyright (C) 2010-2016, 2022 XCG Consulting <http://odoo.consulting>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "SAML2 Authentication",
"version": "16.0.1.2.1",
"category": "Tools",
"author": "XCG Consulting, Odoo Community Association (OCA)",
"maintainers": ["vincent-hatakeyama"],
"website": "https://github.com/OCA/server-auth",
"license": "AGPL-3",
"depends": ["base_setup", "web"],
"external_dependencies": {
"python": ["pysaml2"],
"bin": ["xmlsec1"],
# special definition used by OCA to install packages
"deb": ["xmlsec1"],
},
"demo": [],
"data": [
"data/ir_config_parameter.xml",
"security/ir.model.access.csv",
"views/auth_saml.xml",
"views/res_config_settings.xml",
"views/res_users.xml",
],
"installable": True,
"auto_install": False,
"development_status": "Beta",
}

View file

@ -0,0 +1,3 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import main

View file

@ -0,0 +1,292 @@
# Copyright (C) 2020 GlodoUK <https://www.glodo.uk/>
# Copyright (C) 2010-2016, 2022-2023 XCG Consulting <https://xcg-consulting.fr/>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import functools
import json
import logging
import werkzeug.utils
from werkzeug.exceptions import BadRequest
from werkzeug.urls import url_quote_plus
from odoo import (
SUPERUSER_ID,
_,
api,
exceptions,
http,
models,
registry as registry_get,
)
from odoo.http import request
from odoo.tools.misc import clean_context
from odoo.addons.web.controllers.home import Home
from odoo.addons.web.controllers.utils import _get_login_redirect_url, ensure_db
_logger = logging.getLogger(__name__)
# ----------------------------------------------------------
# helpers
# ----------------------------------------------------------
def fragment_to_query_string(func):
@functools.wraps(func)
def wrapper(self, **kw):
if not kw:
return """<html><head><script>
var l = window.location;
var q = l.hash.substring(1);
var r = '/' + l.search;
if(q.length !== 0) {
var s = l.search ? (l.search === '?' ? '' : '&') : '?';
r = l.pathname + l.search + s + q;
}
window.location = r;
</script></head><body></body></html>"""
return func(self, **kw)
return wrapper
# ----------------------------------------------------------
# Controller
# ----------------------------------------------------------
class SAMLLogin(Home):
# Disable pylint self use as the method is meant to be reused in other modules
def _list_saml_providers_domain(self): # pylint: disable=no-self-use
return []
def list_saml_providers(self, with_autoredirect: bool = False) -> models.Model:
"""Return available providers
:param with_autoredirect: True to only list providers with automatic redirection
:return: a recordset of providers
"""
domain = self._list_saml_providers_domain()
if with_autoredirect:
domain.append(("autoredirect", "=", True))
providers = request.env["auth.saml.provider"].sudo().search_read(domain)
for provider in providers:
provider["auth_link"] = self._auth_saml_request_link(provider)
return providers
def _saml_autoredirect(self):
# automatically redirect if any provider is set up to do that
autoredirect_providers = self.list_saml_providers(True)
# do not redirect if asked too or if a SAML error has been found
disable_autoredirect = (
"disable_autoredirect" in request.params or "saml_error" in request.params
)
if autoredirect_providers and not disable_autoredirect:
return werkzeug.utils.redirect(
self._auth_saml_request_link(autoredirect_providers[0]),
303,
)
return None
def _auth_saml_request_link(self, provider: models.Model):
"""Return the auth request link for the provided provider"""
params = {
"pid": provider["id"],
}
redirect = request.params.get("redirect")
if redirect:
params["redirect"] = redirect
return "/auth_saml/get_auth_request?%s" % werkzeug.urls.url_encode(params)
@http.route()
def web_client(self, s_action=None, **kw):
ensure_db()
if not request.session.uid:
result = self._saml_autoredirect()
if result:
return result
return super().web_client(s_action, **kw)
@http.route()
def web_login(self, *args, **kw):
ensure_db()
if (
request.httprequest.method == "GET"
and request.session.uid
and request.params.get("redirect")
):
# Redirect if already logged in and redirect param is present
return request.redirect(request.params.get("redirect"))
if request.httprequest.method == "GET":
result = self._saml_autoredirect()
if result:
return result
providers = self.list_saml_providers()
response = super().web_login(*args, **kw)
if response.is_qweb:
error = request.params.get("saml_error")
if error == "no-signup":
error = _("Sign up is not allowed on this database.")
elif error == "access-denied":
error = _("Access Denied")
elif error == "expired":
error = _(
"You do not have access to this database. Please contact"
" support."
)
else:
error = None
response.qcontext["saml_providers"] = providers
if error:
response.qcontext["error"] = error
return response
class AuthSAMLController(http.Controller):
def _get_saml_extra_relaystate(self):
"""
Compute any additional extra state to be sent to the IDP so it can
forward it back to us. This is called RelayState.
The provider will automatically set things like the dbname, provider
id, etc.
"""
redirect = request.params.get("redirect") or "web"
if not redirect.startswith(("//", "http://", "https://")):
redirect = "{}{}".format(
request.httprequest.url_root,
redirect[1:] if redirect[0] == "/" else redirect,
)
state = {
"r": url_quote_plus(redirect),
}
return state
@http.route("/auth_saml/get_auth_request", type="http", auth="none")
def get_auth_request(self, pid):
provider_id = int(pid)
provider = request.env["auth.saml.provider"].sudo().browse(provider_id)
redirect_url = provider._get_auth_request(
self._get_saml_extra_relaystate(), request.httprequest.url_root.rstrip("/")
)
if not redirect_url:
raise Exception(
"Failed to get auth request from provider. "
"Either misconfigured SAML provider or unknown provider."
)
redirect = werkzeug.utils.redirect(redirect_url, 303)
redirect.autocorrect_location_header = True
return redirect
@http.route("/auth_saml/signin", type="http", auth="none", csrf=False)
@fragment_to_query_string
def signin(self, **kw):
"""
Client obtained a saml token and passed it back
to us... we need to validate it
"""
saml_response = kw.get("SAMLResponse")
if not kw.get("RelayState"):
# here we are in front of a client that went through
# some routes that "lost" its relaystate... this can happen
# if the client visited his IDP and successfully logged in
# then the IDP gave him a portal with his available applications
# but the provided link does not include the necessary relaystate
url = "/?type=signup"
redirect = werkzeug.utils.redirect(url, 303)
redirect.autocorrect_location_header = True
return redirect
state = json.loads(kw["RelayState"])
provider = state["p"]
dbname = state["d"]
if not http.db_filter([dbname]):
return BadRequest()
ensure_db(db=dbname)
request.update_context(**clean_context(state.get("c", {})))
try:
credentials = (
request.env["res.users"]
.with_user(SUPERUSER_ID)
.auth_saml(
provider,
saml_response,
request.httprequest.url_root.rstrip("/"),
)
)
action = state.get("a")
menu = state.get("m")
redirect = (
werkzeug.urls.url_unquote_plus(state["r"]) if state.get("r") else False
)
url = "/web"
if redirect:
url = redirect
elif action:
url = "/#action=%s" % action
elif menu:
url = "/#menu_id=%s" % menu
pre_uid = request.session.authenticate(*credentials)
resp = request.redirect(_get_login_redirect_url(pre_uid, url), 303)
resp.autocorrect_location_header = False
return resp
except exceptions.AccessDenied:
# saml credentials not valid,
# user could be on a temporary session
_logger.info("SAML2: access denied")
url = "/web/login?saml_error=expired"
redirect = werkzeug.utils.redirect(url, 303)
redirect.autocorrect_location_header = False
return redirect
except Exception as e:
# signup error
_logger.exception("SAML2: failure - %s", str(e))
url = "/web/login?saml_error=access-denied"
redirect = request.redirect(url, 303)
redirect.autocorrect_location_header = False
return redirect
@http.route("/auth_saml/metadata", type="http", auth="none", csrf=False)
def saml_metadata(self, **kw):
provider = kw.get("p")
dbname = kw.get("d")
valid = kw.get("valid", None)
if not dbname or not provider:
_logger.debug("Metadata page asked without database name or provider id")
return request.not_found(_("Missing parameters"))
provider = int(provider)
registry = registry_get(dbname)
with registry.cursor() as cr:
env = api.Environment(cr, SUPERUSER_ID, {})
client = env["auth.saml.provider"].sudo().browse(provider)
if not client.exists():
return request.not_found(_("Unknown provider"))
return request.make_response(
client._metadata_string(
valid, request.httprequest.url_root.rstrip("/")
),
[("Content-Type", "text/xml")],
)

View file

@ -0,0 +1,7 @@
<?xml version="1.0" ?>
<odoo noupdate="1">
<record id="allow_saml_uid_and_internal_password" model="ir.config_parameter">
<field name="key">auth_saml.allow_saml_uid_and_internal_password</field>
<field name="value">True</field>
</record>
</odoo>

View file

@ -0,0 +1,554 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * auth_saml
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.providers
msgid "- or -"
msgstr ""
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Access Denied"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__active
msgid "Active"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__css_class
msgid "Add a CSS class that serves you to style the login button."
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Algorithm used to sign requests."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_config_settings__allow_saml_uid_and_internal_password
msgid ""
"Allow SAML users to possess an Odoo password (warning: decreases security)"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.auth_saml_provider_view_search
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Archived"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__attribute_mapping_ids
msgid "Attribute Mapping"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__matching_attribute
msgid ""
"Attribute to look for in the returned IDP response to match against an Odoo "
"user."
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Attribute to match the user in Odoo with against the IDP (Identity "
"Provider). You may use the special case \"subject.nameId\" to match against "
"the nameId in the IDP response."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__authn_requests_signed
msgid "Authn Requests Signed"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__autoredirect
msgid "Automatic Redirection"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Available after first save. The URL will change if the provider is deleted "
"&amp; recreated or the database is renamed."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sp_baseurl
msgid ""
"Base URL sent to Odoo with this, rather than automatically\n"
" detecting from request or system parameter web.base.url"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__css_class
msgid "Button Icon CSS class"
msgstr ""
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_config_settings
msgid "Config Settings"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__idp_metadata
msgid ""
"Configuration for this Identity Provider. Supplied by the provider, in XML "
"format."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__create_uid
msgid "Created by"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__create_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__create_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__create_date
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__create_date
msgid "Created on"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__saml_request_id
msgid "Current Request ID"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_access_token
msgid "Current SAML token for this user"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__display_name
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__display_name
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__display_name
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__display_name
msgid "Display Name"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Display Settings"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__entity_id
msgid "Entity ID"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Entity Identifier sent to the IDP. Often this would be the metadata URL, but"
" it can be any string."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__entity_id
msgid "EntityID passed to IDP, used to identify the Odoo"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__matching_attribute_to_lower
msgid "Force matching_attribute to lower case before passing back to Odoo."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__id
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__id
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__id
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__id
msgid "ID"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__attribute_name
msgid "IDP Response Attribute"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__idp_metadata
msgid "Identity Provider Metadata"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__idp_metadata_url
msgid "Identity Provider Metadata URL"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Identity Provider Settings"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__matching_attribute
msgid "Identity Provider matching attribute"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "If you provider gives you a URL, use this field preferably"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__authn_requests_signed
msgid ""
"Indicates if the Authentication Requests sent by this SP should be signed by"
" default."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_assertions_signed
msgid "Indicates if this SP wants the IdP to send the assertions signed."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__logout_requests_signed
msgid ""
"Indicates if this entity will sign the Logout Requests originated from it."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_response_signed
msgid "Indicates that Authentication Responses to this SP must be signed."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_assertions_or_response_signed
msgid ""
"Indicates that either the Authentication Response or the assertions "
"contained within the response to this SP must be signed."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping____last_update
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider____last_update
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request____last_update
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml____last_update
msgid "Last Modified on"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__write_uid
msgid "Last Updated by"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__write_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__write_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__write_date
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__write_date
msgid "Last Updated on"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__body
msgid "Link text in Login Dialog"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__body
msgid "Login button label"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__logout_requests_signed
msgid "Logout Requests Signed"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__matching_attribute_to_lower
msgid "Lowercase IDP Matching Attribute"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Mapped attributes are copied from the SAML response at every logon, if "
"available. If multiple values are returned (i.e. a list) then the first "
"value is used."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_metadata_url
msgid "Metadata URL"
msgstr ""
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Missing parameters"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__field_name
msgid "Odoo Field"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_private
msgid "Odoo Private Key"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_private_filename
msgid "Odoo Private Key File Name"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_public
msgid "Odoo Public Certificate"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_public_filename
msgid "Odoo Public Certificate File Name"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Odoo Settings"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__autoredirect
msgid ""
"Only the provider with the higher priority will be automatically redirected"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_baseurl
msgid "Override Base URL"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__provider_id
msgid "Provider"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__name
msgid "Provider Name"
msgstr ""
#. module: auth_saml
#: model:ir.actions.act_window,name:auth_saml.action_saml_provider
#: model_terms:ir.ui.view,arch_db:auth_saml.auth_saml_provider_view_search
msgid "Providers"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Refresh"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_users_form
msgid "SAML"
msgstr ""
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_request
msgid "SAML Outstanding Requests"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_provider_id
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "SAML Provider"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__saml_provider_id
msgid "SAML Provider that issued the token"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_res_users_saml__saml_uid
msgid "SAML Provider user_id"
msgstr ""
#. module: auth_saml
#: model:ir.ui.menu,name:auth_saml.menu_saml_providers
msgid "SAML Providers"
msgstr ""
#. module: auth_saml
#: model:ir.model.constraint,message:auth_saml.constraint_res_users_saml_uniq_users_saml_provider_saml_uid
msgid "SAML UID must be unique per provider"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_uid
msgid "SAML User ID"
msgstr ""
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_provider
msgid "SAML2 Provider"
msgstr ""
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_attribute_mapping
msgid "SAML2 attribute mapping"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users__saml_ids
msgid "Saml"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sequence
msgid "Sequence"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sign_authenticate_requests
msgid "Sign Authenticate Requests"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sign_metadata
msgid "Sign Metadata"
msgstr ""
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Sign up is not allowed on this database."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sig_alg
msgid "Signature Algorithm"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__idp_metadata_url
msgid ""
"Some SAML providers, notably Office365 can have a metadata document which "
"changes over time, and they provide a URL to the document instead. When this"
" field is set, the metadata can be fetched from the provided URL."
msgstr ""
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_ir_config_parameter
msgid "System Parameter"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"The URL configured for the ACS must exactly match what is sent. If you have "
"odoo responding on multiple URLs you can use this to force it to send a "
"specific address rather than rely on automatically detecting."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_res_users_saml__saml_access_token
msgid "The current SAML token in use"
msgstr ""
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/models/res_users.py:0
#, python-format
msgid ""
"This database disallows users to have both passwords and SAML IDs. Error for"
" logins %s"
msgstr ""
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Unknown provider"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Used to sign requests sent to the IDP. You can use openssl to generate a "
"certificate and key."
msgstr ""
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_users
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__user_id
msgid "User"
msgstr ""
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_users_saml
msgid "User to SAML Provider Mapping"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_assertions_or_response_signed
msgid "Want Assertions Or Response Signed"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_assertions_signed
msgid "Want Assertions Signed"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_response_signed
msgid "Want Response Signed"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sign_metadata
msgid "Whether metadata should be signed or not"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sign_authenticate_requests
msgid "Whether the request should be signed or not"
msgstr ""
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "You do not have access to this database. Please contact support."
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Your ACS url will be base_url + /auth_saml/signin"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Your provider will give you this XML once configured."
msgstr ""

View file

@ -0,0 +1,554 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * auth_saml
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.providers
msgid "- or -"
msgstr "— ili —"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Access Denied"
msgstr "Pristup Odbijen"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__active
msgid "Active"
msgstr "Aktivan"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__css_class
msgid "Add a CSS class that serves you to style the login button."
msgstr "Dodajte CSS klasu koja vam služi za stiliziranje dugmeta za prijavu."
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Algorithm used to sign requests."
msgstr "Algoritam koji se koristi za potpisivanje zahtjeva."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_config_settings__allow_saml_uid_and_internal_password
msgid ""
"Allow SAML users to possess an Odoo password (warning: decreases security)"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.auth_saml_provider_view_search
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Archived"
msgstr "Arhivirano"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__attribute_mapping_ids
msgid "Attribute Mapping"
msgstr "Mapiranje atributa"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__matching_attribute
msgid ""
"Attribute to look for in the returned IDP response to match against an Odoo "
"user."
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Attribute to match the user in Odoo with against the IDP (Identity "
"Provider). You may use the special case \"subject.nameId\" to match against "
"the nameId in the IDP response."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__authn_requests_signed
msgid "Authn Requests Signed"
msgstr "Authn zahtjevi potpisani"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__autoredirect
msgid "Automatic Redirection"
msgstr "Automatsko preusmjeravanje"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Available after first save. The URL will change if the provider is deleted "
"&amp; recreated or the database is renamed."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sp_baseurl
msgid ""
"Base URL sent to Odoo with this, rather than automatically\n"
" detecting from request or system parameter web.base.url"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__css_class
msgid "Button Icon CSS class"
msgstr "CSS klasa ikone dugmeta"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_config_settings
msgid "Config Settings"
msgstr "Postavke"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__idp_metadata
msgid ""
"Configuration for this Identity Provider. Supplied by the provider, in XML "
"format."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__create_uid
msgid "Created by"
msgstr "Kreirao"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__create_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__create_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__create_date
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__create_date
msgid "Created on"
msgstr "Kreirano"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__saml_request_id
msgid "Current Request ID"
msgstr "ID trenutnog zahtjeva"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_access_token
msgid "Current SAML token for this user"
msgstr "Trenutni SAML token za ovog korisnika"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__display_name
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__display_name
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__display_name
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__display_name
msgid "Display Name"
msgstr "Prikazani naziv"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Display Settings"
msgstr "Postavke prikaza"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__entity_id
msgid "Entity ID"
msgstr "Entity ID"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Entity Identifier sent to the IDP. Often this would be the metadata URL, but"
" it can be any string."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__entity_id
msgid "EntityID passed to IDP, used to identify the Odoo"
msgstr "EntityID proslijeđen IDP-u, koristi se za identifikaciju Odoo-a"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__matching_attribute_to_lower
msgid "Force matching_attribute to lower case before passing back to Odoo."
msgstr "Forsiraj matching_attribute na mala slova prije vraćanja u Odoo."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__id
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__id
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__id
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__id
msgid "ID"
msgstr "ID"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__attribute_name
msgid "IDP Response Attribute"
msgstr "IDP atribut odgovora"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__idp_metadata
msgid "Identity Provider Metadata"
msgstr "Metapodaci Identity Provider-a"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__idp_metadata_url
msgid "Identity Provider Metadata URL"
msgstr "URL metapodataka Identity Provider-a"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Identity Provider Settings"
msgstr "Postavke Identity Provider-a"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__matching_attribute
msgid "Identity Provider matching attribute"
msgstr "Identity Provider poklapajući atribut"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "If you provider gives you a URL, use this field preferably"
msgstr "Ako vam davatelj daje URL, koristite ovo polje prvenstveno"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__authn_requests_signed
msgid ""
"Indicates if the Authentication Requests sent by this SP should be signed by"
" default."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_assertions_signed
msgid "Indicates if this SP wants the IdP to send the assertions signed."
msgstr "Označava da li ovaj SP želi da IdP pošalje potpisane tvrdnje."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__logout_requests_signed
msgid ""
"Indicates if this entity will sign the Logout Requests originated from it."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_response_signed
msgid "Indicates that Authentication Responses to this SP must be signed."
msgstr "Označava da odgovori za autentifikaciju ovom SP-u moraju biti potpisani."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_assertions_or_response_signed
msgid ""
"Indicates that either the Authentication Response or the assertions "
"contained within the response to this SP must be signed."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping____last_update
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider____last_update
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request____last_update
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml____last_update
msgid "Last Modified on"
msgstr "Zadnje mijenjano"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__write_uid
msgid "Last Updated by"
msgstr "Zadnji ažurirao"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__write_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__write_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__write_date
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__write_date
msgid "Last Updated on"
msgstr "Zadnje ažurirano"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__body
msgid "Link text in Login Dialog"
msgstr "tekst linka u dijalogu prijave"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__body
msgid "Login button label"
msgstr "Natpis na gumbu za prijavu"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__logout_requests_signed
msgid "Logout Requests Signed"
msgstr "Logout zahtjevi potpisani"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__matching_attribute_to_lower
msgid "Lowercase IDP Matching Attribute"
msgstr "Mala slova IDP poklapajućeg atributa"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Mapped attributes are copied from the SAML response at every logon, if "
"available. If multiple values are returned (i.e. a list) then the first "
"value is used."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_metadata_url
msgid "Metadata URL"
msgstr "Metadata URL"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Missing parameters"
msgstr "Nedostaju parametri"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__field_name
msgid "Odoo Field"
msgstr "Polje"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_private
msgid "Odoo Private Key"
msgstr "Odoo privatni ključ"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_private_filename
msgid "Odoo Private Key File Name"
msgstr "Naziv datoteke Odoo privatnog ključa"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_public
msgid "Odoo Public Certificate"
msgstr "Odoo javni certifikat"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_public_filename
msgid "Odoo Public Certificate File Name"
msgstr "Naziv datoteke Odoo javnog certifikata"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Odoo Settings"
msgstr "Odoo postavke"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__autoredirect
msgid ""
"Only the provider with the higher priority will be automatically redirected"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_baseurl
msgid "Override Base URL"
msgstr "Prebriši osnovni URL"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__provider_id
msgid "Provider"
msgstr "Davatelj"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__name
msgid "Provider Name"
msgstr "Naziv davatelja"
#. module: auth_saml
#: model:ir.actions.act_window,name:auth_saml.action_saml_provider
#: model_terms:ir.ui.view,arch_db:auth_saml.auth_saml_provider_view_search
msgid "Providers"
msgstr "Pružatelji"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Refresh"
msgstr "Osvježi"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_users_form
msgid "SAML"
msgstr "SAML"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_request
msgid "SAML Outstanding Requests"
msgstr "SAML neriješeni zahtjevi"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_provider_id
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "SAML Provider"
msgstr "SAML davatelj"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__saml_provider_id
msgid "SAML Provider that issued the token"
msgstr "SAML davatelj koji je izdao token"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_res_users_saml__saml_uid
msgid "SAML Provider user_id"
msgstr "SAML davatelj user_id"
#. module: auth_saml
#: model:ir.ui.menu,name:auth_saml.menu_saml_providers
msgid "SAML Providers"
msgstr "SAML davatelji"
#. module: auth_saml
#: model:ir.model.constraint,message:auth_saml.constraint_res_users_saml_uniq_users_saml_provider_saml_uid
msgid "SAML UID must be unique per provider"
msgstr "SAML UID mora biti jedinstven po davatelju"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_uid
msgid "SAML User ID"
msgstr "SAML ID korisnika"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_provider
msgid "SAML2 Provider"
msgstr "SAML2 davatelj"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_attribute_mapping
msgid "SAML2 attribute mapping"
msgstr "SAML2 mapiranje atributa"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users__saml_ids
msgid "Saml"
msgstr "Saml"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sequence
msgid "Sequence"
msgstr "Sekvenca"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sign_authenticate_requests
msgid "Sign Authenticate Requests"
msgstr "Potpiši zahtjeve za autentifikaciju"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sign_metadata
msgid "Sign Metadata"
msgstr "Potpiši metapodatke"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Sign up is not allowed on this database."
msgstr "Prijava nije dozvoljena na ovoj bazi."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sig_alg
msgid "Signature Algorithm"
msgstr "Algoritam potpisa"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__idp_metadata_url
msgid ""
"Some SAML providers, notably Office365 can have a metadata document which "
"changes over time, and they provide a URL to the document instead. When this"
" field is set, the metadata can be fetched from the provided URL."
msgstr ""
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_ir_config_parameter
msgid "System Parameter"
msgstr "Sistemski parametar"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"The URL configured for the ACS must exactly match what is sent. If you have "
"odoo responding on multiple URLs you can use this to force it to send a "
"specific address rather than rely on automatically detecting."
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_res_users_saml__saml_access_token
msgid "The current SAML token in use"
msgstr "Trenutni SAML token u upotrebi"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/models/res_users.py:0
#, python-format
msgid ""
"This database disallows users to have both passwords and SAML IDs. Error for"
" logins %s"
msgstr ""
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Unknown provider"
msgstr "Nepoznat davatelj"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Used to sign requests sent to the IDP. You can use openssl to generate a "
"certificate and key."
msgstr ""
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_users
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__user_id
msgid "User"
msgstr "Korisnik"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_users_saml
msgid "User to SAML Provider Mapping"
msgstr "Korisnik na SAML davatelj mapiranje"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_assertions_or_response_signed
msgid "Want Assertions Or Response Signed"
msgstr "Želim potpisane tvrdnje ili odgovor"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_assertions_signed
msgid "Want Assertions Signed"
msgstr "Želim potpisane tvrdnje"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_response_signed
msgid "Want Response Signed"
msgstr "Želim potpisan odgovor"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sign_metadata
msgid "Whether metadata should be signed or not"
msgstr "Da li metadata treba biti potpisana ili ne"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sign_authenticate_requests
msgid "Whether the request should be signed or not"
msgstr "Da li zahtjev treba biti potpisan ili ne"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "You do not have access to this database. Please contact support."
msgstr "Nemate pristup ovoj bazi podataka. Molimo kontaktirajte podršku."
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Your ACS url will be base_url + /auth_saml/signin"
msgstr "Vaš ACS url će biti base_url + /auth_saml/signin"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Your provider will give you this XML once configured."
msgstr "Vaš davatelj će vam dati ovaj XML jednom kada je konfiguriran."

View file

@ -0,0 +1,595 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * auth_saml
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-09-02 19:25+0000\n"
"Last-Translator: Ivorra78 <informatica@totmaterial.es>\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.providers
msgid "- or -"
msgstr "- o -"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Access Denied"
msgstr "Acceso Denegado"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__active
msgid "Active"
msgstr "Activo"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__css_class
msgid "Add a CSS class that serves you to style the login button."
msgstr ""
"Añade una clase CSS que te sirva para dar estilo al botón de inicio de "
"sesión."
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Algorithm used to sign requests."
msgstr "Algoritmo utilizado para firmar las solicitudes."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_config_settings__allow_saml_uid_and_internal_password
msgid ""
"Allow SAML users to possess an Odoo password (warning: decreases security)"
msgstr ""
"Permitir a los usuarios SAML poseer una contraseña Odoo (advertencia: "
"disminuye la seguridad)"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.auth_saml_provider_view_search
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Archived"
msgstr "Archivado"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__attribute_mapping_ids
msgid "Attribute Mapping"
msgstr "Mapeo de atributos"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__matching_attribute
msgid ""
"Attribute to look for in the returned IDP response to match against an Odoo "
"user."
msgstr ""
"Atributo a buscar en la respuesta IDP devuelta para comparar con un usuario "
"Odoo."
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Attribute to match the user in Odoo with against the IDP (Identity "
"Provider). You may use the special case \"subject.nameId\" to match against "
"the nameId in the IDP response."
msgstr ""
"Atributo para comparar el usuario en Odoo con el IDP (Proveedor de "
"Identidad). Puede usar el caso especial \"subject.nameId\" para comparar con "
"el nameId en la respuesta IDP."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__authn_requests_signed
msgid "Authn Requests Signed"
msgstr "Solicitudes de autenticación firmadas"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__autoredirect
msgid "Automatic Redirection"
msgstr "Redireccionamiento automático"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Available after first save. The URL will change if the provider is deleted "
"&amp; recreated or the database is renamed."
msgstr ""
"Disponible después de guardar por primera vez. La URL cambiará si se elimina "
"el proveedor &amp; se vuelve a crear o se cambia el nombre de la base de "
"datos."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sp_baseurl
msgid ""
"Base URL sent to Odoo with this, rather than automatically\n"
" detecting from request or system parameter web.base.url"
msgstr ""
"URL base enviada a Odoo con esto, en lugar de automáticamente\n"
" detectando desde la petición o el parámetro del sistema web.base.url"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__css_class
msgid "Button Icon CSS class"
msgstr "Clase CSS de icono de botón"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_config_settings
msgid "Config Settings"
msgstr "Ajustes de Configuración"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__idp_metadata
msgid ""
"Configuration for this Identity Provider. Supplied by the provider, in XML "
"format."
msgstr ""
"Configuración de este proveedor de identidades. Proporcionada por el "
"proveedor, en formato XML."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__create_uid
msgid "Created by"
msgstr "Creado por"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__create_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__create_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__create_date
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__create_date
msgid "Created on"
msgstr "Creado el"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__saml_request_id
msgid "Current Request ID"
msgstr "ID de solicitud actual"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_access_token
msgid "Current SAML token for this user"
msgstr "Ficha SAML actual para este usuario"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__display_name
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__display_name
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__display_name
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__display_name
msgid "Display Name"
msgstr "Mostrar Nombre"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Display Settings"
msgstr "Configuración de la pantalla"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__entity_id
msgid "Entity ID"
msgstr "ID de entidad"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Entity Identifier sent to the IDP. Often this would be the metadata URL, but "
"it can be any string."
msgstr ""
"Identificador de entidad enviado al IDP. Suele ser la URL de los metadatos, "
"pero puede ser cualquier cadena."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__entity_id
msgid "EntityID passed to IDP, used to identify the Odoo"
msgstr "EntityID pasado a IDP, utilizado para identificar el Odoo"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__matching_attribute_to_lower
msgid "Force matching_attribute to lower case before passing back to Odoo."
msgstr "Forzar matching_attribute a minúsculas antes de pasar de nuevo a Odoo."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__id
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__id
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__id
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__id
msgid "ID"
msgstr "ID (identificación)"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__attribute_name
msgid "IDP Response Attribute"
msgstr "Atributo de la respuesta del PDI"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__idp_metadata
msgid "Identity Provider Metadata"
msgstr "Metadatos del proveedor de identidad"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__idp_metadata_url
msgid "Identity Provider Metadata URL"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Identity Provider Settings"
msgstr "Configuración del proveedor de identidades"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__matching_attribute
msgid "Identity Provider matching attribute"
msgstr "Atributo de coincidencia del proveedor de identidad"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "If you provider gives you a URL, use this field preferably"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__authn_requests_signed
msgid ""
"Indicates if the Authentication Requests sent by this SP should be signed by "
"default."
msgstr ""
"Indica si las Solicitudes de Autenticación enviadas por este SP deben ser "
"firmadas por defecto."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_assertions_signed
msgid "Indicates if this SP wants the IdP to send the assertions signed."
msgstr "Indica si este SP desea que el IdP envíe las aserciones firmadas."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__logout_requests_signed
msgid ""
"Indicates if this entity will sign the Logout Requests originated from it."
msgstr ""
"Indica si esta entidad firmará las Solicitudes de Cierre de Sesión "
"originadas desde ella."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_response_signed
msgid "Indicates that Authentication Responses to this SP must be signed."
msgstr ""
"Indica que las respuestas de autenticación a este SP deben estar firmadas."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_assertions_or_response_signed
msgid ""
"Indicates that either the Authentication Response or the assertions "
"contained within the response to this SP must be signed."
msgstr ""
"Indica que la respuesta de autenticación o las afirmaciones contenidas en la "
"respuesta a este SP deben estar firmadas."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping____last_update
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider____last_update
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request____last_update
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml____last_update
msgid "Last Modified on"
msgstr "Última Modificación el"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__write_uid
msgid "Last Updated by"
msgstr "Última actualización por"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__write_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__write_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__write_date
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__write_date
msgid "Last Updated on"
msgstr "Última Actualización el"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__body
msgid "Link text in Login Dialog"
msgstr "Texto de enlace en el diálogo de inicio de sesión"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__body
msgid "Login button label"
msgstr "Texto de enlace en el diálogo de inicio de sesión"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__logout_requests_signed
msgid "Logout Requests Signed"
msgstr "Solicitudes de cierre de sesión firmadas"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__matching_attribute_to_lower
msgid "Lowercase IDP Matching Attribute"
msgstr "Atributo de coincidencia de IDP en minúsculas"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Mapped attributes are copied from the SAML response at every logon, if "
"available. If multiple values are returned (i.e. a list) then the first "
"value is used."
msgstr ""
"Los atributos asignados se copian de la respuesta SAML en cada inicio de "
"sesión, si están disponibles. Si se devuelven varios valores (es decir, una "
"lista), se utiliza el primero."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_metadata_url
msgid "Metadata URL"
msgstr "URL de metadatos"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Missing parameters"
msgstr "Parámetros que faltan"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__field_name
msgid "Odoo Field"
msgstr "Campo de Odoo"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_private
msgid "Odoo Private Key"
msgstr "Clave privada de Odoo"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_private_filename
msgid "Odoo Private Key File Name"
msgstr "Nombre de archivo de la clave privada de Odoo"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_public
msgid "Odoo Public Certificate"
msgstr "Certificado público de Odoo"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_public_filename
msgid "Odoo Public Certificate File Name"
msgstr "Nombre de archivo del certificado público de Odoo"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Odoo Settings"
msgstr "Ajustes Odoo"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__autoredirect
msgid ""
"Only the provider with the higher priority will be automatically redirected"
msgstr "Sólo se redirigirá automáticamente al proveedor con mayor prioridad"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_baseurl
msgid "Override Base URL"
msgstr "Anular URL base"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__provider_id
msgid "Provider"
msgstr "Proveedor"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__name
msgid "Provider Name"
msgstr "Nombre de Proveedor"
#. module: auth_saml
#: model:ir.actions.act_window,name:auth_saml.action_saml_provider
#: model_terms:ir.ui.view,arch_db:auth_saml.auth_saml_provider_view_search
msgid "Providers"
msgstr "Proveedores"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Refresh"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_users_form
msgid "SAML"
msgstr "SAML"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_request
msgid "SAML Outstanding Requests"
msgstr "Solicitudes pendientes SAML"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_provider_id
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "SAML Provider"
msgstr "Proveedor SAML"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__saml_provider_id
msgid "SAML Provider that issued the token"
msgstr "Proveedor SAML que emitió el código"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_res_users_saml__saml_uid
msgid "SAML Provider user_id"
msgstr "ID de usuario del proveedor SAML"
#. module: auth_saml
#: model:ir.ui.menu,name:auth_saml.menu_saml_providers
msgid "SAML Providers"
msgstr "Proveedores SAML"
#. module: auth_saml
#: model:ir.model.constraint,message:auth_saml.constraint_res_users_saml_uniq_users_saml_provider_saml_uid
msgid "SAML UID must be unique per provider"
msgstr "El UID de SAML debe ser único por proveedor"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_uid
msgid "SAML User ID"
msgstr "ID de usuario SAML"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_provider
msgid "SAML2 Provider"
msgstr "Proveedor de SAML2"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_attribute_mapping
msgid "SAML2 attribute mapping"
msgstr "Asignación de atributos SAML2"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users__saml_ids
msgid "Saml"
msgstr "Saml"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sequence
msgid "Sequence"
msgstr "Secuencia"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sign_authenticate_requests
msgid "Sign Authenticate Requests"
msgstr "Firmar Solicitudes de Autenticación"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sign_metadata
msgid "Sign Metadata"
msgstr "Firmar metadatos"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Sign up is not allowed on this database."
msgstr "No está permitido registrarse en esta base de datos."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sig_alg
msgid "Signature Algorithm"
msgstr "Algoritmo de firma"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__idp_metadata_url
msgid ""
"Some SAML providers, notably Office365 can have a metadata document which "
"changes over time, and they provide a URL to the document instead. When this "
"field is set, the metadata can be fetched from the provided URL."
msgstr ""
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_ir_config_parameter
msgid "System Parameter"
msgstr "Parámetro del Sistema"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"The URL configured for the ACS must exactly match what is sent. If you have "
"odoo responding on multiple URLs you can use this to force it to send a "
"specific address rather than rely on automatically detecting."
msgstr ""
"La URL configurada para el ACS debe coincidir exactamente con lo que se "
"envía. Si tiene odoo respondiendo en múltiples URLs puede usar esto para "
"forzarlo a enviar una dirección específica en lugar de confiar en la "
"detección automática."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_res_users_saml__saml_access_token
msgid "The current SAML token in use"
msgstr "El código SAML actual en uso"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/models/res_users.py:0
#, python-format
msgid ""
"This database disallows users to have both passwords and SAML IDs. Error for "
"logins %s"
msgstr ""
"Esta base de datos no permite que los usuarios tengan contraseñas ni ID de "
"SAML. Error al iniciar sesión %s"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Unknown provider"
msgstr "Proveedor desconocido"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Used to sign requests sent to the IDP. You can use openssl to generate a "
"certificate and key."
msgstr ""
"Se utiliza para firmar las solicitudes enviadas al IDP. Puede utilizar "
"openssl para generar un certificado y una clave."
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_users
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__user_id
msgid "User"
msgstr "Usuario"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_users_saml
msgid "User to SAML Provider Mapping"
msgstr "Asignación de usuario a proveedor SAML"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_assertions_or_response_signed
msgid "Want Assertions Or Response Signed"
msgstr "Quiere afirmaciones o respuestas firmadas"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_assertions_signed
msgid "Want Assertions Signed"
msgstr "Desea que se firmen las afirmaciones"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_response_signed
msgid "Want Response Signed"
msgstr "Quiere respuesta firmada"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sign_metadata
msgid "Whether metadata should be signed or not"
msgstr "Si los metadatos deben firmarse o no"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sign_authenticate_requests
msgid "Whether the request should be signed or not"
msgstr "Si la solicitud debe firmarse o no"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "You do not have access to this database. Please contact support."
msgstr ""
"No tiene acceso a esta base de datos. Póngase en contacto con el servicio de "
"asistencia."
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Your ACS url will be base_url + /auth_saml/signin"
msgstr "Su url ACS será base_url + /auth_saml/signin"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Your provider will give you this XML once configured."
msgstr "Su proveedor le proporcionará este XML una vez configurado."

View file

@ -0,0 +1,705 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * auth_saml
# Vincent Lhote-Hatakeyama <vincent.lhote@xcg-consulting.fr>, 2014.
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-03-03 15:51+0000\n"
"PO-Revision-Date: 2024-07-12 15:58+0000\n"
"Last-Translator: mde-spring <79934758+mde-spring@users.noreply.github.com>\n"
"Language-Team: XCG Consulting\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 5.6.2\n"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.providers
msgid "- or -"
msgstr "- ou -"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Access Denied"
msgstr "Accès refusé"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__active
msgid "Active"
msgstr "Actif"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__css_class
msgid "Add a CSS class that serves you to style the login button."
msgstr ""
"Ajoute une classe CSS utile pour mettre en forme le bouton didentification."
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Algorithm used to sign requests."
msgstr "Algorithme utilisé pour signer les requêtes."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_config_settings__allow_saml_uid_and_internal_password
msgid ""
"Allow SAML users to possess an Odoo password (warning: decreases security)"
msgstr ""
"Autoriser les utilisateurs avec SAML à aussi avoir un mot de passe Odoo "
"(attention: abaisse la sécurité)"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.auth_saml_provider_view_search
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Archived"
msgstr "Archivé"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__attribute_mapping_ids
msgid "Attribute Mapping"
msgstr "Correspondance des attributs"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__matching_attribute
msgid ""
"Attribute to look for in the returned IDP response to match against an Odoo "
"user."
msgstr ""
"Attribut retourné par le fournisseur didentité à utiliser pour la "
"correspondance avec un utilisateur Odoo."
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Attribute to match the user in Odoo with against the IDP (Identity "
"Provider). You may use the special case \"subject.nameId\" to match against "
"the nameId in the IDP response."
msgstr ""
"Attribut retourné par le fournisseur didentité à utiliser pour la "
"correspondance avec un utilisateur Odoo. La valeur spéciale «subject."
"nameId» peut être utilisée pour une correspondance avec le nameId de la "
"réponse du fournisseur didentité."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__authn_requests_signed
msgid "Authn Requests Signed"
msgstr "Signature des requêtes Authn"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__autoredirect
msgid "Automatic Redirection"
msgstr "Redirection automatique"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Available after first save. The URL will change if the provider is deleted "
"&amp; recreated or the database is renamed."
msgstr ""
"Disponible après enregistrement. Ladresse universelle changera si le "
"fournisseur est supprimé et recréée ou bien si la base de donnée est "
"renommée."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sp_baseurl
msgid ""
"Base URL sent to Odoo with this, rather than automatically\n"
" detecting from request or system parameter web.base.url"
msgstr ""
"Base de ladresse universelle envoyé à Odoo avec ceci, plutôt que de la "
"détecter depuis la requête ou le paramètre système «web.base.url»"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__css_class
msgid "Button Icon CSS class"
msgstr "Classe CSS pour licône du bouton"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_config_settings
msgid "Config Settings"
msgstr "Paramètres de configuration"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__idp_metadata
msgid ""
"Configuration for this Identity Provider. Supplied by the provider, in XML "
"format."
msgstr ""
"Configuration pour ce fournisseur didentité. Fourni par le fournisseur, au "
"format XML."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__create_uid
msgid "Created by"
msgstr "Créé par"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__create_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__create_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__create_date
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__create_date
msgid "Created on"
msgstr "Créé le"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__saml_request_id
msgid "Current Request ID"
msgstr "Identifiant de la requête courrante"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_access_token
msgid "Current SAML token for this user"
msgstr "Jeton SAML courant de lutilisateur"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__display_name
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__display_name
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__display_name
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__display_name
msgid "Display Name"
msgstr "Nom affiché"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Display Settings"
msgstr "Paramètres daffichage"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__entity_id
msgid "Entity ID"
msgstr "Identifiant de lentité"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Entity Identifier sent to the IDP. Often this would be the metadata URL, but "
"it can be any string."
msgstr ""
"Identifiant de lentité envoyé au fournisseur didentité. Cest souvent "
"lURL des métadonnées, mais peut être toute chaîne."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__entity_id
msgid "EntityID passed to IDP, used to identify the Odoo"
msgstr ""
"EntityID envoyé au fournisseur didentité, utilisé pour identifier cet Odoo"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__matching_attribute_to_lower
msgid "Force matching_attribute to lower case before passing back to Odoo."
msgstr ""
"Forcer le bas de casse pour les correspondances dattribut avant de les "
"envoyer à Odoo."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__id
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__id
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__id
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__id
msgid "ID"
msgstr "Identifiant"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__attribute_name
msgid "IDP Response Attribute"
msgstr "Attribut de réponse du FI"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__idp_metadata
msgid "Identity Provider Metadata"
msgstr "Métadonnées du fournisseur didentité"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__idp_metadata_url
msgid "Identity Provider Metadata URL"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Identity Provider Settings"
msgstr "Paramètres du fournisseur didentité"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__matching_attribute
msgid "Identity Provider matching attribute"
msgstr "Attribut de correspondance du fournisseur didentité"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "If you provider gives you a URL, use this field preferably"
msgstr ""
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__authn_requests_signed
msgid ""
"Indicates if the Authentication Requests sent by this SP should be signed by "
"default."
msgstr ""
"Indique si les requêtes dauthentification envoyé par ce fournisseur de "
"service doivent être signées par défaut."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_assertions_signed
msgid "Indicates if this SP wants the IdP to send the assertions signed."
msgstr ""
"Indique si le fournisseur de service veut que le fournisseur didentité "
"signe les assertions."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__logout_requests_signed
msgid ""
"Indicates if this entity will sign the Logout Requests originated from it."
msgstr ""
"Indique si cet entité signera les requêtes de déconnexion dont il est "
"lorigine."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_response_signed
msgid "Indicates that Authentication Responses to this SP must be signed."
msgstr ""
"Indique que les réponses dauthentification de ce fournisseur de service "
"doivent être signées."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_assertions_or_response_signed
msgid ""
"Indicates that either the Authentication Response or the assertions "
"contained within the response to this SP must be signed."
msgstr ""
"Indique que soit les réponses dauthentification soit les assertions contenu "
"dans la réponse envoyé à ce fournisseur de service doivent être signées."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping____last_update
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider____last_update
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request____last_update
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml____last_update
msgid "Last Modified on"
msgstr "Dernière modification le"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__write_uid
msgid "Last Updated by"
msgstr "Dernière modification par"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__write_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__write_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__write_date
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__write_date
msgid "Last Updated on"
msgstr "Dernière modification le"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__body
msgid "Link text in Login Dialog"
msgstr "Libellé du bouton de connexion"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__body
msgid "Login button label"
msgstr "Libellé du bouton de connexion"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__logout_requests_signed
msgid "Logout Requests Signed"
msgstr "Signature des requêtes de déconnexion"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__matching_attribute_to_lower
msgid "Lowercase IDP Matching Attribute"
msgstr "Bas de casse de lattribut de correspondance du FI"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Mapped attributes are copied from the SAML response at every logon, if "
"available. If multiple values are returned (i.e. a list) then the first "
"value is used."
msgstr ""
"Les attributs, si disponibles, seront copiés depuis la réponse SAML à chaque "
"connexion. Si plusieurs valeurs sont retournées, dans le cas dune liste par "
"exemple, alors seulement la première valeur sera utilisé."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_metadata_url
msgid "Metadata URL"
msgstr "Adresse universelle des métadonnées"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Missing parameters"
msgstr "Paramètres manquants"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__field_name
msgid "Odoo Field"
msgstr "Champ Odoo"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_private
msgid "Odoo Private Key"
msgstr "Clé privée Odoo"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_private_filename
msgid "Odoo Private Key File Name"
msgstr "Nom de la clé privée dOdoo"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_public
msgid "Odoo Public Certificate"
msgstr "Certificat public dOdoo"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_public_filename
msgid "Odoo Public Certificate File Name"
msgstr "Nom de fichier du certificat public dOdoo"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Odoo Settings"
msgstr "Paramètres Odoo"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__autoredirect
msgid ""
"Only the provider with the higher priority will be automatically redirected"
msgstr ""
"Seul le fournisseur avec la plus haute priorité sera automatiquement redirigé"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_baseurl
msgid "Override Base URL"
msgstr "Outrepasser ladresse universelle de base"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__provider_id
msgid "Provider"
msgstr "Fournisseur"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__name
msgid "Provider Name"
msgstr "Nom du fournisseur"
#. module: auth_saml
#: model:ir.actions.act_window,name:auth_saml.action_saml_provider
#: model_terms:ir.ui.view,arch_db:auth_saml.auth_saml_provider_view_search
msgid "Providers"
msgstr "Fournisseurs"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Refresh"
msgstr ""
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_users_form
msgid "SAML"
msgstr "SAML"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_request
msgid "SAML Outstanding Requests"
msgstr "Requêtes en attente SAML"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_provider_id
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "SAML Provider"
msgstr "Fournisseur SAML"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__saml_provider_id
msgid "SAML Provider that issued the token"
msgstr "Fournisseur SAML qui a fourni le jeton"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_res_users_saml__saml_uid
msgid "SAML Provider user_id"
msgstr "Identifiant de lutilisateur pour ce fournisseur SAML"
#. module: auth_saml
#: model:ir.ui.menu,name:auth_saml.menu_saml_providers
msgid "SAML Providers"
msgstr "Fournisseurs SAML"
#. module: auth_saml
#: model:ir.model.constraint,message:auth_saml.constraint_res_users_saml_uniq_users_saml_provider_saml_uid
msgid "SAML UID must be unique per provider"
msgstr "Lidentifiant SAML doit être unique par fournisseur"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_uid
msgid "SAML User ID"
msgstr "Identifiant utilisateur SAML"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_provider
msgid "SAML2 Provider"
msgstr "Fournisseur SAML2"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_attribute_mapping
msgid "SAML2 attribute mapping"
msgstr "Correspondance dattribut SAML2"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users__saml_ids
msgid "Saml"
msgstr "SAML"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sequence
msgid "Sequence"
msgstr "Séquence"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sign_authenticate_requests
msgid "Sign Authenticate Requests"
msgstr "Signer les requêtes dauthentification"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sign_metadata
msgid "Sign Metadata"
msgstr "Signer les métadonnées"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Sign up is not allowed on this database."
msgstr "Linscription nest pas autorisée sur cette base de donnée."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sig_alg
msgid "Signature Algorithm"
msgstr "Algorithme de signature"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__idp_metadata_url
msgid ""
"Some SAML providers, notably Office365 can have a metadata document which "
"changes over time, and they provide a URL to the document instead. When this "
"field is set, the metadata can be fetched from the provided URL."
msgstr ""
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_ir_config_parameter
msgid "System Parameter"
msgstr "Paramètre système"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"The URL configured for the ACS must exactly match what is sent. If you have "
"odoo responding on multiple URLs you can use this to force it to send a "
"specific address rather than rely on automatically detecting."
msgstr ""
"Ladresse universelle configuré pour le SCA doit correspondre exactement à "
"ce qui est envoyé. Si Odoo répond sur plusieurs adresses universelles, vous "
"pouvez utiliser ceci pour le forcer à envoyer une adresse spécifique plutôt "
"que de dépendre sur la détection automatique."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_res_users_saml__saml_access_token
msgid "The current SAML token in use"
msgstr "Le jeton SAML en cours dutilisation"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/models/res_users.py:0
#, python-format
msgid ""
"This database disallows users to have both passwords and SAML IDs. Error for "
"logins %s"
msgstr ""
"Cette base de données interdit aux utilisateurs davoir à la fois un mot de "
"passe et un identifiant SAML. Erreur pour les identifiants %s"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Unknown provider"
msgstr "Fournisseur inconnu"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Used to sign requests sent to the IDP. You can use openssl to generate a "
"certificate and key."
msgstr ""
"Utilisé pour signer des requêtes envoyées au fournisseur didentité. Vous "
"pouvez utiliser openssl pour générer le certificat et la clé."
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_users
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__user_id
msgid "User"
msgstr "Utilisateur"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_users_saml
msgid "User to SAML Provider Mapping"
msgstr "Mappage des utilisateurs aux fournisseurs SAML"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_assertions_or_response_signed
msgid "Want Assertions Or Response Signed"
msgstr "Désire une signature des réponses ou des assertions"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_assertions_signed
msgid "Want Assertions Signed"
msgstr "Désire une signature des assertions"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_response_signed
msgid "Want Response Signed"
msgstr "Désire une signature des réponses"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sign_metadata
msgid "Whether metadata should be signed or not"
msgstr "Signature ou non des métadonnées"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sign_authenticate_requests
msgid "Whether the request should be signed or not"
msgstr "Signature ou non des requêtes"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "You do not have access to this database. Please contact support."
msgstr ""
"Vous navez pas accès à cette base de donnée. Veuillez contacter votre "
"support."
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Your ACS url will be base_url + /auth_saml/signin"
msgstr ""
"Votre adresse universelle SCA sera celle de base suivi de /auth_saml/signin"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Your provider will give you this XML once configured."
msgstr "Votre fournisseur vous donnera ce XML une fois configuré."
#~ msgid "Button Description"
#~ msgstr "Description du bouton"
#~ msgid "Users"
#~ msgstr "Utilisateurs"
#~ msgid ""
#~ "Allow SAML users to posess an Odoo password (warning: decreases security)"
#~ msgstr ""
#~ "Autoriser les utilisateurs avec SAML à aussi avoir un mot de passe Odoo "
#~ "(attention: abaisse la sécurité)"
#~ msgid "Body"
#~ msgstr "Corps"
#~ msgid "CSS Class"
#~ msgstr "Classe CSS"
#~ msgid "Enabled"
#~ msgstr "Activé"
#~ msgid "IDP Configuration"
#~ msgstr "Configuration FI"
#~ msgid "Matching Attribute"
#~ msgstr "Attributs correspondants"
#~ msgid "Private key of our service provider (this openerpserver)"
#~ msgstr "Clé privée de notre fournisseur de service (ce serveur Odoo)"
#~ msgid "Provider name"
#~ msgstr "Nom du fournisseur"
#~ msgid "SAML2 provider"
#~ msgstr "Fournisseur SAML2"
#~ msgid "SP Configuration"
#~ msgstr "Configuration du fournisseur de service"
#~ msgid ""
#~ "This database disallows users to have both passwords and SAML IDs. Errors "
#~ "for login %s"
#~ msgstr ""
#~ "Cette base de données interdit aux utilisateurs davoir à la fois un mot "
#~ "de passe et un identifiant SAML. Erreurs pour lidentifiant %s"
#~ msgid ""
#~ "You do not have access to this database or your invitation has expired. "
#~ "Please ask for an invitation and be sure to follow the link in your "
#~ "invitation email."
#~ msgstr ""
#~ "Vous navez pas accès à cette base de donnée ou votre invitation a "
#~ "expirée. Demandez une invitation et assurez-vous de suivre le lien dans "
#~ "le courriel dinvitation."
#~ msgid "arch"
#~ msgstr "arch"
#~ msgid "auth_saml.token"
#~ msgstr "auth_saml.token"
#~ msgid "res.config.settings"
#~ msgstr "res.config.settings"
#~ msgid ""
#~ "SAML2 authentication: An Odoo user cannot posess both an SAML user ID and "
#~ "an Odoo password."
#~ msgstr ""
#~ "Authentification SAML2 : Un utilisateur Odoo ne peut pas posséder à la "
#~ "fois un ID utilisateur SAML et un mot de passe Odoo."
#~ msgid "Sign up error"
#~ msgstr "Erreur dinscription"
#~ msgid "Authentication error"
#~ msgstr "Erreur dauthentification"
#~ msgid "res.config"
#~ msgstr "res.config"
#~ msgid "unknown"
#~ msgstr "inconnu"
#~ msgid "http://localhost:8000"
#~ msgstr "http://localhost :8000"
#~ msgid "running on"
#~ msgstr "en fonctionnement sur"
#~ msgid "You must have an"
#~ msgstr "Vous devez avoir un"
#~ msgid "authentic2 server"
#~ msgstr "serveur authentic2"
#~ msgid "Allow users to sign in with a Local Authentic"
#~ msgstr "Autoriser les utilisateurs à sinscrire avec un Authentic local"
#~ msgid "Allowed"
#~ msgstr "Autorisé"

View file

@ -0,0 +1,594 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * auth_saml
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-02-15 11:06+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.6.2\n"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.providers
msgid "- or -"
msgstr "- o -"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Access Denied"
msgstr "Accesso negato"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__active
msgid "Active"
msgstr "Attivo"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__css_class
msgid "Add a CSS class that serves you to style the login button."
msgstr "Aggiungere una classe CSS utile a definire il pulsante di accesso."
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Algorithm used to sign requests."
msgstr "Algoritmo utilizzato per firmare le richieste."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_config_settings__allow_saml_uid_and_internal_password
msgid ""
"Allow SAML users to possess an Odoo password (warning: decreases security)"
msgstr ""
"Consente agli utenti SAML di avere una password Odoo (attenzione: diminuisce "
"la sicurezza)"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.auth_saml_provider_view_search
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Archived"
msgstr "In archivio"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__attribute_mapping_ids
msgid "Attribute Mapping"
msgstr "Mappatura attributo"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__matching_attribute
msgid ""
"Attribute to look for in the returned IDP response to match against an Odoo "
"user."
msgstr ""
"Attributo da cercare nella risposta IDP restituita da corrispondere con un "
"utente Odoo."
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Attribute to match the user in Odoo with against the IDP (Identity "
"Provider). You may use the special case \"subject.nameId\" to match against "
"the nameId in the IDP response."
msgstr ""
"Attributo per corrispondere l'utente in Odoo con quello dell'IDP (provider "
"di identità). Si può utilizzare l'opzione speciale \"subject.nameId\" per "
"corrispondere al nameId nella risposta IDP."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__authn_requests_signed
msgid "Authn Requests Signed"
msgstr "Richieste atenticazione firmate"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__autoredirect
msgid "Automatic Redirection"
msgstr "Inoltro automatico"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Available after first save. The URL will change if the provider is deleted "
"&amp; recreated or the database is renamed."
msgstr ""
"Disponibile dopo il primo salvataggio. L'URL cambierà se il forntore è "
"cancellato e ricreato il database rinominato."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sp_baseurl
msgid ""
"Base URL sent to Odoo with this, rather than automatically\n"
" detecting from request or system parameter web.base.url"
msgstr ""
"URL base inviato a Odoo con questo, anziché rilevarlo \n"
" automaticamente dalla richiesta o parametro di sistema web.base.url"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__css_class
msgid "Button Icon CSS class"
msgstr "Pulsante icona classe CSS"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_config_settings
msgid "Config Settings"
msgstr "Impostazioni configurazione"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__idp_metadata
msgid ""
"Configuration for this Identity Provider. Supplied by the provider, in XML "
"format."
msgstr ""
"Configurazione per questo provider di identità. Fornito dal provider, in "
"formato XML."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__create_uid
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__create_uid
msgid "Created by"
msgstr "Creato da"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__create_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__create_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__create_date
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__create_date
msgid "Created on"
msgstr "Creato il"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__saml_request_id
msgid "Current Request ID"
msgstr "ID richiesta attuale"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_access_token
msgid "Current SAML token for this user"
msgstr "Token SAML attuale per questo utente"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__display_name
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__display_name
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__display_name
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__display_name
msgid "Display Name"
msgstr "Nome visualizzato"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Display Settings"
msgstr "Visualizza impostazioni"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__entity_id
msgid "Entity ID"
msgstr "ID entità"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Entity Identifier sent to the IDP. Often this would be the metadata URL, but "
"it can be any string."
msgstr ""
"Identificatore entità inviato al IDP. Spesso è l'URL dei metadati, ma può "
"essere qualsiasi stringa."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__entity_id
msgid "EntityID passed to IDP, used to identify the Odoo"
msgstr "EntityID passato all'IDP, utilizzato per identificate Odoo"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__matching_attribute_to_lower
msgid "Force matching_attribute to lower case before passing back to Odoo."
msgstr "Forza matching_attribute a minuscolo prima di restituirlo ad Odoo."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__id
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__id
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__id
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__id
msgid "ID"
msgstr "ID"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__attribute_name
msgid "IDP Response Attribute"
msgstr "Attributo risposta IDP"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__idp_metadata
msgid "Identity Provider Metadata"
msgstr "Metadati provider identità"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__idp_metadata_url
msgid "Identity Provider Metadata URL"
msgstr "URL metadata identity provider"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Identity Provider Settings"
msgstr "Impostazioni provider identità"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__matching_attribute
msgid "Identity Provider matching attribute"
msgstr "Attributo corrsipondenza provider identità"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "If you provider gives you a URL, use this field preferably"
msgstr "Se il provider fornisce un URL, preferibilmente usare questo campo"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__authn_requests_signed
msgid ""
"Indicates if the Authentication Requests sent by this SP should be signed by "
"default."
msgstr ""
"ndica se la richiesta di atenticazione inviata da questo SP deve essere "
"firmata in modo predefinito."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_assertions_signed
msgid "Indicates if this SP wants the IdP to send the assertions signed."
msgstr "Indica se questo SP richiede che l'IDP invii la conferma firmata."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__logout_requests_signed
msgid ""
"Indicates if this entity will sign the Logout Requests originated from it."
msgstr ""
"Indica se questa entità firmerà la richiesta di uscita da esso generata."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_response_signed
msgid "Indicates that Authentication Responses to this SP must be signed."
msgstr ""
"Indica che le risposte di autenticazione a questo SP devono essere firmate."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__want_assertions_or_response_signed
msgid ""
"Indicates that either the Authentication Response or the assertions "
"contained within the response to this SP must be signed."
msgstr ""
"Indica che la risposta di autenticazione o le dichiarazioni contenute nelle "
"risposte a questo SP deve essere firmata."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping____last_update
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider____last_update
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request____last_update
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml____last_update
msgid "Last Modified on"
msgstr "Ultima modifica il"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__write_uid
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__write_uid
msgid "Last Updated by"
msgstr "Ultimo aggiornamento di"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__write_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__write_date
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__write_date
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__write_date
msgid "Last Updated on"
msgstr "Ultimo aggiornamento il"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__body
msgid "Link text in Login Dialog"
msgstr "Collega il testo nella maschera di accesso"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__body
msgid "Login button label"
msgstr "Etichetta pulsante accesso"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__logout_requests_signed
msgid "Logout Requests Signed"
msgstr "Richiesta uscita firmata"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__matching_attribute_to_lower
msgid "Lowercase IDP Matching Attribute"
msgstr "Attributo corrispondenza IDP minuscolo"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Mapped attributes are copied from the SAML response at every logon, if "
"available. If multiple values are returned (i.e. a list) then the first "
"value is used."
msgstr ""
"Gli attributi mappati sono copiati dalla risposta SAML ad ogni accesso, se "
"disponibile. Se vengono restituiti valori multipli (cioè una lista) allora "
"viene usato il primo valore."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_metadata_url
msgid "Metadata URL"
msgstr "URL metadati"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Missing parameters"
msgstr "Parametri mancanti"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__field_name
msgid "Odoo Field"
msgstr "Campo Odoo"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_private
msgid "Odoo Private Key"
msgstr "Chiave privata Odoo"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_private_filename
msgid "Odoo Private Key File Name"
msgstr "Nome file chiave privata Odoo"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_public
msgid "Odoo Public Certificate"
msgstr "Certificato pubblico Odoo"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_pem_public_filename
msgid "Odoo Public Certificate File Name"
msgstr "Nome file certificato pubblico Odoo"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Odoo Settings"
msgstr "Impostazioni Odoo"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__autoredirect
msgid ""
"Only the provider with the higher priority will be automatically redirected"
msgstr ""
"Solo il provider con la priorità maggiore verrà inoltrato automaticamente"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sp_baseurl
msgid "Override Base URL"
msgstr "Ignora URL base"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_attribute_mapping__provider_id
msgid "Provider"
msgstr "Provider"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__name
msgid "Provider Name"
msgstr "Nome provider"
#. module: auth_saml
#: model:ir.actions.act_window,name:auth_saml.action_saml_provider
#: model_terms:ir.ui.view,arch_db:auth_saml.auth_saml_provider_view_search
msgid "Providers"
msgstr "Provider"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Refresh"
msgstr "Aggiorna"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_users_form
msgid "SAML"
msgstr "SAML"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_request
msgid "SAML Outstanding Requests"
msgstr "Richiesta straordinaria SAML"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_provider_id
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "SAML Provider"
msgstr "Provider SAML"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_request__saml_provider_id
msgid "SAML Provider that issued the token"
msgstr "Provider SAML che ha emesso il token"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_res_users_saml__saml_uid
msgid "SAML Provider user_id"
msgstr "user_id provider SAML"
#. module: auth_saml
#: model:ir.ui.menu,name:auth_saml.menu_saml_providers
msgid "SAML Providers"
msgstr "Provider SAML"
#. module: auth_saml
#: model:ir.model.constraint,message:auth_saml.constraint_res_users_saml_uniq_users_saml_provider_saml_uid
msgid "SAML UID must be unique per provider"
msgstr "UID SAML deve essere univoco per provider"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__saml_uid
msgid "SAML User ID"
msgstr "ID utente SAML"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_provider
msgid "SAML2 Provider"
msgstr "Provider SAML2"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_auth_saml_attribute_mapping
msgid "SAML2 attribute mapping"
msgstr "Mappatura attributo SAML2"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_res_users__saml_ids
msgid "Saml"
msgstr "SAML"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sequence
msgid "Sequence"
msgstr "Sequenza"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sign_authenticate_requests
msgid "Sign Authenticate Requests"
msgstr "Firma richieste autenticazione"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sign_metadata
msgid "Sign Metadata"
msgstr "Frma metadati"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Sign up is not allowed on this database."
msgstr "Non è consentito iscriversi a questo database."
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__sig_alg
msgid "Signature Algorithm"
msgstr "Algoritmo firma"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__idp_metadata_url
msgid ""
"Some SAML providers, notably Office365 can have a metadata document which "
"changes over time, and they provide a URL to the document instead. When this "
"field is set, the metadata can be fetched from the provided URL."
msgstr ""
"Alcuni provider SAML, in particolare Office365, possono avere un documento "
"di metadati che cambia nel tempo e forniscono invece un URL al documento. "
"Quando questo campo è impostato, i metadati possono essere recuperati "
"dall'URL fornito."
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_ir_config_parameter
msgid "System Parameter"
msgstr "Parametro di sistema"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"The URL configured for the ACS must exactly match what is sent. If you have "
"odoo responding on multiple URLs you can use this to force it to send a "
"specific address rather than rely on automatically detecting."
msgstr ""
"L'URL configurato per l'ACS deve corrispondere esattamente a quando inviato. "
"Se si ha Odoo che risponde da URL multipli si può utilizzare per forzarlo ad "
"inviare un indirizzo specifico invece che rispondere al rilevamento "
"automatico."
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_res_users_saml__saml_access_token
msgid "The current SAML token in use"
msgstr "Token SAML attualmente in uso"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/models/res_users.py:0
#, python-format
msgid ""
"This database disallows users to have both passwords and SAML IDs. Error for "
"logins %s"
msgstr ""
"Questo database nn consente agli utenti di avere sia password che ID SAML. "
"Errore per accessi %s"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "Unknown provider"
msgstr "Provider sconosciuto"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid ""
"Used to sign requests sent to the IDP. You can use openssl to generate a "
"certificate and key."
msgstr ""
"Usato per firmare le richieste inviate all'IDP. Si può utilizzare OpenSSL "
"per generare un certificato e una chiave."
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_users
#: model:ir.model.fields,field_description:auth_saml.field_res_users_saml__user_id
msgid "User"
msgstr "Utente"
#. module: auth_saml
#: model:ir.model,name:auth_saml.model_res_users_saml
msgid "User to SAML Provider Mapping"
msgstr "Mappatura tra utente e provider SAML"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_assertions_or_response_signed
msgid "Want Assertions Or Response Signed"
msgstr "Richiede conferma o risposta firmate"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_assertions_signed
msgid "Want Assertions Signed"
msgstr "Richiede conferme firmate"
#. module: auth_saml
#: model:ir.model.fields,field_description:auth_saml.field_auth_saml_provider__want_response_signed
msgid "Want Response Signed"
msgstr "Richiede risposta firmata"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sign_metadata
msgid "Whether metadata should be signed or not"
msgstr "Se i metadata devno essere firmati o meno"
#. module: auth_saml
#: model:ir.model.fields,help:auth_saml.field_auth_saml_provider__sign_authenticate_requests
msgid "Whether the request should be signed or not"
msgstr "Se la richiesta deve essere firmata o meno"
#. module: auth_saml
#. odoo-python
#: code:addons/auth_saml/controllers/main.py:0
#, python-format
msgid "You do not have access to this database. Please contact support."
msgstr "Non si ha accesso a questo database. Contattare il supporto."
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Your ACS url will be base_url + /auth_saml/signin"
msgstr "Il suo URL ACS sarà base_url + /auth_saml/signin"
#. module: auth_saml
#: model_terms:ir.ui.view,arch_db:auth_saml.view_saml_provider_form
msgid "Your provider will give you this XML once configured."
msgstr "Il suo provider fornirà questo XML una volta configurato."

View file

@ -0,0 +1,11 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import (
auth_saml_attribute_mapping,
auth_saml_provider,
auth_saml_request,
ir_config_parameter,
res_config_settings,
res_users,
res_users_saml,
)

View file

@ -0,0 +1,37 @@
from odoo import api, fields, models
class AuthSamlAttributeMapping(models.Model):
"""
Attributes to copy from SAML provider on logon, into Odoo
"""
_name = "auth.saml.attribute.mapping"
_description = "SAML2 attribute mapping"
provider_id = fields.Many2one(
"auth.saml.provider",
index=True,
required=True,
)
attribute_name = fields.Char(
string="IDP Response Attribute",
required=True,
)
field_name = fields.Selection(
string="Odoo Field",
selection="_field_name_selection",
required=True,
)
@api.model
def _field_name_selection(self):
user_fields = self.env["res.users"].fields_get().items()
def valid_field(f, d):
return d.get("type") == "char" and not d.get("readonly")
result = [(f, d.get("string")) for f, d in user_fields if valid_field(f, d)]
result.sort(key=lambda r: r[1])
return result

View file

@ -0,0 +1,445 @@
# Copyright (C) 2020 Glodo UK <https://www.glodo.uk/>
# Copyright (C) 2010-2016, 2022 XCG Consulting <https://xcg-consulting.fr/>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import base64
import json
import logging
import os
import tempfile
import urllib.parse
import requests
# dependency name is pysaml2 # pylint: disable=W7936
import saml2
import saml2.xmldsig as ds
from saml2.client import Saml2Client
from saml2.config import Config as Saml2Config
from saml2.sigver import SignatureError
from odoo import api, fields, models
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class AuthSamlProvider(models.Model):
"""Configuration values of a SAML2 provider"""
_name = "auth.saml.provider"
_description = "SAML2 Provider"
_order = "sequence, name"
name = fields.Char("Provider Name", required=True, index="trigram")
entity_id = fields.Char(
"Entity ID",
help="EntityID passed to IDP, used to identify the Odoo",
required=True,
default="odoo",
)
idp_metadata = fields.Text(
string="Identity Provider Metadata",
help=(
"Configuration for this Identity Provider. Supplied by the"
" provider, in XML format."
),
required=True,
)
idp_metadata_url = fields.Char(
string="Identity Provider Metadata URL",
help="Some SAML providers, notably Office365 can have a metadata "
"document which changes over time, and they provide a URL to the "
"document instead. When this field is set, the metadata can be "
"fetched from the provided URL.",
)
sp_baseurl = fields.Text(
string="Override Base URL",
help="""Base URL sent to Odoo with this, rather than automatically
detecting from request or system parameter web.base.url""",
)
sp_pem_public = fields.Binary(
string="Odoo Public Certificate",
attachment=True,
required=True,
)
sp_pem_public_filename = fields.Char("Odoo Public Certificate File Name")
sp_pem_private = fields.Binary(
string="Odoo Private Key",
attachment=True,
required=True,
)
sp_pem_private_filename = fields.Char("Odoo Private Key File Name")
sp_metadata_url = fields.Char(
compute="_compute_sp_metadata_url",
string="Metadata URL",
readonly=True,
)
matching_attribute = fields.Char(
string="Identity Provider matching attribute",
default="subject.nameId",
required=True,
help=(
"Attribute to look for in the returned IDP response to match"
" against an Odoo user."
),
)
matching_attribute_to_lower = fields.Boolean(
string="Lowercase IDP Matching Attribute",
help="Force matching_attribute to lower case before passing back to Odoo.",
)
attribute_mapping_ids = fields.One2many(
"auth.saml.attribute.mapping",
"provider_id",
string="Attribute Mapping",
)
active = fields.Boolean(default=True)
sequence = fields.Integer(index=True)
css_class = fields.Char(
string="Button Icon CSS class",
help="Add a CSS class that serves you to style the login button.",
default="fa fa-fw fa-sign-in text-primary",
)
body = fields.Char(
string="Login button label", help="Link text in Login Dialog", translate=True
)
autoredirect = fields.Boolean(
"Automatic Redirection",
default=False,
help="Only the provider with the higher priority will be automatically "
"redirected",
)
sig_alg = fields.Selection(
selection=lambda s: s._sig_alg_selection(),
required=True,
string="Signature Algorithm",
)
# help string is from pysaml2 documentation
authn_requests_signed = fields.Boolean(
default=True,
help="Indicates if the Authentication Requests sent by this SP should be signed"
" by default.",
)
logout_requests_signed = fields.Boolean(
default=True,
help="Indicates if this entity will sign the Logout Requests originated from it"
".",
)
want_assertions_signed = fields.Boolean(
default=True,
help="Indicates if this SP wants the IdP to send the assertions signed.",
)
want_response_signed = fields.Boolean(
default=True,
help="Indicates that Authentication Responses to this SP must be signed.",
)
want_assertions_or_response_signed = fields.Boolean(
default=True,
help="Indicates that either the Authentication Response or the assertions "
"contained within the response to this SP must be signed.",
)
# this one is used in Saml2Client.prepare_for_authenticate
sign_authenticate_requests = fields.Boolean(
default=True,
help="Whether the request should be signed or not",
)
sign_metadata = fields.Boolean(
default=True,
help="Whether metadata should be signed or not",
)
@api.model
def _sig_alg_selection(self):
return [(sig[0], sig[0]) for sig in ds.SIG_ALLOWED_ALG]
@api.onchange("name")
def _onchange_name(self):
if not self.body:
self.body = self.name
@api.depends("sp_baseurl")
def _compute_sp_metadata_url(self):
icp_base_url = (
self.env["ir.config_parameter"].sudo().get_param("web.base.url", "")
)
for record in self:
if isinstance(record.id, models.NewId):
record.sp_metadata_url = False
continue
base_url = icp_base_url
if record.sp_baseurl:
base_url = record.sp_baseurl
qs = urllib.parse.urlencode({"p": record.id, "d": self.env.cr.dbname})
record.sp_metadata_url = urllib.parse.urljoin(
base_url, ("/auth_saml/metadata?%s" % qs)
)
def _get_cert_key_path(self, field="sp_pem_public"):
self.ensure_one()
model_attachment = self.env["ir.attachment"].sudo()
keys = model_attachment.search(
[
("res_model", "=", self._name),
("res_field", "=", field),
("res_id", "=", self.id),
],
limit=1,
)
if model_attachment._storage() != "file":
# For non-file locations we need to create a temp file to pass to pysaml.
fd, keys_path = tempfile.mkstemp()
with open(keys_path, "wb") as f:
f.write(base64.b64decode(keys.datas))
os.close(fd)
else:
keys_path = model_attachment._full_path(keys.store_fname)
return keys_path
def _get_config_for_provider(self, base_url: str = None) -> Saml2Config:
"""
Internal helper to get a configured Saml2Client
"""
self.ensure_one()
if self.sp_baseurl:
base_url = self.sp_baseurl
if not base_url:
base_url = (
self.env["ir.config_parameter"].sudo().get_param("web.base.url", "")
)
acs_url = urllib.parse.urljoin(base_url, "/auth_saml/signin")
settings = {
"metadata": {"inline": [self.idp_metadata]},
"entityid": self.entity_id,
"service": {
"sp": {
"endpoints": {
"assertion_consumer_service": [
(acs_url, saml2.BINDING_HTTP_REDIRECT),
(acs_url, saml2.BINDING_HTTP_POST),
(acs_url, saml2.BINDING_HTTP_REDIRECT),
(acs_url, saml2.BINDING_HTTP_POST),
],
},
"allow_unsolicited": False,
"authn_requests_signed": self.authn_requests_signed,
"logout_requests_signed": self.logout_requests_signed,
"want_assertions_signed": self.want_assertions_signed,
"want_response_signed": self.want_response_signed,
"want_assertions_or_response_signed": (
self.want_assertions_or_response_signed
),
},
},
"cert_file": self._get_cert_key_path("sp_pem_public"),
"key_file": self._get_cert_key_path("sp_pem_private"),
}
try:
sp_config = Saml2Config()
sp_config.load(settings)
sp_config.allow_unknown_attributes = True
return sp_config
except saml2.SAMLError:
if self.env.context.get("saml2_retry_after_refresh_metadata", False):
raise
# Retry after refresh metadata
self.action_refresh_metadata_from_url()
return self.with_context(
saml2_retry_after_refresh_metatata=1
)._get_config_for_provider(base_url)
def _get_client_for_provider(self, base_url: str = None) -> Saml2Client:
sp_config = self._get_config_for_provider(base_url)
saml_client = Saml2Client(config=sp_config)
return saml_client
def _get_auth_request(self, extra_state=None, url_root=None):
"""
build an authentication request and give it back to our client
"""
self.ensure_one()
if extra_state is None:
extra_state = {}
state = {
"d": self.env.cr.dbname,
"p": self.id,
}
state.update(extra_state)
sig_alg = ds.SIG_RSA_SHA1
if self.sig_alg:
sig_alg = getattr(ds, self.sig_alg)
saml_client = self._get_client_for_provider(url_root)
reqid, info = saml_client.prepare_for_authenticate(
sign=self.sign_authenticate_requests,
relay_state=json.dumps(state),
sigalg=sig_alg,
)
redirect_url = None
# Select the IdP URL to send the AuthN request to
for key, value in info["headers"]:
if key == "Location":
redirect_url = value
self._store_outstanding_request(reqid)
return redirect_url
def _validate_auth_response(self, token: str, base_url: str = None):
"""return the validation data corresponding to the access token"""
self.ensure_one()
try:
client = self._get_client_for_provider(base_url)
response = client.parse_authn_request_response(
token,
saml2.entity.BINDING_HTTP_POST,
self._get_outstanding_requests_dict(),
)
except SignatureError:
# we have a metadata url: try to refresh the metadata document
if self.idp_metadata_url:
self.action_refresh_metadata_from_url()
# retry: if it fails again, we let the exception flow
client = self._get_client_for_provider(base_url)
response = client.parse_authn_request_response(
token,
saml2.entity.BINDING_HTTP_POST,
self._get_outstanding_requests_dict(),
)
else:
raise
matching_value = None
if self.matching_attribute == "subject.nameId":
matching_value = response.name_id.text
else:
attrs = response.get_identity()
for k, v in attrs.items():
if k == self.matching_attribute:
matching_value = v
break
if not matching_value:
raise Exception(
"Matching attribute %s not found in user attrs: %s"
% (self.matching_attribute, attrs)
)
if matching_value and isinstance(matching_value, list):
matching_value = next(iter(matching_value), None)
if isinstance(matching_value, str) and self.matching_attribute_to_lower:
matching_value = matching_value.lower()
vals = {"user_id": matching_value}
post_vals = self._hook_validate_auth_response(response, matching_value)
if post_vals:
vals.update(post_vals)
return vals
def _get_outstanding_requests_dict(self):
self.ensure_one()
requests = (
self.env["auth_saml.request"]
.sudo()
.search([("saml_provider_id", "=", self.id)])
)
return {record.saml_request_id: record.id for record in requests}
def _store_outstanding_request(self, reqid):
self.ensure_one()
self.env["auth_saml.request"].sudo().create(
{"saml_provider_id": self.id, "saml_request_id": reqid}
)
def _metadata_string(self, valid=None, base_url: str = None):
self.ensure_one()
sp_config = self._get_config_for_provider(base_url)
return saml2.metadata.create_metadata_string(
None,
config=sp_config,
valid=valid,
cert=self._get_cert_key_path("sp_pem_public"),
keyfile=self._get_cert_key_path("sp_pem_private"),
sign=self.sign_metadata,
)
def _hook_validate_auth_response(self, response, matching_value):
self.ensure_one()
vals = {}
attrs = response.get_identity()
for attribute in self.attribute_mapping_ids:
if attribute.attribute_name not in attrs:
_logger.debug(
"SAML attribute '%s' not found in response %s",
attribute.attribute_name,
attrs,
)
continue
attribute_value = attrs[attribute.attribute_name]
if isinstance(attribute_value, list):
attribute_value = attribute_value[0]
vals[attribute.field_name] = attribute_value
return {"mapped_attrs": vals}
def action_refresh_metadata_from_url(self):
providers = self.search(
[("idp_metadata_url", "ilike", "http%"), ("id", "in", self.ids)]
)
if not providers:
return False
providers_to_update = {}
for provider in providers:
document = requests.get(provider.idp_metadata_url, timeout=5)
if document.status_code != 200:
raise UserError(
f"Unable to download the metadata for {provider.name}: {document.reason}"
)
if document.text != provider.idp_metadata:
providers_to_update[provider.id] = document.text
if not providers_to_update:
return False
# lock the records we might update, so that multiple simultaneous login
# attempts will not cause concurrent updates
provider_ids = tuple(providers_to_update.keys())
self.env.cr.execute(
"SELECT id FROM auth_saml_provider WHERE id in %s FOR UPDATE",
(provider_ids,),
)
updated = False
for provider in providers:
if provider.id in providers_to_update:
provider.idp_metadata = providers_to_update[provider.id]
_logger.info(
"Updated metadata for provider %s from %s",
provider.name,
)
updated = True
return updated

View file

@ -0,0 +1,21 @@
# Copyright (C) 2020 GlodoUK <https://glodo.uk/>
# Copyright (C) 2022 XCG Consulting <https://xcg-consulting.fr/>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class AuthSamlRequest(models.TransientModel):
_name = "auth_saml.request"
_description = "SAML Outstanding Requests"
_rec_name = "saml_request_id"
saml_provider_id = fields.Many2one(
"auth.saml.provider",
string="SAML Provider that issued the token",
required=True,
)
saml_request_id = fields.Char(
"Current Request ID",
required=True,
)

View file

@ -0,0 +1,39 @@
# Copyright (C) 2022 XCG Consulting <https://xcg-consulting.fr/>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from odoo import api, models
_logger = logging.getLogger(__name__)
ALLOW_SAML_UID_AND_PASSWORD = "auth_saml.allow_saml_uid_and_internal_password"
class IrConfigParameter(models.Model):
"""Redefined to update users when our parameter is changed."""
_inherit = "ir.config_parameter"
@api.model_create_multi
def create(self, vals_list):
"""Redefined to update users when our parameter is changed."""
result = super().create(vals_list)
if result.filtered(lambda param: param.key == ALLOW_SAML_UID_AND_PASSWORD):
self.env["res.users"].allow_saml_and_password_changed()
return result
def write(self, vals):
"""Redefined to update users when our parameter is changed."""
result = super().write(vals)
if self.filtered(lambda param: param.key == ALLOW_SAML_UID_AND_PASSWORD):
self.env["res.users"].allow_saml_and_password_changed()
return result
def unlink(self):
"""Redefined to update users when our parameter is deleted."""
param_saml = self.filtered(
lambda param: param.key == ALLOW_SAML_UID_AND_PASSWORD
)
result = super().unlink()
if result and param_saml:
self.env["res.users"].allow_saml_and_password_changed()
return result

View file

@ -0,0 +1,15 @@
# Copyright (C) 2010-2016, 2022 XCG Consulting <http://odoo.consulting>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
from .ir_config_parameter import ALLOW_SAML_UID_AND_PASSWORD
class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
allow_saml_uid_and_internal_password = fields.Boolean(
"Allow SAML users to possess an Odoo password (warning: decreases security)",
config_parameter=ALLOW_SAML_UID_AND_PASSWORD,
)

View file

@ -0,0 +1,184 @@
# Copyright (C) 2020 GlodoUK <https://www.glodo.uk>
# Copyright (C) 2010-2016 XCG Consulting <http://odoo.consulting>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from typing import Set
import passlib
from odoo import SUPERUSER_ID, _, api, fields, models, registry, tools
from odoo.exceptions import AccessDenied, ValidationError
from .ir_config_parameter import ALLOW_SAML_UID_AND_PASSWORD
_logger = logging.getLogger(__name__)
class ResUser(models.Model):
"""
Add SAML login capabilities to Odoo users.
"""
_inherit = "res.users"
saml_ids = fields.One2many("res.users.saml", "user_id")
def _auth_saml_validate(self, provider_id: int, token: str, base_url: str = None):
provider = self.env["auth.saml.provider"].sudo().browse(provider_id)
return provider._validate_auth_response(token, base_url)
def _auth_saml_signin(self, provider: int, validation: dict, saml_response) -> str:
"""Sign in Odoo user corresponding to provider and validated access token.
:param provider: SAML provider id
:param validation: result of validation of access token
:param saml_response: saml parameters response from the IDP
:return: user login
:raise: odoo.exceptions.AccessDenied if signin failed
This method can be overridden to add alternative signin methods.
"""
saml_uid = validation["user_id"]
user_saml = self.env["res.users.saml"].search(
[("saml_uid", "=", saml_uid), ("saml_provider_id", "=", provider)],
limit=1,
)
user = user_saml.user_id
if len(user) != 1:
raise AccessDenied()
with registry(self.env.cr.dbname).cursor() as new_cr:
new_env = api.Environment(new_cr, self.env.uid, self.env.context)
# Update the token. Need to be committed, otherwise the token is not visible
# to other envs, like the one used in login_and_redirect
user_saml.with_env(new_env).write({"saml_access_token": saml_response})
if validation.get("mapped_attrs", {}):
user.write(validation.get("mapped_attrs", {}))
return user.login
@api.model
def auth_saml(self, provider: int, saml_response: str, base_url: str = None):
validation = self._auth_saml_validate(provider, saml_response, base_url)
# required check
if not validation.get("user_id"):
raise AccessDenied()
# retrieve and sign in user
login = self._auth_saml_signin(provider, validation, saml_response)
if not login:
raise AccessDenied()
# return user credentials
return self.env.cr.dbname, login, saml_response
def _check_credentials(self, password, env):
"""Override to handle SAML auths.
The token can be a password if the user has used the normal form...
but we are more interested in the case when they are tokens
and the interesting code is inside the "except" clause.
"""
try:
# Attempt a regular login (via other auth addons) first.
return super()._check_credentials(password, env)
except (AccessDenied, passlib.exc.PasswordSizeError):
passwd_allowed = (
env["interactive"] or not self.env.user._rpc_api_keys_only()
)
if passwd_allowed and self.env.user.active:
# since normal auth did not succeed we now try to find if the user
# has an active token attached to his uid
token = (
self.env["res.users.saml"]
.sudo()
.search(
[
("user_id", "=", self.env.user.id),
("saml_access_token", "=", password),
]
)
)
if token:
return
raise AccessDenied() from None
@api.model
def _saml_allowed_user_ids(self) -> Set[int]:
"""Users that can have a password even if the option to disallow it is set.
It includes superuser and the admin if it exists.
"""
allowed_users = {SUPERUSER_ID}
user_admin = self.env.ref("base.user_admin", False)
if user_admin:
allowed_users.add(user_admin.id)
return allowed_users
@api.model
def allow_saml_and_password(self) -> bool:
"""Can both SAML and local password auth methods coexist."""
return tools.str2bool(
self.env["ir.config_parameter"]
.sudo()
.get_param(ALLOW_SAML_UID_AND_PASSWORD)
)
def _set_password(self):
"""Inverse method of the password field."""
# Redefine base method to block setting password on users with SAML ids
# And also to be able to set password to a blank value
if not self.allow_saml_and_password():
saml_users = self.filtered(
lambda user: user.sudo().saml_ids
and user.id not in self._saml_allowed_user_ids()
and user.password
)
if saml_users:
# same error as an api.constrains because it is a constraint.
# a standard constrains would require the use of SQL as the password
# field is obfuscated by the base module.
raise ValidationError(
_(
"This database disallows users to "
"have both passwords and SAML IDs. "
"Error for logins %s"
)
% saml_users.mapped("login")
)
# handle setting password to NULL
blank_password_users = self.filtered(lambda user: user.password is False)
non_blank_password_users = self - blank_password_users
if non_blank_password_users:
# pylint: disable=protected-access
super(ResUser, non_blank_password_users)._set_password()
if blank_password_users:
# similar to what Odoo does in Users._set_encrypted_password
self.env.cr.execute(
"UPDATE res_users SET password = NULL WHERE id IN %s",
(tuple(blank_password_users.ids),),
)
blank_password_users.invalidate_recordset(fnames=["password"])
return
def allow_saml_and_password_changed(self):
"""Called after the parameter is changed."""
if not self.allow_saml_and_password():
# sudo because the user doing the parameter change might not have the right
# to search or write users
users_to_blank_password = self.sudo().search(
[
"&",
("saml_ids", "!=", False),
("id", "not in", list(self._saml_allowed_user_ids())),
]
)
_logger.debug(
"Removing password from %s user(s)", len(users_to_blank_password)
)
users_to_blank_password.write({"password": False})

View file

@ -0,0 +1,36 @@
# Copyright (C) 2022 XCG Consulting <https://xcg-consulting.fr/>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class ResUserSaml(models.Model):
_name = "res.users.saml"
_description = "User to SAML Provider Mapping"
user_id = fields.Many2one("res.users", index=True, required=True)
saml_provider_id = fields.Many2one(
"auth.saml.provider", string="SAML Provider", index=True
)
saml_uid = fields.Char("SAML User ID", help="SAML Provider user_id", required=True)
saml_access_token = fields.Char(
"Current SAML token for this user",
required=False,
help="The current SAML token in use",
)
_sql_constraints = [
(
"uniq_users_saml_provider_saml_uid",
"unique(saml_provider_id, saml_uid)",
"SAML UID must be unique per provider",
)
]
@api.model_create_multi
def create(self, vals_list):
"""Creates new records for the res.users.saml model"""
# Redefined to remove password if necessary
result = super().create(vals_list)
if not self.env["res.users"].allow_saml_and_password():
result.mapped("user_id").write({"password": False})
return result

View file

@ -0,0 +1,21 @@
To use this module, you need an IDP server, properly set up.
#. Configure the module according to your IdPs instructions
(Settings > Users & Companies > SAML Providers).
#. Pre-create your users and set the SAML information against the user.
By default, the module let users have both a password and SAML ids.
To increase security, disable passwords by using the option in Settings.
Note that the admin account can still have a password, even if the option is activated.
Setting the option immediately remove all password from users with a configured SAML ids.
If all the users have a SAML id in a single provider, you can set automatic redirection
in the provider settings. The autoredirection will only be done on the active provider
with the highest priority. It is still possible to access the login without redirection
by using the query parameter ``disable_autoredirect``, as in
``https://example.com/web/login?disable_autoredirect=`` The login is also displayed if
there is an error with SAML login, in order to display any error message.
If you are using Office365 as identity provider, set up the federation metadata document
rather than the document itself. This will allow the module to refresh the document when
needed.

View file

@ -0,0 +1,19 @@
* `XCG Consulting <https://xcg-consulting.fr/>`__:
* Florent Aide <florent.aide@xcg-consulting.fr>
* Vincent Hatakeyama <vincent.hatakeyama@xcg-consulting.fr>
* Alexandre Brun
* Houzéfa Abbasbhay <houzefa.abba@xcg-consulting.fr>
* Szeka Wong <szeka.wong@xcg-consulting.fr>
* Jeremy Co Kim Len <jeremy.cokimlen@vinci-concessions.com>
* Jeffery Chen Fan <jeffery9@gmail.com>
* Bhavesh Odedra <bodedra@opensourceintegrators.com>
* `Tecnativa <https://www.tecnativa.com/>`__:
* Jairo Llopis
* `GlodoUK <https://www.glodo.uk/>`__:
* Karl Southern
* `TAKOBI <https://takobi.online/>`__:
* Lorenzo Battistini

View file

@ -0,0 +1,19 @@
Let users log into Odoo via an SAML2 identity provider.
This module allows to deport the management of users and passwords in an
external authentication system to provide SSO functionality (Single Sign On)
between Odoo and other applications of your ecosystem.
**Benefits**:
* Reducing the time spent typing different passwords for different accounts.
* Reducing the time spent in IT support for password oversights.
* Centralizing authentication systems.
* Securing all input levels / exit / access to multiple systems without
prompting users.
* The centralization of access control information for compliance testing to
different standards.

View file

@ -0,0 +1,12 @@
16.0.1.2.1 (2025-05-13)
~~~~~~~~~~~~~~~~~~~~~~~
**Bugfixes**
- Avoid redirecting when there is a SAML error. ()
16.0.1.0.0
~~~~~~~~~~
Initial migration for 16.0.

View file

@ -0,0 +1,3 @@
This addon requires the python module ``pysaml2``.
``pysaml2`` requires the binary ``xmlsec1`` (on Debian or Ubuntu you can install it with ``apt-get install xmlsec1``)

View file

@ -0,0 +1 @@
* clean up ``auth_saml.request``

View file

@ -0,0 +1 @@
Users can login with the configured SAML IdP with buttons added in the login screen.

View file

@ -0,0 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_auth_saml_provider,auth_saml_provider,model_auth_saml_provider,base.group_system,1,1,1,1
access_auth_res_users_saml,auth_res_users_saml,model_res_users_saml,base.group_erp_manager,1,1,1,1
access_auth_saml_attribute_mapping,auth_saml_attribute_mapping,model_auth_saml_attribute_mapping,base.group_system,1,1,1,1
access_auth_saml_request,access_auth_saml_request,model_auth_saml_request,,0,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_auth_saml_provider auth_saml_provider model_auth_saml_provider base.group_system 1 1 1 1
3 access_auth_res_users_saml auth_res_users_saml model_res_users_saml base.group_erp_manager 1 1 1 1
4 access_auth_saml_attribute_mapping auth_saml_attribute_mapping model_auth_saml_attribute_mapping base.group_system 1 1 1 1
5 access_auth_saml_request access_auth_saml_request model_auth_saml_request 0 0 0 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View file

@ -0,0 +1,183 @@
<a
class="reference external image-reference"
href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"
><object
data="https://img.shields.io/badge/licence-AGPL--3-blue.svg"
type="image/svg+xml"
>
License: AGPL-3
</object></a
>
<div class="section" id="saml2-authentication">
<h1>SAML2 authentication</h1>
<p>Let users log into Odoo via an SAML2 identity provider.</p>
<p>
This module allows to deport the management of users and passwords in an external
authentication system to provide SSO functionality (Single Sign On) between Odoo and
other applications of your ecosystem.
</p>
<div class="section" id="benefits">
<h2>Benefits</h2>
<ul class="simple">
<li>
Reducing the time spent typing different passwords for different accounts.
</li>
<li>Reducing the time spent in IT support for password oversights.</li>
<li>Centralizing authentication systems.</li>
<li>
Securing all input levels / exit / access to multiple systems without prompting
users.
</li>
<li>
The centralization of access control information for compliance testing to
different standards.
</li>
</ul>
</div>
<div class="section" id="installation">
<h2>Installation</h2>
<p>Install as you would install any Odoo addon.</p>
<div class="section" id="dependencies">
<h3>Dependencies</h3>
<p>This addon requires pysaml2 and xmlsec1.</p>
</div>
</div>
<div class="section" id="configuration">
<h2>Configuration</h2>
<p>To use this module, you need an IDP server, properly set up.</p>
<ol>
<li>
Configure the module according to your IdPs instructions (Settings > Users &
Companies > SAML Providers).
</li>
<li>Pre-create your users and set the SAML information against the user.</li>
</ol>
<p>
By default, the module let users have both a password and SAML ids. To increase
security, disable passwords by using the option in Settings. Note that the admin
account can still have a password, even if the option is activated.
</p>
<p>
If all the users have a SAML id in a single provider, you can set automatic
redirection in the provider settings. It is still possible to access the login
without redirection by using the query parameter
<code>disable_autoredirect</code>, as in
<code>https://example.com/web/login?disable_autoredirect=</code> The login is also
displayed if there is an error with SAML login, in order to display any error
message.
</p>
</div>
<div class="section" id="usage">
<h2>Usage</h2>
<p>
Users can login with the configured SAML IdP with buttons added in the login
screen.
</p>
</div>
<div class="section" id="demo">
<h2>Demo</h2>
<a
class="reference external image-reference"
href="https://runbot.odoo-community.org/runbot/149/15.0"
><img
alt="Try me on Runbot"
src="https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas"
/></a>
</div>
<div class="section" id="known-issues-roadmap">
<h2>Known issues / Roadmap</h2>
<p>None for now.</p>
</div>
<div class="section" id="bug-tracker">
<h2>Bug Tracker</h2>
<p>
Bugs are tracked on
<a class="reference external" href="https://github.com/OCA/server-auth/issues"
>GitHub Issues</a
>. In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smash it by providing a detailed and welcomed
feedback
<a
class="reference external"
href="https://github.com/OCA/server-auth/issues/new?body=module:%20auth_saml%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**"
>here</a
>.
</p>
</div>
<div class="section" id="credits">
<h2>Credits</h2>
<div class="section" id="contributors">
<h3>Contributors</h3>
<p>In order of appearance:</p>
<blockquote>
<ul class="simple">
<li>
Florent Aide, &lt;<a
class="reference external"
href="mailto:florent.aide&#64;xcg-consulting.fr"
>florent.aide&#64;xcg-consulting.fr</a
>&gt;
</li>
<li>
Vincent Hatakeyama, &lt;<a
class="reference external"
href="mailto:vincent.hatakeyama&#64;xcg-consulting.fr"
>vincent.hatakeyama&#64;xcg-consulting.fr</a
>&gt;
</li>
<li>
Alexandre Brun, &lt;<a
class="reference external"
href="mailto:alexandre.brun&#64;xcg-consulting.fr"
>alexandre.brun&#64;xcg-consulting.fr</a
>&gt;
</li>
<li>
Jeremy Co Kim Len, &lt;<a
class="reference external"
href="mailto:jeremy.cokimlen&#64;vinci-concessions.com"
>jeremy.cokimlen&#64;vinci-concessions.com</a
>&gt;
</li>
<li>
Houzéfa Abbasbhay &lt;<a
class="reference external"
href="mailto:houzefa.abba&#64;xcg-consulting.fr"
>houzefa.abba&#64;xcg-consulting.fr</a
>&gt;
</li>
<li>
Bhavesh Odedra &lt;<a
class="reference external"
href="mailto:houzefa.abba&#64;xcg-consulting.fr"
>bodedra&#64;opensourceintegrators.com</a
>&gt;
</li>
</ul>
</blockquote>
</div>
<div class="section" id="maintainer">
<h3>Maintainer</h3>
<a class="reference external image-reference" href="https://odoo-community.org"
><img
alt="Odoo Community Association"
src="https://odoo-community.org/logo.png"
/></a>
<p>This module is maintained by the OCA.</p>
<p>
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and promote
its widespread use.
</p>
<p>
To contribute to this module, please visit
<a class="reference external" href="http://odoo-community.org"
>http://odoo-community.org</a
>.
</p>
</div>
</div>
</div>

View file

@ -0,0 +1 @@
from . import fake_idp, test_pysaml

View file

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID7TCCAtWgAwIBAgIUDBX/LJ1BPZOhb2vrDnwIasyEi+AwDQYJKoZIhvcNAQEL
BQAwgYUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMQ4wDAYDVQQH
DAVQYXJpczEMMAoGA1UECgwDT0NBMQwwCgYDVQQLDANPQ0ExFDASBgNVBAMMC2V4
YW1wbGUuY29tMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMB4XDTIz
MDEwMTExMDAyN1oXDTIzMDEzMTExMDAyN1owgYUxCzAJBgNVBAYTAkFVMRMwEQYD
VQQIDApTb21lLVN0YXRlMQ4wDAYDVQQHDAVQYXJpczEMMAoGA1UECgwDT0NBMQww
CgYDVQQLDANPQ0ExFDASBgNVBAMMC2V4YW1wbGUuY29tMR8wHQYJKoZIhvcNAQkB
FhB0ZXN0QGV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAvgeLRr1Q9aS/t8ZuC7/pZRHTB6sqamVwXyR7zh0v51yH7xBy9xs4zJWKneRn
OJw46IogYhY+dyNWElbY+Ckcc6z1eJONiHNtOKAy07VtfhisGviRv1kLE56SHGgW
fIXrOuFqj6F1yTfKyLtq2RZBzmbMTNG7z89rO2hqdTWqhyof9OGWtecrM7Ei9PnL
tqULhQyh6n47KnIXfBMLIeQG7d/WyGU5CnO7yhHkS/51E9gI6g5G0VoueBVFErCl
rjo0clMJrFVpanOG2USGgLfPkomSIv9ZL4SreFN27sbhTbkVWxbk7AOCFCQcaBIv
RThpRrA9YRv2dB/X4yIi7UrrPwIDAQABo1MwUTAdBgNVHQ4EFgQU4WFoM/SL6qvT
jV4YUwH3rggBqyIwHwYDVR0jBBgwFoAU4WFoM/SL6qvTjV4YUwH3rggBqyIwDwYD
VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAYG+CTENdcmiKmyYg/n6H
59RRgaLHSszjsKYdM5Uy/PmwfvPTRuxP38UhF0gWJTsLxWRjI0ejzdy5vzvxpoYl
3pEyYxPzl+jfGA6AUSOfzTT/ksi7SZWak3cqJDFCdnfaCSjYyi+nKz5y6Bm1/fMy
/3zJX92ELIWc8cTUItUIWjlITmxgLhIGr1wYaCinxkaEopGqkX85RYFaWKyYa5ok
8MnoYbPrh/i9EekHUMBMPKWN3tWMMEROTtX9hmxSSTtgdQCahBaOCCU+m8PSNKEc
UA8nSStaolv8t6aOyEb/Kzs7WSbd7v1ovZsy2FYmIRn0eHz8fpMAw2qk7mol6iao
GQ==
-----END CERTIFICATE-----

View file

@ -0,0 +1,49 @@
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAOOSWfQt/sCwMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMjEwMzAyMjE0ODMwWhcNMjEwNDAxMjE0ODMwWjBF
MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAtBIWiINhfAGdNHiNoG9xzz/PPQvgPKKz6RirHpcx7RAaCUcnsTcwZILH
8aQ+lUbP62lIhCQ6o7/EuJt7AGxaOclM6E117o4eDQTcNPekhlqcUpFmL21V2dZg
fL0tfwtu6ny6h/pJsgy66LF1YPgUrHA6e4TFWZOIXL5KHZivuveyeyu+hb14CwzB
uvRMPgI7GeyxrhXlBsfntxKnD9cz4brnM2Eznuy8jSufvh3urCLphek9z4Bnfdsa
7I7bCTAHjkUegWWXiAcRgd+Uluhcu1xQB1h4135dehVKnoaBTgFVUl8IDsqZN1yg
/s7JzCA4iI23JEI0lHIJHTQ2GhpxtQIDAQABo1AwTjAdBgNVHQ4EFgQUXXIizCA8
7eJpzk2fI2od4Inq4CQwHwYDVR0jBBgwFoAUXXIizCA87eJpzk2fI2od4Inq4CQw
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAIohOqEpKjkLBySQYTOdO
ZkO31B3YnVXX9o2rN8ulVzfQNKcvKtGiME8ubcmrFg49sP+T3Y14hG4KfYFJ6zcC
qveitFcmQ8NXHgByTqPGi9ZDdwEBKeHCHIym0R+24hEHw59iH42f6IVtfOZOR02P
UyH1AKY2rktXNatOLysNYigf3JN6WlwahEaQ4XOrx+l0ez8H0mqR51rRUhdxuyBJ
RPhqpWqrWhm6Hmcgv17/PwEQRHsFBF/qWb6iT9Cv0IR8v5gnlimaYyF1gacHKbkb
gpit88248SSh1joHnhyoM+yTVnZiL5xAcuf5HicOmPOMLaKv6pJ/pqNz9jy0V3/U
iw==
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0EhaIg2F8AZ00
eI2gb3HPP889C+A8orPpGKselzHtEBoJRyexNzBkgsfxpD6VRs/raUiEJDqjv8S4
m3sAbFo5yUzoTXXujh4NBNw096SGWpxSkWYvbVXZ1mB8vS1/C27qfLqH+kmyDLro
sXVg+BSscDp7hMVZk4hcvkodmK+697J7K76FvXgLDMG69Ew+AjsZ7LGuFeUGx+e3
EqcP1zPhuuczYTOe7LyNK5++He6sIumF6T3PgGd92xrsjtsJMAeORR6BZZeIBxGB
35SW6Fy7XFAHWHjXfl16FUqehoFOAVVSXwgOypk3XKD+zsnMIDiIjbckQjSUcgkd
NDYaGnG1AgMBAAECggEAFpPCAYG/gkXNiRuoXjo64cpVWIkZp2CbABnYsrAwUVHY
gdtLDbwmtCN1oEWAl0TWouSDdBX6yDcuGhtcc7QiJ+amXuX/aFanS+iVF4sJNNM9
kFisoDusLPDlDh7GCozLblkPJidqgAl6kdxWJD9WkDxOCNifydhmm4I8VrOjLOTV
w5gF0kjSRJmkNbPXiSOmaZzJEuxJYR9tqpanf2Fh/6Xf50Y5Hd+25mqbKUg21lZy
AmHk5DYFZMugPFZgPzcZayr0LotlmfiExrMtrKoI2UX9J//MGrVil1bp8iVTJElJ
fDPAivpcTjO5AlHRl2dfLM4ZUub6E4bw+DDGUX04DQKBgQDYRd0jp8S44763+6sA
/F8jKNzxn5nCOkFUxMalh9+wzd433MsXj50QTVy3BGbm+mmo8ybqcQ2PVDI9mo3n
73g4eG/BeSeYSDfk1gqIP5pb9gaAYKmKrNFphCqvrbJsCwzQ+bgrd3WKAv/isIlT
756B/cl+RP6doDcTsFcBV9VjHwKBgQDVJdRnBAXgeeY8XP2sbd8Mr8Ev8Wf4IpTh
dNN3A+ekcYyBkSdXeB8j+ToAcBWM5T69gofG61V6RHvAUPTf3AU9hWs1K2Xm5+Ln
SEstDwDQ+DdcASCkgC48j2anvlla17Tf5Tcl6DaksxGHYNhT1U+hCiaDDU03mrDF
aVIFbSNEqwKBgDZ0GMLiefindxyx5BOCd53Nqxu3OKqbqlliljWVaXAF1Z6xG/2Z
rk0tfVujYxljEXl1h2XeAzEEXQX/xR0RwW5OfKz1CVAhVtlqPwqhIQdogaiPLgD5
lFyB55GGJXdorNhtF77x/Ak8yhrUoi8dFQbb1IDTdFxRu6xcaPuwlsy3AoGBAKdG
5hfmz1npMOiErkzpeVhygnHGyiqxsRfzYJYRyXSD7Jouuapqyj2oNX3seO03aHLA
AyD4xf+LyXcX0eXxvWcX0xhKM9HwgGG0mdMF6EUX2BJrjButwRukCxNwTp39laT1
Nb+ZK3E8W3Bcb8nzKWggGDNXeBdAXqS/UDCUA067AoGAUhJg9//JkziNYImCjInX
nsYOH1ojfxtm5pr9+yiobKGdtUe36MQZmW6JMfiqJVjw5NYBgykbDXwrOkK97IeR
2xtx0jcNLyadhzoWIHU/OvJRC1tsdOV30PdsiIRYbpuZjoBiDd2wODeylm9WG9Xz
N6TBKKvJflx4Sw2o7+4EfZU=
-----END PRIVATE KEY-----

View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+B4tGvVD1pL+3
xm4Lv+llEdMHqypqZXBfJHvOHS/nXIfvEHL3GzjMlYqd5Gc4nDjoiiBiFj53I1YS
Vtj4KRxzrPV4k42Ic204oDLTtW1+GKwa+JG/WQsTnpIcaBZ8hes64WqPoXXJN8rI
u2rZFkHOZsxM0bvPz2s7aGp1NaqHKh/04Za15yszsSL0+cu2pQuFDKHqfjsqchd8
Ewsh5Abt39bIZTkKc7vKEeRL/nUT2AjqDkbRWi54FUUSsKWuOjRyUwmsVWlqc4bZ
RIaAt8+SiZIi/1kvhKt4U3buxuFNuRVbFuTsA4IUJBxoEi9FOGlGsD1hG/Z0H9fj
IiLtSus/AgMBAAECggEBAKuXUFJeHL7TNzMRAMmnT28uOypPiwtr8Z5X6Vtiy6DU
0wIyDj3H3PAPkI2mcvaRSmngYAFyKJGX3N7OgTkElmZ1pWptgn3WDKf3MC4vQ2F7
kd0A20q3cuMSaskvzC5BFvmiFoD/wMYjlP7RDVhdWqqv9IbhVAAAQcnxLUANZ6CH
/xrieGuYavs62pSu5fnke7zRozdD1Mb7/oolAnycaLuoi1eZBh8wW8EJyFSxcZ5A
pYF5kNqbwAdOZ22Tygxwu7lnh8PUOKxf9pTmO6uUYAJcn/Z3ZHtnBYsjU/LkfNPV
hYLu1bKftm6UEZYwCXE3/ygop1q648NvCvtJB+Gbj9ECgYEA8nB+hS+7MLgi/dv8
FCMJ9HBN76/nlwjOCTZIyIhCs5Jc6zJQGiDNLUFM/1mpBKUWWAss3g0dmJq32ish
apsCUxabzWuKi44fDMEterJrGDWquyJK+jNPqfqOORLdMf0edNfZbjUxev7D52Ak
4Ej3Ggy/fENd8QWLK6PZHV5X1MUCgYEAyKiWlawh7l8eBrba8UFQ4n1HiK/2uEud
yQOLceSRmW/xC6ZCiR0ILinrtZWRxqQg+ZSS24hjnHhcdnRw8TRXx22TkTwGfAXW
wKesPrtGJrn0ADuZwPkGewyeHPsisXNSiuGLPcLiOCoNNYgbIWJ2RknM1Xw+2p8C
qYU8Si6l6DMCgYEA20v4ld7sExCszjZ72XcsXQhs5v+Vm9/iByEsSwA+XZJqLHFx
VYEQNvxXeq8OnN37zR4msqDogY6J+XWEH5shSiksO28ofj3LRk1DJzZWeyqoSeem
LJXXXKkAlw3COaJ9NzG8Qt0o6dmjORqVoK8/nTekyfFh+0+JaKsoDFG3XwUCgYBN
tq2Ljj0d+wzAAPXO1kMjVO3tjGj7e53CinLpS2LwkCBFKMFAJVRTvLyjeSgaTNrQ
jrBKAgrCQQNehT5wzJrqjA/JAfxo8EH6H3ZgXVuQCBjuNicYS9ossfhStRj8rPNd
AnlRFDdVFUREZVBMn7u7AT4puJMHTOpVCVsOR/7NbQKBgApyR1WfsxZYi8vzVosQ
jnMIW18lnZN3s6auyEvmpVowx0U0yd9QU3HHX1j8Kfq7D9uERCwBtIfn9fzZrZnu
Xgbi9LMUT1z1jFXxJNgzaNmm0JHW9cD24BWNeQ60uxaRiGGmCyfmgqrGOXSn2R8w
KoWEnnunZ9nehcD9dkWcH5zG
-----END PRIVATE KEY-----

View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0EhaIg2F8AZ00
eI2gb3HPP889C+A8orPpGKselzHtEBoJRyexNzBkgsfxpD6VRs/raUiEJDqjv8S4
m3sAbFo5yUzoTXXujh4NBNw096SGWpxSkWYvbVXZ1mB8vS1/C27qfLqH+kmyDLro
sXVg+BSscDp7hMVZk4hcvkodmK+697J7K76FvXgLDMG69Ew+AjsZ7LGuFeUGx+e3
EqcP1zPhuuczYTOe7LyNK5++He6sIumF6T3PgGd92xrsjtsJMAeORR6BZZeIBxGB
35SW6Fy7XFAHWHjXfl16FUqehoFOAVVSXwgOypk3XKD+zsnMIDiIjbckQjSUcgkd
NDYaGnG1AgMBAAECggEAFpPCAYG/gkXNiRuoXjo64cpVWIkZp2CbABnYsrAwUVHY
gdtLDbwmtCN1oEWAl0TWouSDdBX6yDcuGhtcc7QiJ+amXuX/aFanS+iVF4sJNNM9
kFisoDusLPDlDh7GCozLblkPJidqgAl6kdxWJD9WkDxOCNifydhmm4I8VrOjLOTV
w5gF0kjSRJmkNbPXiSOmaZzJEuxJYR9tqpanf2Fh/6Xf50Y5Hd+25mqbKUg21lZy
AmHk5DYFZMugPFZgPzcZayr0LotlmfiExrMtrKoI2UX9J//MGrVil1bp8iVTJElJ
fDPAivpcTjO5AlHRl2dfLM4ZUub6E4bw+DDGUX04DQKBgQDYRd0jp8S44763+6sA
/F8jKNzxn5nCOkFUxMalh9+wzd433MsXj50QTVy3BGbm+mmo8ybqcQ2PVDI9mo3n
73g4eG/BeSeYSDfk1gqIP5pb9gaAYKmKrNFphCqvrbJsCwzQ+bgrd3WKAv/isIlT
756B/cl+RP6doDcTsFcBV9VjHwKBgQDVJdRnBAXgeeY8XP2sbd8Mr8Ev8Wf4IpTh
dNN3A+ekcYyBkSdXeB8j+ToAcBWM5T69gofG61V6RHvAUPTf3AU9hWs1K2Xm5+Ln
SEstDwDQ+DdcASCkgC48j2anvlla17Tf5Tcl6DaksxGHYNhT1U+hCiaDDU03mrDF
aVIFbSNEqwKBgDZ0GMLiefindxyx5BOCd53Nqxu3OKqbqlliljWVaXAF1Z6xG/2Z
rk0tfVujYxljEXl1h2XeAzEEXQX/xR0RwW5OfKz1CVAhVtlqPwqhIQdogaiPLgD5
lFyB55GGJXdorNhtF77x/Ak8yhrUoi8dFQbb1IDTdFxRu6xcaPuwlsy3AoGBAKdG
5hfmz1npMOiErkzpeVhygnHGyiqxsRfzYJYRyXSD7Jouuapqyj2oNX3seO03aHLA
AyD4xf+LyXcX0eXxvWcX0xhKM9HwgGG0mdMF6EUX2BJrjButwRukCxNwTp39laT1
Nb+ZK3E8W3Bcb8nzKWggGDNXeBdAXqS/UDCUA067AoGAUhJg9//JkziNYImCjInX
nsYOH1ojfxtm5pr9+yiobKGdtUe36MQZmW6JMfiqJVjw5NYBgykbDXwrOkK97IeR
2xtx0jcNLyadhzoWIHU/OvJRC1tsdOV30PdsiIRYbpuZjoBiDd2wODeylm9WG9Xz
N6TBKKvJflx4Sw2o7+4EfZU=
-----END PRIVATE KEY-----

View file

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAOOSWfQt/sCwMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMjEwMzAyMjE0ODMwWhcNMjEwNDAxMjE0ODMwWjBF
MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAtBIWiINhfAGdNHiNoG9xzz/PPQvgPKKz6RirHpcx7RAaCUcnsTcwZILH
8aQ+lUbP62lIhCQ6o7/EuJt7AGxaOclM6E117o4eDQTcNPekhlqcUpFmL21V2dZg
fL0tfwtu6ny6h/pJsgy66LF1YPgUrHA6e4TFWZOIXL5KHZivuveyeyu+hb14CwzB
uvRMPgI7GeyxrhXlBsfntxKnD9cz4brnM2Eznuy8jSufvh3urCLphek9z4Bnfdsa
7I7bCTAHjkUegWWXiAcRgd+Uluhcu1xQB1h4135dehVKnoaBTgFVUl8IDsqZN1yg
/s7JzCA4iI23JEI0lHIJHTQ2GhpxtQIDAQABo1AwTjAdBgNVHQ4EFgQUXXIizCA8
7eJpzk2fI2od4Inq4CQwHwYDVR0jBBgwFoAUXXIizCA87eJpzk2fI2od4Inq4CQw
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAIohOqEpKjkLBySQYTOdO
ZkO31B3YnVXX9o2rN8ulVzfQNKcvKtGiME8ubcmrFg49sP+T3Y14hG4KfYFJ6zcC
qveitFcmQ8NXHgByTqPGi9ZDdwEBKeHCHIym0R+24hEHw59iH42f6IVtfOZOR02P
UyH1AKY2rktXNatOLysNYigf3JN6WlwahEaQ4XOrx+l0ez8H0mqR51rRUhdxuyBJ
RPhqpWqrWhm6Hmcgv17/PwEQRHsFBF/qWb6iT9Cv0IR8v5gnlimaYyF1gacHKbkb
gpit88248SSh1joHnhyoM+yTVnZiL5xAcuf5HicOmPOMLaKv6pJ/pqNz9jy0V3/U
iw==
-----END CERTIFICATE-----

View file

@ -0,0 +1,177 @@
import os
from urllib.parse import parse_qs, urlparse
from saml2 import BINDING_HTTP_POST, BINDING_HTTP_REDIRECT, pack
from saml2.authn_context import INTERNETPROTOCOLPASSWORD
from saml2.config import Config as Saml2Config
from saml2.metadata import create_metadata_string
from saml2.saml import NAME_FORMAT_URI, NAMEID_FORMAT_PERSISTENT
from saml2.server import Server
TYP = {"GET": [BINDING_HTTP_REDIRECT], "POST": [BINDING_HTTP_POST]}
AUTHN = {
"class_ref": INTERNETPROTOCOLPASSWORD,
"authn_auth": "http://www.example.com/login",
}
BASE = "http://localhost:8000"
CONFIG = {
"entityid": "urn:mace:example.com:saml:example:idp",
"name": "Rolands IdP",
"service": {
"aa": {
"endpoints": {
"attribute_service": [
("%s/aap" % BASE, BINDING_HTTP_POST),
]
},
},
"aq": {
"endpoints": {
"authn_query_service": [("%s/aqs" % BASE, BINDING_HTTP_POST)]
},
},
"idp": {
"endpoints": {
"single_sign_on_service": [
("%s/sso/redirect" % BASE, BINDING_HTTP_REDIRECT),
("%s/sso/post" % BASE, BINDING_HTTP_POST),
],
},
"policy": {
"default": {
"lifetime": {"minutes": 15},
"attribute_restrictions": None,
"name_form": NAME_FORMAT_URI,
},
"urn:mace:example.com:saml:example:sp": {
"lifetime": {"minutes": 5},
"nameid_format": NAMEID_FORMAT_PERSISTENT,
},
},
},
},
"debug": 1,
"key_file": os.path.join(os.path.dirname(__file__), "data", "idp.pem"),
"cert_file": os.path.join(os.path.dirname(__file__), "data", "idp.pem"),
"organization": {
"name": "Example",
"display_name": [("Example", "uk")],
"url": "http://www.example.com/",
},
"contact_person": [
{
"given_name": "Admin",
"sur_name": "Admin",
"email_address": ["admin@example.com"],
"contact_type": "technical",
},
],
}
class DummyResponse:
def __init__(self, status, data, headers=None):
self.status_code = status
self.text = data
self.headers = headers or []
self.content = data
def _unpack(self, ver="SAMLResponse"):
"""
Unpack the response form
"""
_str = self.text
sr_str = 'name="%s" value="' % ver
rs_str = 'name="RelayState" value="'
i = _str.find(sr_str)
i += len(sr_str)
j = _str.find('"', i)
sr = _str[i:j]
start = _str.find(rs_str, j)
start += len(rs_str)
end = _str.find('"', start)
rs = _str[start:end]
return {ver: sr, "RelayState": rs}
class FakeIDP(Server):
def __init__(self, metadatas=None, settings=None):
if settings is None:
settings = CONFIG
if metadatas:
settings.update({"metadata": {"inline": metadatas}})
config = Saml2Config()
config.load(settings)
config.allow_unknown_attributes = True
Server.__init__(self, config=config)
def get_metadata(self):
return create_metadata_string(
None,
config=self.config,
sign=True,
valid=True,
cert=CONFIG.get("cert_file"),
keyfile=CONFIG.get("key_file"),
)
def fake_login(self, url):
# Assumes GET query and HTTP_REDIRECT only.
# This is all that auth_pysaml currently supports.
parsed_url = urlparse(url)
qs_dict = parse_qs(parsed_url.query)
samlreq = qs_dict["SAMLRequest"][0]
rstate = qs_dict["RelayState"][0]
# process the logon request, and automatically "login"
return self.authn_request_endpoint(samlreq, BINDING_HTTP_REDIRECT, rstate)
def authn_request_endpoint(self, req, binding, relay_state):
req = self.parse_authn_request(req, binding)
if req.message.protocol_binding == BINDING_HTTP_REDIRECT:
_binding = BINDING_HTTP_POST
else:
_binding = req.message.protocol_binding
resp_args = self.response_args(req.message, [_binding])
identity = {
"surName": "Example",
"givenName": "Test",
"title": "Ind",
"mail": "test@example.com",
}
resp_args.update({"sign_assertion": True, "sign_response": True})
authn_resp = self.create_authn_response(
identity, userid=identity.get("mail"), authn=AUTHN, **resp_args
)
_dict = pack.factory(
_binding, authn_resp, resp_args["destination"], relay_state, "SAMLResponse"
)
return DummyResponse(**_dict)
class UnsignedFakeIDP(FakeIDP):
def create_authn_response(
self,
*args,
**kwargs,
):
kwargs["sign_assertion"] = False
return super().create_authn_response(*args, **kwargs)

View file

@ -0,0 +1,488 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import base64
import html
import os
import os.path as osp
from copy import deepcopy
from unittest.mock import patch
import responses
from saml2.sigver import SignatureError
from odoo.exceptions import AccessDenied, UserError, ValidationError
from odoo.tests import HttpCase, tagged
from odoo.tools import mute_logger
from .fake_idp import CONFIG, FakeIDP, UnsignedFakeIDP
@tagged("saml", "post_install", "-at_install")
class TestPySaml(HttpCase):
def setUp(self):
super().setUp()
sp_pem_public = None
sp_pem_private = None
with open(
os.path.join(os.path.dirname(__file__), "data", "sp.pem"),
"r",
encoding="UTF-8",
) as file:
sp_pem_public = file.read()
with open(
os.path.join(os.path.dirname(__file__), "data", "sp.key"),
"r",
encoding="UTF-8",
) as file:
sp_pem_private = file.read()
self.saml_provider = self.env["auth.saml.provider"].create(
{
"name": "SAML Provider Demo",
"idp_metadata": FakeIDP().get_metadata(),
"sp_pem_public": base64.b64encode(sp_pem_public.encode()),
"sp_pem_private": base64.b64encode(sp_pem_private.encode()),
"body": "Login with Authentic",
"active": True,
"sig_alg": "SIG_RSA_SHA1",
"matching_attribute": "mail",
}
)
self.url_saml_request = (
"/auth_saml/get_auth_request?pid=%d" % self.saml_provider.id
)
self.idp = FakeIDP([self.saml_provider._metadata_string()])
# Create a user with only password, and another with both password and saml id
self.user, self.user2 = (
self.env["res.users"]
.with_context(no_reset_password=True, tracking_disable=True)
.create(
[
{
"name": "User",
"email": "test@example.com",
"login": "test@example.com",
"password": "Lu,ums-7vRU>0i]=YDLa",
},
{
"name": "User with SAML",
"email": "user@example.com",
"login": "user@example.com",
"password": "NesTNSte9340D720te>/-A",
"saml_ids": [
(
0,
0,
{
"saml_provider_id": self.saml_provider.id,
"saml_uid": "user@example.com",
},
)
],
},
]
)
)
def test_ensure_provider_appears_on_login(self):
# SAML provider should be listed in the login page
response = self.url_open("/web/login")
self.assertIn("Login with Authentic", response.text)
self.assertIn(self.url_saml_request, response.text)
def test_ensure_provider_appears_on_login_with_redirect_param(self):
"""Test that SAML provider is listed in the login page keeping the redirect"""
response = self.url_open(
"/web/login?redirect=%2Fweb%23action%3D37%26model%3Dir.module.module%26view"
"_type%3Dkanban%26menu_id%3D5"
)
self.assertIn("Login with Authentic", response.text)
self.assertIn(
"/auth_saml/get_auth_request?pid={}&amp;redirect=%2Fweb%23action%3D37%26mod"
"el%3Dir.module.module%26view_type%3Dkanban%26menu_id%3D5".format(
self.saml_provider.id
),
response.text,
)
def test_ensure_metadata_present(self):
response = self.url_open(
"/auth_saml/metadata?p=%d&d=%s"
% (self.saml_provider.id, self.env.cr.dbname)
)
self.assertTrue(response.ok)
self.assertTrue("xml" in response.headers.get("Content-Type"))
def test_ensure_get_auth_request_redirects(self):
response = self.url_open(
"/auth_saml/get_auth_request?pid=%d" % self.saml_provider.id,
allow_redirects=False,
)
self.assertTrue(response.ok)
self.assertEqual(response.status_code, 303)
self.assertIn(
"http://localhost:8000/sso/redirect?SAMLRequest=",
response.headers.get("Location"),
)
def test_login_no_saml(self):
"""
Login with a user account, but without any SAML provider setup
against the user
"""
# Standard login using password
self.authenticate(user="test@example.com", password="Lu,ums-7vRU>0i]=YDLa")
self.assertEqual(self.session.uid, self.user.id)
self.logout()
# Try to log in with a non-existing SAML token
with self.assertRaises(AccessDenied):
self.authenticate(user="test@example.com", password="test_saml_token")
redirect_url = self.saml_provider._get_auth_request()
self.assertIn("http://localhost:8000/sso/redirect?SAMLRequest=", redirect_url)
response = self.idp.fake_login(redirect_url)
self.assertEqual(200, response.status_code)
unpacked_response = response._unpack()
with self.assertRaises(AccessDenied):
self.env["res.users"].sudo().auth_saml(
self.saml_provider.id, unpacked_response.get("SAMLResponse"), None
)
def add_provider_to_user(self):
"""Add a provider to self.user"""
self.user.write(
{
"saml_ids": [
(
0,
0,
{
"saml_provider_id": self.saml_provider.id,
"saml_uid": "test@example.com",
},
)
]
}
)
def test_login_with_saml(self):
self.add_provider_to_user()
redirect_url = self.saml_provider._get_auth_request()
self.assertIn("http://localhost:8000/sso/redirect?SAMLRequest=", redirect_url)
response = self.idp.fake_login(redirect_url)
self.assertEqual(200, response.status_code)
unpacked_response = response._unpack()
(database, login, token) = (
self.env["res.users"]
.sudo()
.auth_saml(
self.saml_provider.id, unpacked_response.get("SAMLResponse"), None
)
)
self.assertEqual(database, self.env.cr.dbname)
self.assertEqual(login, self.user.login)
# We should not be able to log in with the wrong token
with self.assertRaises(AccessDenied):
self.authenticate(
user="test@example.com", password="{}-WRONG".format(token)
)
# User should now be able to log in with the token
self.authenticate(user="test@example.com", password=token)
def test_disallow_user_password_when_changing_ir_config_parameter(self):
"""Test that disabling users from having both a password and SAML ids remove
users password."""
# change the option
self.browse_ref(
"auth_saml.allow_saml_uid_and_internal_password"
).value = "False"
# The password should be blank and the user should not be able to connect
with self.assertRaises(AccessDenied):
self.authenticate(
user="user@example.com", password="NesTNSte9340D720te>/-A"
)
def test_disallow_user_password_new_user(self):
"""Test that a new user can not be set up with both password and SAML ids when
the disallow option is set."""
# change the option
self.browse_ref(
"auth_saml.allow_saml_uid_and_internal_password"
).value = "False"
with self.assertRaises(UserError):
self.env["res.users"].with_context(no_reset_password=True).create(
{
"name": "New user with SAML",
"email": "user2@example.com",
"login": "user2@example.com",
"password": "NesTNSte9340D720te>/-A",
"saml_ids": [
(
0,
0,
{
"saml_provider_id": self.saml_provider.id,
"saml_uid": "user2",
},
)
],
}
)
def test_disallow_user_password_no_password_set(self):
"""Test that a new user with SAML ids can not have its password set up when the
disallow option is set."""
# change the option
self.browse_ref(
"auth_saml.allow_saml_uid_and_internal_password"
).value = "False"
# Create a new user with only SAML ids
user = (
self.env["res.users"]
.with_context(no_reset_password=True, tracking_disable=True)
.create(
{
"name": "New user with SAML",
"email": "user2@example.com",
"login": "user2@example.com",
"saml_ids": [
(
0,
0,
{
"saml_provider_id": self.saml_provider.id,
"saml_uid": "unused",
},
)
],
}
)
)
# Assert that the user password can not be set
with self.assertRaises(ValidationError):
user.password = "new password"
def test_disallow_user_password(self):
"""Test that existing user password is deleted when adding an SAML provider when
the disallow option is set."""
# change the option
self.browse_ref(
"auth_saml.allow_saml_uid_and_internal_password"
).value = "False"
# Test that existing user password is deleted when adding an SAML provider
self.authenticate(user="test@example.com", password="Lu,ums-7vRU>0i]=YDLa")
self.add_provider_to_user()
with self.assertRaises(AccessDenied):
self.authenticate(user="test@example.com", password="Lu,ums-7vRU>0i]=YDLa")
def test_disallow_user_admin_can_have_password(self):
"""Test that admin can have its password set even if the disallow option is set."""
# change the option
self.browse_ref(
"auth_saml.allow_saml_uid_and_internal_password"
).value = "False"
# Test base.user_admin exception
self.env.ref("base.user_admin").password = "nNRST4j*->sEatNGg._!"
def test_db_filtering(self):
# change filter to only allow our db.
with patch("odoo.http.db_filter", new=lambda *args, **kwargs: []):
self.add_provider_to_user()
redirect_url = self.saml_provider._get_auth_request()
response = self.idp.fake_login(redirect_url)
unpacked_response = response._unpack()
for key in unpacked_response:
unpacked_response[key] = html.unescape(unpacked_response[key])
response = self.url_open("/auth_saml/signin", data=unpacked_response)
self.assertFalse(response.ok)
self.assertIn(response.status_code, [400, 404])
def test_redirect_after_login(self):
"""Test that providing a redirect will be kept after SAML login."""
self.add_provider_to_user()
redirect_url = self.saml_provider._get_auth_request(
{
"r": "%2Fweb%23action%3D37%26model%3Dir.module.module%26view_type%3Dkan"
"ban%26menu_id%3D5"
}
)
response = self.idp.fake_login(redirect_url)
unpacked_response = response._unpack()
for key in unpacked_response:
unpacked_response[key] = html.unescape(unpacked_response[key])
response = self.url_open(
"/auth_saml/signin",
data=unpacked_response,
allow_redirects=True,
timeout=300,
)
self.assertTrue(response.ok)
self.assertEqual(
response.url,
self.base_url()
+ "/web#action=37&model=ir.module.module&view_type=kanban&menu_id=5",
)
def test_disallow_user_password_when_changing_settings(self):
"""Test that disabling the setting will remove passwords from related users"""
# We activate the settings to allow password login
self.env["res.config.settings"].create(
{
"allow_saml_uid_and_internal_password": True,
}
).execute()
# Test the user can login with the password
self.authenticate(user="user@example.com", password="NesTNSte9340D720te>/-A")
self.env["res.config.settings"].create(
{
"allow_saml_uid_and_internal_password": False,
}
).execute()
with self.assertRaises(AccessDenied):
self.authenticate(
user="user@example.com", password="NesTNSte9340D720te>/-A"
)
@responses.activate
def test_download_metadata(self):
expected_metadata = self.idp.get_metadata()
responses.add(
responses.GET,
"http://localhost:8000/metadata",
status=200,
content_type="text/xml",
body=expected_metadata,
)
self.saml_provider.idp_metadata_url = "http://localhost:8000/metadata"
self.saml_provider.idp_metadata = ""
self.saml_provider.action_refresh_metadata_from_url()
self.assertEqual(self.saml_provider.idp_metadata, expected_metadata)
@responses.activate
def test_download_metadata_no_provider(self):
self.saml_provider.idp_metadata_url = "http://localhost:8000/metadata"
self.saml_provider.idp_metadata = ""
self.saml_provider.active = False
self.saml_provider.action_refresh_metadata_from_url()
self.assertFalse(self.saml_provider.idp_metadata)
@responses.activate
def test_download_metadata_error(self):
responses.add(
responses.GET,
"http://localhost:8000/metadata",
status=500,
content_type="text/xml",
)
self.saml_provider.idp_metadata_url = "http://localhost:8000/metadata"
self.saml_provider.idp_metadata = ""
with self.assertRaises(UserError):
self.saml_provider.action_refresh_metadata_from_url()
self.assertFalse(self.saml_provider.idp_metadata)
@responses.activate
def test_download_metadata_no_update(self):
expected_metadata = self.idp.get_metadata()
responses.add(
responses.GET,
"http://localhost:8000/metadata",
status=200,
content_type="text/xml",
body=expected_metadata,
)
self.saml_provider.idp_metadata_url = "http://localhost:8000/metadata"
self.saml_provider.idp_metadata = expected_metadata
self.saml_provider.action_refresh_metadata_from_url()
self.assertEqual(self.saml_provider.idp_metadata, expected_metadata)
@responses.activate
def test_login_with_saml_metadata_empty(self):
self.saml_provider.idp_metadata_url = "http://localhost:8000/metadata"
self.saml_provider.idp_metadata = ""
expected_metadata = self.idp.get_metadata()
responses.add(
responses.GET,
"http://localhost:8000/metadata",
status=200,
content_type="text/xml",
body=expected_metadata,
)
self.test_login_with_saml()
self.assertEqual(self.saml_provider.idp_metadata, expected_metadata)
@responses.activate
def test_login_with_saml_metadata_key_changed(self):
settings = deepcopy(CONFIG)
settings["key_file"] = osp.join(
osp.dirname(__file__), "data", "key_idp_expired.pem"
)
settings["cert"] = osp.join(
osp.dirname(__file__), "data", "key_idp_expired.pem"
)
expired_idp = FakeIDP(settings=settings)
self.saml_provider.idp_metadata = expired_idp.get_metadata()
self.saml_provider.idp_metadata_url = "http://localhost:8000/metadata"
up_to_date_metadata = self.idp.get_metadata()
self.assertNotEqual(self.saml_provider.idp_metadata, up_to_date_metadata)
responses.add(
responses.GET,
"http://localhost:8000/metadata",
status=200,
content_type="text/xml",
body=up_to_date_metadata,
)
self.test_login_with_saml()
@responses.activate
def test_login_with_saml_unsigned_response(self):
self.add_provider_to_user()
self.saml_provider.idp_metadata_url = "http://localhost:8000/metadata"
unsigned_idp = UnsignedFakeIDP([self.saml_provider._metadata_string()])
redirect_url = self.saml_provider._get_auth_request()
self.assertIn("http://localhost:8000/sso/redirect?SAMLRequest=", redirect_url)
response = unsigned_idp.fake_login(redirect_url)
self.assertEqual(200, response.status_code)
unpacked_response = response._unpack()
responses.add(
responses.GET,
"http://localhost:8000/metadata",
status=200,
content_type="text/xml",
body=self.saml_provider.idp_metadata,
)
with (
self.assertRaises(SignatureError),
mute_logger("saml2.entity"),
mute_logger("saml2.client_base"),
):
(database, login, token) = (
self.env["res.users"]
.sudo()
.auth_saml(
self.saml_provider.id, unpacked_response.get("SAMLResponse"), None
)
)

View file

@ -0,0 +1,218 @@
<?xml version="1.0" ?>
<odoo>
<!-- login form button -->
<template id="auth_saml.providers" name="Auth SAML Providers">
<t t-if="saml_providers">
<em
t-attf-class="d-block text-center text-muted small my-#{len(saml_providers) if len(saml_providers) &lt; 3 else 3}"
>- or -</em>
<div class="o_login_saml2 list-group mt-1 mb-1 text-left">
<a
t-foreach="saml_providers"
t-as="p"
class="list-group-item list-group-item-action py-2"
t-att-href="p['auth_link']"
>
<i t-att-class="p['css_class']" />
<t t-out="p['body']" />
</a>
</div>
</t>
</template>
<template id="auth_saml.login" inherit_id="web.login" name="Samlv2 Login buttons">
<xpath expr="//form" position="before">
<t t-set="form_small" t-value="len(saml_providers) &gt; 2" />
</xpath>
<xpath expr="//div[hasclass('o_login_auth')]" position="inside">
<t t-call="auth_saml.providers" />
</xpath>
</template>
<!-- Views for the auth.saml.provider model. -->
<record model="ir.ui.view" id="auth_saml_provider_view_search">
<field name="name">auth.saml.provider.search</field>
<field name="model">auth.saml.provider</field>
<field name="arch" type="xml">
<search string="Providers">
<field name="name" />
<field name="entity_id" />
<separator />
<filter
name="archived"
string="Archived"
domain="[('active', '=', False)]"
/>
</search>
</field>
</record>
<record model="ir.ui.view" id="view_saml_provider_list">
<field name="name">auth.saml.provider.list</field>
<field name="model">auth.saml.provider</field>
<field name="arch" type="xml">
<tree decoration-muted="not active">
<field name="sequence" widget="handle" />
<field name="name" />
<field name="active" widget="boolean_toggle" />
<field name="autoredirect" />
</tree>
</field>
</record>
<record model="ir.ui.view" id="view_saml_provider_form">
<field name="name">auth.saml.provider.form</field>
<field name="model">auth.saml.provider</field>
<field name="arch" type="xml">
<form string="SAML Provider">
<sheet>
<widget
name="web_ribbon"
title="Archived"
bg_color="bg-danger"
attrs="{'invisible': [('active', '=', True)]}"
/>
<div class="oe_title">
<label for="name" class="oe_edit_only" />
<h1>
<field name="name" required="1" />
</h1>
</div>
<group name="idp_settings">
<group string="Identity Provider Settings">
<span>
<label for="idp_metadata_url" />
<div>
<field name="idp_metadata_url" />
<p
class="help small"
>If you provider gives you a URL, use this field preferably</p>
</div>
<button
type="object"
string="Refresh"
name="action_refresh_metadata_from_url"
attrs="{'invisible': [('idp_metadata_url', '=', False)]}"
/>
</span>
<label for="idp_metadata" />
<div>
<field
name="idp_metadata"
widget="ace"
options="{'mode': 'xml'}"
/>
<p
class="help small"
>Your provider will give you this XML once configured.</p>
</div>
<label for="matching_attribute" />
<div>
<field name="matching_attribute" />
<p
class="help small"
>Attribute to match the user in Odoo with against the IDP (Identity Provider). You may use the special case "subject.nameId" to match against the nameId in the IDP response.</p>
</div>
<field name="matching_attribute_to_lower" />
</group>
<group string="Odoo Settings" name="odoo_settings">
<field name="active" invisible="1" />
<label for="autoredirect" />
<div>
<field name="autoredirect" />
</div>
<label for="sp_baseurl" />
<div>
<field name="sp_baseurl" widget="url" />
<p
class="help small"
>The URL configured for the ACS must exactly match what is sent. If you have odoo responding on multiple URLs you can use this to force it to send a specific address rather than rely on automatically detecting.</p>
<p
class="help small"
>Your ACS url will be base_url + /auth_saml/signin</p>
</div>
<field name="sign_metadata" />
<label for="sp_metadata_url" />
<div>
<field name="sp_metadata_url" widget="url" />
<p
class="help small"
>Available after first save. The URL will change if the provider is deleted &amp; recreated or the database is renamed.</p>
</div>
<label for="entity_id" />
<div>
<field name="entity_id" />
<p
class="help small"
>Entity Identifier sent to the IDP. Often this would be the metadata URL, but it can be any string.</p>
</div>
<field name="sp_pem_public_filename" invisible="1" />
<label for="sp_pem_public" />
<div>
<field
name="sp_pem_public"
filename="sp_pem_public_filename"
/>
<p
class="help small"
>Used to sign requests sent to the IDP. You can use openssl to generate a certificate and key.</p>
</div>
<field name="sp_pem_private_filename" invisible="1" />
<label for="sp_pem_private" />
<div>
<field
name="sp_pem_private"
filename="sp_pem_private_filename"
/>
<p
class="help small"
>Used to sign requests sent to the IDP. You can use openssl to generate a certificate and key.</p>
</div>
<label for="sig_alg" />
<div>
<field name="sig_alg" />
<p class="help small">
Algorithm used to sign requests.
</p>
</div>
<field name="sign_authenticate_requests" />
<field name="authn_requests_signed" />
<field name="logout_requests_signed" />
<field name="want_assertions_signed" />
<field name="want_response_signed" />
<field name="want_assertions_or_response_signed" />
<label for="attribute_mapping_ids" />
<div>
<field
name="attribute_mapping_ids"
context="{'default_provider_id': active_id}"
>
<tree editable="bottom">
<field name="attribute_name" />
<field name="field_name" widget="selection" />
</tree>
</field>
<p class="help small">
Mapped attributes are copied from the SAML response at every logon, if available. If multiple values are returned (i.e. a list) then the first value is used.
</p>
</div>
</group>
<group string="Display Settings" name="display_settings">
<field name="body" />
<field name="css_class" />
</group>
</group>
</sheet>
</form>
</field>
</record>
<!-- Menu command to open the SAML provider list. -->
<record model="ir.actions.act_window" id="action_saml_provider">
<field name="name">Providers</field>
<field name="res_model">auth.saml.provider</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem
id="menu_saml_providers"
name="SAML Providers"
parent="base.menu_users"
sequence="30"
action="action_saml_provider"
/>
</odoo>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" ?>
<odoo>
<!-- Inherit from the configuration form to add a setting. -->
<record id="auth_saml_base_settings_form" model="ir.ui.view">
<field name="name">auth_saml_base_settings_form</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath expr="//div[@id='module_auth_oauth']" position="after">
<div class="col-12 col-sm-6 o_setting_box" id="module_auth_saml">
<div class="o_setting_left_pane">
<field name="allow_saml_uid_and_internal_password" />
</div>
<div class="o_setting_right_pane">
<label for="allow_saml_uid_and_internal_password" />
</div>
</div>
</xpath>
</field>
</record>
</odoo>

View file

@ -0,0 +1,24 @@
<?xml version="1.0" ?>
<odoo>
<!-- Inherit from the user form to add an SAML tab. -->
<record id="view_users_form" model="ir.ui.view">
<field name="name">res.users.form</field>
<field name="model">res.users</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_users_form" />
<field name="arch" type="xml">
<xpath expr="//page[@name='access_rights']" position="after">
<page string="SAML">
<group>
<field name="saml_ids" nolabel="1" colspan="2">
<tree editable="bottom">
<field name="saml_provider_id" />
<field name="saml_uid" />
</tree>
</field>
</group>
</page>
</xpath>
</field>
</record>
</odoo>