diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SpringSecurityConfig.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SpringSecurityConfig.java index 90194ede5..8a3b6c292 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SpringSecurityConfig.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SpringSecurityConfig.java @@ -111,6 +111,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests() .requestMatchers(new AntPathRequestMatcher("/unsecured/**/*"), + new AntPathRequestMatcher("/entities*"), new AntPathRequestMatcher("/entities/**/*"), new AntPathRequestMatcher("/actuator/**"), new AntPathRequestMatcher("/api/beacon/send")).permitAll() diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesController.java index ecb0809ae..833d3f71f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesController.java @@ -28,14 +28,14 @@ import java.time.Instant; import java.time.ZoneOffset; import java.util.Date; +import java.util.List; /** * EntitiesController is here to meet the requirements for this project being an MDQ. Despite similar logic to the * EntitiesDescriptorController, the required endpoints that make this project an MDQ server are served by this controller. */ @RestController -@RequestMapping(value = { "/entities", // per protocol - https://spaces.at.internet2.edu/display/MDQ/Metadata+Query+Protocol - "/api/entities" }, // existing - included to break no existing code +@RequestMapping(value = { "/" }, // per protocol - https://spaces.at.internet2.edu/display/MDQ/Metadata+Query+Protocol method = RequestMethod.GET) @Slf4j @Tags(value = {@Tag(name = "MDQ")}) @@ -49,7 +49,30 @@ public class EntitiesController { @Autowired private EntityDescriptorRepository entityDescriptorRepository; - @RequestMapping(value = "/{entityId:.*}") + @RequestMapping(value = "/entities", produces = "application/xml") + @Operation(description = "Endpoint based on the MDQ spec to return all entity's information. see: https://spaces.at.internet2.edu/display/MDQ/Metadata+Query+Protocol", + summary = "Return all the entities from the entity's id", method = "GET") + @Transactional(readOnly = true) + public ResponseEntity getAllXml() throws MarshallingException, ResolverException, UnsupportedEncodingException { + List entityDescriptors = entityDescriptorRepository.findAll(); + if (entityDescriptors == null || entityDescriptors.isEmpty()) { + return ResponseEntity.notFound().build(); + } + StringBuilder result = new StringBuilder(); + entityDescriptors.forEach(entityDescriptor -> { + try { + final String xml = this.openSamlObjects.marshalToXmlString(entityDescriptor); + result.append(xml); + } + catch (MarshallingException e) { + throw new RuntimeException(e); + } + }); + String xmlDeclarationClean = result.toString().replace("",""); + return new ResponseEntity<>("" + xmlDeclarationClean, new HttpHeaders(), HttpStatus.OK); + } + + @RequestMapping(value = "/entities/{entityId:.*}") @Operation(description = "Endpoint based on the MDQ spec to return a single entity's information. see: https://spaces.at.internet2.edu/display/MDQ/Metadata+Query+Protocol", summary = "Return a single entity from the entity's id", method = "GET") @Transactional(readOnly = true) @@ -70,7 +93,7 @@ private String formatModifiedDate(EntityDescriptorRepresentation entityDescripto return DateUtils.formatDate(date, DateUtils.PATTERN_RFC1123); } - @RequestMapping(value = "/{entityId:.*}", produces = "application/xml") + @RequestMapping(value = "/entities/{entityId:.*}", produces = "application/xml") @Operation(description = "Endpoint based on the MDQ spec to return a single entity's information. see: https://spaces.at.internet2.edu/display/MDQ/Metadata+Query+Protocol", summary = "Return a single entity from the entity's id", method = "GET") @Transactional(readOnly = true) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/EntityDescriptorConversionUtils.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/EntityDescriptorConversionUtils.java index c9386399b..8a45d15d1 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/EntityDescriptorConversionUtils.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/EntityDescriptorConversionUtils.java @@ -207,15 +207,16 @@ public static void setupACSs(EntityDescriptor ed, EntityDescriptorRepresentation if (representation.getAssertionConsumerServices() != null && representation.getAssertionConsumerServices().size() > 0) { // TODO: review if we need more than a naive implementation ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getAssertionConsumerServices().clear()); + int indexPosition = 0; for (AssertionConsumerServiceRepresentation acsRepresentation : representation.getAssertionConsumerServices()) { AssertionConsumerService assertionConsumerService = openSamlObjects.buildDefaultInstanceOfType(AssertionConsumerService.class); getSPSSODescriptorFromEntityDescriptor(ed).getAssertionConsumerServices().add(assertionConsumerService); + assertionConsumerService.setBinding(acsRepresentation.getBinding()); + assertionConsumerService.setLocation(acsRepresentation.getLocationUrl()); if (acsRepresentation.isMakeDefault()) { assertionConsumerService.setIsDefault(true); } - assertionConsumerService.setBinding(acsRepresentation.getBinding()); - assertionConsumerService.setLocation(acsRepresentation.getLocationUrl()); - assertionConsumerService.setIndex(acsRepresentation.getIndex()); + assertionConsumerService.setIndex(acsRepresentation.getIndex() == null ? indexPosition++ : acsRepresentation.getIndex()); } } else { ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getAssertionConsumerServices().clear()); diff --git a/backend/src/test/resources/metadata/SHIBUI-2380.xml b/backend/src/test/resources/metadata/SHIBUI-2380.xml index 7ac4ac393..aa6f462e6 100644 --- a/backend/src/test/resources/metadata/SHIBUI-2380.xml +++ b/backend/src/test/resources/metadata/SHIBUI-2380.xml @@ -67,12 +67,15 @@ urn:mace:shibboleth:metadata:oidc:1.0:nameid-format:pairwise + Location="https://example.org/cb" + index="0"/> + Location="https://example.org/cb2" + index="1"/> + Location="https://example.org/cb3" + index="2"/> \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 683413e04..6c6217cee 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ name=shibui group=edu.internet2.tier.shibboleth.admin.ui -version=2.0.0-SNAPSHOT +version=2.0.0-BETA ### library versions ### ## As of 2-23-23 @@ -42,7 +42,7 @@ i2.github.owner=TIER i2.github.repo=shib-idp-ui i2.github.apiEndpoint=https://github.internet2.edu/api/v3 i2.git.remote=i2 -i2.git.branch=master +i2.git.branch=2.0.0-BETA # set app use.release.app.yml=false