Skip to content

Commit

Permalink
[IMP] authenticated_partner_id in ServiceContextProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
qgroulard committed Oct 11, 2021
1 parent 49bc333 commit b17b817
Show file tree
Hide file tree
Showing 16 changed files with 260 additions and 5 deletions.
1 change: 1 addition & 0 deletions base_rest/components/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from . import service
from . import service_context_provider
from . import cerberus_validator
from . import user_component_context_provider
16 changes: 12 additions & 4 deletions base_rest/components/service_context_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,17 @@ class BaseRestServiceContextProvider(Component):
_name = "base.rest.service.context.provider"
_usage = "component_context_provider"

@property
def request(self):
return self.work.request
def __init__(self, work_context):
super().__init__(work_context)
self.request = work_context.request
# pylint: disable=assignment-from-none
self.authenticated_partner_id = self._get_authenticated_partner_id()

def _get_authenticated_partner_id(self):
return None

def _get_component_context(self):
return {"request": self.request}
return {
"request": self.request,
"authenticated_partner_id": self.authenticated_partner_id,
}
12 changes: 12 additions & 0 deletions base_rest/components/user_component_context_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2021 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).


from odoo.addons.component.core import AbstractComponent


class AbstractUserAuthenticatedPartnerProvider(AbstractComponent):
_name = "abstract.user.authenticated.partner.provider"

