Skip to content

Commit

Permalink
Merged in SHIBUI-306 (pull request #60)
Browse files Browse the repository at this point in the history
SHIBUI-306

Approved-by: Ryan Mathis <rmathis@unicon.net>
  • Loading branch information
rmathis committed Apr 25, 2018
1 parent d230346 commit a38788e
Show file tree
Hide file tree
Showing 104 changed files with 2,965 additions and 776 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ There are currently 2 ways to run the application:
1. As an executable
1. deployed in a Java Servlet 3.0 container

Note that some features require encoded slashes in the URL. In tomcat (which is embedded in the war), this can be
allowed with:

```
-Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true
```

### Running as an executable

`java -jar shibui.war`
Expand Down
4 changes: 3 additions & 1 deletion backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ dependencies {
}

// spring boot auto-config starters
['starter-web', 'starter-data-jpa', 'starter-security', 'starter-actuator', 'devtools'].each {
['starter-web', 'starter-data-jpa', 'starter-security', 'starter-actuator', 'devtools', 'starter-webflux'].each {
compile "org.springframework.boot:spring-boot-${it}"
}
// TODO: figure out what this should really be
Expand Down Expand Up @@ -87,6 +87,8 @@ dependencies {
testCompile "org.xmlunit:xmlunit-core:2.5.1"
testRuntime 'cglib:cglib-nodep:3.2.5'

testCompile "net.shibboleth.ext:spring-extensions:5.4.0-SNAPSHOT"

//JSON schema generator
testCompile 'com.kjetland:mbknor-jackson-jsonschema_2.12:1.0.29'
testCompile 'javax.validation:validation-api:2.0.1.Final'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects;
import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository;
import edu.internet2.tier.shibboleth.admin.ui.scheduled.EntityDescriptorFilesScheduledTasks;
import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService;
import edu.internet2.tier.shibboleth.admin.ui.service.EntityIdsSearchService;
import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl;
import edu.internet2.tier.shibboleth.admin.ui.service.*;
import edu.internet2.tier.shibboleth.admin.util.AttributeUtility;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
Expand All @@ -22,7 +21,11 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -39,9 +42,34 @@ public OpenSamlObjects openSamlObjects() {
return new OpenSamlObjects();
}

@Bean
public EntityService jpaEntityService() {
return new JPAEntityServiceImpl(openSamlObjects());
}

@Bean
public EntityDescriptorService jpaEntityDescriptorService() {
return new JPAEntityDescriptorServiceImpl(openSamlObjects());
return new JPAEntityDescriptorServiceImpl(openSamlObjects(), jpaEntityService());
}

@Bean
public FilterService jpaFilterService() {
return new JPAFilterServiceImpl();
}

@Bean
public FilterTargetService jpaFilterTargetService() {
return new JPAFilterTargetServiceImpl();
}

@Bean
public MetadataResolverService metadataResolverService() {
return new JPAMetadataResolverServiceImpl();
}

@Bean
public AttributeUtility attributeUtility() {
return new AttributeUtility();
}

@Autowired
Expand All @@ -63,7 +91,7 @@ public EntityIdsSearchService entityIdsSearchService() {
try {
IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(directory));
QueryParser parser = new QueryParser("content", fullTokenAnalyzer);
TopDocs topDocs = searcher.search(parser.parse(term), limit);
TopDocs topDocs = searcher.search(parser.parse(term.trim()), limit);
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
Document document = searcher.doc(scoreDoc.doc);
entityIds.add(document.get("id"));
Expand All @@ -74,4 +102,43 @@ public EntityIdsSearchService entityIdsSearchService() {
return new EntityIdsSearchResultRepresentation(entityIds);
};
}

/**
* A WebMvcConfigurer that won't mangle the path for the entities endpoint.
*
* inspired by [ https://stackoverflow.com/questions/13482020/encoded-slash-2f-with-spring-requestmapping-path-param-gives-http-400 ]
*
* @return configurer
*/
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper helper = new UrlPathHelper() {
@Override
public String getServletPath(HttpServletRequest request) {
String servletPath = getOriginatingServletPath(request);
if (servletPath.startsWith("/api/entities")) {
return servletPath;
} else {
return super.getOriginatingServletPath(request);
}
}

@Override
public String getOriginatingServletPath(HttpServletRequest request) {
String servletPath = request.getRequestURI().substring(request.getContextPath().length());
if (servletPath.startsWith("/api/entities")) {
return servletPath;
} else {
return super.getOriginatingServletPath(request);
}
}
};
helper.setUrlDecode(false);
configurer.setUrlPathHelper(helper);
}
};
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package edu.internet2.tier.shibboleth.admin.ui.configuration;

import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects;
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.resolver.ResolverException;
import org.apache.http.impl.client.HttpClients;
Expand All @@ -9,6 +10,7 @@
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.joda.time.DateTime;
import org.opensaml.saml.metadata.resolver.ChainingMetadataResolver;
import org.opensaml.saml.metadata.resolver.MetadataResolver;
import org.opensaml.saml.metadata.resolver.impl.FileBackedHTTPMetadataResolver;
Expand All @@ -18,7 +20,10 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
* this is a temporary class until a better way of doing this is found.
Expand All @@ -33,10 +38,17 @@ public class MetadataResolverConfiguration {
@Autowired
IndexWriter indexWriter;

@Autowired
MetadataResolverRepository metadataResolverRepository;

@Bean
public MetadataResolver metadataResolver() throws ResolverException, ComponentInitializationException {
MetadataResolver metadataResolver = new ChainingMetadataResolver();
ChainingMetadataResolver metadataResolver = new ChainingMetadataResolver();
metadataResolver.setId("chain");

List<MetadataResolver> resolvers = new ArrayList<>();

// TODO: remove this later when we allow for creation of arbitrary metadata resolvers
FileBackedHTTPMetadataResolver incommonMR = new FileBackedHTTPMetadataResolver(HttpClients.createMinimal(), "http://md.incommon.org/InCommon/InCommon-metadata.xml", "/tmp/incommon.xml"){
@Override
protected void initMetadataResolver() throws ComponentInitializationException {
Expand All @@ -58,11 +70,27 @@ protected void initMetadataResolver() throws ComponentInitializationException {
throw new ComponentInitializationException(e);
}
}

@Nullable
@Override
public DateTime getLastRefresh() {
return null;
}
};
incommonMR.setId("incommonmd");
incommonMR.setParserPool(openSamlObjects.getParserPool());
incommonMR.initialize();

resolvers.add(incommonMR);

if (!metadataResolverRepository.findAll().iterator().hasNext()) {
edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver mr = new edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver();
mr.setName("incommonmd");
metadataResolverRepository.save(mr);
}

metadataResolver.setResolvers(resolvers);
metadataResolver.initialize();
return metadataResolver;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

/**
* Web security configuration.
*
* Workaround for slashes in URL from [https://stackoverflow.com/questions/48453980/spring-5-0-3-requestrejectedexception-the-request-was-rejected-because-the-url]
*/
@EnableWebSecurity
public class WebSecurityConfig {

Expand All @@ -19,6 +27,13 @@ public class WebSecurityConfig {
@Value("${shibui.default-password:}")
private String defaultPassword;

@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
return firewall;
}

@Bean
@Profile("default")
public WebSecurityConfigurerAdapter defaultAuth() {
Expand Down Expand Up @@ -48,6 +63,12 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
}

@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
}
};
}

Expand All @@ -60,6 +81,12 @@ protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.headers().frameOptions().disable();
}

