Skip to content

Commit

Permalink
Adding support for mapping groups to roles. Also includes some code c…
Browse files Browse the repository at this point in the history
…leanup.
  • Loading branch information
pcc-cahilp committed Dec 5, 2024
1 parent 32e61bb commit e29adc7
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 14 deletions.
6 changes: 3 additions & 3 deletions src/main/java/gov/cdc/izgateway/security/IzgPrincipal.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ public abstract class IzgPrincipal implements java.security.Principal {
Date validTo;
String serialNumber;
String issuer;
List<String> audience;
Set<String> scopes;
Set<String> roles;
List<String> audience = new ArrayList<>();
Set<String> scopes = new TreeSet<>();
Set<String> roles = new TreeSet<>();

public abstract String getSerialNumberHex();
}
3 changes: 0 additions & 3 deletions src/main/java/gov/cdc/izgateway/security/JWTPrincipal.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package gov.cdc.izgateway.security;

import lombok.Data;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.UUID;

@Data
public class JWTPrincipal extends IzgPrincipal {

public String getSerialNumberHex() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ public IzgPrincipal createPrincipalFromCertificate(HttpServletRequest request) {
X509Certificate[] certs = (X509Certificate[]) request.getAttribute(Globals.CERTIFICATES_ATTR);

if (certs == null || certs.length == 0) {
// Removing the warning log message as it is causing tests to fail
// log.warn("No certificates found in request.");
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gov.cdc.izgateway.security.principal;

import java.util.Set;

public interface GroupToRoleMapper {
Set<String> mapGroupsToRoles(Set<String> groups);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

@Slf4j
Expand All @@ -22,6 +26,16 @@ public class JwtSharedSecretPrincipalProvider implements JwtPrincipalProvider {
@Value("${jwt.shared-secret:}")
private String sharedSecret;

private final GroupToRoleMapper groupToRoleMapper;
private final ScopeToRoleMapper scopeToRoleMapper;

@Autowired
public JwtSharedSecretPrincipalProvider(@Nullable GroupToRoleMapper groupToRoleMapper,
@Nullable ScopeToRoleMapper scopeToRoleMapper) {
this.groupToRoleMapper = groupToRoleMapper;
this.scopeToRoleMapper = scopeToRoleMapper;
}

@Override
public IzgPrincipal createPrincipalFromJwt(HttpServletRequest request) {
if (StringUtils.isBlank(sharedSecret)) {
Expand Down Expand Up @@ -64,13 +78,23 @@ private IzgPrincipal buildPrincipal(Claims claims) {
principal.setSerialNumber(claims.getId());
principal.setIssuer(claims.getIssuer());
principal.setAudience(Collections.singletonList(claims.getAudience()));

TreeSet<String> scopes = extractScopes(claims);
principal.setRoles(RoleMapper.mapScopesToRoles(scopes));
addRolesFromScopes(claims, principal);
addRolesFromGroups(claims, principal);
log.debug("Roles created from JWT: {}", principal.getRoles());

return principal;
}

private void addRolesFromScopes(Claims claims, IzgPrincipal principal) {
if (scopeToRoleMapper == null) {
log.debug("No scope to role mapper was set. Skipping scope to role mapping.");
return;
}

TreeSet<String> scopes = extractScopes(claims);
principal.getRoles().addAll(scopeToRoleMapper.mapScopesToRoles(scopes));
}

private TreeSet<String> extractScopes(Claims claims) {
TreeSet<String> scopes = new TreeSet<>();
String scopeString = claims.get("scope", String.class);
Expand All @@ -79,4 +103,20 @@ private TreeSet<String> extractScopes(Claims claims) {
}
return scopes;
}

private void addRolesFromGroups(Claims claims, IzgPrincipal principal) {
if (groupToRoleMapper == null) {
log.debug("No group to role mapper was set. Skipping group to role mapping.");
return;
}

List<String> groupsList = claims.get("groups", List.class);
if (groupsList == null || groupsList.isEmpty()) {
return;
}
Set<String> groups = new TreeSet<>(groupsList);

Set<String> roles = groupToRoleMapper.mapGroupsToRoles(groups);
principal.getRoles().addAll(roles);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gov.cdc.izgateway.security.principal;

import java.util.Set;

public interface ScopeToRoleMapper {
Set<String> mapScopesToRoles(Set<String> scopes);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package gov.cdc.izgateway.security.principal;

import org.springframework.stereotype.Component;

import java.util.Set;
import java.util.TreeSet;

public class RoleMapper {
public static Set<String> mapScopesToRoles(Set<String> scopes) {
@Component
public class ScopeToRoleMapperImpl implements ScopeToRoleMapper {
public Set<String> mapScopesToRoles(Set<String> scopes) {
// Until we've defined a mapping between scopes and roles, we'll just return the scopes as roles
return new TreeSet<>(scopes);
}

}

0 comments on commit e29adc7

Please sign in to comment.