feat: 🎸Claim sources for extracting AuthenticationContextClassRef and AuthnInstant
parent
d95cd86b4f
commit
d9d3034e55
|
@ -16,5 +16,6 @@ public class AuthenticationStatement {
|
|||
|
||||
private List<String> authenticatingAuthorities;
|
||||
private String authnContextClassRef;
|
||||
private String authnInstant;
|
||||
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ public class SamlAuthenticationDetails {
|
|||
public static final String REMOTE_ENTITY_ID = "remoteEntityId";
|
||||
public static final String ATTRIBUTES = "attributes";
|
||||
public static final String AUTHN_STATEMENTS = "authnStatements";
|
||||
public static final String AUTHN_INSTANT = "authnInstant";
|
||||
public static final String AUTHN_CONTEXT_CLASS_REF = "authnContextClassRef";
|
||||
public static final String AUTHENTICATING_AUTHORITIES = "authenticatingAuthorities";
|
||||
|
||||
|
@ -73,7 +74,8 @@ public class SamlAuthenticationDetails {
|
|||
{
|
||||
authnContextClassRef = as.getAuthnContext().getAuthnContextClassRef().getAuthnContextClassRef();
|
||||
}
|
||||
authenticationStatements.add(new AuthenticationStatement(authenticatingAuthorities, authnContextClassRef));
|
||||
String authnInstant = as.getAuthnInstant() != null ? as.getAuthnInstant().toString() : "";
|
||||
authenticationStatements.add(new AuthenticationStatement(authenticatingAuthorities, authnContextClassRef, authnInstant));
|
||||
}
|
||||
}
|
||||
return authenticationStatements;
|
||||
|
@ -127,11 +129,10 @@ public class SamlAuthenticationDetails {
|
|||
authorities.add(authority.getAsString());
|
||||
}
|
||||
String authnContextClassRef = getStringOrNull(obj.get(AUTHN_CONTEXT_CLASS_REF));
|
||||
authnStatements.add(new AuthenticationStatement(authorities, authnContextClassRef));
|
||||
String authnInstant = getStringOrNull(obj.get(AUTHN_INSTANT));
|
||||
authnStatements.add(new AuthenticationStatement(authorities, authnContextClassRef, authnInstant));
|
||||
}
|
||||
details.setAuthnStatements(authnStatements);
|
||||
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
|
@ -155,9 +156,11 @@ public class SamlAuthenticationDetails {
|
|||
}
|
||||
object.add(ATTRIBUTES, attrs);
|
||||
JsonArray authnStatements = new JsonArray();
|
||||
|
||||
for (AuthenticationStatement as: o.getAuthnStatements()) {
|
||||
JsonObject asJson = new JsonObject();
|
||||
addStringOrNull(asJson, AUTHN_CONTEXT_CLASS_REF, as.getAuthnContextClassRef());
|
||||
addStringOrNull(asJson, AUTHN_INSTANT, as.getAuthnInstant());
|
||||
JsonArray authorities = new JsonArray();
|
||||
for (String authAuthority: as.getAuthenticatingAuthorities()) {
|
||||
if (authAuthority == null) {
|
||||
|
@ -165,7 +168,8 @@ public class SamlAuthenticationDetails {
|
|||
}
|
||||
authorities.add(authAuthority);
|
||||
}
|
||||
asJson.add(AUTHENTICATING_AUTHORITIES, asJson);
|
||||
asJson.add(AUTHENTICATING_AUTHORITIES, authorities);
|
||||
authnStatements.add(asJson);
|
||||
}
|
||||
object.add(AUTHN_STATEMENTS, authnStatements);
|
||||
return object.toString();
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package cz.muni.ics.oauth2.model;
|
||||
|
||||
import cz.muni.ics.oauth2.model.convert.JsonElementStringConverter;
|
||||
import cz.muni.ics.oauth2.model.convert.SamlAuthenticationDetailsStringConverter;
|
||||
import cz.muni.ics.oauth2.model.convert.SimpleGrantedAuthorityStringConverter;
|
||||
import cz.muni.ics.oidc.saml.SamlPrincipal;
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package cz.muni.ics.oidc.server.claims.sources;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import cz.muni.ics.oauth2.model.AuthenticationStatement;
|
||||
import cz.muni.ics.oauth2.model.SamlAuthenticationDetails;
|
||||
import cz.muni.ics.oidc.server.claims.ClaimSource;
|
||||
import cz.muni.ics.oidc.server.claims.ClaimSourceInitContext;
|
||||
import cz.muni.ics.oidc.server.claims.ClaimSourceProduceContext;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Claim source which extracts the AuthenticationContextClassRef value from a SAML AuthN statement.
|
||||
*
|
||||
* @author Dominik Frantisek Bucik <bucik@ics.muni.cz>
|
||||
*/
|
||||
@Slf4j
|
||||
public class SamlAuthenticationContextClassRefClaimSource extends SamlAuthnStatementExtractorBaseClaimSource {
|
||||
|
||||
public static final String KEY_ACR = "acr";
|
||||
|
||||
public SamlAuthenticationContextClassRefClaimSource(ClaimSourceInitContext ctx) {
|
||||
super(ctx);
|
||||
log.debug("{} - initialized", getClaimName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAttrIdentifiers() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode produceValue(ClaimSourceProduceContext pctx) {
|
||||
JsonNode res = JsonNodeFactory.instance.nullNode();
|
||||
|
||||
if (!hasAuthnStatements(pctx)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
List<AuthenticationStatement> statements = pctx.getSamlAuthenticationDetails().getAuthnStatements();
|
||||
for (AuthenticationStatement s: statements) {
|
||||
if (!isValidStatement(s)) {
|
||||
continue;
|
||||
}
|
||||
res = JsonNodeFactory.instance.textNode(s.getAuthnContextClassRef());
|
||||
break;
|
||||
}
|
||||
log.debug("{} - produced value '{}'", getClaimName(), res);
|
||||
return res;
|
||||
}
|
||||
|
||||
private boolean isValidStatement(AuthenticationStatement s) {
|
||||
return s != null && StringUtils.hasText(s.getAuthnContextClassRef());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package cz.muni.ics.oidc.server.claims.sources;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import cz.muni.ics.oauth2.model.AuthenticationStatement;
|
||||
import cz.muni.ics.oauth2.model.SamlAuthenticationDetails;
|
||||
import cz.muni.ics.oidc.server.claims.ClaimSource;
|
||||
import cz.muni.ics.oidc.server.claims.ClaimSourceInitContext;
|
||||
import cz.muni.ics.oidc.server.claims.ClaimSourceProduceContext;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Claim source which extracts the AuthN instant value from a SAML AuthN statement.
|
||||
*
|
||||
* @author Dominik Frantisek Bucik <bucik@ics.muni.cz>
|
||||
*/
|
||||
@Slf4j
|
||||
public class SamlAuthnInstantClaimSource extends SamlAuthnStatementExtractorBaseClaimSource {
|
||||
|
||||
public SamlAuthnInstantClaimSource(ClaimSourceInitContext ctx) {
|
||||
super(ctx);
|
||||
log.debug("{} - initialized", getClaimName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAttrIdentifiers() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode produceValue(ClaimSourceProduceContext pctx) {
|
||||
JsonNode res = JsonNodeFactory.instance.nullNode();
|
||||
|
||||
if (!hasAuthnStatements(pctx)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
List<AuthenticationStatement> statements = pctx.getSamlAuthenticationDetails().getAuthnStatements();
|
||||
for (AuthenticationStatement s: statements) {
|
||||
if (!isValidStatement(s)) {
|
||||
continue;
|
||||
}
|
||||
res = JsonNodeFactory.instance.textNode(s.getAuthnInstant());
|
||||
break;
|
||||
}
|
||||
log.debug("{} - produced value '{}'", getClaimName(), res);
|
||||
return res;
|
||||
}
|
||||
|
||||
private boolean isValidStatement(AuthenticationStatement s) {
|
||||
return s != null && StringUtils.hasText(s.getAuthnInstant());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package cz.muni.ics.oidc.server.claims.sources;
|
||||
|
||||
import cz.muni.ics.oauth2.model.AuthenticationStatement;
|
||||
import cz.muni.ics.oauth2.model.SamlAuthenticationDetails;
|
||||
import cz.muni.ics.oidc.server.claims.ClaimSource;
|
||||
import cz.muni.ics.oidc.server.claims.ClaimSourceInitContext;
|
||||
import cz.muni.ics.oidc.server.claims.ClaimSourceProduceContext;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Base class for a claim source which extracts value from a SAML AuthN statement.
|
||||
*
|
||||
* @author Dominik Frantisek Bucik <bucik@ics.muni.cz>
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class SamlAuthnStatementExtractorBaseClaimSource extends ClaimSource {
|
||||
|
||||
public SamlAuthnStatementExtractorBaseClaimSource(ClaimSourceInitContext ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
protected boolean hasAuthnStatements(ClaimSourceProduceContext pctx) {
|
||||
SamlAuthenticationDetails details = pctx.getSamlAuthenticationDetails();
|
||||
|
||||
if (details == null || details.getAttributes() == null || details.getAttributes().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<AuthenticationStatement> statements = details.getAuthnStatements();
|
||||
if (statements == null || statements.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@ import com.google.common.collect.Sets;
|
|||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import cz.muni.ics.oauth2.model.SystemScope;
|
||||
import cz.muni.ics.oauth2.service.SystemScopeService;
|
||||
import cz.muni.ics.oidc.server.configurations.PerunOidcConfig;
|
||||
|
@ -26,6 +27,7 @@ import java.util.LinkedHashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
|
@ -179,7 +181,14 @@ public class ControllerUtils {
|
|||
JsonArray arr = userJson.getAsJsonArray(claim);
|
||||
List<String> values = new ArrayList<>();
|
||||
for (int i = 0; i < arr.size(); i++) {
|
||||
JsonElement el = arr.get(i);
|
||||
if (el instanceof JsonObject) {
|
||||
values.add(transformObject((JsonObject) el));
|
||||
} else if (el instanceof JsonPrimitive) {
|
||||
values.add(arr.get(i).getAsString());
|
||||
} else if (el instanceof JsonArray) {
|
||||
values.add(transformArray((JsonArray) el));
|
||||
}
|
||||
}
|
||||
claimValues.put(claim, values);
|
||||
}
|
||||
|
@ -204,6 +213,23 @@ public class ControllerUtils {
|
|||
model.put(SCOPES, sortedScopes);
|
||||
}
|
||||
|
||||
private static String transformObject(JsonObject obj) {
|
||||
StringJoiner sj = new StringJoiner(", ");
|
||||
for (String s: obj.keySet()) {
|
||||
sj.add(s + ": " + obj.get(s).getAsString());
|
||||
}
|
||||
return sj.toString();
|
||||
}
|
||||
|
||||
|
||||
private static String transformArray(JsonArray arr) {
|
||||
StringJoiner sj = new StringJoiner(", ");
|
||||
for (int i = 0; i < arr.size(); i++) {
|
||||
sj.add(arr.get(i).getAsString());
|
||||
}
|
||||
return sj.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create URL form base and parameters.
|
||||
|
||||
|
|
Loading…
Reference in New Issue