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 :-(
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"/>
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')
}
}
}
}
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
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)...
Related
My Issue:
I'm not certain, but I believe there is an SAP provided authentication Servlet Filter, redirecting to the SAP default logon page (which of course is causing a problem since it isn't returning an xml response), which is making the FullAjaxExceptionHandler to not be invoked. I'm looking for help in determining what the root cause really is, or if there are any alternatives to fix it.
My environment:
SAP Netweaver 7.4 (servlet 2.5 with JSF 2.1.x support)
SAP JVM 6.1 (SAP's 1.6 jvm)
Mojarra 2.1.29
Omnifaces 1.11
Background:
I have a sample application which has session-config session-timeout set to 1 minute, and have the Omnifaces FullAjaxExceptionHandlerFactory configured in faces-config.xml.
web.xml
...
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/errorPage.xhtml</location>
</error-page>
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/WEB-INF/viewExpired.xhtml</location>
</error-page>
<session-config>
<session-timeout>1</session-timeout>
</session-config>
...
faces-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd" version="2.1">
<factory>
<exception-handler-factory>org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory</exception-handler-factory>
</factory>
...
The way I've determined Netweaver works, is it will mark the session as "expired" after one minute, but the session itself is not cleaned up immediately (I understand this is common in web containers). If I submit an ajax request after the session expired but before the session is completely removed by the server, the FullAjaxExceptionHandler kicks in, and properly displays my View Expired page. If however the server has already cleaned up the expired session, nothing happens, and the page appears "dead" until a physical refresh is performed.
From reading many answers by #balusc, I suspect that an SAP provided http filter is kicking in, which is redirecting the ajax request to the SAP login page, which is why the application isn't working.
Is there any way of determining for sure what is causing it? If it is a server provided filter, is there anyway to override it?
Yet again, thanks BalusC! You pushed me in the right direction.
SAP Netweaver has a global-web.xml file which defines some default functionality. One of them is as follows:
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>com.sap.engine.services.servlets_jsp.server.servlet.AuthenticationFilter</filter-class>
</filter>
So what I did was used the same <filter-name> in my own web.xml, providing a class which extends SAP's Authentication Filter.
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>com.example.AjaxAwareAuthenticationFilter</filter-class>
</filter>
Since the <filter-name> is the same, the one defined in my local web.xml wins.
Here is my class:
package com.example;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import com.sap.engine.services.servlets_jsp.server.servlet.AuthenticationFilter;
public class AjaxAwareAuthenticationFilter extends AuthenticationFilter {
#Override
public void destroy() {
super.destroy();
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpSession session = httpRequest.getSession(false);
if (session == null && "partial/ajax".equals(httpRequest.getHeader("Faces-Request"))) {
// JSF ajax request. Return special XML response which instructs
// JavaScript that it should in turn perform a redirect.
response.setContentType("text/xml");
response.getWriter()
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
.printf("<partial-response><redirect url=\"%s\"></redirect></partial-response>",
httpRequest.getContextPath());
} else {
super.doFilter(request, response, chain);
}
}
#Override
public void init(FilterConfig config) throws ServletException {
super.init(config);
}
}
We have an application which is using spring-security-oauth2:1.0. I was trying to change it to a newer version, spring-security-oauth2:2.0.7.RELEASE. Some classes were removed, some package structure is changed, I managed to sort out all those things and I was able to start the server without any issue. But I am facing a strange issue here.
With OAuth2 - 1.0 version, when the user logs in we used to do a GET request on /oauth/token, For example :
http://localhost:8080/echo/oauth/token?grant_type=password&client_id=ws&client_secret=secret&scope=read,write&username=john#abc.com&password=password123
and It used to work just fine.
When I try the same thing, First of all I am not able to make a GET request because of the logic in TokenEndPoint.java
private Set<HttpMethod> allowedRequestMethods = new HashSet<HttpMethod>(Arrays.asList(HttpMethod.POST));
#RequestMapping(value = "/oauth/token", method=RequestMethod.GET)
public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal, #RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
if (!allowedRequestMethods.contains(HttpMethod.GET)) {
throw new HttpRequestMethodNotSupportedException("GET");
}
return postAccessToken(principal, parameters);
}
I have tried to make a POST request same as above URL, but I get InsufficientAuthenticationException with the error message
There is no client authentication. Try adding an appropriate authentication filter
This is because of the following POST request controller in TokenEndpoint.java. When I debug, I see that principal is null.
#RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, #RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
//principal is null here
if (!(principal instanceof Authentication)) {
throw new InsufficientAuthenticationException(
"There is no client authentication. Try adding an appropriate authentication filter.");
}
.............
}
I have an authentication filter and it worked well when I used version 1.0. This is the relevant prats of my config:
<authentication-manager xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="userDetailsService"/>
</authentication-manager>
<bean id="userDetailsService" class="com.hcl.nc.service.UserDetailsService">
<constructor-arg><ref bean="sessionFactory" /></constructor-arg>
</bean>
I always thought that the request will be authenticated by authentication-provider and goes to token-endpoint but that does not seem to be the correct flow. After debugging the application with version 2.0.7, now I really doubt my understanding about the flow.
Could somebody please explain why it worked in previous version and why it's not working now?
Do I have do to something different to get a OAuth token??
NOTE: I have already checked these questions : here, here, here. But I was not able to find the correct solution.
I don't know the previous version, but I know a bit about 2.0.7.
I suspect your problem is that your TokenEndpoint security tries to authenticate your clients against your user service.
The TokenEndpoint is protected by a BasicAuthenticationFilter. By default this filter would use an AuthenticationManager instance, which itself holds an AuthenticationProvider, which itself depends on an instance of UserDetailsService.
The trick is that this particular instance of UserDetailsService must be client based, not user based : that's why there is a ClientDetailsUserDetailsService, which adapts ClientDetailsService to UserDetailsService.
Normally all this stuff is already done by default when you use the framework's configuration classes AuthorizationServerConfigurerAdapter, #EnableAuthorizationServer, etc..
I had the same problem and my application.yml had this line:
servlet:
path: /auth
so the token address was: /auth/oauth/token
I remove the path from application.yml so the token path became:
/oauth/token
And everything works fine.
I hope this help
One of the problems of the following error, can be that authentication was not performed. I have encountered this problem with older implementation of Spring.
verify that:
TokenEndpoint -> postAccessToken method. Check if Principal is not null. If it is null it means that Basic Authroziation was not performed.
One of the solution to add filter was to use:
#Configuration
public class FilterChainInitializer extends AbstractSecurityWebApplicationInitializer {
}
More information about AbstractSecurityWebApplicationInitializer can be found in Spring docs
The problem can be because of opening all requests. You should remove it.
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/**");
}
in my case, i found this config:
security.allowFormAuthenticationForClients(); // here
then post this
http://localhost:8081/sso/oauth/token?client_id=unity-client&client_secret=unity&grant_type=authorization_code&code=Yk4Sum&redirect_uri=http://localhost:8082/sso-demo/passport/login
its works for me, try it
#Configuration
#EnableAuthorizationServer
public class Oauth2Config extends AuthorizationServerConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(Oauth2Config.class);
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients(); // here
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception { // #formatter:off
clients.inMemory()
.withClient("unity-client")
.secret("unity")
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
.scopes("foo", "read", "write")
.accessTokenValiditySeconds(3600) // 1 hour
.refreshTokenValiditySeconds(2592000) // 30 days
;
} // #formatter:on
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
}
}
I am following this tutorial - Practical Guide to Building an API Back End with Spring Boot'. See https://www.infoq.com/minibooks/spring-boot-building-api-backend , But with the latest SpringBoot Version(2.7)
and I run into this problem:
org.springframework.security.authentication.InsufficientAuthenticationException: There is no client authentication. Try adding an appropriate authentication filter. at org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(TokenEndpoint.java:91) ~[spring-security-oauth2-2.3.5.RELEASE.jar:na]
My solution/fix was to annotate WebSecurityGlobalConfig with #EnableWebSecurity because in the original course this annotation was missing.
So adding this annotaiton has fixed the error for me.
I need to write Unit tests for production routes in Grails which use Services referenced by Camel bean component. My requirement is neither to change nor to copy existing routes in test.
Problem is to somehow mock Service bean and add it to Camel registry.
I was able to do this using 'bind' method on 'context.registry.registry' object. Is there any functionality to do that in more safe way? Camel version is 2.10, Grails 2.1
Route is:
from('direct:validate').to('bean:camelService?method=echo')
CamelService is just simple class:
package com
class CamelService {
def echo(text) {
println "text=$text"
text
}
}
Test is following (route copied only to make question simpler):
package com
import grails.test.mixin.*
import org.apache.camel.builder.RouteBuilder
import org.apache.camel.test.junit4.CamelTestSupport
#TestFor(CamelService)
class RouteTests extends CamelTestSupport {
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from('direct:validate').to('bean:camelService?method=echo')
}
};
}
void testMockBean() throws Exception {
context.registry.registry.bind 'camelService', service
def result = template.requestBody('direct:validate', 'message')
assert result != null
assert result == 'message'
}
}
Camel allows you to plugin any custom registry you want, and out of the box it uses a Jndi based registry, which is why you can bind a service to it with the code example. An alternative is to use a SimpleRegistry which is just a Map, so you can put a service into the registry using the put method from the Map. You would then need to override createCamelContext method from the CamelTestSupport class and
pass in the SimpleRegistry to the constructor of DefaultCamelContext.
Anyway your code is safe as long you use the non-Spring CamelTestSupport class, as its using the JNDI based registrry out of the box. If you use CamelSpringTestSupport, then its a spring based registry, and you would need to use the spring app context to add your bean to it.
You can inject your components using CamelSpringtestSupport rather than CamelTestSupport as your base class.
Reading the documentation on Spring Test will help you for sure, and you might find interesting to use mock in your tests.
Anyway, you can build a custom context for your test, containing your bean's declaration and load it in the test.
public class RouteTests extends CamelSpringTestSupport {
#Override
protected AbstractApplicationContext createApplicationContext() {
return new ClassPathXmlApplicationContext("route-test-context.xml");
}
#Test
public void testMockBean(){
//...
}
}
route-test-context.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cxf="http://camel.apache.org/schema/cxf" xmlns:camel="http://camel.apache.org/schema/spring"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="service" ref="com.CamelService"/>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<package>com</package>
</camelContext>
</beans>
I'm working on a Java EE (which I'm fairly new to) web application (JSF, JPA, EJB), and am in the process of incorporating PrettyFaces for human readable/bookmarkable URLs. I've been using a Filter for 2 reasons.
to make sure a user is logged in.
to wrap the call to filterChain.doFilter(...) in a transaction so JPA lazy loading works when generating views (for example, I can just set a Department object in the backing bean, and use #{backingBean.department.employees} to get the associated list of employees in the .xhmtl file).
Before incorporating PrettyFaces, I was using a url-pattern (in web.xml) of *.xhmtl (although the filter doesn't really need to run for the login page) for the Filter. With PrettyFaces, trying to specify a url-pattern for Filters seems to be a bit of a problem, mainly due to the lack of flexibility of the url-pattern rules (lack of support for regular expressions). Is there another way of accomplishing what I need with-out using Filters (and without having to duplicate code)?
Also, I know I can add a static portion to the beginning of the URL (like, /dept/#{deptName}/... and then use a Filter with a url-pattern of /dept/*, but I was hoping to just start with something like /#{deptName}/... (and using a url-pattern of /* runs the filter on everything, including images, javascript, css, etc.)
Basically, the filter has a transaction injected...
#Resource UserTransaction tx;
And does something like this.
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse httpRes = (HttpServletResponse) response;
HttpSession session = httpReq.getSession();
User currentUser = (User)session.getAttribute("userInSession");
if (currentUser == null) {
httpRes.sendRedirect("...") //redirect to LoginServlet
} else {
try {
tx.begin();
chain.doFilter(httpReq, httpRes);
}
catch (Exception e) { }
finally {
try {
tx.commit();
}
catch (Exception e) { }
}
}
I have a managed bean that is like this...
#ManagedBean
#RequestScoped
#URLMapping(
id="uutSerialNumber",
pattern="/#{uutSerialNumberController.programId}/uut/#{uutSerialNumberController.uutId}",
viewId="/uutSerialNumber.xhtml"
)
public class UutSerialNumberController {
#EJB private ProgramServiceBean programServiceBean;
#EJB private UutServiceBean uutServiceBean;
private Integer programId;
private Integer uutId;
private Program program;
private Uut uut;
#URLAction
public String loadData() {
program = programServiceBean.findByProgramId(programId);
uut = uutServiceBean.findUutByUutId(uutId);
return null;
}
//other stuff, setters/getters
}
In the view uutSerialNumber.xhmtl, I do something like this (which requires lazy-loading, unless I want to go to the trouble of manually pre-fetching collections in my uutServiceBean.findUutByUutId())...
<ul>
<c:forEach var="serialNumber item="#{uut.serialNumbers}>
<li>#{serialNumber.description}</li>
</c:forEach>
</ul>
Turns out I didn't have PrettyFaces configured correctly (doh!). It was a little confusing because in the PrettyFaces Reference Guide, it says that you don't need to edit web.xml if using Servlet 3.0 (which I am). But, doing the following solved my problem.
<!-- PrettyFaces Filter -->
<filter>
<filter-name>Pretty Filter</filter-name>
<filter-class>com.ocpsoft.pretty.PrettyFilter</filter-class>
</filter>
<!-- My own view Filter -->
<filter>
<filter-name>View Filter</filter-name>
<filter-class>com.jasoni.ViewFilter</filter-class>
</filter>
<!-- mappings -->
<filter-mapping>
<filter-name>Pretty Filter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>View Filter</filter-name>
<url-pattern>*.xhtml</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
With this, I'm able to run a filter on my views and use a Transaction View Pattern (similar to the one mentioned in Pro JPA 2, except using a Filter instead of a Serlvet, so lazy loading works with JPA), and also check that the user has a session going.
I cant see how using PrettyFaces may affect your filter - you can always get PrettyContext from HttpServletRequest and get all you need for processing url's, including regular expressions. As for JPA and lazy loading - this is quite a different story, I'd suggest to be more specific in this area and provide more details, code snippets, etc for us to be able to help.
Any captcha plugin for struts 2 other than jcaptcha4struts2 and provide some useful link also
Try this:
http://code.google.com/intl/de-DE/apis/recaptcha/docs/java.html
It is pretty straightforward with Struts 2.
I recommend you to copy over the source code (its ASL licensed and therefore allowed) and then follow the instructions. Or you create a taglib, if you would like to avoid java code in you jsp.
The server side instructions can be modified to use it with the field names recaptcha_challenge_field and recaptcha_response_field.
If you add the following methods to your action:
public HttpServletRequest getHttpServletRequest() {
return httpServletRequest;
}
public void setHttpServletRequest(HttpServletRequest httpServletRequest) {
this.httpServletRequest = httpServletRequest;
}
And add the following listener to web.xml:
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
You are able to catch the HttpServletRequest which is necessary to validate the captcha.
Hope that helps.
Cheers,
Christian