@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package edu.internet2.tier.shibboleth.admin.ui.controller;

import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects;
import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import net.shibboleth.utilities.java.support.resolver.ResolverException;
import org.opensaml.core.criterion.EntityIdCriterion;
import org.opensaml.core.xml.io.MarshallingException;
import org.opensaml.saml.metadata.resolver.MetadataResolver;
import org.opensaml.saml.saml2.metadata.EntityDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

@Controller
@RequestMapping(value = "/api/entities", method = RequestMethod.GET)
public class EntitiesController {
private static final Logger logger = LoggerFactory.getLogger(EntitiesController.class);

@Autowired
private MetadataResolver metadataResolver;

@Autowired
private EntityDescriptorService entityDescriptorService;

@Autowired
private OpenSamlObjects openSamlObjects;

@RequestMapping(value = "{entityId:.*}")
public ResponseEntity<?> getOne(final @PathVariable String entityId, HttpServletRequest request) throws UnsupportedEncodingException, ResolverException {
EntityDescriptor entityDescriptor = this.getEntityDescriptor(entityId);
if (entityDescriptor == null) {
return ResponseEntity.notFound().build();
}
EntityDescriptorRepresentation entityDescriptorRepresentation = entityDescriptorService.createRepresentationFromDescriptor(entityDescriptor);
return ResponseEntity.ok(entityDescriptorRepresentation);
}

@RequestMapping(value = "{entityId:.*}", produces = "application/xml")
public ResponseEntity<?> getOneXml(final @PathVariable String entityId) throws MarshallingException, ResolverException, UnsupportedEncodingException {
EntityDescriptor entityDescriptor = this.getEntityDescriptor(entityId);
if (entityDescriptor == null) {
return ResponseEntity.notFound().build();
}
final String xml = this.openSamlObjects.marshalToXmlString(entityDescriptor);
return ResponseEntity.ok(xml);
}

private EntityDescriptor getEntityDescriptor(final String entityId) throws ResolverException, UnsupportedEncodingException {
String decodedEntityId = URLDecoder.decode(entityId, "UTF-8");
EntityDescriptor entityDescriptor = this.metadataResolver.resolveSingle(new CriteriaSet(new EntityIdCriterion(decodedEntityId)));
// TODO: we need to clean this up sometime
if (entityDescriptor instanceof edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor) {
((edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor) entityDescriptor).setResourceId(null);
}
return entityDescriptor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public ResponseEntity<?> upload(@RequestParam String metadataUrl, @RequestParam
return handleUploadingEntityDescriptorXml(xmlContents, spName);
} catch (Throwable e) {
LOGGER.error("Error fetching XML metadata from the provided URL: [{}]. The error is: {}", metadataUrl, e);
e.printStackTrace();
LOGGER.error(e.getMessage(), e);
return ResponseEntity
.badRequest()
.body(String.format("Error fetching XML metadata from the provided URL. Error: %s", e.getMessage()));
Expand Down
Loading

0 comments on commit a38788e

Please sign in to comment.