Restrict access to java-melody monitoring url

4k views Asked by At

Is there a way I can restrict access to /monitoring url generated by Java-Melody plugin in Grails using Shiro roles?

Update: a little bit more details. It's no problem so secure most Grails ressources with shiro. But in case of the java melody plugin, it seems that the melody filter is executed before the shiro filter gets executed. This renders shiro useless.

There are some solutions which say that this might be fixed through a change in the web.xml, but this is not a quick hit and I (rdmueller) didn't manage to make it work yet. The web.xml plugin also seems to promise some help, but I don't want to add another plugin just to secure one plugin.

Some older statements found on the web state that this problem should be already solved through the usage of the loadAfter list in this file: https://github.com/javamelody/grails-melody-plugin/blob/master/GrailsMelodyGrailsPlugin.groovy - but it seems that this only worked for older versions of Grails.

Update2: In order to make it easier to propose a solution, I've create a Grails 2.2.4 sample: https://github.com/rdmueller/SO30739581

just clone the project, do a grailsw run-app and navigate to

http://localhost:8080/SO30739581/dbdoc

and you'll get a login screen via shiro. Navigate to

http://localhost:8080/SO30739581/monitoring

and you'll get the melody screen without being logged in :-(

4

There are 4 answers

3
AverageJoe On BEST ANSWER

I ended up doing so by making changes to web.xml for HTTP authentication. Add this to you web.config file.

<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>Monitoring</realm-name>
</login-config>
<security-role>
    <role-name>monitoring</role-name>
</security-role>
<security-constraint>
    <web-resource-collection>
        <web-resource-name>Monitoring</web-resource-name>
        <url-pattern>/monitoring</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>monitoring</role-name>
    </auth-constraint>
</security-constraint>

Then add a user and role to your tomcat-users.xml

<user username="yourusername" password="yourpassword" roles="monitoring"/>
0
rdmueller On

Just to list all available options:

the shiro-protect-any - plugin seems to work, but IMHO, it seems to be to be a bit too complicated and the plugin is "not fully tested" (says the author)...

6
YeIIowsnow On

I assume you're using Grails 2.x, you could hardcode it this way :

<!-- language: java-->
// grails-app/conf/MonitoringFilters.groovy
import org.apache.shiro.SecurityUtils
class MonitoringFilters {

    def dependsOn = [ShiroSecurityFilters]

    def filters = {
        myMonitoringArea(uri: "/monitoring") {
           before = {      
              SecurityUtils.subject.hasRole('ADMIN')             
           }
        }       
    }
}
0
user2957868 On

This is not a "quick hit", but the following approach should work with Shiro or whatever security framework your Grails app uses.

In web.xml, add the following elements above any existing <filter> elements:

<filter>
  <filter-name>melodyFilter</filter-name>
  <filter-class>com.your.package.MelodyFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>melodyFilter</filter-name>
  <url-pattern>/monitoring/*</url-pattern>
</filter-mapping>

This will call com.your.package.MelodyFilter any time the /monitoring/* url pattern is invoked.

Next, you'll need to create a MelodyFilter Java class in /src/java/com/your/package/MelodyFilter.java.

In the body of the doFilter method, you may call a Grails service method to perform any desired security checks, as follows:

package com.your.package;

import com.my.grails.app.MyService;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;

public class MelodyFilter implements Filter {

    @Override
    public void destroy() { }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String uri = ((HttpServletRequest)request).getRequestURI();
        HttpSession session = ((HttpServletRequest)request).getSession(false);
        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(session.getServletContext());
        // replace MyService with your actual service
        MyService myService = (MyService)ctx.getBean("myService");
        // replace isUserAuthorized with your actual service method;
        // session and uri params included to demonstrate how to pass them
        // your argument list can be whatever your service method requires
        boolean authorized = myService.isUserAuthorized(session, uri);
        if (authorized) { chain.doFilter(request,response); }
        else {
            request.setAttribute("error", "User is not authorized to access " + uri); 
            request.getRequestDispatcher("/someController/someAction").forward(request, response);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException { }
}

Then simply implement myService.isUserAuthorized() to perform whatever security checks you desire.

I have verified this technique works in Grails-2.3.6 with grails-melody:1.59.0