Skip to content

Commit

Permalink
[18.0][MIG] auth_saml: Migration to 18.0
Browse files Browse the repository at this point in the history
  • Loading branch information
BT-dlagin committed Jan 14, 2025
1 parent 1551708 commit 3d71df6
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 44 deletions.
2 changes: 1 addition & 1 deletion auth_saml/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

{
"name": "SAML2 Authentication",
"version": "17.0.1.0.0",
"version": "18.0.1.0.0",
"category": "Tools",
"author": "XCG Consulting, Odoo Community Association (OCA)",
"maintainers": ["vincent-hatakeyama"],
Expand Down
35 changes: 19 additions & 16 deletions auth_saml/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@
exceptions,
http,
models,
)
from odoo import (
registry as registry_get,
modules,
)
from odoo.http import request
from odoo.tools.misc import clean_context
Expand Down Expand Up @@ -173,10 +171,9 @@ def _get_saml_extra_relaystate(self):
}
return state

@http.route("/auth_saml/get_auth_request", type="http", auth="none")
@http.route("/auth_saml/get_auth_request", type="http", auth="none", readonly=False)
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("/")
Expand All @@ -191,7 +188,9 @@ def get_auth_request(self, pid):
redirect.autocorrect_location_header = True
return redirect

@http.route("/auth_saml/signin", type="http", auth="none", csrf=False)
@http.route(
"/auth_saml/signin", type="http", auth="none", csrf=False, readonly=False
)
@fragment_to_query_string
def signin(self, **kw):
"""
Expand Down Expand Up @@ -241,16 +240,20 @@ def signin(self, **kw):
url = f"/#action={action}"
elif menu:
url = f"/#menu_id={menu}"
pre_uid = request.session.authenticate(*credentials)

credentials_dict = {
"login": credentials[1],
"token": credentials[2],
"type": "saml_token",
}
pre_uid = request.session.authenticate(dbname, credentials_dict)
resp = request.redirect(_get_login_redirect_url(pre_uid, url), 303)
resp.autocorrect_location_header = False
return resp

except exceptions.AccessDenied:

Check warning on line 254 in auth_saml/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

auth_saml/controllers/main.py#L254

Added line #L254 was not covered by tests
# saml credentials not valid,
# user could be on a temporary session
# 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
Expand All @@ -265,25 +268,25 @@ def signin(self, **kw):
redirect.autocorrect_location_header = False
return redirect

Check warning on line 269 in auth_saml/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

auth_saml/controllers/main.py#L267-L269

Added lines #L267 - L269 were not covered by tests

@http.route("/auth_saml/metadata", type="http", auth="none", csrf=False)
@http.route(
"/auth_saml/metadata", type="http", auth="none", csrf=False, readonly=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"))
raise request.not_found(_("Missing parameters"))

provider = int(provider)

registry = registry_get(dbname)

with registry.cursor() as cr:
with modules.registry.Registry(dbname).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"))
raise request.not_found(_("Unknown provider"))

return request.make_response(
client._metadata_string(
Expand Down
2 changes: 1 addition & 1 deletion auth_saml/models/auth_saml_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ def _compute_sp_metadata_url(self):
qs = urllib.parse.urlencode({"p": record.id, "d": self.env.cr.dbname})

record.sp_metadata_url = urllib.parse.urljoin(
base_url, f"/auth_saml/metadata?{qs}"
base_url, (f"/auth_saml/metadata?{qs}")
)

def _get_cert_key_path(self, field="sp_pem_public"):
Expand Down
19 changes: 12 additions & 7 deletions auth_saml/models/res_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import passlib

from odoo import SUPERUSER_ID, _, api, fields, models, registry, tools
from odoo import SUPERUSER_ID, _, api, fields, models, modules, tools
from odoo.exceptions import AccessDenied, ValidationError

from .ir_config_parameter import ALLOW_SAML_UID_AND_PASSWORD
Expand Down Expand Up @@ -48,7 +48,7 @@ def _auth_saml_signin(self, provider: int, validation: dict, saml_response) -> s
if len(user) != 1:
raise AccessDenied()

with registry(self.env.cr.dbname).cursor() as new_cr:
with modules.registry.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
Expand Down Expand Up @@ -76,7 +76,7 @@ def auth_saml(self, provider: int, saml_response: str, base_url: str = None):
# return user credentials
return self.env.cr.dbname, login, saml_response

def _check_credentials(self, password, env):
def _check_credentials(self, credential, env):
"""Override to handle SAML auths.
The token can be a password if the user has used the normal form...
Expand All @@ -85,9 +85,10 @@ def _check_credentials(self, password, env):
"""
try:
# Attempt a regular login (via other auth addons) first.
return super()._check_credentials(password, env)

return super()._check_credentials(credential, env)
except (AccessDenied, passlib.exc.PasswordSizeError):
if not (credential["type"] == "saml_token" and credential["token"]):
raise
passwd_allowed = (
env["interactive"] or not self.env.user._rpc_api_keys_only()
)
Expand All @@ -100,12 +101,16 @@ def _check_credentials(self, password, env):
.search(
[
("user_id", "=", self.env.user.id),
("saml_access_token", "=", password),
("saml_access_token", "=", credential["token"]),
]
)
)
if token:
return
return {
"uid": self.env.user.id,
"auth_method": "saml",
"mfa": "default",
}
raise AccessDenied() from None

Check warning on line 114 in auth_saml/models/res_users.py

View check run for this annotation

Codecov / codecov/patch

auth_saml/models/res_users.py#L114

Added line #L114 was not covered by tests

@api.model
Expand Down
Loading

0 comments on commit 3d71df6

Please sign in to comment.