You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by Michael Osipov <mi...@apache.org> on 2025/05/26 13:14:05 UTC

Consolidating and harmonizing users from multiple realms

Hi folks,

I seek guidance on a larger problem I need to solve where I do have a few ideas,
but am also considering to what degree it would make sense to add code to the
Tomcat codebase for the common good:

I have a realm impl called MyRealm which sources from "store A", is has roles
(groups) in a specific format and user attributes. It returns MyPrincipal.
Consider you have a webapp which has logical roles "user", "editor", "admin",
etc. and also uses those specific attributes. The webapp context maps the
roles from "store A" into the logical roles with the PropertiesRoleMappingListener.

Now an additional MyRealm with "store B" comes into play. Of course, the
CombinedRealm works perfectly, but "store B" has different role names and
different attribute names.

The problem: Both "String Context#findRoleMapping(String)" and
"#addRoleMapping(String, String)" do not support 1:n mapping, e.g.,
"user" role maps to "store_a_role_1" and "store_b_role_5".
This would require changing/extending the interface and other classes.
The other problem is, of course, realm-specific. Say "store A" has attribute
"gid" which is semantically identically identical "store B" attribute
"employeeID". From an application PoV this is a consolidation nightmare
to touch every single spot to accommodate that.

My idea is going from:

<CombinedRealm>
  <MyRealm source="store A" />
  <MyRealm source="store B" />
</CombinedRealm>

to

<TransformingRealm roleMapper=... attributeMapper=...>
  <CombinedRealm>
    <MyRealm source="store A" />
    <MyRealm source="store B" />
  </CombinedRealm>
</TransformingRealm>

While the attributeMapper cannot be part of Tomcat because it is realm-specific,
do you see a benefit of modifying Context to accommodating 1:n mappings and of
course the aftermath?
Let me know your opinion whether this is of good use for the Tomcat code base.

For the same of completeness, I cannot add "user1", etc. to the application
because it will require some hefty code changes as well.

Michael

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Consolidating and harmonizing users from multiple realms

Posted by Michael Osipov <mi...@apache.org>.
Hi Chris,

