nick-boss-tech commented on code in PR #5016:
URL: https://github.com/apache/solr/pull/5016#discussion_r4196711305


##########
solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java:
##########
@@ -219,13 +219,19 @@ public List<String> getCollectionsList() {
   /**
    * The collection(s) to authorize this request against. In SolrCloud, when a 
local core serves the
    * request, this is the collection of that core, since that is where the 
request executes;
-   * requests it sends to other collections are authorized by the receiving 
nodes. Otherwise, this
-   * is {@link #getCollectionsList()}. Not null.
+   * requests it sends to other collections are authorized by the receiving 
nodes. In standalone
+   * mode this is the name of the core serving the request, since there are no 
collections.
+   * Otherwise, this is {@link #getCollectionsList()}. Not null.
    */
   public List<String> getAuthorizationCollectionsList() {
-    if (core == null || !cores.isZooKeeperAware()) {
+    if (core == null) {
       return getCollectionsList();
     }
+    if (!cores.isZooKeeperAware()) {
+      // Standalone mode has no collections; authorize against the serving 
core's name so that
+      // core-scoped authorization rules can match.
+      return List.of(core.getCoreDescriptor().getName());

Review Comment:
   🤖 *AI text below* 🤖 *(posted on behalf of Nick Shanin)*
   
   Good question; I checked what the current code actually does with such a 
request. Two findings:
   
   First, adding the shard cores to this list would not subject each of them to 
its own check. The list this PR authorizes against holds the serving core's 
name only in standalone mode 
([HttpSolrCall.getAuthorizationCollectionsList](https://github.com/nick-boss-tech/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java#L226-L245)).
 
[RuleBasedAuthorizationPluginBase.authorize](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java#L111-L124)
 returns the decision of the first name in the list that has a governing 
permission; a name with no permission mapping is skipped, and the wildcard 
mapping is the fallback 
([RuleBasedAuthorizationPluginBase](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java#L119-L123)),
 so the list 
 is a set of candidate names for one decision on the whole request, not a set 
of checks. Making every name mandatory would change the plugin's semantics for 
all callers, cloud included.
   
   Second, the second core is not silently exposed under the default 
configuration. The sub-requests a shards fan-out sends carry no user 
credentials on the wire under BasicAuth with default settings: 
[HttpShardHandler.prepareLBRequest](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java#L204-L218)
 attaches the caller's Principal to the outgoing request object, and the client 
that sends it, HttpJettySolrClient 
([HttpShardHandlerFactory](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java#L308-L318)
 builds it as the fan-out client), copies that principal into a client-side 
request attribute rather than a header 
([HttpJettySolrClient.decorateRequest](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/solrj-jetty/src/java/org/apache/solr/client/solrj/je
 tty/HttpJettySolrClient.java#L634-L656)). The one BasicAuth path that turns 
the attribute into an Authorization header runs only when forwardCredentials is 
enabled, and it defaults to false 
([BasicAuthPlugin.interceptInternodeRequest](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java#L215-L227);
 [the 
field](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java#L55)).
 With blockUnknown at its default true 
([BasicAuthPlugin](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java#L54)),
 a request without credentials is rejected at authentication: 
[doAuthenticate](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java#L167-L179)
 answers [4
 
01](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java#L111-L115)
 and returns false without invoking the filter chain, so the sub-request never 
reaches the receiving core or its authorization. With blockUnknown=false the 
sub-request passes authentication with no principal and is decided by the 
target core's own authorization: a role-bearing governing permission denies it, 
because a null principal yields USER_REQUIRED 
([RuleBasedAuthorizationPluginBase](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java#L296-L301)),
 which 
[AuthorizationUtils](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/security/AuthorizationUtils.java#L64-L80)
 surfaces as an authorization failure. A target core with no governing 
permission at all, and no 
 wildcard permission either, allows it instead: a name with no permission 
mapping yields NO_PERMISSIONS_FOUND 
([checkCollPerm](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java#L152-L153)),
 and NO_PERMISSIONS_FOUND maps to AuthorizationResponse.OK 
([MatchStatus](https://github.com/apache/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java#L402-L407)).
 That last case is the plugin's designed treatment of names with no configured 
permission, and it behaves the same in cloud mode. The split itself is the one 
the method's Javadoc describes for cloud: requests a node sends to other 
collections are authorized by the receiving nodes 
([HttpSolrCall](https://github.com/nick-boss-tech/solr/blob/4c08aa5751ee4c8742c57a3ed8beeac827816611/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java#L219-L225)).
   
   I also checked the behavior with a temporary probe test (not shipped), 
re-run on 2026-10-06 at the current head (4c08aa5751e) in the three 
configurations above; its outcomes are recorded in the Proof section of this 
PR's description (https://github.com/apache/solr/pull/5016). A plain query on 
the serving core answers normally in each configuration. Under the default 
configuration the shards variants returned no documents, because the 
sub-requests fail at authentication on the receiving core; one variant instead 
failed at the front door with 403, because that caller lacked the serving 
core's role. Under blockUnknown=false, shards naming the governed core returned 
no documents (denied at the authorization stage per the code path above, which 
surfaces to the caller as a 401), and shards naming the ungoverned core 
returned its document.
   
   So I have left the code as the serving core only, and recorded this in the 
PR's Limits. If you think the plugin should move to per-name mandatory checks, 
that seems like its own change, and I am glad to take it on as a follow-up.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to