1

Problem:

After I added a Tomcat ResourceLink <ResourceLink name="UserDatabase" global="UserDatabase" type="org.apache.catalina.UserDatabase"/> my Authentication is broken. I can log in and do a REST CALL that gets me all User from the tomcat-users.xml. Then I just reload the page and there is a 403 - Forbidden error.

I have two Web Applications. One is a REST API that gets data from a database (backend) and the other Web Application is for the administration of the database (frontend).
So I added a FORM authentication for my frontend application and a BASIC authentication for my backend. The users are stored in the tomcat-users.xml file -> everything works fine
Now I want to programmatically edit the tomcat-users with the frontend/backend and I found this how to programmatically add users to tomcat UserDatabaseRealm?
I add the code from the link into my REST API and then I call the URL /api/admin/tomcat/user I got all Users and the User Roles. But after one call on this page, I always get a 403 - Forbidden error!
If I don't do the REST CALL or just remove the ResourceLink the problem does not occur.

Code / Error

(Look at the last two rows at the Tomcat Logs. Why did he found the role in the first time but not in the second one?)

Tomcat Logs:

Logs after I Logged in successfully / first time I call the Page:

[http-nio-8080-exec-8] org.apache.catalina.authenticator.AuthenticatorBase.invoke Security checking request GET /es_admin/admin/setting.jsp
[http-nio-8080-exec-8] org.apache.catalina.authenticator.AuthenticatorBase.invoke We have cached auth type FORM for principal GenericPrincipal[admin(CR8000,SOLIDWORKS,admin,)]
[http-nio-8080-exec-8] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[Adminstrative Pages]' against GET /admin/setting.jsp --> true
[http-nio-8080-exec-8] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[User Pages]' against GET /admin/setting.jsp --> false
[http-nio-8080-exec-8] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[Adminstrative Pages]' against GET /admin/setting.jsp --> true
[http-nio-8080-exec-8] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[User Pages]' against GET /admin/setting.jsp --> false
[http-nio-8080-exec-8] org.apache.catalina.authenticator.AuthenticatorBase.invoke Calling hasUserDataPermission()
[http-nio-8080-exec-8] org.apache.catalina.realm.RealmBase.hasUserDataPermission   User data constraint has no restrictions
[http-nio-8080-exec-8] org.apache.catalina.authenticator.AuthenticatorBase.invoke Calling authenticate()
[http-nio-8080-exec-8] org.apache.catalina.authenticator.AuthenticatorBase.checkForCachedAuthentication Bereits authentifiziert [admin]
[http-nio-8080-exec-8] org.apache.catalina.authenticator.AuthenticatorBase.invoke Calling accessControl()
[http-nio-8080-exec-8] org.apache.catalina.realm.RealmBase.hasResourcePermission   Checking roles GenericPrincipal[admin(CR8000,SOLIDWORKS,admin,)]
[http-nio-8080-exec-8] org.apache.catalina.realm.RealmBase.hasResourcePermission Role found:  admin
[http-nio-8080-exec-8] org.apache.catalina.authenticator.AuthenticatorBase.invoke Successfully passed all security constraints

Logs after I reload the Page or just switch to another one:

[http-nio-8080-exec-15] org.apache.catalina.authenticator.AuthenticatorBase.invoke Security checking request GET /es_admin/admin/setting.jsp
[http-nio-8080-exec-15] org.apache.catalina.authenticator.AuthenticatorBase.invoke We have cached auth type FORM for principal GenericPrincipal[admin(CR8000,SOLIDWORKS,admin,)]
[http-nio-8080-exec-15] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[User Pages]' against GET /admin/setting.jsp --> false
[http-nio-8080-exec-15] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[Adminstrative Pages]' against GET /admin/setting.jsp --> true
[http-nio-8080-exec-15] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[User Pages]' against GET /admin/setting.jsp --> false
[http-nio-8080-exec-15] org.apache.catalina.realm.RealmBase.findSecurityConstraints   Checking constraint 'SecurityConstraint[Adminstrative Pages]' against GET /admin/setting.jsp --> true
[http-nio-8080-exec-15] org.apache.catalina.authenticator.AuthenticatorBase.invoke Calling hasUserDataPermission()
[http-nio-8080-exec-15] org.apache.catalina.realm.RealmBase.hasUserDataPermission   User data constraint has no restrictions
[http-nio-8080-exec-15] org.apache.catalina.authenticator.AuthenticatorBase.invoke Calling authenticate()
[http-nio-8080-exec-15] org.apache.catalina.authenticator.AuthenticatorBase.checkForCachedAuthentication Bereits authentifiziert [admin]
[http-nio-8080-exec-15] org.apache.catalina.authenticator.AuthenticatorBase.invoke Calling accessControl()
[http-nio-8080-exec-15] org.apache.catalina.realm.RealmBase.hasResourcePermission   Checking roles GenericPrincipal[admin(CR8000,SOLIDWORKS,admin,)]
[http-nio-8080-exec-15] org.apache.catalina.realm.RealmBase.hasResourcePermission No role found:  admin
[http-nio-8080-exec-15] org.apache.catalina.authenticator.AuthenticatorBase.invoke Failed accessControl() test

