Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCD-4782: Move user role from Cognito group to custom attribute #1775

Merged
merged 14 commits into from
Jan 23, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,14 @@
"raw": "{\"id\":1,\"acbCode\":\"02\",\"name\":\"UL LLC\",\"website\":\"https://testwebsite.com\",\"address\":{\"addressId\":1,\"line1\":\"address\",\"line2\":null,\"city\":\"city\",\"state\":\"state\",\"zipcode\":\"111111\",\"country\":\"country\"},\"retired\":false,\"retirementDay\":null}"
},
"url": {
"raw": "{{url}}/rest/acbs/1",
"raw": "{{url}}/rest/acbs/3",
"host": [
"{{url}}"
],
"path": [
"rest",
"acbs",
"1"
"3"
]
}
},
Expand Down Expand Up @@ -254,14 +254,14 @@
}
],
"url": {
"raw": "{{url}}/rest/acbs/1/users",
"raw": "{{url}}/rest/acbs/3/users",
"host": [
"{{url}}"
],
"path": [
"rest",
"acbs",
"1",
"3",
"users"
]
}
Expand Down Expand Up @@ -513,14 +513,14 @@
}
],
"url": {
"raw": "{{url}}/rest/acbs/1/users",
"raw": "{{url}}/rest/acbs/3/users",
"host": [
"{{url}}"
],
"path": [
"rest",
"acbs",
"1",
"3",
"users"
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package gov.healthit.chpl.auth.authentication;

import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang3.NotImplementedException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
Expand All @@ -25,39 +22,25 @@
public class CognitoJwtUserConverter implements JWTUserConverter {
private String region;
private String userPoolId;
private String clientId;
private String tokenizeRsaKeyUrl;

public CognitoJwtUserConverter(@Value("${cognito.region}") String region, @Value("${cognito.userPoolId}") String userPoolId,
@Value("${cognito.clientId}") String clientId, @Value("${cognito.tokenizezRsaKeyUrl}") String tokenizeRsaKeyUrl) {
public CognitoJwtUserConverter(@Value("${cognito.region}") String region,
@Value("${cognito.userPoolId}") String userPoolId,
@Value("${cognito.tokenizezRsaKeyUrl}") String tokenizeRsaKeyUrl) {
this.region = region;
this.userPoolId = userPoolId;
this.clientId = clientId;
this.tokenizeRsaKeyUrl = tokenizeRsaKeyUrl;
}

@Override
public JWTAuthenticatedUser getAuthenticatedUser(String jwt) throws JWTValidationException, MultipleUserAccountsException {
try {
DecodedJWT decodeJwt = decodeJwt(jwt);
if (decodeJwt.getClaims().size() != 0) {
DecodedJWT decodedJwt = decodeJwt(jwt);
if (decodedJwt.getClaims().size() != 0) {
return JWTAuthenticatedUser.builder()
.authenticationSystem(AuthenticationSystem.COGNITO)
.authenticated(true)
.cognitoId(UUID.fromString(decodeJwt.getSubject()))
.subjectName(decodeJwt.getClaim("email").asString())
.fullName(decodeJwt.getClaim("name").asString())
.email(decodeJwt.getClaim("email").asString())
.organizationIds(
decodeJwt.getClaims().containsKey("custom:organizations")
? Stream.of(decodeJwt.getClaim("custom:organizations").asString().split(","))
.map(Long::valueOf)
.toList()
: null)
.authorities(decodeJwt.getClaim("cognito:groups").asList(String.class).stream()
.filter(group -> !group.endsWith("-env")) //Remove environment related groups
.map(group -> new SimpleGrantedAuthority(group))
.collect(Collectors.toSet()))
.cognitoId(UUID.fromString(decodedJwt.getSubject()))
.build();
} else {
throw new JWTValidationException("Invalid authentication token.");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package gov.healthit.chpl.auth.authentication;

import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.ff4j.FF4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import gov.healthit.chpl.FeatureList;
Expand All @@ -25,11 +31,15 @@ public class JWTUserConverterFacade implements JWTUserConverter {

private FF4j ff4j;

public JWTUserConverterFacade(JWTConsumer jwtConsumer, UserDAO userDAO, @Value("${cognito.region}") String region,
@Value("${cognito.userPoolId}") String userPoolId, @Value("${cognito.clientId}") String clientId,
@Value("${cognito.tokenizezRsaKeyUrl}") String tokenizeRsaKeyUrl, FF4j ff4j, CognitoApiWrapper cognitoApiWrapper) {
public JWTUserConverterFacade(JWTConsumer jwtConsumer,
UserDAO userDAO,
@Value("${cognito.region}") String region,
@Value("${cognito.userPoolId}") String userPoolId,
@Value("${cognito.tokenizezRsaKeyUrl}") String tokenizeRsaKeyUrl,
FF4j ff4j,
CognitoApiWrapper cognitoApiWrapper) {
chplJwtUserConverter = new ChplJWTUserConverter(jwtConsumer, userDAO);
cognitoJwtUserConverter = new CognitoJwtUserConverter(region, userPoolId, clientId, tokenizeRsaKeyUrl);
cognitoJwtUserConverter = new CognitoJwtUserConverter(region, userPoolId, tokenizeRsaKeyUrl);
this.ff4j = ff4j;
this.cognitoApiWrapper = cognitoApiWrapper;
}
Expand All @@ -42,11 +52,20 @@ public JWTAuthenticatedUser getAuthenticatedUser(String jwt) throws JWTValidatio
user = cognitoJwtUserConverter.getAuthenticatedUser(jwt);
if (user != null) {
try {
//Set some values not avail in the Cognito Access Token that were avail in the CHPL token
//Many values are not available from the Cognito Access Token so we have to set them
//manually here from the Cognito user data
User cognitoUser = cognitoApiWrapper.getUserInfo(user.getCognitoId());
user.setEmail(cognitoUser.getEmail());
user.setSubjectName(cognitoUser.getEmail());
user.setFullName(cognitoUser.getFullName());
if (!StringUtils.isEmpty(cognitoUser.getRole())) {
user.setAuthorities(Stream.of(new SimpleGrantedAuthority(cognitoUser.getRole())).collect(Collectors.toSet()));
}
if (!CollectionUtils.isEmpty(cognitoUser.getOrganizations())) {
user.setOrganizationIds(cognitoUser.getOrganizations().stream()
.map(org -> org.getId())
.toList());
}
} catch (UserRetrievalException e) {
throw new JWTValidationException("Could not locate the Cognito user id");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,19 @@ public final class CognitoGroups {
private CognitoGroups() {}
public static final String CHPL_ADMIN = "chpl-admin";
public static final String CHPL_ONC = "chpl-onc";
public static final String CHPL_USER_CREATOR = "chpl-user-creator";
public static final String CHPL_ACB = "chpl-onc-acb";
public static final String CHPL_CMS_STAFF = "chpl-cms-staff";
public static final String CHPL_DEVELOPER = "chpl-developer";
public static final String CHPL_USER_AUTHENTICATOR = "chpl-user-authenticator";
public static final String CHPL_INVITED_USER_CREATOR = "chpl-invited-user-creator";
public static final String CHPL_STARTUP = "chpl-startup";
public static final String CHPL_SYSTEM = "chpl-system";

public static List<String> getAll() {
return List.of(
CHPL_ADMIN,
CHPL_ONC,
CHPL_USER_CREATOR,
CHPL_ACB,
CHPL_CMS_STAFF,
CHPL_DEVELOPER,
CHPL_USER_AUTHENTICATOR,
CHPL_INVITED_USER_CREATOR,
CHPL_STARTUP,
CHPL_SYSTEM);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatusCode;
Expand Down Expand Up @@ -61,6 +62,9 @@ private JsonNode fetchInsightSubmissionsForDeveloper(Long developerId) throws In
try {
response = insightRestTemplate.getForEntity(url, String.class);
LOGGER.debug("Response: " + response.getBody());
if (response == null || StringUtils.isEmpty(response.getBody())) {
LOGGER.warn("A null or empty response was received from the Insights API.");
}
} catch (Exception ex) {
HttpStatusCode statusCode = (response != null ? response.getStatusCode() : null);
if (statusCode == null && ex instanceof RestClientResponseException) {
Expand All @@ -69,7 +73,7 @@ private JsonNode fetchInsightSubmissionsForDeveloper(Long developerId) throws In
LOGGER.error("Unable to connect to the URL " + url + ". Got response status code " + statusCode);
throw new InsightRequestFailedException(ex.getMessage(), ex, statusCode);
}
String responseBody = response == null ? "" : response.getBody();
String responseBody = ((response == null || StringUtils.isEmpty(response.getBody())) ? "{}" : response.getBody());
JsonNode root = null;
try {
root = objectMapper.readTree(responseBody);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,14 @@ public CertificationBody create(CertificationBody acb) throws EntityCreationExce

@Transactional
@PreAuthorize("@permissions.hasAccess(T(gov.healthit.chpl.permissions.Permissions).CERTIFICATION_BODY, "
+ "T(gov.healthit.chpl.permissions.domains.CertificationBodyDomainPermissions).UPDATE, #acb)")
+ "T(gov.healthit.chpl.permissions.domains.CertificationBodyDomainPermissions).UPDATE, #acbToUpdate)")
@CacheEvict(value = {
CacheNames.GET_DECERTIFIED_DEVELOPERS,
CacheNames.COLLECTIONS_DEVELOPERS,
CacheNames.COLLECTIONS_LISTINGS,
CacheNames.COMPLAINTS
}, allEntries = true)
@ListingStoreRemove(removeBy = RemoveBy.ACB_ID, id = "#acb.id")
@ListingStoreRemove(removeBy = RemoveBy.ACB_ID, id = "#acbToUpdate.id")
@ListingSearchCacheRefresh
// no other caches have ACB data so we do not need to clear all
public CertificationBody update(CertificationBody acbToUpdate) throws EntityRetrievalException, SchedulerException, ValidationException, ActivityException, InvalidArgumentsException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,17 +200,17 @@ public boolean isUserRoleDeveloperAdmin() {

@Override
public boolean isUserRoleUserCreator() {
return doesAuditUserHaveRole(CognitoGroups.CHPL_USER_CREATOR);
return false;
}

@Override
public boolean isUserRoleUserAuthenticator() {
return doesAuditUserHaveRole(CognitoGroups.CHPL_USER_AUTHENTICATOR);
return false;
}

@Override
public boolean isUserRoleInvitedUserCreator() {
return doesAuditUserHaveRole(CognitoGroups.CHPL_INVITED_USER_CREATOR);
return false;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,16 @@ public interface ResourcePermissions {

boolean isUserRoleDeveloperAdmin();

//Not used with Cognito users
@Deprecated
boolean isUserRoleUserCreator();

//Not used with Cognito users
@Deprecated
boolean isUserRoleUserAuthenticator();

//Not used with Cognito users
@Deprecated
boolean isUserRoleInvitedUserCreator();

boolean isUserRoleStartup();
Expand Down
Loading
Loading