On Thu, Feb 10, 2022 at 1:01 PM Rémy Maucherat <r...@apache.org> wrote:
>
> On Tue, Feb 8, 2022 at 2:13 PM <micha...@apache.org> wrote:
> >
> > This is an automated email from the ASF dual-hosted git repository.
> >
> > michaelo pushed a commit to branch main
> > in repository https://gitbox.apache.org/repos/asf/tomcat.git
> >
> >
> > The following commit(s) were added to refs/heads/main by this push:
> >      new c3edf43  Add support for additional user attributes in 
> > TomcatPrincipal
> > c3edf43 is described below
> >
> > commit c3edf437da20af0f11edc0ad6d893399b01e6287
> > Author: Carsten Klein <c.kl...@datagis.com>
> > AuthorDate: Wed Jan 12 15:06:42 2022 +0100
> >
> >     Add support for additional user attributes in TomcatPrincipal
> >
> >     This closes #463
>
> -1 (veto)

The javadoc that has been agreed upon has been added (the wording can
be refined later), so the veto regarding this commit is addressed and
thus dropped.

Rémy

> Unfortunately, the more I think about this, the more it seems wrong to me.
>
> This makes the realm an object store, which is not a good idea at all,
> as it will:
> - Encourage all sorts of bad hacks and practices for users
> - Force us to gradually add more and more features to this generic
> object store feature
> - Have massive discrepancies between realms, with the users trying to
> figure out pros and cons between realms
> - Is a proprietary feature which ties up users to Tomcat
>
> More importantly: application data storage should remain the
> prerogative of the application, Tomcat should not attempt to do that.
> The only thing Tomcat should provide is a principal name, which is
> then the primary key for the third party object store. There is zero
> reason to integrate this within the realm or principal API. It also
> feels like it can never be complete or appropriate.
>
> I apologize for having to do this, after initially helping out with
> this PR, but this is one of those situations where the more I look
> about it, the worse I feel about it.
>
> Rémy
>
> > ---
> >  java/org/apache/catalina/TomcatPrincipal.java      | 28 +++++++++++++
> >  .../apache/catalina/realm/GenericPrincipal.java    | 46 
> > ++++++++++++++++++----
> >  java/org/apache/catalina/realm/JNDIRealm.java      |  2 +-
> >  webapps/docs/changelog.xml                         |  5 +++
> >  webapps/examples/jsp/security/protected/index.jsp  | 44 
> > +++++++++++++++++++++
> >  5 files changed, 117 insertions(+), 8 deletions(-)
> >
> > diff --git a/java/org/apache/catalina/TomcatPrincipal.java 
> > b/java/org/apache/catalina/TomcatPrincipal.java
> > index 83f9035..1e3d9f6 100644
> > --- a/java/org/apache/catalina/TomcatPrincipal.java
> > +++ b/java/org/apache/catalina/TomcatPrincipal.java
> > @@ -17,6 +17,8 @@
> >  package org.apache.catalina;
> >
> >  import java.security.Principal;
> > +import java.util.Collections;
> > +import java.util.Enumeration;
> >
> >  import org.ietf.jgss.GSSCredential;
> >
> > @@ -47,4 +49,30 @@ public interface TomcatPrincipal extends Principal {
> >       *                   exception to LoginContext
> >       */
> >      void logout() throws Exception;
> > +
> > +    /**
> > +     * Returns the value of the named attribute as an <code>Object</code>, 
> > or
> > +     * <code>null</code> if no attribute of the given name exists, or if
> > +     * <code>null</code> has been specified as the attribute's name.
> > +     * <p>
> > +     * Only the servlet container may set attributes to make available 
> > custom
> > +     * information about a Principal or the user it represents.
> > +     *
> > +     * @param name a <code>String</code> specifying the name of the 
> > attribute
> > +     * @return an <code>Object</code> containing the value of the 
> > attribute, or
> > +     *         <code>null</code> if the attribute does not exist, or if
> > +     *         <code>null</code> has been specified as the attribute's name
> > +     */
> > +    Object getAttribute(String name);
> > +
> > +    /**
> > +     * Returns an <code>Enumeration</code> containing the names of the
> > +     * attributes available to this Principal. This method returns an empty
> > +     * <code>Enumeration</code> if the Principal has no attributes 
> > available to
> > +     * it.
> > +     *
> > +     * @return an <code>Enumeration</code> of strings containing the names 
> > of
> > +     *         the Principal's attributes
> > +     */
> > +    Enumeration<String> getAttributeNames();
> >  }
> > diff --git a/java/org/apache/catalina/realm/GenericPrincipal.java 
> > b/java/org/apache/catalina/realm/GenericPrincipal.java
> > index 7260da4..584c104 100644
> > --- a/java/org/apache/catalina/realm/GenericPrincipal.java
> > +++ b/java/org/apache/catalina/realm/GenericPrincipal.java
> > @@ -19,7 +19,10 @@ package org.apache.catalina.realm;
> >  import java.io.Serializable;
> >  import java.security.Principal;
> >  import java.util.Arrays;
> > +import java.util.Collections;
> > +import java.util.Enumeration;
> >  import java.util.List;
> > +import java.util.Map;
> >
> >  import javax.security.auth.login.LoginContext;
> >
> > @@ -120,7 +123,7 @@ public class GenericPrincipal implements 
> > TomcatPrincipal, Serializable {
> >       */
> >      public GenericPrincipal(String name, List<String> roles,
> >              Principal userPrincipal, LoginContext loginContext) {
> > -        this(name, roles, userPrincipal, loginContext, null);
> > +        this(name, roles, userPrincipal, loginContext, null, null);
> >      }
> >
> >      /**
> > @@ -140,7 +143,7 @@ public class GenericPrincipal implements 
> > TomcatPrincipal, Serializable {
> >      @Deprecated
> >      public GenericPrincipal(String name, String password, List<String> 
> > roles,
> >              Principal userPrincipal, LoginContext loginContext) {
> > -        this(name, roles, userPrincipal, loginContext, null);
> > +        this(name, roles, userPrincipal, loginContext, null, null);
> >      }
> >
> >      /**
> > @@ -154,10 +157,12 @@ public class GenericPrincipal implements 
> > TomcatPrincipal, Serializable {
> >       * @param loginContext  - If provided, this will be used to log out 
> > the user
> >       *        at the appropriate time
> >       * @param gssCredential - If provided, the user's delegated credentials
> > +     * @param attributes - If provided, additional attributes associated 
> > with
> > +     *        this Principal
> >       */
> >      public GenericPrincipal(String name, List<String> roles,
> >              Principal userPrincipal, LoginContext loginContext,
> > -            GSSCredential gssCredential) {
> > +            GSSCredential gssCredential, Map<String, Object> attributes) {
> >          super();
> >          this.name = name;
> >          this.userPrincipal = userPrincipal;
> > @@ -171,6 +176,7 @@ public class GenericPrincipal implements 
> > TomcatPrincipal, Serializable {
> >          }
> >          this.loginContext = loginContext;
> >          this.gssCredential = gssCredential;
> > +        this.attributes = attributes != null ? 
> > Collections.unmodifiableMap(attributes) : null;
> >      }
> >
> >
> > @@ -193,7 +199,7 @@ public class GenericPrincipal implements 
> > TomcatPrincipal, Serializable {
> >      public GenericPrincipal(String name, String password, List<String> 
> > roles,
> >              Principal userPrincipal, LoginContext loginContext,
> >              GSSCredential gssCredential) {
> > -        this(name, roles, userPrincipal, loginContext, gssCredential);
> > +        this(name, roles, userPrincipal, loginContext, gssCredential, 
> > null);
> >      }
> >
> >
> > @@ -254,6 +260,11 @@ public class GenericPrincipal implements 
> > TomcatPrincipal, Serializable {
> >          this.gssCredential = gssCredential;
> >      }
> >
> > +    /**
> > +     * The additional attributes associated with this Principal.
> > +     */
> > +    protected final Map<String, Object> attributes;
> > +
> >
> >      // ---------------------------------------------------------- Public 
> > Methods
> >
> > @@ -304,10 +315,28 @@ public class GenericPrincipal implements 
> > TomcatPrincipal, Serializable {
> >      }
> >
> >
> > +    @Override
> > +    public Object getAttribute(String name) {
> > +        if (attributes == null || name == null) {
> > +            return null;
> > +        }
> > +        return attributes.get(name);
> > +    }
> > +
> > +
> > +    @Override
> > +    public Enumeration<String> getAttributeNames() {
> > +        if (attributes == null) {
> > +            return Collections.emptyEnumeration();
> > +        }
> > +        return Collections.enumeration(attributes.keySet());
> > +    }
> > +
> > +
> >      // ----------------------------------------------------------- 
> > Serialization
> >
> >      private Object writeReplace() {
> > -        return new SerializablePrincipal(name, roles, userPrincipal);
> > +        return new SerializablePrincipal(name, roles, userPrincipal, 
> > attributes);
> >      }
> >
> >      private static class SerializablePrincipal implements Serializable {
> > @@ -316,9 +345,10 @@ public class GenericPrincipal implements 
> > TomcatPrincipal, Serializable {
> >          private final String name;
> >          private final String[] roles;
> >          private final Principal principal;
> > +        private final Map<String, Object> attributes;
> >
> >          public SerializablePrincipal(String name, String[] roles,
> > -                Principal principal) {
> > +                Principal principal, Map<String, Object> attributes) {
> >              this.name = name;
> >              this.roles = roles;
> >              if (principal instanceof Serializable) {
> > @@ -326,10 +356,12 @@ public class GenericPrincipal implements 
> > TomcatPrincipal, Serializable {
> >              } else {
> >                  this.principal = null;
> >              }
> > +            this.attributes = attributes;
> >          }
> >
> >          private Object readResolve() {
> > -            return new GenericPrincipal(name, Arrays.asList(roles), 
> > principal);
> > +            return new GenericPrincipal(name, Arrays.asList(roles), 
> > principal, null, null,
> > +                    attributes);
> >          }
> >      }
> >  }
> > diff --git a/java/org/apache/catalina/realm/JNDIRealm.java 
> > b/java/org/apache/catalina/realm/JNDIRealm.java
> > index a9dac4a..e971439 100644
> > --- a/java/org/apache/catalina/realm/JNDIRealm.java
> > +++ b/java/org/apache/catalina/realm/JNDIRealm.java
> > @@ -2516,7 +2516,7 @@ public class JNDIRealm extends RealmBase {
> >          }
> >
> >          if (user != null) {
> > -            return new GenericPrincipal(user.getUserName(), roles, null, 
> > null, gssCredential);
> > +            return new GenericPrincipal(user.getUserName(), roles, null, 
> > null, gssCredential, null);
> >          }
> >
> >          return null;
> > diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
> > index 3a22409..bd2a124 100644
> > --- a/webapps/docs/changelog.xml
> > +++ b/webapps/docs/changelog.xml
> > @@ -123,6 +123,11 @@
> >          is not supported by the configured providers as will be the case 
> > for a
> >          FIPS compliant configuration. (markt)
> >        </fix>
> > +      <add>
> > +        <pr>463</pr>: Add support for additional user attributes to
> > +        <code>TomcatPrincipal</code> and <code>GenericPrincipal</code>.
> > +        Patch provided by Carsten Klein. (michaelo)
> > +      </add>
> >        <fix>
> >          <pr>469</pr>: Include the Jakarata Annotations API in the classes 
> > that
> >          Tomcat will not load from web applications. Pull request provided 
> > by
> > diff --git a/webapps/examples/jsp/security/protected/index.jsp 
> > b/webapps/examples/jsp/security/protected/index.jsp
> > index 31122eb..75ac4bc 100644
> > --- a/webapps/examples/jsp/security/protected/index.jsp
> > +++ b/webapps/examples/jsp/security/protected/index.jsp
> > @@ -15,6 +15,8 @@
> >    limitations under the License.
> >  --%>
> >  <%@ page import="java.util.Enumeration" %>
> > +<%@ page import="java.security.Principal" %>
> > +<%@ page import="org.apache.catalina.TomcatPrincipal" %>
> >  <%
> >    if (request.getParameter("logoff") != null) {
> >      session.invalidate();
> > @@ -73,6 +75,48 @@ enter it here:
> >  </form>
> >  <br><br>
> >
> > +<%
> > +  Principal p = request.getUserPrincipal();
> > +  if (!(p instanceof TomcatPrincipal)) {
> > +%>
> > +<p>The principal does not support attributes.</p>
> > +<%
> > +  } else {
> > +    TomcatPrincipal principal = (TomcatPrincipal) p;
> > +%>
> > +<p>The principal contains the following attributes:</p>
> > +<table>
> > +<tr><th>Name</th><th>Value</th><th>Type</th></tr>
> > +<%
> > +    Enumeration<String> names = principal.getAttributeNames();
> > +    while (names.hasMoreElements()) {
> > +      String name = names.nextElement();
> > +      Object value = principal.getAttribute(name);
> > +      String type = value != null ? value.getClass().getName() : "unknown";
> > +      if (value instanceof Object[]) {
> > +        Object[] values = (Object[]) value;
> > +        value = "";
> > +        for (int i = 0; i < values.length; i++) {
> > +          value += values[i] + "<br/>";
> > +        }
> > +        if (values.length > 0) {
> > +          type = values[0].getClass().getName() + "[]";
> > +        } else {
> > +          type = "unknown";
> > +        }
> > +      }
> > +      type = type.replaceFirst("^java\\.lang\\.", "");
> > +%>
> > +<tr><td><%= name %></td><td><%= value %></td><td><%= type %></td>
> > +<%
> > +    }
> > +%>
> > +</table>
> > +<%
> > +  }
> > +%>
> > +<br><br>
> > +
> >  To add some data to the authenticated session, enter it here:
> >  <form method="GET" action='<%= response.encodeURL("index.jsp") %>'>
> >  <input type="text" name="dataName">
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
> > For additional commands, e-mail: dev-h...@tomcat.apache.org
> >

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to