Skip to content

Commit b126a05

Browse files
authored
Merge pull request #26 from companieshouse/lp-204-authentication-interceptor
LP-204 Introduced user authentication and token permissions
2 parents ae0e6fb + 0b05a2d commit b126a05

File tree

5 files changed

+157
-9
lines changed

5 files changed

+157
-9
lines changed

pom.xml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
2828
<log4j.version>2.24.1</log4j.version>
2929
<jib-maven-plugin.version>3.4.2</jib-maven-plugin.version>
30+
<api-security-java.version>2.0.8</api-security-java.version>
3031
</properties>
3132

3233
<profiles>
@@ -79,6 +80,11 @@
7980
<artifactId>api-sdk-java</artifactId>
8081
<version>${api-sdk-java.version}</version>
8182
</dependency>
83+
<dependency>
84+
<groupId>uk.gov.companieshouse</groupId>
85+
<artifactId>api-security-java</artifactId>
86+
<version>${api-security-java.version}</version>
87+
</dependency>
8288
<dependency>
8389
<groupId>org.mapstruct</groupId>
8490
<artifactId>mapstruct</artifactId>
@@ -97,11 +103,6 @@
97103
<version>${sonar-maven-plugin.version}</version>
98104
<scope>test</scope>
99105
</dependency>
100-
<dependency>
101-
<groupId>junit</groupId>
102-
<artifactId>junit</artifactId>
103-
<scope>test</scope>
104-
</dependency>
105106
</dependencies>
106107

107108
<build>

src/main/java/uk/gov/companieshouse/limitedpartnershipsapi/config/InterceptorConfig.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,23 @@
44
import org.springframework.lang.NonNull;
55
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
66
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
7+
import uk.gov.companieshouse.api.interceptor.TokenPermissionsInterceptor;
8+
import uk.gov.companieshouse.limitedpartnershipsapi.interceptor.CustomUserAuthenticationInterceptor;
79
import uk.gov.companieshouse.limitedpartnershipsapi.interceptor.LoggingInterceptor;
810

