title: Implement a Tomcat Realm with LDAP authentication and JDBC authorization
body: <p>I'm working in a legacy environment where an LDAP server is used only for authentication and contains no roles, and authorization is done against a database which contains the user-role mapping, but no passwords.</p>
  
  <p>My plan is to implement a new Tomcat Realm by extending JNDIRealm, and overriding the role methods to call an encapsulated JDBCRealm.</p>
  
  <p>My realm is declared in server.xml:</p>
  
  <pre><code>&lt;Realm className="com.example.LdapJdbcRealm"
     connectionURL="ldap://ldaphost:389"
     resourceName="LDAP Auth"
     userPattern="uid={0}, ou=Portal, dc=example, dc=com"
     dbConnectionURL="jdbc:oracle:thin:@oracledb:1521:dbname"
     userTable="db_user" userNameCol="user_id"
     userRoleTable="db_user_role_xref" roleNameCol="role_id" /&gt;
  </code></pre>
  
  <p>This is a combination of the standard property names for JNDIRealm &amp; JDBCRealm, with a little change as they both use connectionURL.</p>
  
  <pre><code>package com.example;
  
  import org.apache.catalina.Realm;
  import org.apache.catalina.Context;
  import org.apache.catalina.deploy.SecurityConstraint;
  import org.apache.catalina.connector.Request;
  import org.apache.catalina.connector.Response;
  import org.apache.catalina.realm.JNDIRealm;
  import org.apache.catalina.realm.JDBCRealm;
  
  import java.security.Principal;
  import java.io.IOException;
  
  public class LdapJdbcRealm extends JNDIRealm implements Realm
  {
      private JDBCRealm jdbcRealm = new JDBCRealm();
  
      protected static final String info = "com.example.LdapJdbcRealm/1.0";
      protected static final String name = "LdapJdbcRealm";
  
      public String getDbConnectionURL() {
          return jdbcRealm.getConnectionURL();
      }
  
      public void setDbConnectionURL(String dbConnectionURL) {
          jdbcRealm.setConnectionURL(dbConnectionURL);
      }
  
      public String getUserTable() {
          return jdbcRealm.getUserTable();
      }
  
      public void setUserTable(String userTable) {
          jdbcRealm.setUserTable(userTable);
      }
  
      public String getUserNameCol() {
          return jdbcRealm.getUserNameCol();
      }
  
      public void setUserNameCol(String userNameCol) {
          jdbcRealm.setUserNameCol(userNameCol);
      }
  
      public String getUserRoleTable() {
          return jdbcRealm.getUserRoleTable();
      }
  
      public void setUserRoleTable(String userRoleTable) {
          jdbcRealm.setUserRoleTable(userRoleTable);
      }
  
      public String getRoleNameCol() {
          return jdbcRealm.getRoleNameCol();
      }
  
      public void setRoleNameCol(String roleNameCol) {
          jdbcRealm.setRoleNameCol(roleNameCol);
      }
  
      public boolean hasResourcePermission(Request request,
                                           Response response,
                                           SecurityConstraint[]constraints,
                                           Context context) throws IOException
      {
          return jdbcRealm.hasResourcePermission(request, response, constraints, context);
      }
  
      public boolean hasRole(Principal principal, String role) {
          return jdbcRealm.hasRole(principal, role);
      }
  }
  </code></pre>
  
  <p>This mostly seems to work, the authorization returns a Principal from LDAP, which has no roles as expected. That same Principal enters <code>hasResourcePermission()</code> and fails because it doesn't have the require roles in it. Clearly I'm missing some crucial code.</p>
  
  <p>I'm looking for solutions. I could try extending JDBCRealm and adding LDAP authentication, but that seems like more work.</p>
  
  <p>I also believe that this LDAP authentication/DB authorization is not an uncommon pattern. Is there an alternative solution already available?</p>
  
  <p>It is <strong>not</strong> within my control to add roles to LDAP or passwords to the DB, so those are not solutions for me.</p>