feat: 🎸Claim sources for extracting AuthenticationContextClassRef and AuthnInstant
parent
d95cd86b4f
commit
d9d3034e55
|
@ -16,5 +16,6 @@ public class AuthenticationStatement {
|
||||||
|
|
||||||
private List<String> authenticatingAuthorities;
|
private List<String> authenticatingAuthorities;
|
||||||
private String authnContextClassRef;
|
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 REMOTE_ENTITY_ID = "remoteEntityId";
|
||||||
public static final String ATTRIBUTES = "attributes";
|
public static final String ATTRIBUTES = "attributes";
|
||||||
public static final String AUTHN_STATEMENTS = "authnStatements";
|
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 AUTHN_CONTEXT_CLASS_REF = "authnContextClassRef";
|
||||||
public static final String AUTHENTICATING_AUTHORITIES = "authenticatingAuthorities";
|
public static final String AUTHENTICATING_AUTHORITIES = "authenticatingAuthorities";
|
||||||
|
|
||||||
|
@ -73,7 +74,8 @@ public class SamlAuthenticationDetails {
|
||||||
{
|
{
|
||||||
authnContextClassRef = as.getAuthnContext().getAuthnContextClassRef().getAuthnContextClassRef();
|
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;
|
return authenticationStatements;
|
||||||
|
@ -127,11 +129,10 @@ public class SamlAuthenticationDetails {
|
||||||
authorities.add(authority.getAsString());
|
authorities.add(authority.getAsString());
|
||||||
}
|
}
|
||||||
String authnContextClassRef = getStringOrNull(obj.get(AUTHN_CONTEXT_CLASS_REF));
|
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);
|
details.setAuthnStatements(authnStatements);
|
||||||
|
|
||||||
|
|
||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,9 +156,11 @@ public class SamlAuthenticationDetails {
|
||||||
}
|
}
|
||||||
object.add(ATTRIBUTES, attrs);
|
object.add(ATTRIBUTES, attrs);
|
||||||
JsonArray authnStatements = new JsonArray();
|
JsonArray authnStatements = new JsonArray();
|
||||||
|
|
||||||
for (AuthenticationStatement as: o.getAuthnStatements()) {
|
for (AuthenticationStatement as: o.getAuthnStatements()) {
|
||||||
JsonObject asJson = new JsonObject();
|
JsonObject asJson = new JsonObject();
|
||||||
addStringOrNull(asJson, AUTHN_CONTEXT_CLASS_REF, as.getAuthnContextClassRef());
|
addStringOrNull(asJson, AUTHN_CONTEXT_CLASS_REF, as.getAuthnContextClassRef());
|
||||||
|
addStringOrNull(asJson, AUTHN_INSTANT, as.getAuthnInstant());
|
||||||
JsonArray authorities = new JsonArray();
|
JsonArray authorities = new JsonArray();
|
||||||
for (String authAuthority: as.getAuthenticatingAuthorities()) {
|
for (String authAuthority: as.getAuthenticatingAuthorities()) {
|
||||||
if (authAuthority == null) {
|
if (authAuthority == null) {
|
||||||
|
@ -165,7 +168,8 @@ public class SamlAuthenticationDetails {
|
||||||
}
|
}
|
||||||
authorities.add(authAuthority);
|
authorities.add(authAuthority);
|
||||||
}
|
}
|
||||||
asJson.add(AUTHENTICATING_AUTHORITIES, asJson);
|
asJson.add(AUTHENTICATING_AUTHORITIES, authorities);
|
||||||
|
authnStatements.add(asJson);
|
||||||
}
|
}
|
||||||
object.add(AUTHN_STATEMENTS, authnStatements);
|
object.add(AUTHN_STATEMENTS, authnStatements);
|
||||||
return object.toString();
|
return object.toString();
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package cz.muni.ics.oauth2.model;
|
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.SamlAuthenticationDetailsStringConverter;
|
||||||
import cz.muni.ics.oauth2.model.convert.SimpleGrantedAuthorityStringConverter;
|
import cz.muni.ics.oauth2.model.convert.SimpleGrantedAuthorityStringConverter;
|
||||||
import cz.muni.ics.oidc.saml.SamlPrincipal;
|
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.JsonArray;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
import cz.muni.ics.oauth2.model.SystemScope;
|
import cz.muni.ics.oauth2.model.SystemScope;
|
||||||
import cz.muni.ics.oauth2.service.SystemScopeService;
|
import cz.muni.ics.oauth2.service.SystemScopeService;
|
||||||
import cz.muni.ics.oidc.server.configurations.PerunOidcConfig;
|
import cz.muni.ics.oidc.server.configurations.PerunOidcConfig;
|
||||||
|
@ -26,6 +27,7 @@ import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.StringJoiner;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
@ -179,7 +181,14 @@ public class ControllerUtils {
|
||||||
JsonArray arr = userJson.getAsJsonArray(claim);
|
JsonArray arr = userJson.getAsJsonArray(claim);
|
||||||
List<String> values = new ArrayList<>();
|
List<String> values = new ArrayList<>();
|
||||||
for (int i = 0; i < arr.size(); i++) {
|
for (int i = 0; i < arr.size(); i++) {
|
||||||
values.add(arr.get(i).getAsString());
|
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);
|
claimValues.put(claim, values);
|
||||||
}
|
}
|
||||||
|
@ -204,6 +213,23 @@ public class ControllerUtils {
|
||||||
model.put(SCOPES, sortedScopes);
|
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.
|
* Create URL form base and parameters.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue