Grails 3 - manipulating web.xml - grails

As now web.xml will be generated dynamically, I'd need to add a simple filter class at the top of of it without writing or using a plugin.
What code would I need and where should I put it?
Or could I somehow get a template of what grails would generate for a web.xml and I could modify and override it?
I saw in the documentation
grails.war.resources = { stagingDir, args ->
copy(file: "grails-app/conf/custom-web.xml",
tofile: "${stagingDir}/WEB-INF/web.xml")
}
but first: would this function in application.yml?
and second: I'd still need an appropriate web.xml template to change...
Thanks!

From cfrick's suggestion, I tried this, (applies to grails 3)
Create a filter e.g. [grails-project]/src/main/java/hello/SimpleCORSFilter.java
(Create the 'java' directory, or create a SimpleCORSFilter.groovy in the [grails-project/src/main/groovy directory)
You can use the example in one of the spring guides.
package hello;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
#Component
public class SimpleCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
// response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
// I used this instead
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
Then in [grails-project]/grails-app/conf/spring/resources.groovy, add the filter, e.g.
import hello.SimpleCORSFilter
// Place your Spring DSL code here
beans = {
myFilter(SimpleCORSFilter)
}
Also, in the grails docs, to order your filter:
import hello.SimpleCORSFilter
import org.springframework.boot.context.embedded.FilterRegistrationBean
import org.springframework.core.Ordered
myFilter(FilterRegistrationBean) {
filter = bean(SimpleCORSFilter)
urlPatterns = ['/*']
order = Ordered.HIGHEST_PRECEDENCE
}

i know it's not the anwers to the actual question, but adding filters "springboot" style is just providing a #Bean for it. E.g. you can just put it in grails-app/conf/spring/resources.groovy. More details can be found at: How to add a filter class in Spring Boot?
Grails 3 embraces Springboot and with that came the good bye to XML based config (not saying it is forbidden, but its discouraged)

You can use install-templates command, which will copy templates to src/templates directory, there you will find web.xml template inside war directory, you can customize it
See http://www.grails.org/doc/latest/ref/Command%20Line/install-templates.html

Related

How to tell OpenAPI to use context-root of application in it's URL

I am running two different Payara Micro microservices in one cluster.
The issue I have is that when I try to access the OpenAPI URL of MyApp1 like http://mylink.com/myApp1/openapi it does not work. It actually works when I use URL http://mylink.com/openapi.
This becomes an issue when I want to see the API for the other microservice like http://mylink.com/myApp2/openapi which does not work.
Is there a way in Payara Micro of telling OpenAPI to use the application's context in it's path just like all the other URL in the application do?
As you can see in my previous comment, I've also struggled with the same situation.
Context - openapi and microprofile
First let me say that having /openapi URL in the root is the intended behaviour of microprofile-open. Documentation always uses /openapi path as the right to get the document LINK
In the implementation, is very clear that this behaviour is both wanted as enforced:
In the ServletContainerInitializer for OpenApi one can see the following code
// Only deploy to app root
if (!"".equals(ctx.getContextPath())) {
return;
}
Workaround aka Solution.
Now that is clear that we cannot configured this, since it's intended behaviour, one solution ( the one I'm proposing ) is to proxy the request to /YOUR_APP/openapi to /openapi.
Since my application is a jax-rs one, deployed on openshift, and I don't want to have a dedicated proxy application for this, I've just created a simple Resource/Controller to proxy this specific request for me.
The outstanding method behind:
#GET
#Path("")
public Response proxyOpenApiCall(){
log.debug("proxyOpenApiCall called");
String entity = client.target("http://localhost:8080")
.path("openapi").request()
.get(String.class);
return Response.ok(entity).build();
}
I was able to fix this with a small forward proxy. Therefore I create a new REST enpoint wich is callable from public and returns the content of internal http endpoint.
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.RequestScoped;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#RequestScoped
#ApplicationPath("/")
#Path("/")
public class OpenApiProxyRestFacade extends Application {
private Client client;
#PostConstruct
public void init() {
this.client = ClientBuilder.newClient();
}
#GET
#Path("/openapi")
#Produces(MediaType.APPLICATION_JSON)
public Response proxyOpenApiCall() {
String entity = client.target("http://localhost:9080").path("openapi").request().get(String.class);
return Response.ok(entity).build();
}
#GET
#Path("/openapi/ui")
#Produces(MediaType.APPLICATION_JSON)
public Response proxyOpenApiUiCall() {
String entity = client.target("http://localhost:9080/openapi").path("ui").request().get(String.class);
return Response.ok(entity).build();
}
#PreDestroy
public void destroy() {
this.client.close();
}
}
For openapi, you can set this property for change of url, so it is configurable after all
mp.openapi.extensions.path=/yourapi/whatever
and for the openapi-UI set this
openapi.ui.yamlUrl=/yourapi/whatever
Sources: I first googled for mp.openapi.xxx parameters, (I found them in source code) which led me to this url
https://download.eclipse.org/microprofile/microprofile-open-api-1.0/microprofile-openapi-spec.html
and after looking for more stuff there was one simple sentence mentioning that there is also mp.openapi.extensions and after googling those further I found this random doc here https://github.com/wildfly/wildfly/blob/main/docs/src/main/asciidoc/_admin-guide/subsystem-configuration/MicroProfile_OpenAPI.adoc