def _get_authenticated_partner_id(self):
return self.env.user.partner_id.id
7 changes: 7 additions & 0 deletions base_rest/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,13 @@ def work_on_component(self):
"""
collection = self.collection
params = self._get_component_context()
env = collection.env
collection.env = env(
context=dict(
env.context,
authenticated_partner_id=params.get("authenticated_partner_id"),
)
)
yield WorkContext(
model_name="rest.service.registration", collection=collection, **params
)
Expand Down
1 change: 1 addition & 0 deletions base_rest/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import ir_rule
from . import rest_service_registration
27 changes: 27 additions & 0 deletions base_rest/models/ir_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright 2021 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import api, models


class IrRule(models.Model):
"""Add authenticated_partner_id in record rule evaluation context.
This come from the env context, which is populated by the base_rest service layer
context provider.
"""

_inherit = "ir.rule"

@api.model
def _eval_context(self):
ctx = super()._eval_context()
if "authenticated_partner_id" in self.env.context:
ctx["authenticated_partner_id"] = self.env.context[
"authenticated_partner_id"
]
return ctx

def _compute_domain_keys(self):
"""Return the list of context keys to use for caching ``_compute_domain``."""
return super()._compute_domain_keys() + ["authenticated_partner_id"]
8 changes: 8 additions & 0 deletions base_rest/readme/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,11 @@ into your services.
for p in self.env["res.partner"].search(domain):
res.append(PartnerShortInfo(id=p.id, name=p.name))
return res
The BaseRestServiceContextProvider provides context for your services,
including authenticated_partner_id.
You are free to redefine the method _get_authenticated_partner_id() to pass the
authenticated_partner_id based on the authentication mechanism of your choice.
See base_rest_auth_jwt for an example.

In addition, authenticated_partner_id is available in record rule evaluation context.
1 change: 1 addition & 0 deletions base_rest/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
from . import test_cerberus_validator
from . import test_controller_builder
from . import test_openapi_generator
from . import test_service_context_provider
135 changes: 135 additions & 0 deletions base_rest/tests/test_service_context_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Copyright 2021 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo.addons.component.core import Component
from odoo.addons.website.tools import MockRequest

from .. import restapi
from .common import TransactionRestServiceRegistryCase


class TestServiceContextProvider(TransactionRestServiceRegistryCase):
"""Test Odoo service context provider
In this class we test the context provided by the service context provider
"""

def test_01(self):
"""Test authenticated_partner_id
In this case we check that the default service context provider provides
no authenticated_partner_id
"""

# pylint: disable=R7980
class TestServiceNewApi(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "test"

@restapi.method(
[(["/<int:id>/get", "/<int:id>"], "GET")],
output_param=restapi.CerberusValidator("_get_partner_schema"),
auth="public",
)
def get(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}

self._build_services(self, TestServiceNewApi)
controller = self._get_controller_for(TestServiceNewApi)
with MockRequest(self.env), controller().service_component(
"partner"
) as service:
self.assertFalse(service.work.authenticated_partner_id)

def test_02(self):
"""Test authenticated_partner_id
In this case we check that the 'abstract.user.authenticated.partner.provider'
service context provider provides the current user's partner as
authenticated_partner_id
"""

# pylint: disable=R7880
class TestComponentContextprovider(Component):
_name = "test.component.context.provider"
_inherit = [
"abstract.user.authenticated.partner.provider",
"base.rest.service.context.provider",
]
_usage = "test_component_context_provider"

self._BaseTestController._component_context_provider = (
"test_component_context_provider"
)

# pylint: disable=R7980
class TestServiceNewApi(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "test"

@restapi.method(
[(["/<int:id>/get", "/<int:id>"], "GET")],
output_param=restapi.CerberusValidator("_get_partner_schema"),
auth="public",
)
def get(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}

self._build_components(TestComponentContextprovider)
self._build_services(self, TestServiceNewApi)
controller = self._get_controller_for(TestServiceNewApi)
with MockRequest(self.env), controller().service_component(
"partner"
) as service:
self.assertEqual(
service.work.authenticated_partner_id, self.env.user.partner_id.id
)

def test_03(self):
"""Test authenticated_partner_id
In this case we check that redefining the method _get_authenticated_partner_id
changes the authenticated_partner_id provided by the service context provider
"""

# pylint: disable=R7880
class TestComponentContextprovider(Component):
_name = "test.component.context.provider"
_inherit = "base.rest.service.context.provider"
_usage = "test_component_context_provider"

def _get_authenticated_partner_id(self):
return 9999

self._BaseTestController._component_context_provider = (
"test_component_context_provider"
)

# pylint: disable=R7980
class TestServiceNewApi(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "test"

@restapi.method(
[(["/<int:id>/get", "/<int:id>"], "GET")],
output_param=restapi.CerberusValidator("_get_partner_schema"),
auth="public",
)
def get(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}

self._build_components(TestComponentContextprovider)
self._build_services(self, TestServiceNewApi)
controller = self._get_controller_for(TestServiceNewApi)
with MockRequest(self.env), controller().service_component(
"partner"
) as service:
self.assertEqual(service.work.authenticated_partner_id, 9999)
1 change: 1 addition & 0 deletions base_rest_auth_jwt/components/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import auth_jwt_component_context_provider
from . import service
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2021 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).


from odoo.http import request

from odoo.addons.component.core import AbstractComponent, Component


class AbstractAuthJwtAuthenticatedPartnerProvider(AbstractComponent):
_name = "abstract.auth.jwt.authenticated.partner.provider"

def _get_authenticated_partner_id(self):
return request.jwt_partner_id


class BaseRestAuthJwtComponentContextProvider(Component):
_name = "base.rest.auth.jwt.component.context.provider"
_inherit = [
"abstract.auth.jwt.authenticated.partner.provider",
"base.rest.service.context.provider",
]
_usage = "auth_jwt_component_context_provider"
10 changes: 10 additions & 0 deletions base_rest_demo/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,13 @@ class BaseRestDemoNewApiController(main.RestController):
_root_path = "/base_rest_demo_api/new_api/"
_collection_name = "base.rest.demo.new_api.services"
_default_auth = "public"


class BaseRestDemoJwtApiController(main.RestController):
# JWT Demo Controller, to be used with auth_jwt_demo
# https://github.com/OCA/server-auth/tree/14.0/auth_jwt_demo
_root_path = "/base_rest_demo_api/jwt/"
_collection_name = "base.rest.demo.jwt.services"
_default_auth = "jwt_demo_keycloak"
_component_context_provider = "auth_jwt_component_context_provider"
_default_cors = "*"
1 change: 1 addition & 0 deletions base_rest_demo/services/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from . import ping_services
from . import partner_services
from . import partner_image_services
from . import partner_jwt_services
from . import exception_services
from . import partner_new_api_services
10 changes: 10 additions & 0 deletions base_rest_demo/services/partner_jwt_services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright 2021 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from odoo.addons.component.core import Component


class PingJwtService(Component):
_inherit = "ping.service"
_name = "ping.jwt.service"
_collection = "base.rest.demo.jwt.services"
11 changes: 10 additions & 1 deletion base_rest_demo/tests/test_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from odoo.http import controllers_per_module

from ..controllers.main import (
BaseRestDemoJwtApiController,
BaseRestDemoNewApiController,
BaseRestDemoPrivateApiController,
BaseRestDemoPublicApiController,
Expand All @@ -16,7 +17,7 @@ def test_controller_registry(self):
# at the end of the start process, our tow controllers must into the
# controller registered
controllers = controllers_per_module["base_rest_demo"]
self.assertEqual(len(controllers), 3)
self.assertEqual(len(controllers), 4)

self.assertIn(
(
Expand All @@ -42,3 +43,11 @@ def test_controller_registry(self):
),
controllers,
)
self.assertIn(
(
"odoo.addons.base_rest_demo.controllers.main."
"BaseRestDemoJwtApiController",
BaseRestDemoJwtApiController,
),
controllers,
)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# generated from manifests external_dependencies
apispec
apispec>=4.0.0
cerberus
jsondiff
Expand Down

0 comments on commit b17b817

Please sign in to comment.