Skip to content

Include user-defined attributes when constructing PolarisPrincipal from PrincipalEntity (#4291)#4292

Closed
sneethiraj wants to merge 5 commits into
apache:mainfrom
sneethiraj:user-attr-fix
Closed

Include user-defined attributes when constructing PolarisPrincipal from PrincipalEntity (#4291)#4292
sneethiraj wants to merge 5 commits into
apache:mainfrom
sneethiraj:user-attr-fix

Conversation

@sneethiraj
Copy link
Copy Markdown
Member

@sneethiraj sneethiraj commented Apr 25, 2026

This change provides a fix for issue #4291 ; It updates the construction of PolarisPrincipal from PrincipalEntity to include user-defined attributes in addition to internal attributes.

Currently, PolarisPrincipal is initialized using:
principalEntity.getInternalPropertiesAsMap()

This results in only internal attributes (such as client_id) being available in the PolarisPrincipal, while user-defined attributes supplied during principal creation are not propagated.

This PR updates the implementation to use:
principalEntity.getPropertiesAsMap() along with principalEntity.getInternalPropertiesAsMap()

so that both internal and user-defined attributes are available during request processing.

Fixes: #4291

Problem

When a principal is created with user-defined attributes (for example, region=northamerica), those attributes are successfully stored in PrincipalEntity.

However, during authentication and request handling, the constructed PolarisPrincipal only contains internal properties, and user-defined attributes are not available.

This prevents downstream components from accessing user attributes that were explicitly configured during principal creation.

Testing

  • Verified that principals created with user-defined attributes retain those attributes during authenticated API calls.
  • Confirmed that internal attributes (e.g., client_id) continue to be preserved.
  • Existing authentication flows remain unaffected.
  • Added test coverage to validate that user-defined attributes are available in PolarisPrincipal after authentication.
    • Added testcase with Ranger Attribute Based Access Control Policy based Integration Test (org.apache.polaris.extension.auth.ranger.test.RangerABACWithUserAttrIT)
      • Provides access to catalog listing only for those who have region = 'region2'

Checklist

  • 🛡️ Don't disclose security issues! (contact security@apache.org)
  • 🔗 Clearly explained why the changes are needed, or linked related issues: Fixes #
  • 🧪 Added/updated tests with good coverage, or manually tested (and explained how)
  • 💡 Added comments for complex logic
  • 🧾 Updated CHANGELOG.md (if needed)
  • 📚 Updated documentation in site/content/in-dev/unreleased (if needed)

@github-project-automation github-project-automation Bot moved this to PRs In Progress in Basic Kanban Board Apr 25, 2026
@sneethiraj sneethiraj changed the title Include user-defined attributes when constructing PolarisPrincipal from PrincipalEntity (#4291) Include user-defined attributes when constructing PolarisPrincipal from PrincipalEntity #4291 Apr 25, 2026
@sneethiraj sneethiraj changed the title Include user-defined attributes when constructing PolarisPrincipal from PrincipalEntity #4291 Include user-defined attributes when constructing PolarisPrincipal from PrincipalEntity (#4291) Apr 25, 2026
@Vishwaspatel2401
Copy link
Copy Markdown

Thanks for the fix! The approach of merging both getPropertiesAsMap() and getInternalPropertiesAsMap() is correct. However, I'd like to flag a potential issue with the merge order in PolarisPrincipal.java:

Map<String, String> allProperties = new HashMap<>(principalEntity.getInternalPropertiesAsMap());
allProperties.putAll(principalEntity.getPropertiesAsMap()); 

With this order, user-defined properties take precedence over internal ones on key conflicts. This means a user could potentially supply a custom attribute like client_id and silently overwrite the system-managed internal value, which could be a security concern.

Would it be safer to reverse the order so internal properties always win? 

Map<String, String> allProperties = new HashMap<>(principalEntity.getPropertiesAsMap());
allProperties.putAll(principalEntity.getInternalPropertiesAsMap());

Also noticed the checklist has Fixes # without the issue numbershould be Fixes #4291.

…e internal properties take precedence over user-defined properties during merge
@sneethiraj
Copy link
Copy Markdown
Member Author

@Vishwaspatel2401 Thanks for the suggestion — good catch on the merge order.
I’ve updated the implementation to ensure internal properties take precedence over user-defined ones, so system-managed values cannot be overridden. This change is included in commit - e0a3573.

Also updated the PR description to include Fixes #4291.

RangerAccessContext context = new RangerAccessContext(SERVICE_TYPE, serviceName);
RangerMultiAuthzRequest authzRequest =
new RangerMultiAuthzRequest(userInfo, accessInfos, context);
RangerUtils.setUserAttribsInRequestContext(userInfo, context);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the context is getting modified after the request is created? Is the request holding a pointer to the context?


protected String getUserToken(String userName) {
protected String getUserToken(String userName, Properties userProperties) {
String token = user2Token.get(userName);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems odd that we are adding userProperties to this test method but we're not actually using that field when asking the token cache for the token...?

import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.TestProfile;
import io.restassured.http.ContentType;
import java.util.Properties;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

java.util.Properties is used here (and in the base class) purely as a Map<String, String>. would prefer using Map instead to standardize (since that's what we're using elsewhere in this PR) rather than having Properties's Hashtable causing any little-known side-effects later on.

public class RangerABACWithUserAttrIT extends RangerIntegrationTestBase {

@Test
void getCatalogListsWithoutAuthorization() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: getCatalogListsWithoutAuthorization / getCatalogListsWithAuthorization are ambiguous — they could be read as "without an Authorization header", which is a different scenario. Something like denyAccessForPrincipalWithNonMatchingRegionAttribute / allowAccessForPrincipalWithMatchingRegionAttribute would make the intent immediately clear.

* roles.
*
* <p>The created principal will have the same ID and name as the {@link PrincipalEntity}, and its
* properties will be derived from the internal properties of the entity.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change this Javadoc for both functions that were changed?

Map.Entry::getKey, e -> e.getValue() == null ? "" : e.getValue().toString()));
userAttrMapping.put(userInfo.getName(), attributeMap);
store.setUserAttrMapping(userAttrMapping);
if (rangerAccessContext.getAdditionalInfo() == null) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this has a side effect on rangerAccessContext, it modifies it's state. We do this to avoid passing null to RangerAccessRequestUtil.setRequestUserStoreInContext?

@sneethiraj sneethiraj closed this May 5, 2026
@github-project-automation github-project-automation Bot moved this from PRs In Progress to Done in Basic Kanban Board May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PolarisPrincipal missing user-defined attributes supplied during principal creation

5 participants