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.
(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?)
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
<!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>
<!-- 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>
<?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 className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
</Realm>
@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;
}
I found two issues in my servlet to solve the problem:
.open()
from my UserDatabase
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);
}