Skip to content

Commit

Permalink
[3019] Add the Spring MVC configuration necessary to use the frontend
Browse files Browse the repository at this point in the history
Bug: #3019
Signed-off-by: Stéphane Bégaudeau <stephane.begaudeau@obeo.fr>
  • Loading branch information
sbegaudeau committed Feb 23, 2024
1 parent 337239a commit 5940967
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 0 deletions.
@@ -0,0 +1,58 @@
/*******************************************************************************
* Copyright (c) 2019, 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.web.infrastructure.mvc;

import java.io.IOException;
import java.util.Objects;

import org.springframework.core.io.Resource;
import org.springframework.web.servlet.resource.PathResourceResolver;

/**
* The path resource resolver used to exclude API calls from the resolution of static resource.
* <p>
* We want to ensure that users can only request real static resources and prevent any attempt at retrieving static
* resources if the requested resource starts with the path of our API.
* </p>
* <p>
* We could attempt to do this using a whitelist approach by saying that only some specific paths can be retrieved but
* maintaing such a whitelist would be cumbersome over time since we won't know all the paths used in the front-end of
* the application.
* </p>
*
* @author sbegaudeau
*/
public class SpringPathResourceResolver extends PathResourceResolver {

/**
* The path of the URL used as a prefix for our API.
*/
private final String apiBasePath;

public SpringPathResourceResolver(String apiBasePath) {
this.apiBasePath = Objects.requireNonNull(apiBasePath);
}

@Override
protected Resource getResource(String resourcePath, Resource location) throws IOException {
boolean isValid = !resourcePath.startsWith(this.apiBasePath);
isValid = isValid && !resourcePath.startsWith(this.apiBasePath.substring(1));
isValid = isValid && location.exists();
isValid = isValid && location.isReadable();

if (isValid) {
return location;
}
return null;
}
}
@@ -0,0 +1,87 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.web.infrastructure.mvc;

import java.util.Objects;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
* Used to configure the server side routing.
*
* @author sbegaudeau
*/
@Configuration
public class SpringWebMvcConfigurer implements WebMvcConfigurer {
private final String[] allowedOriginPatterns;

private final String[] allowedHeaders;

private final String[] allowedMethods;

private final boolean allowedCredentials;

public SpringWebMvcConfigurer(
@Value("${sirius.components.cors.allowedOriginPatterns:}") String[] allowedOriginPatterns,
@Value("${sirius.components.cors.allowedHeaders:}") String[] allowedHeaders,
@Value("${sirius.components.cors.allowedMethods:}") String[] allowedMethods,
@Value("${sirius.components.cors.allowedCredentials:false}") boolean allowedCredentials
) {
this.allowedOriginPatterns = Objects.requireNonNull(allowedOriginPatterns);
this.allowedHeaders = Objects.requireNonNull(allowedHeaders);
this.allowedMethods = Objects.requireNonNull(allowedMethods);
this.allowedCredentials = allowedCredentials;
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Make sure that all static assets are redirected properly to the proper path
registry.addResourceHandler(
SpringWebMvcConfigurerConstants.CSS_PATTERN,
SpringWebMvcConfigurerConstants.HTML_PATTERN,
SpringWebMvcConfigurerConstants.JS_PATTERN,
SpringWebMvcConfigurerConstants.JS_CHUNK_PATTERN,
SpringWebMvcConfigurerConstants.JS_MAP_PATTERN,
SpringWebMvcConfigurerConstants.JSON_PATTERN,
SpringWebMvcConfigurerConstants.ICO_PATTERN,
SpringWebMvcConfigurerConstants.TTF_PATTERN,
SpringWebMvcConfigurerConstants.MEDIA_PATTERN
).addResourceLocations(SpringWebMvcConfigurerConstants.STATIC_ASSETS_PATH);

// Make sure that all other requests are redirected to index.html, the React router will handle it
registry.addResourceHandler(SpringWebMvcConfigurerConstants.EMPTY_PATTERN, SpringWebMvcConfigurerConstants.HOMEPAGE_PATTERN, SpringWebMvcConfigurerConstants.ANY_PATTERN)
.addResourceLocations(SpringWebMvcConfigurerConstants.INDEX_HTML_PATH)
.resourceChain(true)
.addResolver(new SpringPathResourceResolver(SpringWebMvcConfigurerConstants.API_BASE_PATH));
}

@Override
public void addCorsMappings(CorsRegistry registry) {
CorsRegistration corsRegistration = registry.addMapping(SpringWebMvcConfigurerConstants.ANY_PATTERN);
if (this.allowedOriginPatterns.length > 0) {
corsRegistration.allowedOriginPatterns(this.allowedOriginPatterns);
}
if (this.allowedHeaders.length > 0) {
corsRegistration.allowedHeaders(this.allowedHeaders);
}
if (this.allowedMethods.length > 0) {
corsRegistration.allowedMethods(this.allowedMethods);
}
corsRegistration.allowCredentials(this.allowedCredentials);
}
}
@@ -0,0 +1,70 @@
/*******************************************************************************
* Copyright (c) 2019, 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.web.infrastructure.mvc;

/**
* Constants used by the WebMvc configurer.
*
* @author sbegaudeau
*/
public final class SpringWebMvcConfigurerConstants {

/** Base path of the API. */
public static final String API_BASE_PATH = "/api";

/** Pattern used to match CSS resources from the front-end. */
public static final String CSS_PATTERN = "/**/*.css";

/** Pattern used to match HTML resources from the front-end. */
public static final String HTML_PATTERN = "/**/*.html";

/** Pattern used to match JS resources from the front-end. */
public static final String JS_PATTERN = "/**/*.js";

/** Pattern used to match JS source maps resources from the front-end. */
public static final String JS_MAP_PATTERN = "/**/*.js.map";

/** Pattern used to match JS chunk resources from the front-end. */
public static final String JS_CHUNK_PATTERN = "/**/*.chunk.js";

/** Pattern used to match JSON resources from the front-end. */
public static final String JSON_PATTERN = "/**/*.json";

/** Pattern used to match ICO resources from the front-end. */
public static final String ICO_PATTERN = "/**/*.ico";

/** Pattern used to match TTF resources from the front-end. */
public static final String TTF_PATTERN = "/**/*.ttf";

/** Pattern used to match media resources from the front-end. */
public static final String MEDIA_PATTERN = "/**/media/**";

/** Pattern used to match the raw hostname with any specific path. */
public static final String EMPTY_PATTERN = "";

/** Pattern used to match the path of the homepage. */
public static final String HOMEPAGE_PATTERN = "/";

/** Pattern used to match any path. */
public static final String ANY_PATTERN = "/**";

/** Path of the folder containing the static resources. */
public static final String STATIC_ASSETS_PATH = "classpath:/static/";

/** Path of the index.html file. */
public static final String INDEX_HTML_PATH = "classpath:/static/index.html";

private SpringWebMvcConfigurerConstants() {
// Prevent instantiation
}
}
Expand Up @@ -16,6 +16,8 @@ spring.datasource.url=jdbc:postgresql://localhost:5438/sirius-web-db
spring.datasource.username=dbuser
spring.datasource.password=dbpwd

spring.mvc.pathmatch.matching-strategy=ant_path_matcher

spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
Expand Down

0 comments on commit 5940967

Please sign in to comment.