Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into jj-play
Browse files Browse the repository at this point in the history
  • Loading branch information
jj committed Mar 4, 2019
2 parents 600199e + fbec675 commit 8f99faf
Show file tree
Hide file tree
Showing 44 changed files with 1,323 additions and 104 deletions.
2 changes: 1 addition & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pipeline {
steps {
sh '''
docker stop shibui || true && docker rm shibui || true
docker run -d --restart always --name shibui -p 8080:8080 -v /etc/shibui:/conf -v /etc/shibui/application.yml:/application.yml -m 3GB --memory-swap=3GB unicon/shibui-pac4j:latest
docker run -d --restart always --name shibui -p 8080:8080 -v /etc/shibui:/conf -v /etc/shibui/application.yml:/application.yml -m 4GB --memory-swap=4GB unicon/shibui-pac4j:latest /usr/bin/java -Xmx3G -jar app.jar
'''
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers

import edu.internet2.tier.shibboleth.admin.util.DurationUtility

class DurationMetadataResolverValidator implements MetadataResolverValidator {
boolean supports(MetadataResolver resolver) {
return resolver.hasProperty('dynamicMetadataResolverAttributes') || resolver.hasProperty('reloadableMetadataResolverAttributes')
}

ValidationResult validate(MetadataResolver resolver) {
if (resolver.hasProperty('dynamicMetadataResolverAttributes')) {
DynamicMetadataResolverAttributes dynamicMetadataResolverAttributes = resolver.dynamicMetadataResolverAttributes
if (dynamicMetadataResolverAttributes != null) {
if (DurationUtility.toMillis(dynamicMetadataResolverAttributes.minCacheDuration) > DurationUtility.toMillis(dynamicMetadataResolverAttributes.maxCacheDuration)) {
return new ValidationResult('minimum cache duration larger than maximum')
}
}
}

if (resolver.hasProperty('reloadableMetadataResolverAttributes')) {
ReloadableMetadataResolverAttributes reloadableMetadataResolverAttributes = resolver.reloadableMetadataResolverAttributes
if (reloadableMetadataResolverAttributes != null) {
if (DurationUtility.toMillis(reloadableMetadataResolverAttributes.minRefreshDelay) > DurationUtility.toMillis(reloadableMetadataResolverAttributes.maxRefreshDelay)) {
return new ValidationResult('minimum refresh delay duration larger than maximum')
}
}
}
return new ValidationResult()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
private String generateJavaScriptRegexScript(String regex) {
return """
"use strict";
${regex}.test(input.getEntityID());\n"""
${regex.startsWith('/') ? '' : '/'}${regex}${regex.endsWith('/') ? '' : '/'}.test(input.getEntityID());\n"""
}

void constructXmlNodeForFilter(EntityRoleWhiteListFilter filter, def markupBuilderDelegate) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.internet2.tier.shibboleth.admin.ui.configuration;

import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DurationMetadataResolverValidator;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidationService;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidator;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMetadataResolverValidator;
Expand All @@ -21,4 +22,9 @@ ResourceBackedMetadataResolverValidator resourceBackedMetadataResolverValidator(
MetadataResolverValidationService metadataResolverValidationService(List<MetadataResolverValidator> metadataResolverValidators) {
return new MetadataResolverValidationService(metadataResolverValidators);
}

@Bean
DurationMetadataResolverValidator durationMetadataResolverValidator() {
return new DurationMetadataResolverValidator();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,17 @@
public class ErrorResponse {
private String errorCode;
private String errorMessage;
private String cause;

public ErrorResponse(String errorCode, String errorMessage) {
this(errorCode, errorMessage, null);
}

public ErrorResponse(HttpStatus httpStatus, String errorMessage) {
this.errorCode = String.valueOf(httpStatus.value());
this.errorMessage = errorMessage;
this(httpStatus, errorMessage, null);
}

public ErrorResponse(HttpStatus httpStatus, String errorCode, String cause) {
this(String.valueOf(httpStatus.value()), errorCode, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public class MetadataResolversController {

@ExceptionHandler({InvalidTypeIdException.class, IOException.class, HttpMessageNotReadableException.class})
public ResponseEntity<?> unableToParseJson(Exception ex) {
return ResponseEntity.badRequest().body(new ErrorResponse(HttpStatus.BAD_REQUEST.toString(), ex.getMessage()));
return ResponseEntity.badRequest().body(new ErrorResponse(HttpStatus.BAD_REQUEST.toString(), ex.getMessage(), ex.getCause().getMessage()));
}

@GetMapping("/MetadataResolvers")
Expand Down Expand Up @@ -153,7 +153,8 @@ public ResponseEntity<?> update(@PathVariable String resourceId, @RequestBody Me
private ResponseEntity<?> validate(MetadataResolver metadataResolver) {
ValidationResult validationResult = metadataResolverValidationService.validateIfNecessary(metadataResolver);
if (!validationResult.isValid()) {
return ResponseEntity.badRequest().body(validationResult.getErrorMessage());
ErrorResponse errorResponse = new ErrorResponse("400", String.join("\n", validationResult.getErrorMessages()));
return ResponseEntity.badRequest().body(errorResponse);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public ResponseEntity<ErrorResponse> handleDatabaseConstraintViolation(Constrain

@ExceptionHandler(Exception.class)
public final ResponseEntity<ErrorResponse> handleAllOtherExceptions(Exception ex) {
ErrorResponse errorResponse = new ErrorResponse("400", ex.getLocalizedMessage());
ErrorResponse errorResponse = new ErrorResponse("400", ex.getLocalizedMessage(), ex.getCause() == null ? null : ex.getCause().getLocalizedMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.internet2.tier.shibboleth.admin.ui.domain;

import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type;
import org.joda.time.DateTime;
import org.opensaml.core.xml.XMLObject;
Expand All @@ -16,6 +17,7 @@


@MappedSuperclass
@EqualsAndHashCode(callSuper = true)
public abstract class AbstractDescriptor extends AbstractAttributeExtensibleXMLObject implements CacheableSAMLObject, TimeBoundSAMLObject, SignableXMLObject {
private Long cacheDuration;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.internet2.tier.shibboleth.admin.ui.domain;

import lombok.EqualsAndHashCode;
import org.opensaml.core.xml.ElementExtensibleXMLObject;
import org.opensaml.core.xml.XMLObject;

Expand All @@ -18,6 +19,7 @@

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@EqualsAndHashCode(callSuper = true)
public abstract class AbstractElementExtensibleXMLObject extends AbstractXMLObject implements ElementExtensibleXMLObject {
@OneToMany(cascade = CascadeType.ALL)
@OrderColumn
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public List<Logo> getLogos() {
return this.xmlObjects.stream().filter(p -> p instanceof Logo).map(p -> (Logo) p).collect(Collectors.toList());
}

public void addLog(edu.internet2.tier.shibboleth.admin.ui.domain.Logo logo) {
public void addLogo(edu.internet2.tier.shibboleth.admin.ui.domain.Logo logo) {
this.xmlObjects.add(logo);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
* A facade that aggregates {@link MetadataResolverValidator}s available to call just one of them supporting the type of a given resolver.
Expand All @@ -24,13 +23,13 @@ public MetadataResolverValidationService(List<MetadataResolverValidator<T>> vali

@SuppressWarnings("Unchecked")
public ValidationResult validateIfNecessary(T metadataResolver) {
Optional<MetadataResolverValidator<T>> validator =
this.validators
.stream()
.filter(v -> v.supports(metadataResolver))
.findFirst();
return validator.isPresent() ? validator.get().validate(metadataResolver) : new ValidationResult(null);

// TODO: make this more streamsish
ValidationResult validationResult = new ValidationResult();
this.validators
.stream()
.filter(v -> v.supports(metadataResolver))
.forEach(v -> v.validate(metadataResolver).getErrorMessages().stream().filter(m -> m != null).forEach(r -> validationResult.getErrorMessages().add(r)));
return validationResult;
}

//Package-private - used for unit tests
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers;

import java.util.ArrayList;
import java.util.List;

/**
* An SPI to validate different types of {@link MetadataResolver}s.
* <p>
Expand All @@ -17,18 +20,22 @@ public interface MetadataResolverValidator<T extends MetadataResolver> {

class ValidationResult {

public ValidationResult() {}

public ValidationResult(String errorMessage) {
this.errorMessage = errorMessage;
if (errorMessage != null) {
this.errorMessages.add(errorMessage);
}
}

private String errorMessage;
private List<String> errorMessages = new ArrayList<>();

public String getErrorMessage() {
return errorMessage;
public List<String> getErrorMessages() {
return errorMessages;
}

public boolean isValid() {
return this.errorMessage == null;
return this.errorMessages == null || this.errorMessages.isEmpty();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ public ValidationResult validate(ResourceBackedMetadataResolver resolver) {
catch (ResourceBackedMetadataResolver.InvalidResourceTypeException e) {
return new ValidationResult(e.getMessage());
}
return new ValidationResult(null);
return new ValidationResult();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.xml.BasicParserPool;
import net.shibboleth.utilities.java.support.xml.ParserPool;
import net.shibboleth.utilities.java.support.xml.XMLParserException;
import org.opensaml.core.config.ConfigurationService;
import org.opensaml.core.config.InitializationException;
import org.opensaml.core.xml.XMLObject;
Expand All @@ -15,6 +16,7 @@
import org.opensaml.core.xml.io.MarshallingException;
import org.opensaml.core.xml.io.Unmarshaller;
import org.opensaml.core.xml.io.UnmarshallerFactory;
import org.opensaml.core.xml.io.UnmarshallingException;
import org.opensaml.saml.saml2.metadata.EntityDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -130,6 +132,17 @@ public EntityDescriptor unmarshalFromXml(byte[] entityDescriptorXml) throws Exce
}
}

public <T> T unmarshallFromXml(byte[] xml, Class<T> type) throws IOException, XMLParserException, UnmarshallingException {
try (InputStream is = ByteSource.wrap(xml).openBufferedStream()) {
Document d = this.parserPool.parse(is);
Unmarshaller unmarshaller = this.unmarshallerFactory.getUnmarshaller(d.getDocumentElement());
if (unmarshaller != null) {
return type.cast(unmarshaller.unmarshall(d.getDocumentElement()));
}
return null;
}
}

// TODO: yeah, I'm not happy with this...
public <T extends XMLObject> T buildDefaultInstanceOfType(Class<T> type) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.SecurityInfoRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.ServiceProviderSsoDescriptorRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects;
import edu.internet2.tier.shibboleth.admin.ui.security.model.User;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;
import edu.internet2.tier.shibboleth.admin.util.MDDCConstants;
import edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions;
Expand All @@ -59,6 +58,7 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -178,6 +178,8 @@ void setupSecurity(EntityDescriptor ed, EntityDescriptorRepresentation represent
if (securityInfoRepresentation.isWantAssertionsSigned()) {
getSPSSODescriptorFromEntityDescriptor(ed).setWantAssertionsSigned(true);
}
// TODO: review if we need more than a naive implementation
ed.getOptionalSPSSODescriptor().ifPresent( i -> i.getKeyDescriptors().clear());
if (securityInfoRepresentation.isX509CertificateAvailable()) {
for (SecurityInfoRepresentation.X509CertificateRepresentation x509CertificateRepresentation : securityInfoRepresentation.getX509Certificates()) {
KeyDescriptor keyDescriptor = createKeyDescriptor(x509CertificateRepresentation.getName(), x509CertificateRepresentation.getType(), x509CertificateRepresentation.getValue());
Expand All @@ -196,6 +198,8 @@ void setupSecurity(EntityDescriptor ed, EntityDescriptorRepresentation represent
void setupUIInfo(EntityDescriptor ed, EntityDescriptorRepresentation representation) {
// set up mdui
if (representation.getMdui() != null) {
// TODO: check if we need more than a naive implementation
removeUIInfo(ed);
MduiRepresentation mduiRepresentation = representation.getMdui();

if (!Strings.isNullOrEmpty(mduiRepresentation.getDisplayName())) {
Expand Down Expand Up @@ -248,7 +252,7 @@ void setupUIInfo(EntityDescriptor ed, EntityDescriptorRepresentation representat

if (!Strings.isNullOrEmpty(mduiRepresentation.getLogoUrl())) {
Logo logo = openSamlObjects.buildDefaultInstanceOfType(Logo.class);
getUIInfo(ed).addLog(logo);
getUIInfo(ed).addLogo(logo);
logo.setURL(mduiRepresentation.getLogoUrl());
logo.setHeight(mduiRepresentation.getLogoHeight());
logo.setWidth(mduiRepresentation.getLogoWidth());
Expand All @@ -267,6 +271,7 @@ void setupUIInfo(EntityDescriptor ed, EntityDescriptorRepresentation representat
void setupContacts(EntityDescriptor ed, EntityDescriptorRepresentation representation) {
// set up contacts
if (representation.getContacts() != null && representation.getContacts().size() > 0) {
ed.getContactPersons().clear();
for (ContactRepresentation contactRepresentation : representation.getContacts()) {
ContactPerson contactPerson = ((ContactPersonBuilder) openSamlObjects.getBuilderFactory().getBuilder(ContactPerson.DEFAULT_ELEMENT_NAME)).buildObject();

Expand Down Expand Up @@ -319,13 +324,14 @@ void setupSPSSODescriptor(EntityDescriptor ed, EntityDescriptorRepresentation re
if (representation.getServiceProviderSsoDescriptor() != null) {
SPSSODescriptor spssoDescriptor = getSPSSODescriptorFromEntityDescriptor(ed);

spssoDescriptor.setSupportedProtocols(Collections.EMPTY_LIST);
if (!Strings.isNullOrEmpty(representation.getServiceProviderSsoDescriptor().getProtocolSupportEnum())) {
spssoDescriptor.setSupportedProtocols(
Arrays.stream(representation.getServiceProviderSsoDescriptor().getProtocolSupportEnum().split(",")).map(p -> MDDCConstants.PROTOCOL_BINDINGS.get(p.trim())).collect(Collectors.toList())
);
}


spssoDescriptor.getNameIDFormats().clear();
if (representation.getServiceProviderSsoDescriptor() != null && representation.getServiceProviderSsoDescriptor().getNameIdFormats() != null && representation.getServiceProviderSsoDescriptor().getNameIdFormats().size() > 0) {
for (String nameidFormat : representation.getServiceProviderSsoDescriptor().getNameIdFormats()) {
NameIDFormat nameIDFormat = openSamlObjects.buildDefaultInstanceOfType(NameIDFormat.class);
Expand Down Expand Up @@ -387,7 +393,7 @@ private Attribute createAttributeWithArbitraryValues(String name, String friendl
return createAttributeWithArbitraryValues(name, friendlyName, values.toArray(new String[]{}));
}

private KeyDescriptor createKeyDescriptor(String name, String type, String value) {
KeyDescriptor createKeyDescriptor(String name, String type, String value) {
KeyDescriptor keyDescriptor = openSamlObjects.buildDefaultInstanceOfType(KeyDescriptor.class);

if (!Strings.isNullOrEmpty(name)) {
Expand Down Expand Up @@ -652,7 +658,7 @@ public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.ope
}
if (overrideProperty.getPersistType() != null &&
!overrideProperty.getPersistType().equals(overrideProperty.getDisplayType())) {
attributeValues = getValueFromXMLObject(jpaAttribute.getAttributeValues().get(0));
attributeValues = overrideProperty.getPersistValue().equals(getValueFromXMLObject(jpaAttribute.getAttributeValues().get(0)));
} else {
attributeValues = Boolean.valueOf(((XSBoolean) jpaAttribute.getAttributeValues()
.get(0)).getStoredValue());
Expand All @@ -669,6 +675,9 @@ public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.ope
}
}

// TODO: fix this; there is a problem with the way that defaults are working and the processing from the front end
ModelRepresentationConversions.completeMe(relyingPartyOverrides);

representation.setRelyingPartyOverrides(relyingPartyOverrides);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,25 @@ public static Map<String, Object> getRelyingPartyOverridesRepresentationFromAttr
return relyingPartyOverrides;
}

// TODO: fix this; currently there is a problem with not returning a value
public static Map<String,Object> completeMe(Map<String, Object> relyingPartyOverrides) {
customPropertiesConfiguration
.getOverrides()
.stream()
.filter(o -> !relyingPartyOverrides.containsKey(o.getName()))
.filter(o -> o.getDisplayType().equals("boolean"))
.forEach(p -> relyingPartyOverrides.put(p.getName(), getDefaultValueFromProperty(p)));
return relyingPartyOverrides;
}

private static Object getDefaultValueFromProperty(RelyingPartyOverrideProperty property) {
switch (property.getDisplayType()) {
case "boolean":
return Boolean.getBoolean(property.getDefaultValue());
}
return null;
}

public static Object getOverrideFromAttribute(Attribute attribute) {
RelyingPartyOverrideProperty relyingPartyOverrideProperty = customPropertiesConfiguration.getOverrides().stream()
.filter(it -> it.getAttributeFriendlyName().equals(attribute.getFriendlyName())).findFirst().get();
Expand Down
2 changes: 2 additions & 0 deletions backend/src/main/resources/i18n/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,8 @@ message.database-constraint=There was a database constraint problem processing t
message.user-request-received-title=User request received
message.user-request-received-body=Your request has been received and is being reviewed. You will be notified with access status.

message.filter-fail=A server error occured, and the filter failed to save.

tooltip.entity-id=Entity ID
tooltip.service-provider-name=Service Provider Name (Dashboard Display Only)
tooltip.force-authn=Disallows use (or reuse) of authentication results and login flows that don\u0027t provide a real-time proof of user presence in the login process
Expand Down
Loading

0 comments on commit 8f99faf

Please sign in to comment.