9-
1011
@Configuration
1112
public class InterceptorConfig implements WebMvcConfigurer {
1213

14+
private static final String PARTNERSHIP = "/transactions/**/partnership";
15+
1316
private final LoggingInterceptor loggingInterceptor;
1417

15-
public InterceptorConfig(LoggingInterceptor loggingInterceptor) {
18+
private final CustomUserAuthenticationInterceptor customUserAuthenticationInterceptor;
19+
20+
21+
public InterceptorConfig(LoggingInterceptor loggingInterceptor, CustomUserAuthenticationInterceptor customUserAuthenticationInterceptor) {
1622
this.loggingInterceptor = loggingInterceptor;
23+
this.customUserAuthenticationInterceptor = customUserAuthenticationInterceptor;
1724
}
1825

1926
/**
@@ -24,5 +31,9 @@ public InterceptorConfig(LoggingInterceptor loggingInterceptor) {
2431
@Override
2532
public void addInterceptors(@NonNull InterceptorRegistry registry) {
2633
registry.addInterceptor(loggingInterceptor);
34+
registry.addInterceptor(new TokenPermissionsInterceptor())
35+
.addPathPatterns(PARTNERSHIP);
36+
registry.addInterceptor(customUserAuthenticationInterceptor)
37+
.addPathPatterns(PARTNERSHIP);
2738
}
2839
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package uk.gov.companieshouse.limitedpartnershipsapi.interceptor;
2+
3+
import jakarta.servlet.http.HttpServletRequest;
4+
import jakarta.servlet.http.HttpServletResponse;
5+
import org.springframework.lang.NonNull;
6+
import org.springframework.stereotype.Component;
7+
import org.springframework.web.servlet.HandlerInterceptor;
8+
import uk.gov.companieshouse.api.util.security.AuthorisationUtil;
9+
import uk.gov.companieshouse.api.util.security.Permission;
10+
import uk.gov.companieshouse.api.util.security.SecurityConstants;
11+
import uk.gov.companieshouse.limitedpartnershipsapi.utils.ApiLogger;
12+
13+
import java.util.HashMap;
14+
15+
import static uk.gov.companieshouse.limitedpartnershipsapi.utils.Constants.ERIC_REQUEST_ID_KEY;
16+
17+
@Component
18+
public class CustomUserAuthenticationInterceptor implements HandlerInterceptor {
19+
20+
@Override
21+
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
22+
String reqId = request.getHeader(ERIC_REQUEST_ID_KEY);
23+
24+
if (skipTokenPermissionChecksWhenApiKeyUsed(reqId, request)) {
25+
return true;
26+
}
27+
28+
// TokenPermissions should have been set up in the request by TokenPermissionsInterceptor
29+
final var tokenPermissions = AuthorisationUtil.getTokenPermissions(request)
30+
.orElseThrow(() -> new IllegalStateException("CustomUserAuthenticationInterceptor - TokenPermissions object not present in request"));
31+
32+
boolean hasCompanyIncorporationCreatePermission = tokenPermissions.hasPermission(Permission.Key.COMPANY_INCORPORATION, Permission.Value.CREATE);
33+
34+
var authInfoMap = new HashMap<String, Object>();
35+
authInfoMap.put("request_method", request.getMethod());
36+
authInfoMap.put("has_company_incorporation_create_permission", hasCompanyIncorporationCreatePermission);
37+
38+
if (hasCompanyIncorporationCreatePermission) {
39+
ApiLogger.debugContext(reqId, "CustomUserAuthenticationInterceptor authorised with company_incorporation=create permission",
40+
authInfoMap);
41+
return true;
42+
}
43+
ApiLogger.infoContext(reqId, "CustomUserAuthenticationInterceptor unauthorised", authInfoMap);
44+
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
45+
return false;
46+
}
47+
48+
private boolean skipTokenPermissionChecksWhenApiKeyUsed(String reqId, HttpServletRequest request) {
49+
var logMap = new HashMap<String, Object>();
50+
51+
if (SecurityConstants.API_KEY_IDENTITY_TYPE.equals(AuthorisationUtil.getAuthorisedIdentityType(request))) {
52+
ApiLogger.debugContext(reqId, "CustomUserAuthenticationInterceptor skipping token permission checks for api key request", logMap);
53+
return true;
54+
}
55+
return false;
56+
}
57+
}

src/test/java/uk/gov/companieshouse/limitedpartnershipsapi/config/InterceptorConfigTest.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
import org.mockito.junit.jupiter.MockitoExtension;
99
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
1010
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
11+
import uk.gov.companieshouse.api.interceptor.TokenPermissionsInterceptor;
12+
import uk.gov.companieshouse.limitedpartnershipsapi.interceptor.CustomUserAuthenticationInterceptor;
1113
import uk.gov.companieshouse.limitedpartnershipsapi.interceptor.LoggingInterceptor;
1214

13-
1415
import static org.mockito.ArgumentMatchers.any;
1516
import static org.mockito.Mockito.inOrder;
1617
import static org.mockito.Mockito.times;
@@ -29,6 +30,8 @@ class InterceptorConfigTest {
2930
@Mock
3031
private LoggingInterceptor loggingInterceptor;
3132

33+
@Mock
34+
private CustomUserAuthenticationInterceptor customUserAuthenticationInterceptor;
3235

3336
@InjectMocks
3437
private InterceptorConfig interceptorConfig;
@@ -41,7 +44,9 @@ void addInterceptorsTest() {
4144

4245
InOrder inOrder = inOrder(interceptorRegistry, interceptorRegistration);
4346
inOrder.verify(interceptorRegistry).addInterceptor(loggingInterceptor);
47+
inOrder.verify(interceptorRegistry).addInterceptor(any(TokenPermissionsInterceptor.class));
48+
inOrder.verify(interceptorRegistry).addInterceptor(customUserAuthenticationInterceptor);
4449

45-
verify(interceptorRegistry, times(1)).addInterceptor(any());
50+
verify(interceptorRegistry, times(3)).addInterceptor(any());
4651
}
4752
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package uk.gov.companieshouse.limitedpartnershipsapi.interceptor;
2+
3+
import jakarta.servlet.http.HttpServletRequest;
4+
import jakarta.servlet.http.HttpServletResponse;
5+
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.api.extension.ExtendWith;
7+
import org.mockito.InjectMocks;
8+
import org.mockito.Mock;
9+
import org.mockito.junit.jupiter.MockitoExtension;
10+
import org.springframework.mock.web.MockHttpServletResponse;
11+
import uk.gov.companieshouse.api.util.security.Permission;
12+
import uk.gov.companieshouse.api.util.security.SecurityConstants;
13+
import uk.gov.companieshouse.api.util.security.TokenPermissions;
14+
15+
import static org.junit.jupiter.api.Assertions.assertEquals;
16+
import static org.junit.jupiter.api.Assertions.assertFalse;
17+
import static org.junit.jupiter.api.Assertions.assertTrue;
18+
import static org.mockito.Mockito.when;
19+
import static uk.gov.companieshouse.limitedpartnershipsapi.utils.Constants.ERIC_REQUEST_ID_KEY;
20+
21+
@ExtendWith(MockitoExtension.class)
22+
class CustomUserAuthenticationInterceptorTest {
23+
24+
private static final String REQ_ID = "43hj5jh345";
25+
private static final String TOKEN_PERMISSIONS = "token_permissions";
26+
public static final String ERIC_IDENTITY_TYPE = "ERIC-Identity-Type";
27+
28+
@Mock
29+
private HttpServletRequest mockHttpServletRequest;
30+
@Mock
31+
private TokenPermissions mockTokenPermissions;
32+
@InjectMocks
33+
private CustomUserAuthenticationInterceptor userAuthenticationInterceptor;
34+
35+
@Test
36+
void testInterceptorReturnsTrueWhenRequestHasCorrectTokenPermission() {
37+
MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
38+
Object mockHandler = new Object();
39+
40+
when(mockHttpServletRequest.getAttribute(TOKEN_PERMISSIONS)).thenReturn(mockTokenPermissions);
41+
42+
when(mockTokenPermissions.hasPermission(Permission.Key.COMPANY_INCORPORATION, Permission.Value.CREATE)).thenReturn(true);
43+
44+
var result = userAuthenticationInterceptor.preHandle(mockHttpServletRequest, mockHttpServletResponse, mockHandler);
45+
assertTrue(result);
46+
}
47+
48+
@Test
49+
void testInterceptorReturnsFalseWhenRequestHasIncorrectTokenPermission() {
50+
MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
51+
Object mockHandler = new Object();
52+
53+
when(mockHttpServletRequest.getAttribute(TOKEN_PERMISSIONS)).thenReturn(mockTokenPermissions);
54+
55+
when(mockTokenPermissions.hasPermission(Permission.Key.COMPANY_INCORPORATION, Permission.Value.CREATE)).thenReturn(false);
56+
57+
var result = userAuthenticationInterceptor.preHandle(mockHttpServletRequest, mockHttpServletResponse, mockHandler);
58+
assertFalse(result);
59+
assertEquals(HttpServletResponse.SC_UNAUTHORIZED, mockHttpServletResponse.getStatus());
60+
}
61+
62+
@Test
63+
void testTokenPermissionIsSkippedAndInterceptorReturnsTrueWhenAnApiKeyIsUsed() {
64+
MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
65+
Object mockHandler = new Object();
66+
67+
when(mockHttpServletRequest.getHeader(ERIC_REQUEST_ID_KEY)).thenReturn(REQ_ID);
68+
69+
when(mockHttpServletRequest.getHeader(ERIC_IDENTITY_TYPE)).thenReturn(SecurityConstants.API_KEY_IDENTITY_TYPE);
70+
71+
var result = userAuthenticationInterceptor.preHandle(mockHttpServletRequest, mockHttpServletResponse, mockHandler);
72+
assertTrue(result);
73+
}
74+
}

0 commit comments

Comments
 (0)