Get access to springfox swagger-ui behind spring-security

I am going to use springfox (2.6.1v) with swagger-ui in my Spring Boot (1.4.2v).
The configuration for it looks:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.genericModelSubstitutes(ResponseEntity.class);
}
}
The problem is that my swagger is behind spring security and I need to allow access there only by admin.
Question is what should be the set of matchers to allow swagger-ui to work within my application?
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("??? <<<what_should_be_here>>> ???") // what should be here?
.hasRole("TENANT_ADMIN");
}
}
Ok first I have found the solution here so following lines:
.antMatchers("/admin/system/state/*", "/v2/api-docs", "/configuration/ui", "/swagger-resources", "/configuration/security", "/swagger-ui.html", "/webjars/**")
But still sth did not work and because of that I have asked this question. But after deeper investigation it occured that spring-fox does not support GSON. When you use GSON as "to json" converter swagger-ui receives a slightly different JSON format what causes problems...
When we changed converter to Jackson and added above paths to spring-config it works without any problems.
I have even requested the new feature on spring-fox github here.

is it possible to use Spring MVC Test directly in grails, and how?

We think the Spring Rest Doc is great for documenting rest api. But it is based on Spring MVC Test,and we can't figure out how to use Spring MVC Test in my grails apps(Grails 3.0.5).
I tried to use a config class (with #Configuration and#ComponentScan) to scan grails components into the test context, but it seems like nothing had been loaded (when performing a http request to the mockmvc, it got 404).
I also tried to configure the grails controller directly, and got a run time error.
Could not autowire field: private reactor.bus.EventBus
I also tried to add #Integration (from grails) on the test class but recieved the same error.
Please help.
Here are some code samples.
what i tried was adding config class or class locations or grails controller to ContextConfiguration of the test class below. And the test class itself is basically following spring rest doc reference.
import org.junit.Before;
import org.junit.Rule;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.restdocs.RestDocumentation;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration
//TODO how to scan Grails components into the test context
public class QuestionRestSpec {
#Rule
public final RestDocumentation restDocumentation = new RestDocumentation("build/generated-snippets");
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
.apply(documentationConfiguration(this.restDocumentation))
.build();
}
}
The config class(which has no use):
#Configuration
#EnableWebMvc
#ComponentScan
public class AskConfig {
}
Unfortunately it doesn't appear to be possible to use MockMvc with Grails. Calls to MockMvc.perform fail:
HandlerMapping requires a Grails web request. Stacktrace follows:
java.lang.IllegalArgumentException: HandlerMapping requires a Grails web request
at org.springframework.util.Assert.notNull(Assert.java:112) ~[spring-core-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at org.grails.web.mapping.mvc.UrlMappingsHandlerMapping.getHandlerInternal(UrlMappingsHandlerMapping.groovy:113) ~[grails-web-url-mappings-3.0.9.jar:3.0.9]
at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:299) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1120) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at org.springframework.test.web.servlet.TestDispatcherServlet.getHandler(TestDispatcherServlet.java:90) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:932) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) [tomcat-embed-core-8.0.26.jar:8.0.26]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) [tomcat-embed-core-8.0.26.jar:8.0.26]
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:144) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
…
Spring REST Docs 1.1 will hopefully add support for REST Assured. This removes the need for MockMvc and, instead, will allow the API to be documented by making HTTP calls to a running server in a functional test.
For reference, here's the Spock specification that I used:
package com.example.notes
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import grails.test.mixin.integration.Integration
import grails.transaction.*
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.restdocs.RestDocumentation;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext
import spock.lang.*
#Integration
#Rollback
class ApiDocumentationSpec extends Specification {
#Rule
public final RestDocumentation restDocumentation = new RestDocumentation("build/generated-snippets");
#Autowired
WebApplicationContext context
MockMvc mockMvc
def setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).build();
}
def cleanup() {
}
void "list notes"() {
when:
MvcResult result = this.mockMvc.perform(get("/notes")).andReturn()
then:
result.andExpect(status().isOk())
.andDo(document("notes-list-example"));
}
}

