Skip to content

Commit 48f8013

Browse files
authored
Merge pull request #571 from ita-social-projects/feature/9272/automatic-secret-refresh-from-azure
Feature/9272/automatic secret refresh from azure
2 parents 3f55c1c + 38e31d4 commit 48f8013

30 files changed

+1263
-160
lines changed

core/src/main/java/greencity/security/controller/ManagementSecurityController.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package greencity.security.controller;
22

33
import greencity.exception.exceptions.*;
4+
import greencity.properties.RemoteWebClientProperties;
45
import greencity.security.dto.SuccessSignInDto;
56
import greencity.security.dto.ownsecurity.OwnSignInDto;
67
import greencity.security.service.OwnSecurityService;
78
import greencity.service.UserService;
89
import io.swagger.v3.oas.annotations.Hidden;
910
import jakarta.validation.Valid;
1011
import lombok.RequiredArgsConstructor;
11-
import org.springframework.beans.factory.annotation.Value;
1212
import org.springframework.stereotype.Controller;
1313
import org.springframework.ui.Model;
1414
import org.springframework.validation.BindingResult;
@@ -26,8 +26,7 @@ public class ManagementSecurityController {
2626
private static final String MANAGEMENT_LOGIN_PAGE = "core/management_login";
2727
private final OwnSecurityService service;
2828
private final UserService userService;
29-
@Value("${greencity.server.address}")
30-
private String greenCityServerAddress;
29+
private final RemoteWebClientProperties remoteWebClientProperties;
3130

3231
/**
3332
* Controller returns view for management log in.
@@ -83,6 +82,7 @@ public String signIn(@Valid @ModelAttribute("signInForm") OwnSignInDto dto,
8382
return MANAGEMENT_LOGIN_PAGE;
8483
}
8584

86-
return "redirect:" + greenCityServerAddress + "/token?accessToken=" + result.getAccessToken();
85+
return "redirect:" + remoteWebClientProperties.getGreencityServerAddress() + "/token?accessToken="
86+
+ result.getAccessToken();
8787
}
8888
}

core/src/main/resources/application.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
spring.cloud.azure.keyvault.secret.property-sources[0].endpoint=${KEY_VAULT_ENDPOINT}
2+
spring.cloud.azure.keyvault.secret.property-sources[0].refresh-interval=15m
23

34
#Azure Connection
45
spring.cloud.azure.credential.client-id=${AZURE_CLIENT_ID}

core/src/test/java/greencity/security/controller/ManagementSecurityControllerTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import greencity.exception.exceptions.UserDeactivatedException;
88
import greencity.exception.exceptions.WrongEmailException;
99
import greencity.exception.exceptions.WrongPasswordException;
10+
import greencity.properties.RemoteWebClientProperties;
1011
import greencity.security.dto.SuccessSignInDto;
1112
import greencity.security.dto.ownsecurity.OwnSignInDto;
1213
import greencity.security.service.OwnSecurityService;
@@ -47,6 +48,9 @@ class ManagementSecurityControllerTest {
4748
@Mock
4849
BindingResult bindingResult;
4950

51+
@Mock
52+
private RemoteWebClientProperties remoteWebClientProperties;
53+
5054
@BeforeEach
5155
void setUp() {
5256
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();

service-api/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,5 +194,11 @@
194194
<artifactId>mockwebserver</artifactId>
195195
<scope>test</scope>
196196
</dependency>
197+
<dependency>
198+
<groupId>io.github.hakky54</groupId>
199+
<artifactId>logcaptor</artifactId>
200+
<version>2.12.0</version>
201+
<scope>test</scope>
202+
</dependency>
197203
</dependencies>
198204
</project>

service-api/src/main/java/greencity/client/config/GreenCityRemoteWebClientConfig.java

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99
import greencity.exception.exceptions.ErrorParsingException;
1010
import greencity.exception.exceptions.GreenCityServiceException;
1111
import greencity.exception.exceptions.NotFoundException;
12+
import greencity.properties.EmailProperties;
13+
import greencity.properties.RemoteWebClientProperties;
1214
import greencity.security.jwt.JwtTool;
1315
import io.netty.channel.ChannelOption;
1416
import java.net.URI;
1517
import lombok.RequiredArgsConstructor;
16-
import org.springframework.beans.factory.annotation.Value;
1718
import org.springframework.context.annotation.Bean;
1819
import org.springframework.context.annotation.Configuration;
1920
import org.springframework.http.HttpHeaders;
@@ -37,59 +38,46 @@ public class GreenCityRemoteWebClientConfig {
3738
private static final String EMAIL_QUERY_PARAMETER = "email";
3839
private static final String PLUS_SYMBOL = "+";
3940
private static final String ENCODED_PLUS_SYMBOL = "%2B";
40-
41-
@Value("${greencity.server.address}")
42-
private String greenCityBaseUrl;
43-
44-
@Value("${greencityubs.server.address}")
45-
private String greenCityUbsBaseUrl;
46-
47-
@Value("${contacts.authorization.system-email-address}")
48-
private String systemEmail;
49-
50-
@Value("${webclient.connection-timeout-millis}")
51-
private Integer connectionTimeoutMillis;
52-
53-
@Value("${webclient.response-timeout-millis}")
54-
private Integer responseTimeoutMillis;
41+
private final RemoteWebClientProperties remoteWebClientProperties;
42+
private final EmailProperties emailProperties;
5543

5644
private final JwtTool jwtTool;
5745

5846
@Bean("greenCityWebClient")
5947
public WebClient webClient(WebClient.Builder builder) {
60-
return builder.baseUrl(greenCityBaseUrl)
48+
return builder.baseUrl(remoteWebClientProperties.getGreencityServerAddress())
6149
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
6250
.filter(authorizationHeaderFilter())
6351
.filter(handlingWebClientExceptions())
6452
.filter(encodePlusInQuery())
6553
.clientConnector(
6654
new ReactorClientHttpConnector(
6755
HttpClient.create()
68-
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeoutMillis)
69-
.responseTimeout(Duration.ofMillis(responseTimeoutMillis))))
56+
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, remoteWebClientProperties.getConnectionTimeout())
57+
.responseTimeout(Duration.ofMillis(remoteWebClientProperties.getResponseTimeout()))))
7058
.build();
7159
}
7260

7361
@Bean("greenCityUbsWebClient")
7462
public WebClient greenCityUbsWebClient(WebClient.Builder builder) {
75-
return builder.baseUrl(greenCityUbsBaseUrl)
63+
return builder.baseUrl(remoteWebClientProperties.getGreencityUbsServerAddress())
7664
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
7765
.filter(authorizationHeaderFilterForGreenCityUbs())
7866
.filter(handlingWebClientExceptions())
7967
.filter(encodePlusInQuery())
8068
.clientConnector(
8169
new ReactorClientHttpConnector(
8270
HttpClient.create()
83-
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeoutMillis)
84-
.responseTimeout(Duration.ofMillis(responseTimeoutMillis))))
71+
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, remoteWebClientProperties.getConnectionTimeout())
72+
.responseTimeout(Duration.ofMillis(remoteWebClientProperties.getResponseTimeout()))))
8573
.build();
8674
}
8775

8876
private ExchangeFilterFunction authorizationHeaderFilter() {
8977
List<Role> roles = List.of(Role.ROLE_USER, Role.ROLE_ADMIN);
9078

9179
return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
92-
String jwt = jwtTool.createAccessToken(systemEmail, roles);
80+
String jwt = jwtTool.createAccessToken(emailProperties.getSystemEmailAddress(), roles);
9381
String authHeader = AppConstant.TOKEN_PREFIX + jwt;
9482

9583
ClientRequest authorizedRequest = ClientRequest.from(clientRequest)
@@ -104,7 +92,7 @@ private ExchangeFilterFunction authorizationHeaderFilterForGreenCityUbs() {
10492
List<Role> roles = List.of(Role.ROLE_USER, Role.ROLE_EMPLOYEE, Role.ROLE_UBS_EMPLOYEE);
10593

10694
return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
107-
String jwt = jwtTool.createAccessToken(systemEmail, roles);
95+
String jwt = jwtTool.createAccessToken(emailProperties.getSystemEmailAddress(), roles);
10896
String authHeader = AppConstant.TOKEN_PREFIX + jwt;
10997

11098
ClientRequest authorizedRequest = ClientRequest.from(clientRequest)

service-api/src/main/java/greencity/constant/ErrorMessage.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,23 @@ public class ErrorMessage {
6262
"Can`t convert To Multipart Image. Bad inputted image string : ";
6363
public static final String PARSING_URL_FAILED = "Can't parse image's url: ";
6464
public static final String AUTHORITY_CATEGORY_NOT_FOUND = "The authority category does not exist by this id: %s";
65+
public static final String ACCESS_TOKEN_EXPIRATION_NOT_SET = "access token expiration not set.";
66+
public static final String REFRESH_TOKEN_EXPIRATION_NOT_SET = "refresh token expiration not set.";
67+
public static final String ACCESS_TOKEN_NOT_SET = "access token key not set.";
68+
public static final String VERIFY_EMAIL_EXPIRATION_NOT_SET = "verify email expiration not set.";
69+
public static final String BRUTEFORCE_MAX_ATTEMPTS_NOT_SET = "security bruteforce max attempts not set.";
70+
public static final String BRUTEFORCE_BLOCK_TIME_NOT_SET = "security bruteforce block time not set.";
71+
public static final String TESTER_SIGN_IN_TOKEN_NOT_SET = "tester signin token not set.";
72+
public static final String GREENCITY_SERVER_ADDRESS_NOT_SET = "greencity server address not set.";
73+
public static final String GREENCITY_UBS_SERVER_ADDRESS_NOT_SET = "greencity ubs server address not set.";
74+
public static final String WEBCLIENT_CONNECTION_TIMEOUT_NOT_SET = "webclient connection timeout not set.";
75+
public static final String WEBCLIENT_RESPONSE_TIMEOUT_NOT_SET = "webclient response timeout not set.";
76+
public static final String CLIENT_ADDRESS_NOT_SET = "client address not set.";
77+
public static final String AZURE_CONNECTION_STRING_NOT_SET = "azure connection string not set.";
78+
public static final String AZURE_CONTAINER_NAME_NOT_SET = "azure container name not set.";
79+
public static final String GOOGLE_API_KEY_NOT_SET = "google api key not set.";
80+
public static final String SENDER_EMAIL_ADDRESS_NOT_SET = "sender email address not set.";
81+
public static final String GREENCITY_OFFICE_EMAIL_ADDRESS_NOT_SET = "greencity office email address not set.";
82+
public static final String TELEGRAM_EMAIL_ADDRESS_NOT_SET = "telegram email address not set.";
83+
public static final String SYSTEM_EMAIL_ADDRESS_NOT_SET = "system email address not set.";
6584
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package greencity.properties;
2+
3+
import greencity.constant.ErrorMessage;
4+
import jakarta.annotation.PostConstruct;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.core.env.Environment;
8+
import org.springframework.stereotype.Component;
9+
import org.springframework.util.StringUtils;
10+
11+
/**
12+
* Сlass for retrieving configuration values from the runtime environment. Used
13+
* to access dynamic properties. Provides a flexible alternative to @Value,
14+
* always getting the latest values without having to restart the application or
15+
* use /actuator/refresh.
16+
*/
17+
18+
@Component
19+
@RequiredArgsConstructor
20+
@Slf4j
21+
public class AzureProperties {
22+
private final Environment environment;
23+
24+
@PostConstruct
25+
public void validateProperties() {
26+
getAzureConnectionString();
27+
getAzureContainerName();
28+
log.info("All azure properties validated successfully.");
29+
}
30+
31+
public String getAzureConnectionString() {
32+
String connectionString = environment.getProperty("azure.connection.string");
33+
if (!StringUtils.hasText(connectionString)) {
34+
log.error(ErrorMessage.AZURE_CONNECTION_STRING_NOT_SET);
35+
throw new IllegalStateException(ErrorMessage.AZURE_CONNECTION_STRING_NOT_SET);
36+
}
37+
return connectionString;
38+
}
39+
40+
public String getAzureContainerName() {
41+
String containerName = environment.getProperty("azure.container.name");
42+
if (!StringUtils.hasText(containerName)) {
43+
log.error(ErrorMessage.AZURE_CONTAINER_NAME_NOT_SET);
44+
throw new IllegalStateException(ErrorMessage.AZURE_CONTAINER_NAME_NOT_SET);
45+
}
46+
return containerName;
47+
}
48+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package greencity.properties;
2+
3+
import greencity.constant.ErrorMessage;
4+
import jakarta.annotation.PostConstruct;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.core.env.Environment;
8+
import org.springframework.stereotype.Component;
9+
import org.springframework.util.StringUtils;
10+
11+
/**
12+
* Сlass for retrieving configuration values from the runtime environment. Used
13+
* to access dynamic properties. Provides a flexible alternative to @Value,
14+
* always getting the latest values without having to restart the application or
15+
* use /actuator/refresh.
16+
*/
17+
18+
@Component
19+
@RequiredArgsConstructor
20+
@Slf4j
21+
public class EmailProperties {
22+
private final Environment environment;
23+
24+
@PostConstruct
25+
public void validateProperties() {
26+
getSenderEmailAddress();
27+
getGreenCityOfficeEmailAddress();
28+
getTelegramFeedbackEmailAddress();
29+
getSystemEmailAddress();
30+
log.info("All Email properties validated successfully.");
31+
}
32+
33+
public String getSenderEmailAddress() {
34+
String senderEmail = environment.getProperty("contacts.sender.email-address");
35+
if (!StringUtils.hasText(senderEmail)) {
36+
log.error(ErrorMessage.SENDER_EMAIL_ADDRESS_NOT_SET);
37+
throw new IllegalStateException(ErrorMessage.SENDER_EMAIL_ADDRESS_NOT_SET);
38+
}
39+
return senderEmail;
40+
}
41+
42+
public String getGreenCityOfficeEmailAddress() {
43+
String officeEmailAddress = environment.getProperty("contacts.greenoffice.email-address");
44+
if (!StringUtils.hasText(officeEmailAddress)) {
45+
log.error(ErrorMessage.GREENCITY_OFFICE_EMAIL_ADDRESS_NOT_SET);
46+
throw new IllegalStateException(ErrorMessage.GREENCITY_OFFICE_EMAIL_ADDRESS_NOT_SET);
47+
}
48+
return officeEmailAddress;
49+
}
50+
51+
public String getTelegramFeedbackEmailAddress() {
52+
String feedbackEmailAddress = environment.getProperty("contacts.tgbot.feedbacks-email-address");
53+
if (!StringUtils.hasText(feedbackEmailAddress)) {
54+
log.error(ErrorMessage.TELEGRAM_EMAIL_ADDRESS_NOT_SET);
55+
throw new IllegalStateException(ErrorMessage.TELEGRAM_EMAIL_ADDRESS_NOT_SET);
56+
}
57+
return feedbackEmailAddress;
58+
}
59+
60+
public String getSystemEmailAddress() {
61+
String systemEmailAddress = environment.getProperty("contacts.authorization.system-email-address");
62+
if (!StringUtils.hasText(systemEmailAddress)) {
63+
log.error(ErrorMessage.SYSTEM_EMAIL_ADDRESS_NOT_SET);
64+
throw new IllegalStateException(ErrorMessage.SYSTEM_EMAIL_ADDRESS_NOT_SET);
65+
}
66+
return systemEmailAddress;
67+
}
68+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package greencity.properties;
2+
3+
import greencity.constant.ErrorMessage;
4+
import jakarta.annotation.PostConstruct;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.core.env.Environment;
8+
import org.springframework.stereotype.Component;
9+
import org.springframework.util.StringUtils;
10+
11+
/**
12+
* Сlass for retrieving configuration values from the runtime environment. Used
13+
* to access dynamic properties. Provides a flexible alternative to @Value,
14+
* always getting the latest values without having to restart the application or
15+
* use /actuator/refresh.
16+
*/
17+
18+
@Component
19+
@RequiredArgsConstructor
20+
@Slf4j
21+
public class GoogleProperties {
22+
private final Environment environment;
23+
24+
@PostConstruct
25+
public void validateProperties() {
26+
getGoogleApiKey();
27+
log.info("All google properties validated successfully.");
28+
}
29+
30+
public String getGoogleApiKey() {
31+
String googleApiKey = environment.getProperty("external.google.api-key");
32+
if (!StringUtils.hasText(googleApiKey)) {
33+
log.error(ErrorMessage.GOOGLE_API_KEY_NOT_SET);
34+
throw new IllegalStateException(ErrorMessage.GOOGLE_API_KEY_NOT_SET);
35+
}
36+
return googleApiKey;
37+
}
38+
}

0 commit comments

Comments
 (0)