On 2025/05/28 14:23:56 Christopher Schultz wrote:
> Michael,
> 
> On 5/26/25 9:14 AM, Michael Osipov wrote:
> > Hi folks,
> > 
> > I seek guidance on a larger problem I need to solve where I do have a few ideas,
> > but am also considering to what degree it would make sense to add code to the
> > Tomcat codebase for the common good:
> > 
> > I have a realm impl called MyRealm which sources from "store A", is has roles
> > (groups) in a specific format and user attributes. It returns MyPrincipal.
> > Consider you have a webapp which has logical roles "user", "editor", "admin",
> > etc. and also uses those specific attributes. The webapp context maps the
> > roles from "store A" into the logical roles with the PropertiesRoleMappingListener.
> > 
> > Now an additional MyRealm with "store B" comes into play. Of course, the
> > CombinedRealm works perfectly, but "store B" has different role names and
> > different attribute names.
> > 
> > The problem: Both "String Context#findRoleMapping(String)" and
> > "#addRoleMapping(String, String)" do not support 1:n mapping, e.g.,
> > "user" role maps to "store_a_role_1" and "store_b_role_5".
> > This would require changing/extending the interface and other classes.
> > The other problem is, of course, realm-specific. Say "store A" has attribute
> > "gid" which is semantically identically identical "store B" attribute
> > "employeeID". From an application PoV this is a consolidation nightmare
> > to touch every single spot to accommodate that.
> > 
> > My idea is going from:
> > 
> > <CombinedRealm>
> >    <MyRealm source="store A" />
> >    <MyRealm source="store B" />
> > </CombinedRealm>
> > 
> > to
> > 
> > <TransformingRealm roleMapper=... attributeMapper=...>
> >    <CombinedRealm>
> >      <MyRealm source="store A" />
> >      <MyRealm source="store B" />
> >    </CombinedRealm>
> > </TransformingRealm>
> > 
> > While the attributeMapper cannot be part of Tomcat because it is realm-specific,
> > do you see a benefit of modifying Context to accommodating 1:n mappings and of
> > course the aftermath?
> > Let me know your opinion whether this is of good use for the Tomcat code base.
> > 
> > For the same of completeness, I cannot add "user1", etc. to the application
> > because it will require some hefty code changes as well.
> 
> I must admit I've never used the role-mapping capabilities of the 
> servlet context before; I've only written applications that use the 
> one-and-only-one set of roles exposed by my user database. So perhaps my 
> comments come from a position of ignorance to your particular situation.
> 
> It seems to me that this can be entirely fixed by using a custom 
> Principal object: one that is already under the control of the Realm.
> 
> public class MyRealm extends RealmBase {
>    public Principal authenticate(...) {
>      ...
>      return new CustomPrincipal(...);
>    }
> 
>    public boolean isInRole(Wrapper wrapper, Principal principal, String 
> role) {
>      if(principal instanceof CustomPrincipal
>         && ((CustomPrincipal)principal).isMine(this) {
>        return super.isInRole(reverseMapRole(role));
>      } else {
>        // Principal is not from this Realm
> 
>        return false;
>      }
>    }
> 
>    protected String reverseMapRole(String role) {
>      if("user".equals(role)) {
>        // Obviously, implement this as a configurable Map
>        return "CN=users,OU=groups,DC=example,DC=org";
>      } else {
>        return role;
>    }
> 
>    public class CustomPrincipal extends GenericPrincipal {
>      private boolean isMine(MyRealm realm) {
>        return realm == MyRealm.this;
>      }
>      ...
>    }
> }
> 
> You can use the same class MyRealm for both of your Realms, each with a 
> different configuration. Since Tomcat already supports any attribute you 
> want to configure, just do something like:
> 
>     <Realm class="com.example.authn.MyRealm"
>        roleMappings="user=CN=users,OU=groups,DC=example,DC=org; 
> editor=CN=editors,OU=groups,DC=example,DC=org"; and so on" />
> 
> Would what I have above work for you? It doesn't work for an application 
> that tries to manually perform reverse-role-mapping (because the Context 
> doesn't know about these mappings), but without changes to Jakarta EE 
> APIs, the application will /never/ be able to do this.

Thanks for your input. I have actually ended up doing the following:
> <Realm className="com.innomotics.dynamowerk.tomcat.realm.PrincipalTransformingRealm">
> 	<Realm className="net.sf.michaelo.tomcat.realm.ActiveDirectoryRealm" connectionPoolSize="5"
> 		dirContextSourceName="gc/ad001.siemens.net" additionalAttributes="displayName,siemens-gid" />
> 	<Realm className="net.sf.michaelo.tomcat.realm.ActiveDirectoryRealm" connectionPoolSize="5"
> 		dirContextSourceName="gc/innomotics.net" additionalAttributes="displayName,extensionAttribute6" />
> </Realm>

and the realm is:
https://gist.github.com/michael-o/39dff88679427badf2e617bf2285b437

I will look later into Tomcat code whether the one-to-many mapping can be added. The benefit is that if you have many groups for the same role they can be managed by individual groups from different departments in a central portal. All you need is to add them one to your application and the rest is self-service. The app will treat both equally.

Michael

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Consolidating and harmonizing users from multiple realms

Posted by Christopher Schultz <ch...@christopherschultz.net>.
Michael,

On 5/26/25 9:14 AM, Michael Osipov wrote:
> Hi folks,
> 
> I seek guidance on a larger problem I need to solve where I do have a few ideas,
> but am also considering to what degree it would make sense to add code to the
> Tomcat codebase for the common good:
> 
> I have a realm impl called MyRealm which sources from "store A", is has roles
> (groups) in a specific format and user attributes. It returns MyPrincipal.
> Consider you have a webapp which has logical roles "user", "editor", "admin",
> etc. and also uses those specific attributes. The webapp context maps the
> roles from "store A" into the logical roles with the PropertiesRoleMappingListener.
> 
> Now an additional MyRealm with "store B" comes into play. Of course, the
> CombinedRealm works perfectly, but "store B" has different role names and
> different attribute names.
> 
> The problem: Both "String Context#findRoleMapping(String)" and
> "#addRoleMapping(String, String)" do not support 1:n mapping, e.g.,
> "user" role maps to "store_a_role_1" and "store_b_role_5".
> This would require changing/extending the interface and other classes.
> The other problem is, of course, realm-specific. Say "store A" has attribute
> "gid" which is semantically identically identical "store B" attribute
> "employeeID". From an application PoV this is a consolidation nightmare
> to touch every single spot to accommodate that.
> 
> My idea is going from:
> 
> <CombinedRealm>
>    <MyRealm source="store A" />
>    <MyRealm source="store B" />
> </CombinedRealm>
> 
> to
> 
> <TransformingRealm roleMapper=... attributeMapper=...>
>    <CombinedRealm>
>      <MyRealm source="store A" />
>      <MyRealm source="store B" />
>    </CombinedRealm>
> </TransformingRealm>
> 
> While the attributeMapper cannot be part of Tomcat because it is realm-specific,
> do you see a benefit of modifying Context to accommodating 1:n mappings and of
> course the aftermath?
> Let me know your opinion whether this is of good use for the Tomcat code base.
> 
> For the same of completeness, I cannot add "user1", etc. to the application
> because it will require some hefty code changes as well.

I must admit I've never used the role-mapping capabilities of the 
servlet context before; I've only written applications that use the 
one-and-only-one set of roles exposed by my user database. So perhaps my 
comments come from a position of ignorance to your particular situation.

It seems to me that this can be entirely fixed by using a custom 
Principal object: one that is already under the control of the Realm.

public class MyRealm extends RealmBase {
   public Principal authenticate(...) {
     ...
     return new CustomPrincipal(...);
   }

   public boolean isInRole(Wrapper wrapper, Principal principal, String 
role) {
     if(principal instanceof CustomPrincipal
        && ((CustomPrincipal)principal).isMine(this) {
       return super.isInRole(reverseMapRole(role));
     } else {
       // Principal is not from this Realm

       return false;
     }
   }

   protected String reverseMapRole(String role) {
     if("user".equals(role)) {
       // Obviously, implement this as a configurable Map
       return "CN=users,OU=groups,DC=example,DC=org";
     } else {
       return role;
   }

   public class CustomPrincipal extends GenericPrincipal {
     private boolean isMine(MyRealm realm) {
       return realm == MyRealm.this;
     }
     ...
   }
}

You can use the same class MyRealm for both of your Realms, each with a 
different configuration. Since Tomcat already supports any attribute you 
want to configure, just do something like:

    <Realm class="com.example.authn.MyRealm"
       roleMappings="user=CN=users,OU=groups,DC=example,DC=org; 
editor=CN=editors,OU=groups,DC=example,DC=org"; and so on" />

Would what I have above work for you? It doesn't work for an application 
that tries to manually perform reverse-role-mapping (because the Context 
doesn't know about these mappings), but without changes to Jakarta EE 
APIs, the application will /never/ be able to do this.

-chris


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org