web.xml - frontend

 <!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
  <display-name>frontend</display-name>
   <welcome-file-list>
        <welcome-file>login.jsp</welcome-file>
   </welcome-file-list>

    <!--Defines Security Constraint -->
    <security-constraint>
        <display-name>frontend admin</display-name>
        <web-resource-collection>
            <web-resource-name>Adminstrative Pages</web-resource-name>
            <description/>
            <url-pattern>/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <description/>
            <role-name>admin</role-name>
        </auth-constraint>
    </security-constraint>

    <security-constraint>
        <display-name>frontend user</display-name>
        <web-resource-collection>
            <web-resource-name>User Pages</web-resource-name>
            <description/>
            <url-pattern>/user/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <description/>
            <role-name>SOLIDWORKS</role-name>
        </auth-constraint>
        <auth-constraint>
            <description/>
            <role-name>CR8000</role-name>
        </auth-constraint>
    </security-constraint>

<!--Defines Login Config -->
    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>file</realm-name>
        <form-login-config>
            <form-login-page>/login.jsp</form-login-page>
            <form-error-page>/error.jsp</form-error-page>
        </form-login-config>
    </login-config>

<!--Defines Security Role -->
    <security-role>
        <description/>
        <role-name>admin</role-name>
    </security-role>
    <security-role>
        <description/>
        <role-name>SOLIDWORKS</role-name>
    </security-role>
    <security-role>
        <description/>
        <role-name>CR8000</role-name>
    </security-role>
</web-app>

context.xml

<Context>
    <!-- Default set of monitored resources. If one of these changes, the    -->
    <!-- web application will be reloaded.                                   -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>

    <ResourceLink name="UserDatabase" global="UserDatabase" type="org.apache.catalina.UserDatabase" />

</Context>

tomcat-users.xml

<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users xmlns="http://tomcat.apache.org/xml"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
              version="1.0">

<role rolename="admin"/>
<role rolename="CR8000"/>
<role rolename="SOLIDWORKS"/>

<user username="admin" password="password" roles="admin,CR8000,SOLIDWORKS"/>
<user username="testSolid" password="password" roles="SOLIDWORKS"/>
<user username="testZuken" password="password" roles="CR8000"/>
</tomcat-users>

Realm Config

<Realm className="org.apache.catalina.realm.LockOutRealm">
    <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
</Realm>