Restrict access to java-melody monitoring url

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)...

Shiro or Spring Security with Ninja framework or Spark

can I use Spring Security or Shiro Security with Ninja Framework or Spark Framework? I can't find any example to integrate this security frames with web frames. there is not any information in the ninja web site about user auth and web app security.
This question is quite old, but I am unable to find any example of integrating Apache Shiro with Ninja Framework, so here are my findings about this.
Ninja Framework requires an instance of AbstractModule, it shows me an error when you try something like the configuration that is recommended here:
package conf;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import org.apache.shiro.config.Ini;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.guice.ShiroModule;
class Module extends ShiroModule {
protected void configureShiro() {
try {
bindRealm().toConstructor(IniRealm.class.getConstructor(Ini.class));
} catch (NoSuchMethodException e) {
addError(e);
}
}
#Provides
Ini loadShiroIni() {
return Ini.fromResourcePath("classpath:shiro.ini");
}
}
However checking the source code of ShiroModule class I noticed that it extends PrivateModule so this should work, at least I get no compilation errors:
package conf;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import org.apache.shiro.config.Ini;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.guice.ShiroModule;
public class Module extends AbstractModule
{
#Override
protected void configure()
{
install(new ShiroModule()
{
#Override
protected void configureShiro()
{
//shiro recomended configuration
try {
bindRealm().toConstructor(IniRealm.class.getConstructor(Ini.class));
} catch (NoSuchMethodException e) {
addError(e);
}
}
#Provides
Ini loadShiroIni()
{
return Ini.fromResourcePath("classpath:shiro.ini");
}
});
}
}
Spark has the notion of filters. http://sparkjava.com/documentation.html#filters therefore you can add the Shiro or Spring Security filter. That said, it isn't as simple as dropping in a filter for either of these security frameworks. But in theory...
if you want to secure web restful api, suggest use the sureness - https://github.com/tomsun28/sureness
It is no specific framework dependency(support springboot, quarkus, javalin, ktor and more).
The essence of sureness is to intercept all rest requests for authenticating and Authorizing.
The interceptor can be a filter or a spring interceptor, it intercepts all request to check them.
What you need to know is that sureness is a project created by us, welcome to use.

Resources