Skip to content

Commit

Permalink
Update dependencies, restructure
Browse files Browse the repository at this point in the history
  • Loading branch information
octylFractal committed Jul 29, 2023
1 parent 7651a9d commit 94485d8
Show file tree
Hide file tree
Showing 23 changed files with 209 additions and 211 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
cassette-deck
=============
A server to provide various metadata about Minecraft versions.

Requires Java 17 to build.
32 changes: 0 additions & 32 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -66,38 +66,6 @@ dependencies {
// For Log4J2 async
runtimeOnly(libs.disruptor)

// Force 2.15
constraints {
add("implementation", "org.apache.logging.log4j:log4j-api") {
version {
strictly("[2.15, 3[")
prefer("2.15.0")
}
because("CVE-2021-44228: Log4j vulnerable to remote code execution")
}
add("implementation", "org.apache.logging.log4j:log4j-core") {
version {
strictly("[2.15, 3[")
prefer("2.15.0")
}
because("CVE-2021-44228: Log4j vulnerable to remote code execution")
}
add("implementation", "org.apache.logging.log4j:log4j-jul") {
version {
strictly("[2.15, 3[")
prefer("2.15.0")
}
because("CVE-2021-44228: Log4j vulnerable to remote code execution")
}
add("implementation", "org.apache.logging.log4j:log4j-slf4j-impl") {
version {
strictly("[2.15, 3[")
prefer("2.15.0")
}
because("CVE-2021-44228: Log4j vulnerable to remote code execution")
}
}

testImplementation(libs.spring.boot.starter.test)
testImplementation(platform(libs.junit.bom))
testImplementation(libs.junit.jupiter.api)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package org.enginehub.cassettedeck;

import jakarta.servlet.Filter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
Expand All @@ -27,7 +28,6 @@
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.WebContentInterceptor;

import javax.servlet.Filter;
import java.time.Duration;

@Configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/
package org.enginehub.cassettedeck;

import org.apache.logging.log4j.LogManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
Expand All @@ -34,6 +35,12 @@ public class CassetteDeck {
public static void main(String[] args) {
System.setProperty("org.jooq.no-logo", "true");
System.setProperty("org.jooq.no-tips", "true");
SpringApplication.run(CassetteDeck.class, args);
try {
SpringApplication.run(CassetteDeck.class, args);
} catch (Throwable t) {
// Spring replaced the uncaught exception handler, so we need to do this manually.
LogManager.getLogger().error("Uncaught exception", t);
throw t;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,9 @@
import org.enginehub.cassettedeck.data.downstream.CliData;
import org.enginehub.cassettedeck.exception.NotFoundException;
import org.enginehub.cassettedeck.service.WorldEditCliDataService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;

Expand Down Expand Up @@ -60,6 +57,7 @@ public CliData getWeCliData(
* Uploads a WE CLI Data file for a given data version and CLI data version.
*/
@PutMapping("/{dataVersion}/{cliDataVersion}")
@PreAuthorize("hasRole('ROLE_SERVER')")
public void putWeCliData(
@PathVariable int dataVersion,
@PathVariable int cliDataVersion,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
import org.enginehub.cassettedeck.exception.DownloadException;
import org.enginehub.cassettedeck.exception.NotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
Expand All @@ -37,15 +40,15 @@ public class ExceptionMapper {
private static final Logger LOGGER = LogManager.getLogger();

// Catch-all
@ExceptionHandler({Throwable.class})
@ExceptionHandler
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Map<String, String> handle(Throwable e) {
LOGGER.warn("Error handling request", e);
return Map.of("code", "internal.server.error");
}

// Client problems
@ExceptionHandler({MissingServletRequestParameterException.class})
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> handle(MissingServletRequestParameterException e) {
return Map.of(
Expand All @@ -54,15 +57,41 @@ public Map<String, String> handle(MissingServletRequestParameterException e) {
);
}

@ExceptionHandler({NotFoundException.class})
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> handle(HttpMediaTypeNotSupportedException e) {
return Map.of(
"code", "bad.request",
"unsupported.content-type", String.valueOf(e.getContentType())
);
}

@ExceptionHandler
@ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
public Map<String, Object> handle(HttpMediaTypeNotAcceptableException e) {
return Map.of(
"code", "not.acceptable",
"accepted.content-types", e.getSupportedMediaTypes()
);
}

@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> handle(HttpMessageNotReadableException e) {
return Map.of(
"code", "bad.request"
);
}

@ExceptionHandler
@ResponseStatus(HttpStatus.NOT_FOUND)
public Map<String, String> handle(NotFoundException e) {
return Map.of("code", e.type() + ".not.found");
}

// Upstream problems

@ExceptionHandler({DownloadException.class})
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_GATEWAY)
public Map<String, String> handle(DownloadException e) {
LOGGER.warn("Download error occurred", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@
import org.enginehub.cassettedeck.exception.NotFoundException;
import org.enginehub.cassettedeck.service.MinecraftVersionService;
import org.jetbrains.annotations.Nullable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

import java.time.Instant;
import java.util.Collection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ interface OutputStreamConsumer {
* If there is no blob for the given key, use {@code consumer} to fill it, then return a stream to get the contents
* of the blob.
*
* @param key the key
* @param key the key
* @param consumer the blob provider
* @return the content of the blob
* @throws IOException if there is an I/O error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,41 @@

package org.enginehub.cassettedeck.data.downstream;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.List;
import java.util.Map;

public record CliData(
@JsonProperty(required = true)
Map<String, BlockManifest> blocks,
@JsonProperty(required = true)
List<String> items,
@JsonProperty(required = true)
List<String> entities,
@JsonProperty(required = true)
List<String> biomes,
Map<String, List<String>> blocktags,
Map<String, List<String>> itemtags,
Map<String, List<String>> entitytags
@JsonProperty(value = "blocktags", required = true)
Map<String, List<String>> blockTags,
@JsonProperty(value = "itemtags", required = true)
Map<String, List<String>> itemTags,
@JsonProperty(value = "entitytags", required = true)
Map<String, List<String>> entityTags
) {
public record BlockManifest(String defaultstate, Map<String, BlockProperty> properties) {
public record BlockManifest(
@JsonProperty(value = "defaultstate", required = true)
String defaultState,
@JsonProperty(required = true)
Map<String, BlockProperty> properties
) {
}

public record BlockProperty(List<String> values, String type) {
public record BlockProperty(
@JsonProperty(required = true)
List<String> values,
@JsonProperty(required = true)
String type
) {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package org.enginehub.cassettedeck.data.upstream;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.enginehub.cassettedeck.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;

@Component
public class CassetteDeckAccessDeniedHandler {
private final ObjectMapper mapper;

public CassetteDeckAccessDeniedHandler(ObjectMapper mapper) {
this.mapper = mapper;
}

public void handle(HttpServletResponse response) throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
mapper.writeValue(
response.getWriter(),
Map.of("code", "access.denied")
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,55 +18,48 @@

package org.enginehub.cassettedeck.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.servlet.Filter;

@Configuration
@EnableWebSecurity
public class CassetteDeckSecurity extends WebSecurityConfigurerAdapter {
private final ObjectMapper mapper;
private final DatabaseAuthenticationProvider authenticationProvider;

public CassetteDeckSecurity(ObjectMapper mapper,
DatabaseAuthenticationProvider authenticationProvider) {
this.mapper = mapper;
this.authenticationProvider = authenticationProvider;
}

@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider);
@EnableMethodSecurity
public class CassetteDeckSecurity {
@Bean
public AuthenticationManager authenticationManager() {
return authentication -> {
if (!authentication.isAuthenticated()) {
throw new BadCredentialsException("Invalid credentials");
}
return authentication;
};
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable()
.csrf().disable()
.formLogin().disable()
.logout().disable()
@Bean
public SecurityFilterChain filterChain(
HttpSecurity http, CassetteDeckAccessDeniedHandler accessDeniedHandler, TokenExtractingFilter filter
) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.logout(AbstractHttpConfigurer::disable)
.cors(Customizer.withDefaults())
.headers().cacheControl().disable().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authenticationProvider(authenticationProvider)
.addFilterBefore(getFilter(), AnonymousAuthenticationFilter.class).authorizeRequests()
.requestMatchers(getRequestMatcher()).access("hasRole('ROLE_SERVER')").and();
}

private RequestMatcher getRequestMatcher() {
return request -> "PUT".equals(request.getMethod());
}

private Filter getFilter() throws Exception {
return new TokenExtractingFilter(mapper, getRequestMatcher(), authenticationManager());
.headers(h -> h.cacheControl(HeadersConfigurer.CacheControlConfig::disable))
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(filter, AnonymousAuthenticationFilter.class)
.exceptionHandling(e -> e.accessDeniedHandler((request, response, accessDeniedException) ->
accessDeniedHandler.handle(response)
))
.build();
}
}
Loading

0 comments on commit 94485d8

Please sign in to comment.