After this Method the error came

    @GET
    @Path("/tomcat/user")
    @Produces(MediaType.APPLICATION_JSON)
    public ArrayList<TomcatUser> getTomcatUser() {

        getLogWriter().writeLog("GET call on /tomcat/user");
        ArrayList<TomcatUser> tomcatUsers = new ArrayList<TomcatUser>();

        try {
            UserDatabase ud =  (UserDatabase) new InitialContext().lookup("java:comp/env/UserDatabase");

            ud.open();

            Iterator<org.apache.catalina.User> iteratorUsers = ud.getUsers();

            while (iteratorUsers.hasNext()) {
                org.apache.catalina.User user = iteratorUsers.next();

                TomcatUser tmpTomcatUser = new TomcatUser();
                String username = user.getUsername();

                Iterator<Role> iteratorRoles = user.getRoles();
                ArrayList<Role> roles = new ArrayList<Role>();

                // Change from Iterator to ArrayList
                iteratorRoles.forEachRemaining(roles::add);

                ArrayList<String> strRoles = new ArrayList<String>();

                for (int x = 0; x < roles.size(); x++) {
                    strRoles.add(roles.get(x).getRolename());
                }

                tmpTomcatUser.setUsername(username);
                tmpTomcatUser.setRoles(strRoles);

                tomcatUsers.add(tmpTomcatUser);
                tmpTomcatUser = null;
            }

            ud.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

        return tomcatUsers;
    }
Floskinner
  • 21
  • 1
  • 7
  • @Pandurang - the role name in the `tomcat_roles` table and the role-name in the web.xml are the same. I added the `tomcat-users.xml` to my question – Floskinner Jun 04 '20 at 14:40
  • Could you use `*` in web.xml file.Restart tomcat server and test. – Pandurang Jun 04 '20 at 14:48
  • @Pandurang - **That worked!** The issue is gone! ...But why, how can I fix it with my roles? Only user with the admin role should access the page – Floskinner Jun 04 '20 at 15:43

2 Answers2

1

This looks like roles issue in tomcat. You can use below configuration to allow all roles.

<role-name>*</role-name>

As per question default, tomcat-user.xml file is working fine with Basic authentication. You are facing roles issue after using how to programmatically add users to tomcat UserDatabaseRealm?. Is it true? If yes then I think you also need to pass the role programmatically.

Pandurang
  • 1,656
  • 2
  • 6
  • 10
  • Added the realm config and with `tomcat_roles` you mean the `tomcat-users.xml`, right? – Floskinner Jun 04 '20 at 16:38
  • As per question default, tomcat-user.xml file is working fine with `Basic authentication`. You are facing roles issue after using `how to programmatically add users to tomcat UserDatabaseRealm?`. Is it true? If yes then I think you also need to pass the role programmatically. – Pandurang Jun 04 '20 at 17:12
  • Yes, that's true. After I added my Servlet Call to get all Users and Roles the Problem appear. I add my Java Method where I read the `tomcat_users`. I try the programmatically roles and give feedback – Floskinner Jun 04 '20 at 17:30
  • You need to focus on your servlet. tomcat configuration looks good. – Pandurang Jun 04 '20 at 17:32
  • You are right, the problem was the servlet, thank you for your support! – Floskinner Jun 05 '20 at 07:03
  • I can not upvote your answer, I got not enough reputation :( – Floskinner Jun 05 '20 at 08:52
1

I found two issues in my servlet to solve the problem:

  1. I removed the method call .open() from my UserDatabase
  2. I have to return a String and not the TomcatUser Object. To return a String I format my Object with Gson to a JSON String.

So this is my final servlet method:

@GET
@Path("/tomcat/user")
@Produces(MediaType.APPLICATION_JSON)
public String getTomcatUser() {

    getLogWriter().writeLog("GET call on /tomcat/user");
    ArrayList<TomcatUser> tomcatUsers = new ArrayList<TomcatUser>();

    try {
        UserDatabase ud =  (UserDatabase) new InitialContext().lookup("java:comp/env/UserDatabase");

        Iterator<org.apache.catalina.User> iteratorUsers = ud.getUsers();

        while (iteratorUsers.hasNext()) {

            org.apache.catalina.User user = iteratorUsers.next();
            System.out.println(user.getUsername());

            TomcatUser tmpTomcatUser = new TomcatUser();
            String username = user.getUsername();

            Iterator<Role> iteratorRoles = user.getRoles();
            ArrayList<String> strRoles = new ArrayList<String>();

            while (iteratorRoles.hasNext()) {
                Role role = iteratorRoles.next();
                strRoles.add(role.getRolename());
            }

            tmpTomcatUser.setUsername(username);
            tmpTomcatUser.setRoles(strRoles);

            tomcatUsers.add(tmpTomcatUser);
            tmpTomcatUser = null;
        }


    } catch (Exception e) {
        e.printStackTrace();
    }

    return new Gson().toJson(tomcatUsers);
}
Floskinner
  • 21
  • 1
  • 7