Skip to content
Open
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
11 changes: 0 additions & 11 deletions Jenkinsfile_CNP

This file was deleted.

14 changes: 0 additions & 14 deletions Jenkinsfile_nightly

This file was deleted.

11 changes: 9 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ plugins {
}

group = 'uk.gov.hmcts'
version = '0.1.9'
version = '0.2.0'

java {
toolchain {
Expand Down Expand Up @@ -140,12 +140,12 @@ ext['snakeyaml.version'] = '2.0'

dependencyManagement {
imports {
mavenBom 'org.springframework.boot:spring-boot-dependencies:4.0.0'
mavenBom 'org.springframework.cloud:spring-cloud-dependencies:2025.1.0'
}
}

dependencies {
implementation group: 'uk.gov.hmcts', name: 'spring-exception-handler', version: '0.0.3'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springVersion
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: springVersion
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-aop', version: springVersion
Expand Down Expand Up @@ -222,3 +222,10 @@ rootProject.tasks.named("processSmokeTestResources") {
wrapper {
distributionType = Wrapper.DistributionType.ALL
}

tasks.register('runAllStyleChecks') {
dependsOn 'checkstyleMain'
dependsOn 'checkstyleTest'
dependsOn 'checkstyleIntegrationTest'
dependsOn 'checkstyleFunctionalTest'
}
23 changes: 0 additions & 23 deletions src/main/java/uk/gov/hmcts/opal/common/exception/OpalApiError.java

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package uk.gov.hmcts.opal.common.spring.security;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;

@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig {

@Bean
@Primary
public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
return new OpalMethodSecurityExpressionHandler();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package uk.gov.hmcts.opal.common.spring.security;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.jwt.BadJwtException;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtException;
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.util.Assert;
import uk.gov.hmcts.opal.common.user.authorisation.client.service.UserStateClientService;
import uk.gov.hmcts.opal.common.user.authorisation.model.UserState;

import java.util.Collection;

@Slf4j
public class OpalJwtAuthenticationProvider implements AuthenticationProvider {

private final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter;
private final JwtDecoder jwtDecoder;
private final UserStateClientService userStateClientService;


public OpalJwtAuthenticationProvider(JwtDecoder jwtDecoder, UserStateClientService userStateClientService,
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter) {
Assert.notNull(jwtDecoder, "jwtDecoder cannot be null");
this.jwtDecoder = jwtDecoder;
this.userStateClientService = userStateClientService;
this.jwtGrantedAuthoritiesConverter = jwtGrantedAuthoritiesConverter;
}

/**
* Decode and validate the
* <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer
* Token</a>.
*
* @param authentication the authentication request object.
* @return A successful authentication
* @throws AuthenticationException if authentication failed for some reason
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//This can only ever be BearerTokenAuthenticationToken due to the supports() method
BearerTokenAuthenticationToken bearer = (BearerTokenAuthenticationToken) authentication;
Jwt jwt = getJwt(bearer);
UserState userState = userStateClientService.getUserStateByAuthenticationToken(jwt)
.orElseThrow(() -> new InvalidBearerTokenException("User state not found for authenticated user"));

Collection<GrantedAuthority> authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt);

OpalJwtAuthenticationToken token = new OpalJwtAuthenticationToken(userState, jwt, authorities);
token.setDetails(bearer.getDetails());

return token;
}


Jwt getJwt(BearerTokenAuthenticationToken bearer) {
try {
return this.jwtDecoder.decode(bearer.getToken());
} catch (BadJwtException failed) {
log.debug("Failed to authenticate since the JWT was invalid");
throw new InvalidBearerTokenException(failed.getMessage(), failed);
} catch (JwtException failed) {
throw new AuthenticationServiceException(failed.getMessage(), failed);
}
}

@Override
public boolean supports(Class<?> authentication) {
return BearerTokenAuthenticationToken.class.isAssignableFrom(authentication);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package uk.gov.hmcts.opal.common.spring.security;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimNames;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import uk.gov.hmcts.opal.common.user.authorisation.model.BusinessUnitUser;
import uk.gov.hmcts.opal.common.user.authorisation.model.Permission;
import uk.gov.hmcts.opal.common.user.authorisation.model.UserState;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

@EqualsAndHashCode(callSuper = true)
@Getter
public class OpalJwtAuthenticationToken extends JwtAuthenticationToken {

private final UserState userState;

private final Set<String> permissionNames;
private final Map<Short, Set<String>> businessUnitIdsToPermissionNames;

public OpalJwtAuthenticationToken(UserState userState, Jwt jwt,
Collection<? extends GrantedAuthority> authorities) {
super(jwt, authorities, jwt.getClaimAsString(JwtClaimNames.SUB));
this.userState = userState;

Function<Permission, String> toPermissionNameString = permission ->
permission.getPermissionName()
.toUpperCase()
.replace(" ", "_");

this.permissionNames = userState.getBusinessUnitUser().stream()
.flatMap(buUser -> buUser.getPermissions().stream())
.map(toPermissionNameString)
.collect(Collectors.toSet());

this.businessUnitIdsToPermissionNames = userState.getBusinessUnitUser().stream()
.collect(Collectors.toMap(
BusinessUnitUser::getBusinessUnitId,
buUser -> buUser.getPermissions().stream()
.map(toPermissionNameString)
.collect(Collectors.toSet())
));
}

public boolean hasBusinessUnit(String businessUnitId) {
return businessUnitIdsToPermissionNames
.containsKey(Short.parseShort(businessUnitId));
}

public boolean hasPermission(String permission) {
return getPermissionNames().contains(permission);
}

public boolean hasPermissionInBusinessUnit(String permission, String businessUnitId) {
List<String> permissionsInBusinessUnit = businessUnitIdsToPermissionNames
.getOrDefault(Short.parseShort(businessUnitId), Set.of())
.stream()
.toList();
return permissionsInBusinessUnit.contains(permission);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package uk.gov.hmcts.opal.common.spring.security;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.core.Authentication;

import java.util.function.Supplier;

public class OpalMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

@Override
public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, MethodInvocation mi) {
final StandardEvaluationContext ctx =
(StandardEvaluationContext) super.createEvaluationContext(authentication, mi);

Authentication auth = authentication.get();
OpalMethodSecurityExpressionRoot root = new OpalMethodSecurityExpressionRoot(auth);

root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(getTrustResolver());
root.setRoleHierarchy(getRoleHierarchy());
root.setThis(mi.getThis());

ctx.setRootObject(root);
return ctx;
}
}
Loading