Affects Version/s: Resource Resolver 1.7.8
Fix Version/s: None
The sling mappings provide the possibility of creating a multi-tenant setup. Multiple websites can be hosted, by providing Sling mappings that point to their specific content paths. These mappings consist of domains. I.e.:
There is, however, an issue with this setup, when using sling:vanityPath.
Let’s say, we have the following structure:
When requesting https://my-website-a.com/openinghours , we see the content of /content/my-website-a/info/openinghours (sling:vanityPath) – Correct
When requesting https://my-website-b.com/openinghours , we see the content of /content/my-website-b/info/openinghours – Incorrect! This content does not belong to this website!
The /test vanity is also available within both websites.
There seems to be support in Apache Sling for sling:vanityPath properties containing a full URL: https://github.com/apache/sling-org-apache-sling-resourceresolver/blob/4406b8fed0fedb48202fc6472fb552c36aa06e35/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java#L1285 This could be used to make the above work. However, that would require us to store the protocol, domain & portnumber inside the sling:vanityPath property. That does not feel right.
There is another option: We could try to use custom code to put the domains in front of sling:vanityPath property values, by using a ResourceDecorator. However, the ResourceDecorator in our custom bundle would not be started yet, when Sling is starting up and doing the inventory, because our bundle is dependent on the resourceresolver-bundle.
Also, another issue is that, when the mappings update, we would need to let Sling re-scan everything (because our ResourceDecorator now has put the wrong domain in front of all property values).
This basically leaves us without any good “hook” into this entire process.
If the sling:vanityPath property value contains a path (not a URL), https://github.com/apache/sling-org-apache-sling-resourceresolver/blob/4406b8fed0fedb48202fc6472fb552c36aa06e35/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java#L1291 , the ANY_SCHEME_HOST constant is used, which has a value of “[^/]” which basically means that any scheme and any host matches. Given the usecase I just described, it means that no matter where the sling:vanityPath properties are stored, as long as they match the request path only, they match. Then, the first one that is encountered (through the Iterator provided by getResolveMapsIterator) – Potentially the wrong one!
When handling sling:vanityPath properties, there is not really any context of domains, Sling mappings, … Purely a vanity path.
I think it could be possible to take the map entries into account, when handling the vanity paths. This should mean that the ANY_SCHEME_HOST-constant would not be used as part of the regex, but instead, we determine the host at a later part, by taking into account the Sling mappings.
This would make the following cases possible:
1. Scope sling:vanityPath defined in a content path covered by a mapping, to only that mapping, causing them to not be able to be requested by other domains;
2. When having found multiple content paths defining the same sling:vanityPath, be able to actually select the right one (not tackling the issue where 2 or more of the same vanity paths are defined under the same content path);
3. Add a new Sling mapping (or remove / update an existing one) and the sling:vanityPath scoping work right away.
So far, I’ve been experimenting a bit with the above and it seems most of it is possible already, just that MapEntry (which is where the ANY_SCHEME_HOST + vanity path value ends up as a regex) doesn’t have any knowledge of the Sling mappings. I was thinking of making a wrapper class, to store the vanity path without the scheme/host/port and allow it to dynamically select the right one based on the request coming in.
I suppose it’s a good idea to be able to toggle this functionality, so that we don’t interfere with existing setups. And also, if a content path defines a sling:vanityPath that is not covered by a Sling Mapping – Should that sling:vanityPath be global then? Or not work at all? (I think it should be global, then.)
Does anyone have any thoughts about this? 😊
If we’re able to decide how we want to approach this, I can put in some effort to make the changes.