2

Our web application is deployed on Tomcat, currently using the UserDatabaseRealm for security. We want to supply a page in the application where the user can change their password - a simple, common web application function. I cannot find any example servlet code to do this. The Tomcat description of the UserDatabaseRealm implies that it can be updated programmatically after loading the XML when the server starts, and it can also save changes back to the XML file. There is a brief mention of JMX as a means, but no details.

Our goal is to have no database in this application, so we really don't want to use the JDBC Realm. What does the Java servlet code look like to change a user's password (and for an admin, to add/remove users)?

Thanks for the clues, here is my working Tomcat MemoryUserDatabase servlet (minus any encryption, password verification, error handling, etc):

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    try {
        // Get current password for currently authenticated user
        String username =  request.getRemoteUser();
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();

        ObjectName userObjName = new ObjectName("Users:type=User,username=\""+username+"\",database=UserDatabase");
        Object password = server.getAttribute(userObjName, "password");
        System.out.println("Current Password = "+password.toString());

        // Get new password from request parms and update the DB
        String newPw = request.getParameter("newpw");
        server.setAttribute(userObjName, new Attribute("password", newPw));

        // Password is updated in-memory, now write to file.
        // Note Tomcat MemoryUserDatabase.save() implementation does not synchronize this 
        // operation, so it can fail badly if multiple users do this at the same time. 
        // Ugh. Should do this in a static synchronized method.
        server.invoke(
                new ObjectName("Users:type=UserDatabase,database=UserDatabase"),
                "save",
                new Object[0],
                new String[0]);

        // If no exception, save was OK (it returns VOID, so there is no return value to check)
    }
    catch (Throwable t) {
        // Should return proper HTTP error code...
        t.printStackTrace(System.err);
    }
}
user3191192
  • 169
  • 13

1 Answers1

0

I just figures this out.

first you have to update the server.xml and add readonly=false to:

<Resource auth="Container" readonly="false" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>

then in a jsp file:

 <%!public static boolean changePasswd(String user, String passwd, MBeanServer mbeanServer, JspWriter out) throws Throwable{ 
try {
String userFDN = "Users:type=User,username=\""+user+"\",database=UserDatabase"; 
ObjectName userObjName = new ObjectName(userFDN);
MBeanInfo info = mbeanServer.getMBeanInfo(userObjName);

Attribute attr=new Attribute("password",passwd);
mbeanServer.setAttribute(userObjName, attr);
ObjectName databaseObjName=new ObjectName("Users:type=UserDatabase,database=UserDatabase");
Object result= mbeanServer.invoke(databaseObjName,"save",new Object[0],new String[0]); 
out.println("<b>Changed password and, Saved: "+result+"</b>");
return true;    
} catch (Throwable t) {
out.print("<font color='red'>WHY: </font>" + t);
} 
return false; 
}%>

  <%MBeanServer mbeanServer = (MBeanServer) list.get(0); 
  //ObjectName obname = new ObjectName(   "Catalina:type=Resource,resourcetype=Global,class=org.apache.catalina.UserDatabase,name=\"UserDatabase\"" );
   ArrayList list = MBeanServerFactory.findMBeanServer(null);  
   MBeanServer mbeanServer = (MBeanServer) list.get(0);
   changePasswd("user","passwd",mbeanServer,out);

/N

NaAl
  • 26
  • 2
  • It doesn't. There's nothing here that lets a user change his password. – user207421 Jan 08 '15 at 08:30
  • EJP, did you see my edited answer? (if so, could you please mark it as answer please? and maybe change the vote?) – NaAl Jan 09 '15 at 17:40
  • This provided the clues I needed to do this in a servlet, especially the object name strings... still had to figure out how to get the MBeanServer instance. Working servlet code posted below. – user3191192 Jan 12 '15 at 17:08