Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/java/com/github/havlli/EventPilot/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@SpringBootApplication
@ConfigurationPropertiesScan
public class Application {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.github.havlli.EventPilot.api.jwt;

import com.github.havlli.EventPilot.api.security.ApiSecurityProperties;
import com.github.havlli.EventPilot.api.auth.UserDetailsImpl;
import com.github.havlli.EventPilot.entity.user.User;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

Expand All @@ -23,8 +23,8 @@ public class JWTService {
private static final Logger LOG = LoggerFactory.getLogger(JWTService.class);
private final String jwtSecret;

public JWTService(@Value("${security.jwt.secret}") String jwtSecret) {
this.jwtSecret = jwtSecret;
public JWTService(ApiSecurityProperties securityProperties) {
this.jwtSecret = securityProperties.jwt().secret();
}

public boolean hasClaim(String token, String claimName) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.github.havlli.EventPilot.api.security;

Check warning on line 1 in src/main/java/com/github/havlli/EventPilot/api/security/ApiSecurityProperties.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this package name to match the regular expression '^[a-z_]+(\.[a-z_][a-z0-9_]*)*$'.

See more on https://sonarcloud.io/project/issues?id=havlli_EventPilot&issues=AZ3LNWGnhSlndr8jiHhd&open=AZ3LNWGnhSlndr8jiHhd&pullRequest=19

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

import java.util.List;

@Validated
@ConfigurationProperties(prefix = "security")
public record ApiSecurityProperties(
@Valid @NotNull Jwt jwt,
@Valid @NotNull Cors cors
) {

public record Jwt(@NotBlank String secret) {
}

public record Cors(
@NotEmpty List<String> allowedOrigins,
@NotEmpty List<String> allowedMethods,
@NotEmpty List<String> allowedHeaders,
List<String> exposedHeaders
) {
}
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,29 @@
package com.github.havlli.EventPilot.api.security;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class CorsConfig implements WebMvcConfigurer {

@Value("#{'${security.cors.allowed-origins}'.split(',')}")
private List<String> allowedOrigins;
@Value("#{'${security.cors.allowed-methods}'.split(',')}")
private List<String> allowedMethods;

@Value("#{'${security.cors.allowed-headers}'.split(',')}")
private List<String> allowedHeaders;
private final ApiSecurityProperties securityProperties;

@Value("#{'${security.cors.exposed-headers}'.split(',')}")
private List<String> exposedHeaders;
public CorsConfig(ApiSecurityProperties securityProperties) {
this.securityProperties = securityProperties;
}

@Bean
public CorsConfigurationSource corsConfigurationSource() {
ApiSecurityProperties.Cors cors = securityProperties.cors();
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(allowedOrigins);
configuration.setAllowedMethods(allowedMethods);
configuration.setAllowedHeaders(allowedHeaders);
configuration.setExposedHeaders(exposedHeaders);
configuration.setAllowedOrigins(cors.allowedOrigins());
configuration.setAllowedMethods(cors.allowedMethods());
configuration.setAllowedHeaders(cors.allowedHeaders());
configuration.setExposedHeaders(cors.exposedHeaders());
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", configuration);
return source;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.github.havlli.EventPilot.command.onreadyevent;

import com.github.havlli.EventPilot.core.DiscordService;
import com.github.havlli.EventPilot.core.DiscordProperties;
import com.github.havlli.EventPilot.entity.event.Event;
import com.github.havlli.EventPilot.entity.event.EventService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import reactor.core.Disposable;
import reactor.core.publisher.Flux;
Expand All @@ -30,11 +30,11 @@ public class ScheduledTask {
public ScheduledTask(
EventService eventService,
DiscordService discordService,
@Value("${discord.scheduler.interval-seconds}") Integer intervalSeconds
DiscordProperties discordProperties
) {
this.eventService = eventService;
this.discordService = discordService;
this.intervalSeconds = intervalSeconds;
this.intervalSeconds = discordProperties.scheduler().intervalSeconds();
}

public Flux<Void> getSchedulersFlux() {
Expand Down
9 changes: 4 additions & 5 deletions src/main/java/com/github/havlli/EventPilot/core/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,22 @@
import discord4j.core.object.presence.ClientActivity;
import discord4j.core.object.presence.ClientPresence;
import discord4j.rest.RestClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;

@Component
@Profile("!test")
public class Client {
private final String token;
private final DiscordProperties discordProperties;

public Client(@Value("${discord.token}") String token) {
this.token = token;
public Client(DiscordProperties discordProperties) {
this.discordProperties = discordProperties;
}

@Bean
public GatewayDiscordClient createDiscordClient() {
return DiscordClient.create(token)
return DiscordClient.create(discordProperties.token())
.gateway()
.setInitialPresence(__ -> ClientPresence.online(ClientActivity.listening("to /commands")))
.login()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.github.havlli.EventPilot.core;

Check warning on line 1 in src/main/java/com/github/havlli/EventPilot/core/DiscordProperties.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this package name to match the regular expression '^[a-z_]+(\.[a-z_][a-z0-9_]*)*$'.

See more on https://sonarcloud.io/project/issues?id=havlli_EventPilot&issues=AZ3LNWKKhSlndr8jiHhe&open=AZ3LNWKKhSlndr8jiHhe&pullRequest=19

import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@Validated
@ConfigurationProperties(prefix = "discord")
public record DiscordProperties(
@NotBlank String token,
@Valid @NotNull Commands commands,
@Valid @NotNull Scheduler scheduler
) {

public record Commands(@NotBlank String folder) {
}

public record Scheduler(@Min(1) int intervalSeconds) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import discord4j.rest.service.ApplicationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.DependsOn;
Expand All @@ -32,16 +31,16 @@ public class GlobalCommandRegistrar implements ApplicationRunner {
private static final Logger LOG = LoggerFactory.getLogger(GlobalCommandRegistrar.class);
private final RestClient restClient;
private final PathMatchingResourcePatternResolver pathMatcher;
private final String parentFolder;
private final DiscordProperties discordProperties;

public GlobalCommandRegistrar(
RestClient restClient,
PathMatchingResourcePatternResolver pathMatcher,
@Value(value = "${discord.commands.folder}") String parentFolder
DiscordProperties discordProperties
) {
this.restClient = restClient;
this.pathMatcher = pathMatcher;
this.parentFolder = parentFolder;
this.discordProperties = discordProperties;
}

@Override
Expand Down Expand Up @@ -104,7 +103,7 @@ protected ApplicationCommandRequest readJsonValueOrThrow(JacksonResources jackso
}

private String getLocationPattern() {
return parentFolder + "/*.json";
return discordProperties.commands().folder() + "/*.json";
}

protected ApplicationCommandRequest readJsonValue(JacksonResources jacksonResources, Resource resource) throws IOException {
Expand Down

This file was deleted.

7 changes: 2 additions & 5 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,11 @@ spring:
show-sql: false
data:
redis:
host: localhost
port: 6379
repositories:
enabled: false

cache:
redis:
host: localhost
port: 6379

security:
jwt:
secret: ${JWT_SECRET}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ private static void registerContainerProperties(DynamicPropertyRegistry registry
registry.add("spring.datasource.url", () -> postgresSQLContainer.getJdbcUrl());
registry.add("spring.datasource.username", () -> postgresSQLContainer.getUsername());
registry.add("spring.datasource.password", () -> postgresSQLContainer.getPassword());
registry.add("cache.redis.host", () -> redisContainer.getHost());
registry.add("cache.redis.port", () -> redisContainer.getFirstMappedPort());
registry.add("spring.data.redis.host", () -> redisContainer.getHost());
registry.add("spring.data.redis.port", () -> redisContainer.getFirstMappedPort());
registry.add("security.jwt.secret", () -> "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=");
registry.add("discord.token", () -> "test");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.havlli.EventPilot.api.jwt;

import com.github.havlli.EventPilot.api.security.ApiSecurityProperties;
import com.github.havlli.EventPilot.api.auth.UserDetailsImpl;
import com.github.havlli.EventPilot.entity.user.User;
import com.github.havlli.EventPilot.entity.user.UserRole;
Expand All @@ -14,6 +15,7 @@
import org.mockito.MockitoAnnotations;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand All @@ -32,14 +34,26 @@ class JWTServiceTest {
void setUp() {
autoCloseable = MockitoAnnotations.openMocks(this);
jwtSecret = generateKey();
underTest = new JWTService(jwtSecret);
underTest = createJwtService(jwtSecret);
}

private String generateKey() {
byte[] encodedSecret = Jwts.SIG.HS256.key().build().getEncoded();
return Encoders.BASE64.encode(encodedSecret);
}

private JWTService createJwtService(String jwtSecret) {
return new JWTService(new ApiSecurityProperties(
new ApiSecurityProperties.Jwt(jwtSecret),
new ApiSecurityProperties.Cors(
List.of("http://localhost:3000"),
List.of("GET"),
List.of("Authorization"),
List.of("Authorization")
)
));
}

@AfterEach
void tearDown() throws Exception {
autoCloseable.close();
Expand Down Expand Up @@ -83,7 +97,7 @@ void hasClaim_ThrowsSignatureException_WhenTokenSignatureIsInvalid() {
User user = new User(null, "username", "email", "password", Set.of(userRole));

String jwtSecret = generateKey();
JWTService underTestSpy = spy(new JWTService(jwtSecret));
JWTService underTestSpy = spy(createJwtService(jwtSecret));
String jwtToken = underTestSpy.generateToken(user);
String invalidSignatureToken = jwtToken.substring(0, jwtToken.length() - 5);

Expand All @@ -99,7 +113,7 @@ void hasClaim_ThrowsMalformedException_WhenTokenHeaderIsInvalid() {
User user = new User(null, "username", "email", "password", Set.of(userRole));

String jwtSecret = generateKey();
JWTService underTestSpy = spy(new JWTService(jwtSecret));
JWTService underTestSpy = spy(createJwtService(jwtSecret));
String jwtToken = underTestSpy.generateToken(user);
String invalidHeaderToken = jwtToken.substring(5);

Expand All @@ -115,7 +129,7 @@ void hasClaim_ThrowsJwtException_WhenTokenPayloadIsInvalid() {
User user = new User(null, "username", "email", "password", Set.of(userRole));

String jwtSecret = generateKey();
JWTService underTestSpy = spy(new JWTService(jwtSecret));
JWTService underTestSpy = spy(createJwtService(jwtSecret));
String jwtToken = underTestSpy.generateToken(user);

String[] splitToken = jwtToken.split("\\.");
Expand All @@ -134,7 +148,7 @@ void isTokenValid_ReturnsTrue_WhenTokenIsValidAndMatchesUserDetails() {
User user = new User(null, "username", "email", "password", Set.of(userRole));

String jwtSecret = generateKey();
JWTService underTestSpy = spy(new JWTService(jwtSecret));
JWTService underTestSpy = spy(createJwtService(jwtSecret));
String validToken = underTestSpy.generateToken(user);

UserDetailsImpl userDetails = UserDetailsImpl.of(user);
Expand All @@ -153,7 +167,7 @@ void isTokenValid_ReturnsFalse_WhenTokenIsValidAndNotMatchesUsername() {
User user = new User(null, "username", "email", "password", Set.of(userRole));

String jwtSecret = generateKey();
JWTService underTestSpy = spy(new JWTService(jwtSecret));
JWTService underTestSpy = spy(createJwtService(jwtSecret));
String validToken = underTestSpy.generateToken(user);

User differentUser = new User(null, "username1", "email", "password", Set.of(userRole));
Expand All @@ -173,7 +187,7 @@ void isTokenValid_ThrowsJWTException_WhenTokenIsInvalidAndMatchesUsername() {
User user = new User(null, "username", "email", "password", Set.of(userRole));

String jwtSecret = generateKey();
JWTService underTestSpy = spy(new JWTService(jwtSecret));
JWTService underTestSpy = spy(createJwtService(jwtSecret));
String validToken = underTestSpy.generateToken(user);
String[] splitToken = validToken.split("\\.");
splitToken[1] = "eyJzdWIiOiJ1c2VybmFtZSIsImF1dGhvcml0aWVzIjpboiJhdXRob3JpdHkiOiJVU0VSIn1dLCJpYXQiOjE2OTczOTIxMzMsImV4cCI6MTY5NzQyODEzM30";
Expand Down
Loading