I am attempting to upgrade an existing web application to run on Tomcat 10.1 from Tomcat 9.0 where it works fine. My web application currently uses JDBCRealm but this appears to now be deprecated with Tomcat 10.1 and are now advised to use DataSourceRealm instead.
I have attempted to follow the instructions on the Apache website to use the DataSourceRealm instead and upgraded both my context.xml and server.xml however my authentication isn't now working.
Here is my context.xml file:
<Context path="/TASSU"> <Realm className="org.apache.catalina.realm.DataSourceRealm" connectionName="root" connectionPassword="sesame"
connectionURL="jdbc:mysql://localhost:3306/login_details" debug="99" driverName="com.mysql.jdbc.Driver"
roleNameCol="Rolename" userCredCol="Password" userNameCol="Username" userRoleTable="userrole" userTable="userpass"/>
And here is the realm information within my server.xml file:
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
<Realm className="org.apache.catalina.realm.DataSourceRealm" connectionName="root" connectionPassword="sesame"
connectionURL="jdbc:mysql://localhost:3306/login_details" debug="99" driverName="com.mysql.jdbc.Driver"
roleNameCol="Rolename" userCredCol="Password" userNameCol="Username" userRoleTable="userrole" userTable="userpass"/>
Here is the error that is being displayed in the logs:
25-Jan-2023 10:13:50.699 SEVERE [http-nio-8080-exec-23] org.apache.catalina.realm.DataSourceRealm.open Exception performing authentication
Can anyone see at first glance what the issue could be here? Have I set these two files up correctly?
We have access to the tomcat manager app restricted by authentication and IP address. See example config:
<Context docBase="${catalina.home}/server/webapps/manager"
privileged="true" antiResourceLocking="false">
<Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="{some-ips}"/>
<!-- Link to the user database we will get roles from -->
<ResourceLink name="users" global="UserDatabase"
type="org.apache.catalina.UserDatabase"/>
</Context>
Is there any way to reference an additional file on the local machine that determines additional IP addresses that are allowed to access the manager app?
Thanks.
I am using tomcat security for my application:
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
<!--This is only applicable for Tomcat 8.5 and higher version with SQL server authentication-->
<Realm className="org.apache.catalina.realm.JDBCRealm"
driverName="com.microsoft.sqlserver.jdbc.SQLServerDriver" connectionURL="jdbc:sqlserver://xxx.xx.xxx.xxx:1433;databaseName=xxxxx" connectionName="xxxx" connectionPassword="xxxx" roleNameCol="role_name" userCredCol="user_pass" userNameCol="user_name" userRoleTable="user_roles" userTable="users">
<CredentialHandler algorithm="MD5" className="org.apache.catalina.realm.MessageDigestCredentialHandler"/>
</Realm>
</Realm>
Now I want to be able to dynamically set new roles to the logged-in user from within the application whatever the authentication datasource may be. I need this to be deployed in an application and the client won't let us have the authentication database details and would prefer to themselves set the realm details. So it should work for all server/database types and hence be generic to the extent as possible.
Plenty of time i saw this entry in context.xml when someone doing Authentication of user from Servelt3 login method
<realm classname="org.apache.catalina.realm.JDBCRealm" debug="99"
drivername="org.gjt.mm.mysql.Driver"
connectionurl="jdbc:mysql://localhost/tomcatusers?user=test;password=test"
usertable="users" usernamecol="user_name" usercredcol="user_pass"
userroletable="user_roles" rolenamecol="role_name"/>
Now in Java classes they doing something this code
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
request.login(username, password);
Can anyone tell what query will fire in this case ? And how this relm entry will authenticate user with username and password ?
I'm trying to nest Realms as follows in Tomcat 7.0.32 (written here in pseudo-XML):
<CombinedRealm>
<LockoutRealm>
<DataSourceRealm/>
</LockoutRealm>
<UserDatabaseRealm/>
</CombinedRealm>
This doesn't seem to work - is it possible to nest Realms in Tomcat by more than two levels? I get a warning in the logs:
No rules found matching 'Server/Service/Engine/Realm/Realm/Realm'.
The idea behind is that the web service has some critical users that must not be locked out (e.g. as a DOS) and some normal users, which may have weaker passwords, where the lockoutRealm should be active. I'm sure other people have been in this situation.
If there is another way to achieve this (e.g. a whitelist for the LockoutRealm), please let me know.
Single sign on is also needed.
I guess extending the existing LockoutRealm code with a list of accounts never to lock out would be an option, but I'm not so keen on writing my own Realm, I would rather not add custom code on that level to Tomcat, as this will complicate setup for others and with every Tomcat update it might break etc.
Thanks for any help!
Here is the relevant part of server.xml of my test config:
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.CombinedRealm">
<!-- Lockout realm for the DB users -->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- PRIMARY: DataSourceRealm with user DB -->
<Realm className="org.apache.catalina.realm.DataSourceRealm"
dataSourceName="jdbc/authority"
userTable="user" userNameCol="username"
userCredCol="password" digest="SHA"
userRoleTable="user_role" roleNameCol="rolename" />
</Realm>
<!-- FALLBACK:
This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
Apache commons-digester is used to parse the configuration files, so I suspect this particular use case simply wasn't expected.
Tomcat's org.apache.catalina.startup.RealmRuleSet.addRuleInstances seems rigged to only go 2-levels deep for Realm configuration. Seems simple enough to add another layer in there.
I'd have to look at how the digester can be configured to see if arbitrary levels could be supported, or if some subset would have to be manually configured.
Feel free to head over to the Tomcat users' list to request such a change.
The new answer is now:
Update to Tomcat 7.0.33 or later. Then it works perfectly.
Christopher Schultz was so friendly to forward my question here to the Tomcat user list. The great Tomcat developers have immediately addressed the issue and put this in the next release. Thanks a lot!
So you can now use a construction like the one in the question or like this with different order / "priorities":
...
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.CombinedRealm">
<!-- PRIMARY: tomcat-users.xml with critical system users
that should always work, DB independent and without lockout
NOTE: If the wrong password is given, the secondary path with
lockout is still attempted, so that a lockout on that path
will still occur and be logged. Still the primary path is not
locked for access by that happening. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
<!-- SECONDARY: DataSourceRealm with DB with lockout functionality -->
<!-- (three level nesting of realms requires Tomcat >= 7.0.33) -->
<Realm className="org.apache.catalina.realm.LockOutRealm"
failureCount="5" lockOutTime="60" > <!-- note that when an account is locked correct password
login is no longer possible (would otherwise defeat purpose of lockout),
but also lockoutTime is still reset in each correct attempt -->
<Realm className="org.apache.catalina.realm.DataSourceRealm"
dataSourceName="jdbc/authority"
userTable="user" userNameCol="username"
userCredCol="password" digest="SHA"
userRoleTable="user_role" roleNameCol="rolename" />
</Realm>
</Realm>
<Host >
...
</Host>
</Engine>
...
Of course you may also use other Realms and other combinations.
Note that one thing can be misleading in the logs: In this construction, if a wrong password is given for one of the critical users stored in the primary realm, the primary realm denies access, then the secondary realm via the lockout realm is tried and also denies access eventually locking out the username. This is logged by the lockout realm as a warning "An attempt was made to authenticate the locked user ...". Still with correct password, access keeps working via the primary realm, as it does not go via the lockout realm. I.e. all works as intended, just the log message could lead to confusion (of course this is impossible to avoid).