Skip to content

Commit 834d358

Browse files
committed
fix: keep the mount prefix in the location of resources
1 parent 3b09657 commit 834d358

3 files changed

Lines changed: 48 additions & 4 deletions

File tree

‎scim2_server/backend.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ def create_resource(
9898
:param resource: Resource to create.
9999
:return: The created resource. Creation should set system-
100100
defined attributes (ID, Metadata). May be the same object
101-
that is passed in.
101+
that is passed in. A relative ``meta.location`` is relative to
102+
the root of the application.
102103
"""
103104
raise NotImplementedError
104105

@@ -230,7 +231,7 @@ def create_resource(
230231
resource_type=resource_type.name,
231232
created=utcnow,
232233
last_modified=utcnow,
233-
location="/v2" + resource_type.endpoint + "/" + resource.id,
234+
location=f"v2/{resource_type.endpoint.strip('/')}/{resource.id}",
234235
)
235236
self._touch_resource(resource, utcnow)
236237
self._check_uniqueness(resource)

‎scim2_server/provider.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,12 @@ def etag_supported(self) -> bool:
196196
def publish(self, request: Request, resource: Resource[Any]) -> Resource[Any]:
197197
"""Return a copy of a resource in the form sent to the client.
198198
199-
Its location is made absolute from the URL the client requested, and
199+
Its location is made absolute from the root URL of the application, and
200200
its version is left out when the service does not support ETags.
201201
"""
202202
assert resource.meta is not None
203203
update: dict[str, Any] = {
204-
"location": urljoin(request.url + "/", resource.meta.location)
204+
"location": urljoin(request.url_root, resource.meta.location)
205205
}
206206
if not self.etag_supported:
207207
update["version"] = None

‎tests/integration/test_scim_application.py‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,49 @@ def test_location_mapping(self, app):
4242
== "https://sub.testserver.company:1234/foo/bar/v2/ServiceProviderConfig"
4343
)
4444

45+
def test_resource_location_under_a_mount_prefix(self, app, fake_user_data):
46+
"""The location of a resource keeps the prefix the application is mounted under."""
47+
transport = httpx2.WSGITransport(app=app, script_name="/foo/bar")
48+
with httpx2.Client(
49+
transport=transport, base_url="https://scim.example.com"
50+
) as client:
51+
r = client.post("/v2/Users", json=fake_user_data[0])
52+
location = f"https://scim.example.com/foo/bar/v2/Users/{r.json()['id']}"
53+
assert r.headers["Location"] == location
54+
assert r.json()["meta"]["location"] == location
55+
assert client.get("/Users").json()["Resources"][0]["meta"]["location"] == (
56+
location
57+
)
58+
59+
r = client.post(
60+
"/v2/Bulk",
61+
json={
62+
"schemas": ["urn:ietf:params:scim:api:messages:2.0:BulkRequest"],
63+
"Operations": [
64+
{
65+
"method": "POST",
66+
"path": "/Users",
67+
"bulkId": "u",
68+
"data": fake_user_data[1],
69+
}
70+
],
71+
},
72+
)
73+
assert r.json()["Operations"][0]["location"].startswith(
74+
"https://scim.example.com/foo/bar/v2/Users/"
75+
)
76+
77+
def test_absolute_location_from_the_backend_is_kept(self, app, fake_user_data):
78+
"""A backend may store an absolute location, which is published as it is."""
79+
transport = httpx2.WSGITransport(app=app, script_name="/foo")
80+
with httpx2.Client(
81+
transport=transport, base_url="https://scim.example.com"
82+
) as client:
83+
user_id = client.post("/v2/Users", json=fake_user_data[0]).json()["id"]
84+
app.backend.resources[0].meta.location = "https://other.example/Users/x"
85+
r = client.get(f"/v2/Users/{user_id}")
86+
assert r.json()["meta"]["location"] == "https://other.example/Users/x"
87+
4588
def test_service_provider_configuration(self, wsgi):
4689
r = wsgi.get("/v2/ServiceProviderConfig")
4790
assert r.status_code == 200

0 commit comments

Comments
 (0)