Skip to content

Commit

Permalink
#329: Added CORS starter (#330)
Browse files Browse the repository at this point in the history
  • Loading branch information
sujith-mn authored Jan 22, 2021
1 parent 7e3b2ff commit 0fd0b4e
Show file tree
Hide file tree
Showing 12 changed files with 328 additions and 34 deletions.
54 changes: 49 additions & 5 deletions documentation/guide-cors-support.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ toc::[]

= CORS support

When you are developing Javascript client and server application separately, you have to deal with cross domain issues. We have to request from a origin domain distinct to target domain and browser does not allow this.
When you are developing Javascript client and server application separately, you have to deal with cross domain issues. We have to request from a origin domain distinct to target domain and browser does not allow this.

So , we need to prepare server side to accept request from other domains. We need to cover the following points:

Expand All @@ -17,12 +17,56 @@ It is important to note that if you are using security in your request (sending

== Configuring CORS support

On the server side we have defined a new filter in Spring security chain filters to support CORS and we have configured devonfw security chain filter to use it.
=== Dependency

You only have to change `CORSDisabled` property value in `application-default.properties` properties file.
To enable the CORS support from the server side add the below dependency.

[source,xml]
----
<dependency>
<groupId>com.devonfw.java.starters</groupId>
<artifactId>devon4j-starter-security-cors</artifactId>
</dependency>
----

=== Configuration

Add the below properties in your application.properties file.

[source]
----
#CORS support
security.cors.enabled=false
----
security.cors.spring.allowCredentials=true
security.cors.spring.allowedOrigins=*
security.cors.spring.allowedHeaders=*
security.cors.spring.allowedMethods=OPTIONS,HEAD,GET,PUT,POST,DELETE,PATCH
security.cors.pathPattern=/**
----


[cols="1,1,1"]
|===
|Attribute |Description |HTTP Header

|allowCredentials
|Decides the browser should include any cookies associated with the request (`true` if cookies should be included).
|Access-Control-Allow-Credentials

|allowedOrigins
|List of allowed origins (use `*` to allow all orgins).
|Access-Control-Allow-Origin

|allowedMethods
|List of allowed HTTP request methods (`OPTIONS`, `HEAD`, `GET`, `PUT`, `POST`, `DELETE`, `PATCH`, etc.).
|-

|allowedHeaders
|List of allowed headers that can be used during the request (use `*` to allow all headers requested by the client)
|Access-Control-Allow-Headers

|pathPattern
|Ant-style pattern for the URL paths where to apply CORS. Use "/**" to match all URL paths.
|
|===

More information about the CORS headers can be found https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#cors[here]
47 changes: 47 additions & 0 deletions modules/security-cors/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.devonfw.java.dev</groupId>
<artifactId>devon4j-modules</artifactId>
<version>dev-SNAPSHOT</version>
</parent>
<groupId>com.devonfw.java.modules</groupId>
<artifactId>devon4j-security-cors</artifactId>
<version>${devon4j.version}</version>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>Security Module of the Open Application Standard Platform for Java (devon4j) specifically for Cors protection.</description>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.devonfw.java.modules</groupId>
<artifactId>devon4j-security</artifactId>
</dependency>
<dependency>
<groupId>com.devonfw.java.modules</groupId>
<artifactId>devon4j-rest</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.devonfw.java.modules</groupId>
<artifactId>devon4j-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.devonfw.module.security.cors.common.impl;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.devonfw.module.security.common.api.config.WebSecurityConfigurerAspect;

/**
* Auto-{@link Configuration} for spring to enable CORS protection.
*
* @since 2020.12.002
*/
@Configuration
public class CorsAutoConfiguration {

/**
* @return the {@link WebSecurityConfigurerAspect} to enable CORS.
*/
@Bean
public WebSecurityConfigurerAspect corsConfigAspect() {

return new WebSecurityConfigurerAspectCors();
}

/**
* @return the {@link CorsConfigProperties} to configure CORS.
*/
@Bean
public CorsConfigProperties corsConfigProperties() {

return new CorsConfigProperties();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.devonfw.module.security.cors.common.impl;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.cors.CorsConfiguration;

/**
* {@link ConfigurationProperties} to configure Cross-Origin Resource Sharing (CORS).
*
* @since 2020.12.002
*/
@ConfigurationProperties(prefix = "security.cors")
public class CorsConfigProperties extends CorsConfiguration {
private CorsConfiguration spring;

private String pathPattern = "/**";

/**
* @return the {@link CorsConfiguration} from spring-web.
*/
public CorsConfiguration getSpring() {

return this.spring;
}

/**
* Sets the cors configuration from the properties file.
*
* @param spring
* @return
*/
public void setSpring(CorsConfiguration spring) {

this.spring = spring;
}

/**
* @return the ant-style pattern for the URL paths where to apply CORS. Use "/**" to match all URL paths.
* @see org.springframework.web.cors.UrlBasedCorsConfigurationSource#registerCorsConfiguration(String,
* CorsConfiguration)
*/
public String getPathPattern() {

return this.pathPattern;
}

/**
* @param pathPattern the new value of {@link #getPathPattern()}.
*/
public void setPathPattern(String pathPattern) {

this.pathPattern = pathPattern;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.devonfw.module.security.cors.common.impl;

import javax.inject.Inject;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import com.devonfw.module.security.common.api.config.WebSecurityConfigurerAspect;

/**
* Implementation of {@link WebSecurityConfigurerAspect} for CORS protection.
*
* @since 2020.12.002
*/
public class WebSecurityConfigurerAspectCors implements WebSecurityConfigurerAspect {

private CorsConfigProperties corsConfigProperties;

/**
* @param corsConfigProperties new value of {@link #getcorsConfigProperties}.
*/
@Inject
public void setCorsConfigProperties(CorsConfigProperties corsConfigProperties) {

this.corsConfigProperties = corsConfigProperties;
}

private CorsFilter getCorsFilter() {

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration(this.corsConfigProperties.getPathPattern(), this.corsConfigProperties.getSpring());
return new CorsFilter(source);
}

@Override
public HttpSecurity configure(HttpSecurity http) throws Exception {

return http.addFilter(getCorsFilter());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.devonfw.test.app;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;

import com.devonfw.module.security.cors.common.impl.CorsAutoConfiguration;

/**
* Spring-boot app for testing.
*/
@SpringBootApplication
@Import({ CorsAutoConfiguration.class })
public class TestApplication {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.devonfw.test.app;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

import java.util.HashMap;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;

import com.devonfw.module.security.cors.common.impl.CorsConfigProperties;
import com.devonfw.module.security.cors.common.impl.WebSecurityConfigurerAspectCors;

/**
* WebSecurityConfigurerAspectCorsTest
*/

@ExtendWith(MockitoExtension.class)
public class WebSecurityConfigurerAspectCorsTest {

private WebSecurityConfigurerAspectCors webSecurityConfigurerAspectCors;

@BeforeEach
public void init() {

this.webSecurityConfigurerAspectCors = new WebSecurityConfigurerAspectCors();
CorsConfigProperties corsConfigProperties = new CorsConfigProperties();
this.webSecurityConfigurerAspectCors.setCorsConfigProperties(corsConfigProperties);
}

/**
* To test webSecurityConfigurer configure
*
* @throws Exception
*/
@Test
public void shouldConfigure_withProperties() throws Exception {

// Arrange
HttpSecurity expectedHttp = new HttpSecurity(mock(ObjectPostProcessor.class),
mock(AuthenticationManagerBuilder.class), new HashMap<>());
// Act
HttpSecurity resultHttp = this.webSecurityConfigurerAspectCors.configure(expectedHttp);
// Assert
assertThat(resultHttp).isEqualTo(expectedHttp);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
security.cors.spring.allowCredentials=true
security.cors.spring.allowedOrigins=*
security.cors.spring.allowedHeaders=*
security.cors.spring.allowedMethods=OPTIONS,HEAD,GET,PUT,POST,DELETE,PATCH
security.cors.pathPattern=/**
28 changes: 28 additions & 0 deletions starters/starter-security-cors/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.devonfw.java.dev</groupId>
<artifactId>devon4j-starters</artifactId>
<version>dev-SNAPSHOT</version>
</parent>
<groupId>com.devonfw.java.starters</groupId>
<artifactId>devon4j-starter-security-cors</artifactId>
<version>${devon4j.version}</version>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>Spring-boot starter for CORS protection.</description>

<dependencies>
<dependency>
<groupId>com.devonfw.java.starters</groupId>
<artifactId>devon4j-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.devonfw.java.modules</groupId>
<artifactId>devon4j-security-cors</artifactId>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.devonfw.module.security.cors.common.impl.CorsAutoConfiguration
Loading

0 comments on commit 0fd0b4e

Please sign in to comment.