Add Nullability to opensaml5Main Source Set
Issue gh-17823 Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
This commit is contained in:
@@ -138,6 +138,10 @@ javadoc {
|
||||
source = sourceSets.main.allJava + sourceSets.opensaml5Main.allJava
|
||||
}
|
||||
|
||||
tasks.named("compileOpensaml5MainJava") {
|
||||
options.nullability.checking = "main"
|
||||
}
|
||||
|
||||
tasks.register("opensaml5Test", Test) {
|
||||
useJUnitPlatform()
|
||||
testClassesDirs = sourceSets.opensaml5Test.output.classesDirs
|
||||
|
||||
+2
-2
@@ -118,7 +118,7 @@ class BaseOpenSamlAssertingPartyMetadataRepository implements AssertingPartyMeta
|
||||
return OpenSamlAssertingPartyDetails.withEntityDescriptor(descriptor).build();
|
||||
}
|
||||
|
||||
private EntityDescriptor resolveSingle(EntityIdCriterion criterion) {
|
||||
private @Nullable EntityDescriptor resolveSingle(EntityIdCriterion criterion) {
|
||||
try {
|
||||
return this.metadataResolver.resolveSingle(criterion);
|
||||
}
|
||||
@@ -147,7 +147,7 @@ class BaseOpenSamlAssertingPartyMetadataRepository implements AssertingPartyMeta
|
||||
this.metadataResolver = metadataResolver;
|
||||
}
|
||||
|
||||
abstract EntityDescriptor resolveSingle(EntityIdCriterion entityId) throws Exception;
|
||||
abstract @Nullable EntityDescriptor resolveSingle(EntityIdCriterion entityId) throws Exception;
|
||||
|
||||
abstract Iterable<EntityDescriptor> resolve(EntityRoleCriterion role) throws Exception;
|
||||
|
||||
|
||||
+26
-7
@@ -34,9 +34,12 @@ import java.util.Set;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import net.shibboleth.shared.resolver.CriteriaSet;
|
||||
import net.shibboleth.shared.xml.ParserPool;
|
||||
import net.shibboleth.shared.xml.SerializeSupport;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.core.criterion.EntityIdCriterion;
|
||||
import org.opensaml.core.xml.XMLObject;
|
||||
import org.opensaml.core.xml.XMLObjectBuilder;
|
||||
@@ -117,6 +120,7 @@ import org.springframework.web.util.UriUtils;
|
||||
/**
|
||||
* For internal use only. Subject to breaking changes at any time.
|
||||
*/
|
||||
@NullMarked
|
||||
final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OpenSaml5Template.class);
|
||||
@@ -138,7 +142,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public <T extends XMLObject> T deserialize(InputStream serialized) {
|
||||
try {
|
||||
Document document = XMLObjectProviderRegistrySupport.getParserPool().parse(serialized);
|
||||
ParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();
|
||||
Assert.notNull(pool, "ParserPool must be configured");
|
||||
Document document = pool.parse(serialized);
|
||||
Element element = document.getDocumentElement();
|
||||
UnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();
|
||||
Unmarshaller unmarshaller = factory.getUnmarshaller(element);
|
||||
@@ -158,6 +164,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public OpenSaml5SerializationConfigurer serialize(XMLObject object) {
|
||||
Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);
|
||||
Assert.notNull(marshaller, "Marshaller for " + object.getElementQName() + " must be configured");
|
||||
try {
|
||||
return serialize(marshaller.marshall(object));
|
||||
}
|
||||
@@ -252,7 +259,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
SignatureSigningParameters parameters = resolveSigningParameters();
|
||||
this.components.putAll(params);
|
||||
Credential credential = parameters.getSigningCredential();
|
||||
Assert.notNull(credential, "credential cannot be null when signing a SAML payload");
|
||||
String algorithmUri = parameters.getSignatureAlgorithm();
|
||||
Assert.notNull(algorithmUri, "algorithmUri cannot be null when signing a SAML payload");
|
||||
this.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||
for (Map.Entry<String, String> component : this.components.entrySet()) {
|
||||
@@ -328,14 +337,14 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private final Collection<Saml2X509Credential> credentials;
|
||||
|
||||
private String entityId;
|
||||
private @Nullable String entityId;
|
||||
|
||||
OpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerificationConfigurer entityId(String entityId) {
|
||||
public VerificationConfigurer entityId(@Nullable String entityId) {
|
||||
this.entityId = entityId;
|
||||
return this;
|
||||
}
|
||||
@@ -354,6 +363,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
}
|
||||
|
||||
private CriteriaSet verificationCriteria(Issuer issuer) {
|
||||
Assert.notNull(issuer.getValue(), "required elements must have a value");
|
||||
return new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),
|
||||
new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),
|
||||
new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));
|
||||
@@ -362,12 +372,21 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public Collection<Saml2Error> verify(SignableXMLObject signable) {
|
||||
if (signable instanceof StatusResponseType response) {
|
||||
Assert.notNull(response.getID(), "Response#ID cannot be null");
|
||||
Assert.notNull(response.getIssuer(), "Response#Issuer cannot be null");
|
||||
Assert.notNull(response.getSignature(), "Response#Signature cannot be null");
|
||||
return verifySignature(response.getID(), response.getIssuer(), response.getSignature());
|
||||
}
|
||||
if (signable instanceof RequestAbstractType request) {
|
||||
Assert.notNull(request.getID(), "Request#ID cannot be null");
|
||||
Assert.notNull(request.getIssuer(), "Request#Issuer cannot be null");
|
||||
Assert.notNull(request.getSignature(), "Request#Signature cannot be null");
|
||||
return verifySignature(request.getID(), request.getIssuer(), request.getSignature());
|
||||
}
|
||||
if (signable instanceof Assertion assertion) {
|
||||
Assert.notNull(assertion.getID(), "Assertion#ID cannot be null");
|
||||
Assert.notNull(assertion.getIssuer(), "Assertion#Issuer cannot be null");
|
||||
Assert.notNull(assertion.getSignature(), "Assertion#Signature cannot be null");
|
||||
return verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());
|
||||
}
|
||||
throw new Saml2Exception("Unsupported object of type: " + signable.getClass().getName());
|
||||
@@ -408,15 +427,15 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature algorithm for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
if (!parameters.hasSignature()) {
|
||||
byte[] signature = parameters.getSignature();
|
||||
if (signature == null) {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
Collection<Saml2Error> errors = new ArrayList<>();
|
||||
String algorithmUri = parameters.getAlgorithm();
|
||||
try {
|
||||
if (!trustEngine.validate(parameters.getSignature(), parameters.getContent(), algorithmUri, criteria,
|
||||
null)) {
|
||||
if (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {
|
||||
errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Invalid signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
@@ -558,7 +577,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
statement.getAttributes().addAll(decrypteds);
|
||||
}
|
||||
|
||||
private void decryptSubject(Subject subject) {
|
||||
private void decryptSubject(@Nullable Subject subject) {
|
||||
if (subject != null) {
|
||||
if (subject.getEncryptedID() != null) {
|
||||
try {
|
||||
|
||||
+26
-20
@@ -24,10 +24,13 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.saml.common.assertion.AssertionValidationException;
|
||||
import org.opensaml.saml.common.assertion.ValidationContext;
|
||||
import org.opensaml.saml.common.assertion.ValidationResult;
|
||||
@@ -43,8 +46,11 @@ import org.opensaml.saml.saml2.assertion.impl.ProxyRestrictionConditionValidator
|
||||
import org.opensaml.saml.saml2.core.Assertion;
|
||||
import org.opensaml.saml.saml2.core.Condition;
|
||||
import org.opensaml.saml.saml2.core.EncryptedAssertion;
|
||||
import org.opensaml.saml.saml2.core.Issuer;
|
||||
import org.opensaml.saml.saml2.core.NameID;
|
||||
import org.opensaml.saml.saml2.core.OneTimeUse;
|
||||
import org.opensaml.saml.saml2.core.Response;
|
||||
import org.opensaml.saml.saml2.core.Subject;
|
||||
import org.opensaml.saml.saml2.core.SubjectConfirmation;
|
||||
import org.opensaml.saml.saml2.core.SubjectConfirmationData;
|
||||
import org.opensaml.saml.saml2.encryption.Decrypter;
|
||||
@@ -52,8 +58,6 @@ import org.opensaml.xmlsec.signature.support.SignaturePrevalidator;
|
||||
import org.opensaml.xmlsec.signature.support.SignatureTrustEngine;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -111,6 +115,7 @@ import org.springframework.util.StringUtils;
|
||||
* StatusResponse</a>
|
||||
* @see <a href="https://shibboleth.atlassian.net/wiki/spaces/OSAML/overview">OpenSAML</a>
|
||||
*/
|
||||
@NullMarked
|
||||
public final class OpenSaml5AuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final String AUTHORITY = FactorGrantedAuthority.SAML_RESPONSE_AUTHORITY;
|
||||
@@ -367,11 +372,12 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv
|
||||
}
|
||||
catch (Exception ex) {
|
||||
String message = String.format("Invalid assertion [%s] for SAML response [%s]: %s", assertion.getID(),
|
||||
((Response) assertion.getParent()).getID(), ex.getMessage());
|
||||
((Response) Objects.requireNonNull(assertion.getParent())).getID(), ex.getMessage());
|
||||
return Saml2ResponseValidatorResult.failure(new Saml2Error(Saml2ErrorCodes.INVALID_ASSERTION, message));
|
||||
}
|
||||
String message = String.format("Invalid assertion [%s] for SAML response [%s]: %s", assertion.getID(),
|
||||
((Response) assertion.getParent()).getID(), context.getValidationFailureMessages());
|
||||
((Response) Objects.requireNonNull(assertion.getParent())).getID(),
|
||||
context.getValidationFailureMessages());
|
||||
return Saml2ResponseValidatorResult.failure(new Saml2Error(Saml2ErrorCodes.INVALID_ASSERTION, message));
|
||||
};
|
||||
}
|
||||
@@ -491,7 +497,6 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv
|
||||
public static final class InResponseToValidator implements Converter<ResponseToken, Saml2ResponseValidatorResult> {
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Saml2ResponseValidatorResult convert(ResponseToken responseToken) {
|
||||
AbstractSaml2AuthenticationRequest request = responseToken.getToken().getAuthenticationRequest();
|
||||
Response response = responseToken.getResponse();
|
||||
@@ -510,7 +515,6 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv
|
||||
public static final class DestinationValidator implements Converter<ResponseToken, Saml2ResponseValidatorResult> {
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Saml2ResponseValidatorResult convert(ResponseToken responseToken) {
|
||||
Response response = responseToken.getResponse();
|
||||
Saml2AuthenticationToken token = responseToken.getToken();
|
||||
@@ -536,15 +540,15 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv
|
||||
public static final class IssuerValidator implements Converter<ResponseToken, Saml2ResponseValidatorResult> {
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Saml2ResponseValidatorResult convert(ResponseToken responseToken) {
|
||||
Response response = responseToken.getResponse();
|
||||
Saml2AuthenticationToken token = responseToken.getToken();
|
||||
String issuer = response.getIssuer().getValue();
|
||||
Issuer issuer = response.getIssuer();
|
||||
Assert.notNull(issuer, "Response#Issuer cannot be null");
|
||||
String assertingPartyEntityId = token.getRelyingPartyRegistration()
|
||||
.getAssertingPartyMetadata()
|
||||
.getEntityId();
|
||||
if (!StringUtils.hasText(issuer) || !issuer.equals(assertingPartyEntityId)) {
|
||||
if (!StringUtils.hasText(issuer.getValue()) || !assertingPartyEntityId.equals(issuer.getValue())) {
|
||||
String message = String.format("Invalid issuer [%s] for SAML response [%s]", issuer, response.getID());
|
||||
return Saml2ResponseValidatorResult.failure(new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, message));
|
||||
}
|
||||
@@ -642,11 +646,12 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv
|
||||
}
|
||||
catch (Exception ex) {
|
||||
String message = String.format("Invalid assertion [%s] for SAML response [%s]: %s", assertion.getID(),
|
||||
((Response) assertion.getParent()).getID(), ex.getMessage());
|
||||
((Response) Objects.requireNonNull(assertion.getParent())).getID(), ex.getMessage());
|
||||
return Saml2ResponseValidatorResult.failure(new Saml2Error(Saml2ErrorCodes.INVALID_ASSERTION, message));
|
||||
}
|
||||
String message = String.format("Invalid assertion [%s] for SAML response [%s]: %s", assertion.getID(),
|
||||
((Response) assertion.getParent()).getID(), validationContext.getValidationFailureMessages());
|
||||
((Response) Objects.requireNonNull(assertion.getParent())).getID(),
|
||||
validationContext.getValidationFailureMessages());
|
||||
return Saml2ResponseValidatorResult.failure(new Saml2Error(Saml2ErrorCodes.INVALID_ASSERTION, message));
|
||||
}
|
||||
|
||||
@@ -704,7 +709,7 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String getAuthnRequestId(AbstractSaml2AuthenticationRequest serialized) {
|
||||
private static @Nullable String getAuthnRequestId(@Nullable AbstractSaml2AuthenticationRequest serialized) {
|
||||
return (serialized != null) ? serialized.getId() : null;
|
||||
}
|
||||
|
||||
@@ -835,16 +840,13 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public QName getServicedCondition() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ValidationResult validate(@NonNull Condition condition, @NonNull Assertion assertion,
|
||||
@NonNull ValidationContext context) {
|
||||
public ValidationResult validate(Condition condition, Assertion assertion, ValidationContext context) {
|
||||
return ValidationResult.VALID;
|
||||
}
|
||||
|
||||
@@ -855,16 +857,15 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv
|
||||
private ValidSignatureAssertionValidator(@Nullable Collection<ConditionValidator> newConditionValidators,
|
||||
@Nullable Collection<SubjectConfirmationValidator> newConfirmationValidators,
|
||||
@Nullable Collection<StatementValidator> newStatementValidators,
|
||||
@Nullable org.opensaml.saml.saml2.assertion.AssertionValidator newAssertionValidator,
|
||||
org.opensaml.saml.saml2.assertion.@Nullable AssertionValidator newAssertionValidator,
|
||||
@Nullable SignatureTrustEngine newTrustEngine,
|
||||
@Nullable SignaturePrevalidator newSignaturePrevalidator) {
|
||||
super(newConditionValidators, newConfirmationValidators, newStatementValidators, newAssertionValidator,
|
||||
newTrustEngine, newSignaturePrevalidator);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ValidationResult validateSignature(@NonNull Assertion token, @NonNull ValidationContext context)
|
||||
protected ValidationResult validateSignature(Assertion token, ValidationContext context)
|
||||
throws AssertionValidationException {
|
||||
return ValidationResult.VALID;
|
||||
}
|
||||
@@ -895,6 +896,7 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv
|
||||
Response response = responseToken.response;
|
||||
Saml2AuthenticationToken token = responseToken.token;
|
||||
Assertion assertion = CollectionUtils.firstElement(response.getAssertions());
|
||||
Assert.notNull(assertion, "samlResponse must have at least one assertion");
|
||||
String username = this.principalNameConverter.convert(assertion);
|
||||
String registrationId = responseToken.token.getRelyingPartyRegistration().getRegistrationId();
|
||||
Saml2ResponseAssertionAccessor accessor = Saml2ResponseAssertion.withResponseValue(token.getSaml2Response())
|
||||
@@ -944,7 +946,11 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv
|
||||
throw new Saml2AuthenticationException(
|
||||
Saml2Error.subjectNotFound("Assertion [" + assertion.getID() + "] is missing a subject"));
|
||||
}
|
||||
return assertion.getSubject().getNameID().getValue();
|
||||
Subject subject = assertion.getSubject();
|
||||
Assert.notNull(subject, "Assertion#Subject cannot be null");
|
||||
NameID nameId = subject.getNameID();
|
||||
Assert.notNull(nameId, "Assertion#Subject#NameID cannot be null");
|
||||
return Objects.requireNonNull(nameId.getValue());
|
||||
}
|
||||
|
||||
private static Collection<GrantedAuthority> grantedAuthorities(Assertion assertion) {
|
||||
|
||||
+26
-7
@@ -34,9 +34,12 @@ import java.util.Set;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import net.shibboleth.shared.resolver.CriteriaSet;
|
||||
import net.shibboleth.shared.xml.ParserPool;
|
||||
import net.shibboleth.shared.xml.SerializeSupport;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.core.criterion.EntityIdCriterion;
|
||||
import org.opensaml.core.xml.XMLObject;
|
||||
import org.opensaml.core.xml.XMLObjectBuilder;
|
||||
@@ -117,6 +120,7 @@ import org.springframework.web.util.UriUtils;
|
||||
/**
|
||||
* For internal use only. Subject to breaking changes at any time.
|
||||
*/
|
||||
@NullMarked
|
||||
final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OpenSaml5Template.class);
|
||||
@@ -138,7 +142,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public <T extends XMLObject> T deserialize(InputStream serialized) {
|
||||
try {
|
||||
Document document = XMLObjectProviderRegistrySupport.getParserPool().parse(serialized);
|
||||
ParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();
|
||||
Assert.notNull(pool, "ParserPool must be configured");
|
||||
Document document = pool.parse(serialized);
|
||||
Element element = document.getDocumentElement();
|
||||
UnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();
|
||||
Unmarshaller unmarshaller = factory.getUnmarshaller(element);
|
||||
@@ -158,6 +164,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public OpenSaml5SerializationConfigurer serialize(XMLObject object) {
|
||||
Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);
|
||||
Assert.notNull(marshaller, "Marshaller for " + object.getElementQName() + " must be configured");
|
||||
try {
|
||||
return serialize(marshaller.marshall(object));
|
||||
}
|
||||
@@ -252,7 +259,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
SignatureSigningParameters parameters = resolveSigningParameters();
|
||||
this.components.putAll(params);
|
||||
Credential credential = parameters.getSigningCredential();
|
||||
Assert.notNull(credential, "credential cannot be null when signing a SAML payload");
|
||||
String algorithmUri = parameters.getSignatureAlgorithm();
|
||||
Assert.notNull(algorithmUri, "algorithmUri cannot be null when signing a SAML payload");
|
||||
this.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||
for (Map.Entry<String, String> component : this.components.entrySet()) {
|
||||
@@ -328,14 +337,14 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private final Collection<Saml2X509Credential> credentials;
|
||||
|
||||
private String entityId;
|
||||
private @Nullable String entityId;
|
||||
|
||||
OpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerificationConfigurer entityId(String entityId) {
|
||||
public VerificationConfigurer entityId(@Nullable String entityId) {
|
||||
this.entityId = entityId;
|
||||
return this;
|
||||
}
|
||||
@@ -354,6 +363,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
}
|
||||
|
||||
private CriteriaSet verificationCriteria(Issuer issuer) {
|
||||
Assert.notNull(issuer.getValue(), "required elements must have a value");
|
||||
return new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),
|
||||
new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),
|
||||
new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));
|
||||
@@ -362,12 +372,21 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public Collection<Saml2Error> verify(SignableXMLObject signable) {
|
||||
if (signable instanceof StatusResponseType response) {
|
||||
Assert.notNull(response.getID(), "Response#ID cannot be null");
|
||||
Assert.notNull(response.getIssuer(), "Response#Issuer cannot be null");
|
||||
Assert.notNull(response.getSignature(), "Response#Signature cannot be null");
|
||||
return verifySignature(response.getID(), response.getIssuer(), response.getSignature());
|
||||
}
|
||||
if (signable instanceof RequestAbstractType request) {
|
||||
Assert.notNull(request.getID(), "Request#ID cannot be null");
|
||||
Assert.notNull(request.getIssuer(), "Request#Issuer cannot be null");
|
||||
Assert.notNull(request.getSignature(), "Request#Signature cannot be null");
|
||||
return verifySignature(request.getID(), request.getIssuer(), request.getSignature());
|
||||
}
|
||||
if (signable instanceof Assertion assertion) {
|
||||
Assert.notNull(assertion.getID(), "Assertion#ID cannot be null");
|
||||
Assert.notNull(assertion.getIssuer(), "Assertion#Issuer cannot be null");
|
||||
Assert.notNull(assertion.getSignature(), "Assertion#Signature cannot be null");
|
||||
return verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());
|
||||
}
|
||||
throw new Saml2Exception("Unsupported object of type: " + signable.getClass().getName());
|
||||
@@ -408,15 +427,15 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature algorithm for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
if (!parameters.hasSignature()) {
|
||||
byte[] signature = parameters.getSignature();
|
||||
if (signature == null) {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
Collection<Saml2Error> errors = new ArrayList<>();
|
||||
String algorithmUri = parameters.getAlgorithm();
|
||||
try {
|
||||
if (!trustEngine.validate(parameters.getSignature(), parameters.getContent(), algorithmUri, criteria,
|
||||
null)) {
|
||||
if (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {
|
||||
errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Invalid signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
@@ -558,7 +577,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
statement.getAttributes().addAll(decrypteds);
|
||||
}
|
||||
|
||||
private void decryptSubject(Subject subject) {
|
||||
private void decryptSubject(@Nullable Subject subject) {
|
||||
if (subject != null) {
|
||||
if (subject.getEncryptedID() != null) {
|
||||
try {
|
||||
|
||||
+3
@@ -16,12 +16,15 @@
|
||||
|
||||
package org.springframework.security.saml2.provider.service.authentication.logout;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* An OpenSAML 5.x compatible implementation of {@link Saml2LogoutResponseValidator}
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 5.6
|
||||
*/
|
||||
@NullMarked
|
||||
public final class OpenSaml5LogoutRequestValidator implements Saml2LogoutRequestValidator {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
||||
+3
@@ -16,12 +16,15 @@
|
||||
|
||||
package org.springframework.security.saml2.provider.service.authentication.logout;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* An OpenSAML 5.x compatible implementation of {@link Saml2LogoutResponseValidator}
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 5.6
|
||||
*/
|
||||
@NullMarked
|
||||
public final class OpenSaml5LogoutResponseValidator implements Saml2LogoutResponseValidator {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
||||
+26
-7
@@ -34,9 +34,12 @@ import java.util.Set;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import net.shibboleth.shared.resolver.CriteriaSet;
|
||||
import net.shibboleth.shared.xml.ParserPool;
|
||||
import net.shibboleth.shared.xml.SerializeSupport;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.core.criterion.EntityIdCriterion;
|
||||
import org.opensaml.core.xml.XMLObject;
|
||||
import org.opensaml.core.xml.XMLObjectBuilder;
|
||||
@@ -117,6 +120,7 @@ import org.springframework.web.util.UriUtils;
|
||||
/**
|
||||
* For internal use only. Subject to breaking changes at any time.
|
||||
*/
|
||||
@NullMarked
|
||||
final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OpenSaml5Template.class);
|
||||
@@ -138,7 +142,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public <T extends XMLObject> T deserialize(InputStream serialized) {
|
||||
try {
|
||||
Document document = XMLObjectProviderRegistrySupport.getParserPool().parse(serialized);
|
||||
ParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();
|
||||
Assert.notNull(pool, "ParserPool must be configured");
|
||||
Document document = pool.parse(serialized);
|
||||
Element element = document.getDocumentElement();
|
||||
UnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();
|
||||
Unmarshaller unmarshaller = factory.getUnmarshaller(element);
|
||||
@@ -158,6 +164,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public OpenSaml5SerializationConfigurer serialize(XMLObject object) {
|
||||
Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);
|
||||
Assert.notNull(marshaller, "Marshaller for " + object.getElementQName() + " must be configured");
|
||||
try {
|
||||
return serialize(marshaller.marshall(object));
|
||||
}
|
||||
@@ -252,7 +259,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
SignatureSigningParameters parameters = resolveSigningParameters();
|
||||
this.components.putAll(params);
|
||||
Credential credential = parameters.getSigningCredential();
|
||||
Assert.notNull(credential, "credential cannot be null when signing a SAML payload");
|
||||
String algorithmUri = parameters.getSignatureAlgorithm();
|
||||
Assert.notNull(algorithmUri, "algorithmUri cannot be null when signing a SAML payload");
|
||||
this.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||
for (Map.Entry<String, String> component : this.components.entrySet()) {
|
||||
@@ -328,14 +337,14 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private final Collection<Saml2X509Credential> credentials;
|
||||
|
||||
private String entityId;
|
||||
private @Nullable String entityId;
|
||||
|
||||
OpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerificationConfigurer entityId(String entityId) {
|
||||
public VerificationConfigurer entityId(@Nullable String entityId) {
|
||||
this.entityId = entityId;
|
||||
return this;
|
||||
}
|
||||
@@ -354,6 +363,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
}
|
||||
|
||||
private CriteriaSet verificationCriteria(Issuer issuer) {
|
||||
Assert.notNull(issuer.getValue(), "required elements must have a value");
|
||||
return new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),
|
||||
new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),
|
||||
new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));
|
||||
@@ -362,12 +372,21 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public Collection<Saml2Error> verify(SignableXMLObject signable) {
|
||||
if (signable instanceof StatusResponseType response) {
|
||||
Assert.notNull(response.getID(), "Response#ID cannot be null");
|
||||
Assert.notNull(response.getIssuer(), "Response#Issuer cannot be null");
|
||||
Assert.notNull(response.getSignature(), "Response#Signature cannot be null");
|
||||
return verifySignature(response.getID(), response.getIssuer(), response.getSignature());
|
||||
}
|
||||
if (signable instanceof RequestAbstractType request) {
|
||||
Assert.notNull(request.getID(), "Request#ID cannot be null");
|
||||
Assert.notNull(request.getIssuer(), "Request#Issuer cannot be null");
|
||||
Assert.notNull(request.getSignature(), "Request#Signature cannot be null");
|
||||
return verifySignature(request.getID(), request.getIssuer(), request.getSignature());
|
||||
}
|
||||
if (signable instanceof Assertion assertion) {
|
||||
Assert.notNull(assertion.getID(), "Assertion#ID cannot be null");
|
||||
Assert.notNull(assertion.getIssuer(), "Assertion#Issuer cannot be null");
|
||||
Assert.notNull(assertion.getSignature(), "Assertion#Signature cannot be null");
|
||||
return verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());
|
||||
}
|
||||
throw new Saml2Exception("Unsupported object of type: " + signable.getClass().getName());
|
||||
@@ -408,15 +427,15 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature algorithm for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
if (!parameters.hasSignature()) {
|
||||
byte[] signature = parameters.getSignature();
|
||||
if (signature == null) {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
Collection<Saml2Error> errors = new ArrayList<>();
|
||||
String algorithmUri = parameters.getAlgorithm();
|
||||
try {
|
||||
if (!trustEngine.validate(parameters.getSignature(), parameters.getContent(), algorithmUri, criteria,
|
||||
null)) {
|
||||
if (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {
|
||||
errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Invalid signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
@@ -558,7 +577,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
statement.getAttributes().addAll(decrypteds);
|
||||
}
|
||||
|
||||
private void decryptSubject(Subject subject) {
|
||||
private void decryptSubject(@Nullable Subject subject) {
|
||||
if (subject != null) {
|
||||
if (subject.getEncryptedID() != null) {
|
||||
try {
|
||||
|
||||
+3
@@ -26,6 +26,8 @@ import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterOutputStream;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import org.springframework.security.saml2.Saml2Exception;
|
||||
|
||||
/**
|
||||
@@ -35,6 +37,7 @@ import org.springframework.security.saml2.Saml2Exception;
|
||||
*
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
@NullMarked
|
||||
final class Saml2Utils {
|
||||
|
||||
private Saml2Utils() {
|
||||
|
||||
+2
@@ -18,6 +18,7 @@ package org.springframework.security.saml2.provider.service.metadata;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.opensaml.saml.saml2.metadata.EntityDescriptor;
|
||||
|
||||
import org.springframework.security.saml2.core.OpenSamlInitializationService;
|
||||
@@ -31,6 +32,7 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
|
||||
* @author Josh Cummings
|
||||
* @since 5.4
|
||||
*/
|
||||
@NullMarked
|
||||
public final class OpenSaml5MetadataResolver implements Saml2MetadataResolver {
|
||||
|
||||
static {
|
||||
|
||||
+26
-7
@@ -34,9 +34,12 @@ import java.util.Set;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import net.shibboleth.shared.resolver.CriteriaSet;
|
||||
import net.shibboleth.shared.xml.ParserPool;
|
||||
import net.shibboleth.shared.xml.SerializeSupport;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.core.criterion.EntityIdCriterion;
|
||||
import org.opensaml.core.xml.XMLObject;
|
||||
import org.opensaml.core.xml.XMLObjectBuilder;
|
||||
@@ -117,6 +120,7 @@ import org.springframework.web.util.UriUtils;
|
||||
/**
|
||||
* For internal use only. Subject to breaking changes at any time.
|
||||
*/
|
||||
@NullMarked
|
||||
final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OpenSaml5Template.class);
|
||||
@@ -138,7 +142,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public <T extends XMLObject> T deserialize(InputStream serialized) {
|
||||
try {
|
||||
Document document = XMLObjectProviderRegistrySupport.getParserPool().parse(serialized);
|
||||
ParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();
|
||||
Assert.notNull(pool, "ParserPool must be configured");
|
||||
Document document = pool.parse(serialized);
|
||||
Element element = document.getDocumentElement();
|
||||
UnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();
|
||||
Unmarshaller unmarshaller = factory.getUnmarshaller(element);
|
||||
@@ -158,6 +164,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public OpenSaml5SerializationConfigurer serialize(XMLObject object) {
|
||||
Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);
|
||||
Assert.notNull(marshaller, "Marshaller for " + object.getElementQName() + " must be configured");
|
||||
try {
|
||||
return serialize(marshaller.marshall(object));
|
||||
}
|
||||
@@ -252,7 +259,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
SignatureSigningParameters parameters = resolveSigningParameters();
|
||||
this.components.putAll(params);
|
||||
Credential credential = parameters.getSigningCredential();
|
||||
Assert.notNull(credential, "credential cannot be null when signing a SAML payload");
|
||||
String algorithmUri = parameters.getSignatureAlgorithm();
|
||||
Assert.notNull(algorithmUri, "algorithmUri cannot be null when signing a SAML payload");
|
||||
this.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||
for (Map.Entry<String, String> component : this.components.entrySet()) {
|
||||
@@ -328,14 +337,14 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private final Collection<Saml2X509Credential> credentials;
|
||||
|
||||
private String entityId;
|
||||
private @Nullable String entityId;
|
||||
|
||||
OpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerificationConfigurer entityId(String entityId) {
|
||||
public VerificationConfigurer entityId(@Nullable String entityId) {
|
||||
this.entityId = entityId;
|
||||
return this;
|
||||
}
|
||||
@@ -354,6 +363,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
}
|
||||
|
||||
private CriteriaSet verificationCriteria(Issuer issuer) {
|
||||
Assert.notNull(issuer.getValue(), "required elements must have a value");
|
||||
return new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),
|
||||
new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),
|
||||
new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));
|
||||
@@ -362,12 +372,21 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public Collection<Saml2Error> verify(SignableXMLObject signable) {
|
||||
if (signable instanceof StatusResponseType response) {
|
||||
Assert.notNull(response.getID(), "Response#ID cannot be null");
|
||||
Assert.notNull(response.getIssuer(), "Response#Issuer cannot be null");
|
||||
Assert.notNull(response.getSignature(), "Response#Signature cannot be null");
|
||||
return verifySignature(response.getID(), response.getIssuer(), response.getSignature());
|
||||
}
|
||||
if (signable instanceof RequestAbstractType request) {
|
||||
Assert.notNull(request.getID(), "Request#ID cannot be null");
|
||||
Assert.notNull(request.getIssuer(), "Request#Issuer cannot be null");
|
||||
Assert.notNull(request.getSignature(), "Request#Signature cannot be null");
|
||||
return verifySignature(request.getID(), request.getIssuer(), request.getSignature());
|
||||
}
|
||||
if (signable instanceof Assertion assertion) {
|
||||
Assert.notNull(assertion.getID(), "Assertion#ID cannot be null");
|
||||
Assert.notNull(assertion.getIssuer(), "Assertion#Issuer cannot be null");
|
||||
Assert.notNull(assertion.getSignature(), "Assertion#Signature cannot be null");
|
||||
return verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());
|
||||
}
|
||||
throw new Saml2Exception("Unsupported object of type: " + signable.getClass().getName());
|
||||
@@ -408,15 +427,15 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature algorithm for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
if (!parameters.hasSignature()) {
|
||||
byte[] signature = parameters.getSignature();
|
||||
if (signature == null) {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
Collection<Saml2Error> errors = new ArrayList<>();
|
||||
String algorithmUri = parameters.getAlgorithm();
|
||||
try {
|
||||
if (!trustEngine.validate(parameters.getSignature(), parameters.getContent(), algorithmUri, criteria,
|
||||
null)) {
|
||||
if (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {
|
||||
errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Invalid signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
@@ -558,7 +577,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
statement.getAttributes().addAll(decrypteds);
|
||||
}
|
||||
|
||||
private void decryptSubject(Subject subject) {
|
||||
private void decryptSubject(@Nullable Subject subject) {
|
||||
if (subject != null) {
|
||||
if (subject.getEncryptedID() != null) {
|
||||
try {
|
||||
|
||||
+10
-9
@@ -27,6 +27,9 @@ import java.util.Iterator;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.shibboleth.shared.resolver.CriteriaSet;
|
||||
import net.shibboleth.shared.xml.ParserPool;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.core.criterion.EntityIdCriterion;
|
||||
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
|
||||
import org.opensaml.saml.criterion.EntityRoleCriterion;
|
||||
@@ -45,8 +48,6 @@ import org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngin
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.saml2.Saml2Exception;
|
||||
import org.springframework.security.saml2.core.OpenSamlInitializationService;
|
||||
import org.springframework.security.saml2.provider.service.registration.BaseOpenSamlAssertingPartyMetadataRepository.MetadataResolverAdapter;
|
||||
@@ -65,6 +66,7 @@ import org.springframework.util.Assert;
|
||||
* @see AssertingPartyMetadataRepository
|
||||
* @see RelyingPartyRegistrations
|
||||
*/
|
||||
@NullMarked
|
||||
public final class OpenSaml5AssertingPartyMetadataRepository implements AssertingPartyMetadataRepository {
|
||||
|
||||
static {
|
||||
@@ -93,7 +95,6 @@ public final class OpenSaml5AssertingPartyMetadataRepository implements Assertin
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@NonNull
|
||||
public Iterator<AssertingPartyMetadata> iterator() {
|
||||
return this.delegate.iterator();
|
||||
}
|
||||
@@ -101,9 +102,8 @@ public final class OpenSaml5AssertingPartyMetadataRepository implements Assertin
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public AssertingPartyMetadata findByEntityId(String entityId) {
|
||||
public @Nullable AssertingPartyMetadata findByEntityId(String entityId) {
|
||||
return this.delegate.findByEntityId(entityId);
|
||||
}
|
||||
|
||||
@@ -222,7 +222,9 @@ public final class OpenSaml5AssertingPartyMetadataRepository implements Assertin
|
||||
}
|
||||
|
||||
private MetadataResolver initialize(ResourceBackedMetadataResolver metadataResolver) {
|
||||
metadataResolver.setParserPool(XMLObjectProviderRegistrySupport.getParserPool());
|
||||
ParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();
|
||||
Assert.notNull(pool, "ParserPool must be configured");
|
||||
metadataResolver.setParserPool(pool);
|
||||
return BaseOpenSamlAssertingPartyMetadataRepository.initialize(metadataResolver);
|
||||
}
|
||||
|
||||
@@ -264,7 +266,6 @@ public final class OpenSaml5AssertingPartyMetadataRepository implements Assertin
|
||||
return this.resource.getFile();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return this.resource.getInputStream();
|
||||
@@ -287,7 +288,7 @@ public final class OpenSaml5AssertingPartyMetadataRepository implements Assertin
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
public @Nullable String getFilename() {
|
||||
return this.resource.getFilename();
|
||||
}
|
||||
|
||||
@@ -307,7 +308,7 @@ public final class OpenSaml5AssertingPartyMetadataRepository implements Assertin
|
||||
}
|
||||
|
||||
@Override
|
||||
EntityDescriptor resolveSingle(EntityIdCriterion entityId) throws Exception {
|
||||
@Nullable EntityDescriptor resolveSingle(EntityIdCriterion entityId) throws Exception {
|
||||
return super.metadataResolver.resolveSingle(new CriteriaSet(entityId));
|
||||
}
|
||||
|
||||
|
||||
+26
-7
@@ -34,9 +34,12 @@ import java.util.Set;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import net.shibboleth.shared.resolver.CriteriaSet;
|
||||
import net.shibboleth.shared.xml.ParserPool;
|
||||
import net.shibboleth.shared.xml.SerializeSupport;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.core.criterion.EntityIdCriterion;
|
||||
import org.opensaml.core.xml.XMLObject;
|
||||
import org.opensaml.core.xml.XMLObjectBuilder;
|
||||
@@ -117,6 +120,7 @@ import org.springframework.web.util.UriUtils;
|
||||
/**
|
||||
* For internal use only. Subject to breaking changes at any time.
|
||||
*/
|
||||
@NullMarked
|
||||
final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OpenSaml5Template.class);
|
||||
@@ -138,7 +142,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public <T extends XMLObject> T deserialize(InputStream serialized) {
|
||||
try {
|
||||
Document document = XMLObjectProviderRegistrySupport.getParserPool().parse(serialized);
|
||||
ParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();
|
||||
Assert.notNull(pool, "ParserPool must be configured");
|
||||
Document document = pool.parse(serialized);
|
||||
Element element = document.getDocumentElement();
|
||||
UnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();
|
||||
Unmarshaller unmarshaller = factory.getUnmarshaller(element);
|
||||
@@ -158,6 +164,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public OpenSaml5SerializationConfigurer serialize(XMLObject object) {
|
||||
Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);
|
||||
Assert.notNull(marshaller, "Marshaller for " + object.getElementQName() + " must be configured");
|
||||
try {
|
||||
return serialize(marshaller.marshall(object));
|
||||
}
|
||||
@@ -252,7 +259,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
SignatureSigningParameters parameters = resolveSigningParameters();
|
||||
this.components.putAll(params);
|
||||
Credential credential = parameters.getSigningCredential();
|
||||
Assert.notNull(credential, "credential cannot be null when signing a SAML payload");
|
||||
String algorithmUri = parameters.getSignatureAlgorithm();
|
||||
Assert.notNull(algorithmUri, "algorithmUri cannot be null when signing a SAML payload");
|
||||
this.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||
for (Map.Entry<String, String> component : this.components.entrySet()) {
|
||||
@@ -328,14 +337,14 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private final Collection<Saml2X509Credential> credentials;
|
||||
|
||||
private String entityId;
|
||||
private @Nullable String entityId;
|
||||
|
||||
OpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerificationConfigurer entityId(String entityId) {
|
||||
public VerificationConfigurer entityId(@Nullable String entityId) {
|
||||
this.entityId = entityId;
|
||||
return this;
|
||||
}
|
||||
@@ -354,6 +363,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
}
|
||||
|
||||
private CriteriaSet verificationCriteria(Issuer issuer) {
|
||||
Assert.notNull(issuer.getValue(), "required elements must have a value");
|
||||
return new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),
|
||||
new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),
|
||||
new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));
|
||||
@@ -362,12 +372,21 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public Collection<Saml2Error> verify(SignableXMLObject signable) {
|
||||
if (signable instanceof StatusResponseType response) {
|
||||
Assert.notNull(response.getID(), "Response#ID cannot be null");
|
||||
Assert.notNull(response.getIssuer(), "Response#Issuer cannot be null");
|
||||
Assert.notNull(response.getSignature(), "Response#Signature cannot be null");
|
||||
return verifySignature(response.getID(), response.getIssuer(), response.getSignature());
|
||||
}
|
||||
if (signable instanceof RequestAbstractType request) {
|
||||
Assert.notNull(request.getID(), "Request#ID cannot be null");
|
||||
Assert.notNull(request.getIssuer(), "Request#Issuer cannot be null");
|
||||
Assert.notNull(request.getSignature(), "Request#Signature cannot be null");
|
||||
return verifySignature(request.getID(), request.getIssuer(), request.getSignature());
|
||||
}
|
||||
if (signable instanceof Assertion assertion) {
|
||||
Assert.notNull(assertion.getID(), "Assertion#ID cannot be null");
|
||||
Assert.notNull(assertion.getIssuer(), "Assertion#Issuer cannot be null");
|
||||
Assert.notNull(assertion.getSignature(), "Assertion#Signature cannot be null");
|
||||
return verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());
|
||||
}
|
||||
throw new Saml2Exception("Unsupported object of type: " + signable.getClass().getName());
|
||||
@@ -408,15 +427,15 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature algorithm for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
if (!parameters.hasSignature()) {
|
||||
byte[] signature = parameters.getSignature();
|
||||
if (signature == null) {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
Collection<Saml2Error> errors = new ArrayList<>();
|
||||
String algorithmUri = parameters.getAlgorithm();
|
||||
try {
|
||||
if (!trustEngine.validate(parameters.getSignature(), parameters.getContent(), algorithmUri, criteria,
|
||||
null)) {
|
||||
if (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {
|
||||
errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Invalid signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
@@ -558,7 +577,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
statement.getAttributes().addAll(decrypteds);
|
||||
}
|
||||
|
||||
private void decryptSubject(Subject subject) {
|
||||
private void decryptSubject(@Nullable Subject subject) {
|
||||
if (subject != null) {
|
||||
if (subject.getEncryptedID() != null) {
|
||||
try {
|
||||
|
||||
+4
-1
@@ -17,6 +17,8 @@
|
||||
package org.springframework.security.saml2.provider.service.web;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||
@@ -35,6 +37,7 @@ import org.springframework.util.Assert;
|
||||
* @author Josh Cummings
|
||||
* @since 6.1
|
||||
*/
|
||||
@NullMarked
|
||||
public final class OpenSaml5AuthenticationTokenConverter implements AuthenticationConverter {
|
||||
|
||||
private final BaseOpenSamlAuthenticationTokenConverter delegate;
|
||||
@@ -76,7 +79,7 @@ public final class OpenSaml5AuthenticationTokenConverter implements Authenticati
|
||||
* non-existent {@code registrationId}
|
||||
*/
|
||||
@Override
|
||||
public Saml2AuthenticationToken convert(HttpServletRequest request) {
|
||||
public @Nullable Saml2AuthenticationToken convert(HttpServletRequest request) {
|
||||
return this.delegate.convert(request);
|
||||
}
|
||||
|
||||
|
||||
+26
-7
@@ -34,9 +34,12 @@ import java.util.Set;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import net.shibboleth.shared.resolver.CriteriaSet;
|
||||
import net.shibboleth.shared.xml.ParserPool;
|
||||
import net.shibboleth.shared.xml.SerializeSupport;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.core.criterion.EntityIdCriterion;
|
||||
import org.opensaml.core.xml.XMLObject;
|
||||
import org.opensaml.core.xml.XMLObjectBuilder;
|
||||
@@ -117,6 +120,7 @@ import org.springframework.web.util.UriUtils;
|
||||
/**
|
||||
* For internal use only. Subject to breaking changes at any time.
|
||||
*/
|
||||
@NullMarked
|
||||
final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OpenSaml5Template.class);
|
||||
@@ -138,7 +142,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public <T extends XMLObject> T deserialize(InputStream serialized) {
|
||||
try {
|
||||
Document document = XMLObjectProviderRegistrySupport.getParserPool().parse(serialized);
|
||||
ParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();
|
||||
Assert.notNull(pool, "ParserPool must be configured");
|
||||
Document document = pool.parse(serialized);
|
||||
Element element = document.getDocumentElement();
|
||||
UnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();
|
||||
Unmarshaller unmarshaller = factory.getUnmarshaller(element);
|
||||
@@ -158,6 +164,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public OpenSaml5SerializationConfigurer serialize(XMLObject object) {
|
||||
Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);
|
||||
Assert.notNull(marshaller, "Marshaller for " + object.getElementQName() + " must be configured");
|
||||
try {
|
||||
return serialize(marshaller.marshall(object));
|
||||
}
|
||||
@@ -252,7 +259,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
SignatureSigningParameters parameters = resolveSigningParameters();
|
||||
this.components.putAll(params);
|
||||
Credential credential = parameters.getSigningCredential();
|
||||
Assert.notNull(credential, "credential cannot be null when signing a SAML payload");
|
||||
String algorithmUri = parameters.getSignatureAlgorithm();
|
||||
Assert.notNull(algorithmUri, "algorithmUri cannot be null when signing a SAML payload");
|
||||
this.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||
for (Map.Entry<String, String> component : this.components.entrySet()) {
|
||||
@@ -328,14 +337,14 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private final Collection<Saml2X509Credential> credentials;
|
||||
|
||||
private String entityId;
|
||||
private @Nullable String entityId;
|
||||
|
||||
OpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerificationConfigurer entityId(String entityId) {
|
||||
public VerificationConfigurer entityId(@Nullable String entityId) {
|
||||
this.entityId = entityId;
|
||||
return this;
|
||||
}
|
||||
@@ -354,6 +363,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
}
|
||||
|
||||
private CriteriaSet verificationCriteria(Issuer issuer) {
|
||||
Assert.notNull(issuer.getValue(), "required elements must have a value");
|
||||
return new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),
|
||||
new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),
|
||||
new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));
|
||||
@@ -362,12 +372,21 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public Collection<Saml2Error> verify(SignableXMLObject signable) {
|
||||
if (signable instanceof StatusResponseType response) {
|
||||
Assert.notNull(response.getID(), "Response#ID cannot be null");
|
||||
Assert.notNull(response.getIssuer(), "Response#Issuer cannot be null");
|
||||
Assert.notNull(response.getSignature(), "Response#Signature cannot be null");
|
||||
return verifySignature(response.getID(), response.getIssuer(), response.getSignature());
|
||||
}
|
||||
if (signable instanceof RequestAbstractType request) {
|
||||
Assert.notNull(request.getID(), "Request#ID cannot be null");
|
||||
Assert.notNull(request.getIssuer(), "Request#Issuer cannot be null");
|
||||
Assert.notNull(request.getSignature(), "Request#Signature cannot be null");
|
||||
return verifySignature(request.getID(), request.getIssuer(), request.getSignature());
|
||||
}
|
||||
if (signable instanceof Assertion assertion) {
|
||||
Assert.notNull(assertion.getID(), "Assertion#ID cannot be null");
|
||||
Assert.notNull(assertion.getIssuer(), "Assertion#Issuer cannot be null");
|
||||
Assert.notNull(assertion.getSignature(), "Assertion#Signature cannot be null");
|
||||
return verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());
|
||||
}
|
||||
throw new Saml2Exception("Unsupported object of type: " + signable.getClass().getName());
|
||||
@@ -408,15 +427,15 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature algorithm for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
if (!parameters.hasSignature()) {
|
||||
byte[] signature = parameters.getSignature();
|
||||
if (signature == null) {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
Collection<Saml2Error> errors = new ArrayList<>();
|
||||
String algorithmUri = parameters.getAlgorithm();
|
||||
try {
|
||||
if (!trustEngine.validate(parameters.getSignature(), parameters.getContent(), algorithmUri, criteria,
|
||||
null)) {
|
||||
if (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {
|
||||
errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Invalid signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
@@ -558,7 +577,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
statement.getAttributes().addAll(decrypteds);
|
||||
}
|
||||
|
||||
private void decryptSubject(Subject subject) {
|
||||
private void decryptSubject(@Nullable Subject subject) {
|
||||
if (subject != null) {
|
||||
if (subject.getEncryptedID() != null) {
|
||||
try {
|
||||
|
||||
+5
-2
@@ -21,6 +21,8 @@ import java.time.Instant;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.saml.saml2.core.AuthnRequest;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
@@ -38,6 +40,7 @@ import org.springframework.util.Assert;
|
||||
* @author Josh Cummings
|
||||
* @since 5.7
|
||||
*/
|
||||
@NullMarked
|
||||
public final class OpenSaml5AuthenticationRequestResolver implements Saml2AuthenticationRequestResolver {
|
||||
|
||||
private final BaseOpenSamlAuthenticationRequestResolver delegate;
|
||||
@@ -65,7 +68,7 @@ public final class OpenSaml5AuthenticationRequestResolver implements Saml2Authen
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends AbstractSaml2AuthenticationRequest> T resolve(HttpServletRequest request) {
|
||||
public <T extends AbstractSaml2AuthenticationRequest> @Nullable T resolve(HttpServletRequest request) {
|
||||
return this.delegate.resolve(request);
|
||||
}
|
||||
|
||||
@@ -107,7 +110,7 @@ public final class OpenSaml5AuthenticationRequestResolver implements Saml2Authen
|
||||
* @param relayStateResolver the {@link Converter} to use
|
||||
* @since 5.8
|
||||
*/
|
||||
public void setRelayStateResolver(Converter<HttpServletRequest, String> relayStateResolver) {
|
||||
public void setRelayStateResolver(Converter<HttpServletRequest, @Nullable String> relayStateResolver) {
|
||||
Assert.notNull(relayStateResolver, "relayStateResolver cannot be null");
|
||||
this.delegate.setRelayStateResolver(relayStateResolver);
|
||||
}
|
||||
|
||||
+26
-7
@@ -34,9 +34,12 @@ import java.util.Set;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import net.shibboleth.shared.resolver.CriteriaSet;
|
||||
import net.shibboleth.shared.xml.ParserPool;
|
||||
import net.shibboleth.shared.xml.SerializeSupport;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.core.criterion.EntityIdCriterion;
|
||||
import org.opensaml.core.xml.XMLObject;
|
||||
import org.opensaml.core.xml.XMLObjectBuilder;
|
||||
@@ -117,6 +120,7 @@ import org.springframework.web.util.UriUtils;
|
||||
/**
|
||||
* For internal use only. Subject to breaking changes at any time.
|
||||
*/
|
||||
@NullMarked
|
||||
final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OpenSaml5Template.class);
|
||||
@@ -138,7 +142,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public <T extends XMLObject> T deserialize(InputStream serialized) {
|
||||
try {
|
||||
Document document = XMLObjectProviderRegistrySupport.getParserPool().parse(serialized);
|
||||
ParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();
|
||||
Assert.notNull(pool, "ParserPool must be configured");
|
||||
Document document = pool.parse(serialized);
|
||||
Element element = document.getDocumentElement();
|
||||
UnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();
|
||||
Unmarshaller unmarshaller = factory.getUnmarshaller(element);
|
||||
@@ -158,6 +164,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public OpenSaml5SerializationConfigurer serialize(XMLObject object) {
|
||||
Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);
|
||||
Assert.notNull(marshaller, "Marshaller for " + object.getElementQName() + " must be configured");
|
||||
try {
|
||||
return serialize(marshaller.marshall(object));
|
||||
}
|
||||
@@ -252,7 +259,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
SignatureSigningParameters parameters = resolveSigningParameters();
|
||||
this.components.putAll(params);
|
||||
Credential credential = parameters.getSigningCredential();
|
||||
Assert.notNull(credential, "credential cannot be null when signing a SAML payload");
|
||||
String algorithmUri = parameters.getSignatureAlgorithm();
|
||||
Assert.notNull(algorithmUri, "algorithmUri cannot be null when signing a SAML payload");
|
||||
this.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||
for (Map.Entry<String, String> component : this.components.entrySet()) {
|
||||
@@ -328,14 +337,14 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private final Collection<Saml2X509Credential> credentials;
|
||||
|
||||
private String entityId;
|
||||
private @Nullable String entityId;
|
||||
|
||||
OpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerificationConfigurer entityId(String entityId) {
|
||||
public VerificationConfigurer entityId(@Nullable String entityId) {
|
||||
this.entityId = entityId;
|
||||
return this;
|
||||
}
|
||||
@@ -354,6 +363,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
}
|
||||
|
||||
private CriteriaSet verificationCriteria(Issuer issuer) {
|
||||
Assert.notNull(issuer.getValue(), "required elements must have a value");
|
||||
return new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),
|
||||
new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),
|
||||
new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));
|
||||
@@ -362,12 +372,21 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public Collection<Saml2Error> verify(SignableXMLObject signable) {
|
||||
if (signable instanceof StatusResponseType response) {
|
||||
Assert.notNull(response.getID(), "Response#ID cannot be null");
|
||||
Assert.notNull(response.getIssuer(), "Response#Issuer cannot be null");
|
||||
Assert.notNull(response.getSignature(), "Response#Signature cannot be null");
|
||||
return verifySignature(response.getID(), response.getIssuer(), response.getSignature());
|
||||
}
|
||||
if (signable instanceof RequestAbstractType request) {
|
||||
Assert.notNull(request.getID(), "Request#ID cannot be null");
|
||||
Assert.notNull(request.getIssuer(), "Request#Issuer cannot be null");
|
||||
Assert.notNull(request.getSignature(), "Request#Signature cannot be null");
|
||||
return verifySignature(request.getID(), request.getIssuer(), request.getSignature());
|
||||
}
|
||||
if (signable instanceof Assertion assertion) {
|
||||
Assert.notNull(assertion.getID(), "Assertion#ID cannot be null");
|
||||
Assert.notNull(assertion.getIssuer(), "Assertion#Issuer cannot be null");
|
||||
Assert.notNull(assertion.getSignature(), "Assertion#Signature cannot be null");
|
||||
return verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());
|
||||
}
|
||||
throw new Saml2Exception("Unsupported object of type: " + signable.getClass().getName());
|
||||
@@ -408,15 +427,15 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature algorithm for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
if (!parameters.hasSignature()) {
|
||||
byte[] signature = parameters.getSignature();
|
||||
if (signature == null) {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
Collection<Saml2Error> errors = new ArrayList<>();
|
||||
String algorithmUri = parameters.getAlgorithm();
|
||||
try {
|
||||
if (!trustEngine.validate(parameters.getSignature(), parameters.getContent(), algorithmUri, criteria,
|
||||
null)) {
|
||||
if (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {
|
||||
errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Invalid signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
@@ -558,7 +577,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
statement.getAttributes().addAll(decrypteds);
|
||||
}
|
||||
|
||||
private void decryptSubject(Subject subject) {
|
||||
private void decryptSubject(@Nullable Subject subject) {
|
||||
if (subject != null) {
|
||||
if (subject.getEncryptedID() != null) {
|
||||
try {
|
||||
|
||||
+4
-1
@@ -21,6 +21,8 @@ import java.time.Instant;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.saml.saml2.core.LogoutRequest;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
@@ -39,6 +41,7 @@ import org.springframework.util.Assert;
|
||||
* @author Gerhard Haege
|
||||
* @since 5.6
|
||||
*/
|
||||
@NullMarked
|
||||
public final class OpenSaml5LogoutRequestResolver implements Saml2LogoutRequestResolver {
|
||||
|
||||
private final BaseOpenSamlLogoutRequestResolver delegate;
|
||||
@@ -64,7 +67,7 @@ public final class OpenSaml5LogoutRequestResolver implements Saml2LogoutRequestR
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Saml2LogoutRequest resolve(HttpServletRequest request, Authentication authentication) {
|
||||
public @Nullable Saml2LogoutRequest resolve(HttpServletRequest request, Authentication authentication) {
|
||||
return this.delegate.resolve(request, authentication);
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -17,6 +17,7 @@
|
||||
package org.springframework.security.saml2.provider.service.web.authentication.logout;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -32,6 +33,7 @@ import org.springframework.util.Assert;
|
||||
* An OpenSAML-based implementation of
|
||||
* {@link Saml2LogoutRequestValidatorParametersResolver}
|
||||
*/
|
||||
@NullMarked
|
||||
public final class OpenSaml5LogoutRequestValidatorParametersResolver
|
||||
implements Saml2LogoutRequestValidatorParametersResolver {
|
||||
|
||||
|
||||
+7
-5
@@ -21,6 +21,7 @@ import java.time.Instant;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.saml.saml2.core.LogoutRequest;
|
||||
|
||||
@@ -39,6 +40,7 @@ import org.springframework.util.Assert;
|
||||
* @author Josh Cummings
|
||||
* @since 5.6
|
||||
*/
|
||||
@NullMarked
|
||||
public final class OpenSaml5LogoutResponseResolver implements Saml2LogoutResponseResolver {
|
||||
|
||||
private final BaseOpenSamlLogoutResponseResolver delegate;
|
||||
@@ -64,7 +66,7 @@ public final class OpenSaml5LogoutResponseResolver implements Saml2LogoutRespons
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @Nullable Saml2LogoutResponse resolve(HttpServletRequest request, Authentication authentication) {
|
||||
public @Nullable Saml2LogoutResponse resolve(HttpServletRequest request, @Nullable Authentication authentication) {
|
||||
return this.delegate.resolve(request, authentication);
|
||||
}
|
||||
|
||||
@@ -72,7 +74,7 @@ public final class OpenSaml5LogoutResponseResolver implements Saml2LogoutRespons
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @Nullable Saml2LogoutResponse resolve(HttpServletRequest request, Authentication authentication,
|
||||
public @Nullable Saml2LogoutResponse resolve(HttpServletRequest request, @Nullable Authentication authentication,
|
||||
Saml2AuthenticationException exception) {
|
||||
return this.delegate.resolve(request, authentication, exception);
|
||||
}
|
||||
@@ -103,12 +105,12 @@ public final class OpenSaml5LogoutResponseResolver implements Saml2LogoutRespons
|
||||
|
||||
private final RelyingPartyRegistration registration;
|
||||
|
||||
private final Authentication authentication;
|
||||
private final @Nullable Authentication authentication;
|
||||
|
||||
private final LogoutRequest logoutRequest;
|
||||
|
||||
public LogoutResponseParameters(HttpServletRequest request, RelyingPartyRegistration registration,
|
||||
Authentication authentication, LogoutRequest logoutRequest) {
|
||||
@Nullable Authentication authentication, LogoutRequest logoutRequest) {
|
||||
this.request = request;
|
||||
this.registration = registration;
|
||||
this.authentication = authentication;
|
||||
@@ -128,7 +130,7 @@ public final class OpenSaml5LogoutResponseResolver implements Saml2LogoutRespons
|
||||
return this.registration;
|
||||
}
|
||||
|
||||
public Authentication getAuthentication() {
|
||||
public @Nullable Authentication getAuthentication() {
|
||||
return this.authentication;
|
||||
}
|
||||
|
||||
|
||||
+26
-7
@@ -34,9 +34,12 @@ import java.util.Set;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import net.shibboleth.shared.resolver.CriteriaSet;
|
||||
import net.shibboleth.shared.xml.ParserPool;
|
||||
import net.shibboleth.shared.xml.SerializeSupport;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.core.criterion.EntityIdCriterion;
|
||||
import org.opensaml.core.xml.XMLObject;
|
||||
import org.opensaml.core.xml.XMLObjectBuilder;
|
||||
@@ -117,6 +120,7 @@ import org.springframework.web.util.UriUtils;
|
||||
/**
|
||||
* For internal use only. Subject to breaking changes at any time.
|
||||
*/
|
||||
@NullMarked
|
||||
final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OpenSaml5Template.class);
|
||||
@@ -138,7 +142,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public <T extends XMLObject> T deserialize(InputStream serialized) {
|
||||
try {
|
||||
Document document = XMLObjectProviderRegistrySupport.getParserPool().parse(serialized);
|
||||
ParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();
|
||||
Assert.notNull(pool, "ParserPool must be configured");
|
||||
Document document = pool.parse(serialized);
|
||||
Element element = document.getDocumentElement();
|
||||
UnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();
|
||||
Unmarshaller unmarshaller = factory.getUnmarshaller(element);
|
||||
@@ -158,6 +164,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public OpenSaml5SerializationConfigurer serialize(XMLObject object) {
|
||||
Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);
|
||||
Assert.notNull(marshaller, "Marshaller for " + object.getElementQName() + " must be configured");
|
||||
try {
|
||||
return serialize(marshaller.marshall(object));
|
||||
}
|
||||
@@ -252,7 +259,9 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
SignatureSigningParameters parameters = resolveSigningParameters();
|
||||
this.components.putAll(params);
|
||||
Credential credential = parameters.getSigningCredential();
|
||||
Assert.notNull(credential, "credential cannot be null when signing a SAML payload");
|
||||
String algorithmUri = parameters.getSignatureAlgorithm();
|
||||
Assert.notNull(algorithmUri, "algorithmUri cannot be null when signing a SAML payload");
|
||||
this.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||
for (Map.Entry<String, String> component : this.components.entrySet()) {
|
||||
@@ -328,14 +337,14 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
|
||||
private final Collection<Saml2X509Credential> credentials;
|
||||
|
||||
private String entityId;
|
||||
private @Nullable String entityId;
|
||||
|
||||
OpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerificationConfigurer entityId(String entityId) {
|
||||
public VerificationConfigurer entityId(@Nullable String entityId) {
|
||||
this.entityId = entityId;
|
||||
return this;
|
||||
}
|
||||
@@ -354,6 +363,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
}
|
||||
|
||||
private CriteriaSet verificationCriteria(Issuer issuer) {
|
||||
Assert.notNull(issuer.getValue(), "required elements must have a value");
|
||||
return new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),
|
||||
new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),
|
||||
new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));
|
||||
@@ -362,12 +372,21 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
@Override
|
||||
public Collection<Saml2Error> verify(SignableXMLObject signable) {
|
||||
if (signable instanceof StatusResponseType response) {
|
||||
Assert.notNull(response.getID(), "Response#ID cannot be null");
|
||||
Assert.notNull(response.getIssuer(), "Response#Issuer cannot be null");
|
||||
Assert.notNull(response.getSignature(), "Response#Signature cannot be null");
|
||||
return verifySignature(response.getID(), response.getIssuer(), response.getSignature());
|
||||
}
|
||||
if (signable instanceof RequestAbstractType request) {
|
||||
Assert.notNull(request.getID(), "Request#ID cannot be null");
|
||||
Assert.notNull(request.getIssuer(), "Request#Issuer cannot be null");
|
||||
Assert.notNull(request.getSignature(), "Request#Signature cannot be null");
|
||||
return verifySignature(request.getID(), request.getIssuer(), request.getSignature());
|
||||
}
|
||||
if (signable instanceof Assertion assertion) {
|
||||
Assert.notNull(assertion.getID(), "Assertion#ID cannot be null");
|
||||
Assert.notNull(assertion.getIssuer(), "Assertion#Issuer cannot be null");
|
||||
Assert.notNull(assertion.getSignature(), "Assertion#Signature cannot be null");
|
||||
return verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());
|
||||
}
|
||||
throw new Saml2Exception("Unsupported object of type: " + signable.getClass().getName());
|
||||
@@ -408,15 +427,15 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature algorithm for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
if (!parameters.hasSignature()) {
|
||||
byte[] signature = parameters.getSignature();
|
||||
if (signature == null) {
|
||||
return Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Missing signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
Collection<Saml2Error> errors = new ArrayList<>();
|
||||
String algorithmUri = parameters.getAlgorithm();
|
||||
try {
|
||||
if (!trustEngine.validate(parameters.getSignature(), parameters.getContent(), algorithmUri, criteria,
|
||||
null)) {
|
||||
if (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {
|
||||
errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,
|
||||
"Invalid signature for object [" + parameters.getId() + "]"));
|
||||
}
|
||||
@@ -558,7 +577,7 @@ final class OpenSaml5Template implements OpenSamlOperations {
|
||||
statement.getAttributes().addAll(decrypteds);
|
||||
}
|
||||
|
||||
private void decryptSubject(Subject subject) {
|
||||
private void decryptSubject(@Nullable Subject subject) {
|
||||
if (subject != null) {
|
||||
if (subject.getEncryptedID() != null) {
|
||||
try {
|
||||
|
||||
+8
-15
@@ -22,6 +22,7 @@ import java.util.List;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import net.shibboleth.shared.xml.ElementSupport;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.opensaml.core.xml.AbstractXMLObject;
|
||||
import org.opensaml.core.xml.AbstractXMLObjectBuilder;
|
||||
import org.opensaml.core.xml.ElementExtensibleXMLObject;
|
||||
@@ -38,8 +39,6 @@ import org.opensaml.saml.common.xml.SAMLConstants;
|
||||
import org.opensaml.saml.saml2.core.AttributeValue;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.saml2.core.OpenSamlInitializationService;
|
||||
|
||||
public final class TestCustomOpenSaml5Objects {
|
||||
@@ -103,7 +102,6 @@ public final class TestCustomOpenSaml5Objects {
|
||||
|
||||
public static class CustomOpenSamlObjectImpl extends AbstractXMLObject implements CustomOpenSamlObject {
|
||||
|
||||
@NonNull
|
||||
private IndexedXMLObjectChildrenList<XMLObject> unknownXMLObjects;
|
||||
|
||||
/**
|
||||
@@ -113,28 +111,25 @@ public final class TestCustomOpenSaml5Objects {
|
||||
* represents
|
||||
* @param namespacePrefix the prefix for the given namespace
|
||||
*/
|
||||
protected CustomOpenSamlObjectImpl(@Nullable String namespaceURI, @NonNull String elementLocalName,
|
||||
protected CustomOpenSamlObjectImpl(@Nullable String namespaceURI, String elementLocalName,
|
||||
@Nullable String namespacePrefix) {
|
||||
super(namespaceURI, elementLocalName, namespacePrefix);
|
||||
super.getNamespaceManager().registerNamespaceDeclaration(new Namespace(CUSTOM_NS, TYPE_CUSTOM_PREFIX));
|
||||
this.unknownXMLObjects = new IndexedXMLObjectChildrenList<>(this);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<XMLObject> getUnknownXMLObjects() {
|
||||
return this.unknownXMLObjects;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<XMLObject> getUnknownXMLObjects(@NonNull QName typeOrName) {
|
||||
public List<XMLObject> getUnknownXMLObjects(QName typeOrName) {
|
||||
return (List<XMLObject>) this.unknownXMLObjects.subList(typeOrName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<XMLObject> getOrderedChildren() {
|
||||
public @Nullable List<XMLObject> getOrderedChildren() {
|
||||
return Collections.unmodifiableList(this.unknownXMLObjects);
|
||||
}
|
||||
|
||||
@@ -162,9 +157,8 @@ public final class TestCustomOpenSaml5Objects {
|
||||
|
||||
public static class CustomSamlObjectBuilder extends AbstractXMLObjectBuilder<CustomOpenSamlObject> {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public CustomOpenSamlObject buildObject(@Nullable String namespaceURI, @NonNull String localName,
|
||||
public CustomOpenSamlObject buildObject(@Nullable String namespaceURI, String localName,
|
||||
@Nullable String namespacePrefix) {
|
||||
return new CustomOpenSamlObjectImpl(namespaceURI, localName, namespacePrefix);
|
||||
}
|
||||
@@ -178,7 +172,7 @@ public final class TestCustomOpenSaml5Objects {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void marshallElementContent(@NonNull XMLObject xmlObject, @NonNull Element domElement) {
|
||||
protected void marshallElementContent(XMLObject xmlObject, Element domElement) {
|
||||
final CustomOpenSamlObject customSamlObject = (CustomOpenSamlObject) xmlObject;
|
||||
|
||||
for (XMLObject object : customSamlObject.getOrderedChildren()) {
|
||||
@@ -195,15 +189,14 @@ public final class TestCustomOpenSaml5Objects {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processChildElement(@NonNull XMLObject parentXMLObject, @NonNull XMLObject childXMLObject)
|
||||
protected void processChildElement(XMLObject parentXMLObject, XMLObject childXMLObject)
|
||||
throws UnmarshallingException {
|
||||
final CustomOpenSamlObject customSamlObject = (CustomOpenSamlObject) parentXMLObject;
|
||||
customSamlObject.getUnknownXMLObjects().add(childXMLObject);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected XMLObject buildXMLObject(@NonNull Element domElement) {
|
||||
protected XMLObject buildXMLObject(Element domElement) {
|
||||
return new CustomOpenSamlObjectImpl(SAMLConstants.SAML20_NS, AttributeValue.DEFAULT_ELEMENT_LOCAL_NAME,
|
||||
CustomOpenSamlObject.TYPE_CUSTOM_PREFIX);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user