diff --git a/README.md b/README.md index a7f5b9994..07ce59ab0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # shibui +For more information, see `docs` + ## Requirements * Java 8 (note that ONLY Java 8 is supported at this time) diff --git a/backend/build.gradle b/backend/build.gradle index 0710d1888..f03f9f9ed 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -44,7 +44,7 @@ bootWar { // into '/' into '/public' } - archiveName = "${baseName}.war" + archiveName = "${baseName}-${version}.war" } bootJar.dependsOn ':ui:npm_run_buildProd' @@ -61,7 +61,7 @@ bootJar { // into '/' into '/public' } - archiveName = "${baseName}.jar" + archiveName = "${baseName}-${version}.jar" } springBoot { @@ -131,11 +131,14 @@ dependencies { testCompile "org.xmlunit:xmlunit-core:2.5.1" testRuntime 'cglib:cglib-nodep:3.2.5' - compile "net.shibboleth.ext:spring-extensions:5.4.0-SNAPSHOT" + compile "net.shibboleth.ext:spring-extensions:5.4.0" //JSON schema generator testCompile 'com.kjetland:mbknor-jackson-jsonschema_2.12:1.0.29' testCompile 'javax.validation:validation-api:2.0.1.Final' + + //JSON schema validator + compile 'org.sharegov:mjson:1.4.1' } def generatedSrcDir = new File(buildDir, 'generated/src/main/java') @@ -241,5 +244,5 @@ docker { noCache true files tasks.bootJar.outputs files 'src/main/docker-files/loader.properties' - buildArgs(['JAR_FILE': 'shibui.jar']) + buildArgs(['JAR_FILE': "shibui-${version}.jar"]) } \ No newline at end of file diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy index 86ece39b9..13ff91201 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy @@ -2,16 +2,13 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import com.fasterxml.jackson.databind.ObjectMapper import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.MetadataSourcesJsonSchemaResourceLocation import groovy.json.JsonOutput import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.context.properties.ConfigurationProperties -import org.springframework.core.io.ResourceLoader import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RestController -import javax.annotation.PostConstruct - import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR /** @@ -21,17 +18,10 @@ import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR * @author Dmitriy Kopylenko */ @RestController('/api/ui/MetadataSources') -@ConfigurationProperties('shibui') class MetadataSourcesUiDefinitionController { - //Configured via @ConfigurationProperties with 'shibui.metadata-sources-ui-schema-location' property and default - //value set here if that property is not explicitly set in application.properties - String metadataSourcesUiSchemaLocation = 'classpath:metadata-sources-ui-schema.json' - - URL jsonSchemaUrl - @Autowired - ResourceLoader resourceLoader + MetadataSourcesJsonSchemaResourceLocation jsonSchemaLocation @Autowired ObjectMapper jacksonObjectMapper @@ -42,7 +32,7 @@ class MetadataSourcesUiDefinitionController { @GetMapping ResponseEntity getUiDefinitionJsonSchema() { try { - def parsedJson = jacksonObjectMapper.readValue(this.jsonSchemaUrl, Map) + def parsedJson = jacksonObjectMapper.readValue(this.jsonSchemaLocation.url, Map) addReleaseAttributesToJson(parsedJson["properties"]["attributeRelease"]["widget"]) addRelyingPartyOverridesToJson(parsedJson["properties"]["relyingPartyOverrides"]) addRelyingPartyOverridesCollectionDefinitions(parsedJson["definitions"]) @@ -53,7 +43,7 @@ class MetadataSourcesUiDefinitionController { e.printStackTrace() return ResponseEntity.status(INTERNAL_SERVER_ERROR) .body([jsonParseError : e.getMessage(), - sourceUiSchemaDefinitionFile: this.jsonSchemaUrl]) + sourceUiSchemaDefinitionFile: this.jsonSchemaLocation.url]) } } @@ -112,9 +102,4 @@ class MetadataSourcesUiDefinitionController { json[(String)it["name"]] = definition } } - - @PostConstruct - def init() { - jsonSchemaUrl = this.resourceLoader.getResource(this.metadataSourcesUiSchemaLocation).getURL() - } } diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy new file mode 100644 index 000000000..7a4e8111a --- /dev/null +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy @@ -0,0 +1,24 @@ +package edu.internet2.tier.shibboleth.admin.ui.jsonschema + +import org.springframework.core.MethodParameter +import org.springframework.http.converter.HttpMessageConverter +import org.springframework.web.bind.annotation.ControllerAdvice +import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter + +import java.lang.reflect.Type + +/** + * Controller advice implementation for validating relying party overrides payload coming from UI layer + * against pre-defined JSON schema. + * + * @author Dmitriy Kopylenko + */ +@ControllerAdvice +class RelyingPartyOverridesJsonSchemaValidatingControllerAdvice extends RequestBodyAdviceAdapter { + + @Override + boolean supports(MethodParameter methodParameter, Type targetType, Class> converterType) { + def cls = targetType.typeName + print('cx') + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaValidationComponentsConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaValidationComponentsConfiguration.java new file mode 100644 index 000000000..c753d38c7 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaValidationComponentsConfiguration.java @@ -0,0 +1,24 @@ +package edu.internet2.tier.shibboleth.admin.ui.configuration; + +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.MetadataSourcesJsonSchemaResourceLocation; +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.RelyingPartyOverridesJsonSchemaValidatingControllerAdvice; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ResourceLoader; + +/** + * @author Dmitriy Kopylenko + */ +@Configuration +public class JsonSchemaValidationComponentsConfiguration { + + @Bean + public MetadataSourcesJsonSchemaResourceLocation metadataSourcesJsonSchemaResourceLocation(ResourceLoader resourceLoader) { + return new MetadataSourcesJsonSchemaResourceLocation(resourceLoader); + } + + @Bean + public RelyingPartyOverridesJsonSchemaValidatingControllerAdvice relyingPartyOverridesJsonSchemaValidatingControllerAdvice() { + return new RelyingPartyOverridesJsonSchemaValidatingControllerAdvice(); + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaValidationFailedException.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaValidationFailedException.java new file mode 100644 index 000000000..3f8463d66 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaValidationFailedException.java @@ -0,0 +1,20 @@ +package edu.internet2.tier.shibboleth.admin.ui.jsonschema; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +/** + * Indicates JSON schema validation failure. Encapsulates a list of error messages produced by JSON schema validator + * component. + * + * @author Dmitriy Kopylenko + */ +@RequiredArgsConstructor +@Getter +public class JsonSchemaValidationFailedException extends RuntimeException { + + private final List errors; + +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/MetadataSourcesJsonSchemaResourceLocation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/MetadataSourcesJsonSchemaResourceLocation.java new file mode 100644 index 000000000..5e3fab651 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/MetadataSourcesJsonSchemaResourceLocation.java @@ -0,0 +1,58 @@ +package edu.internet2.tier.shibboleth.admin.ui.jsonschema; + +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +/** + * Encapsulates metadata sources JSON schema location. + * + * @author Dmitriy Kopylenko + */ +@ConfigurationProperties("shibui") +public class MetadataSourcesJsonSchemaResourceLocation { + + //Configured via @ConfigurationProperties with 'shibui.metadata-sources-ui-schema-location' property and default + //value set here if that property is not explicitly set in application.properties + private String metadataSourcesUiSchemaLocation = "classpath:metadata-sources-ui-schema.json"; + + private URL jsonSchemaUrl; + + ResourceLoader resourceLoader; + + public MetadataSourcesJsonSchemaResourceLocation(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + public URL getUrl() { + return this.jsonSchemaUrl; + } + + public URI getUri() { + try { + return this.jsonSchemaUrl.toURI(); + } + catch (URISyntaxException ex) { + throw new RuntimeException(ex); + } + } + + @PostConstruct + public void init() { + try { + this.jsonSchemaUrl = this.resourceLoader.getResource(this.metadataSourcesUiSchemaLocation).getURL(); + } + catch (IOException ex) { + throw new BeanCreationException(ex.getMessage(), ex); + } + } +} diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index f7e1eed11..cc5a34059 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -35,7 +35,7 @@ spring.liquibase.enabled=false # Hibernate properties # for production never ever use create, create-drop. It's BEST to use validate -spring.jpa.hibernate.ddl-auto=create +spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl spring.jpa.show-sql=false spring.jpa.properties.hibernate.format_sql=false diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties index ed9e61e67..72d6e706d 100644 --- a/backend/src/main/resources/i18n/messages.properties +++ b/backend/src/main/resources/i18n/messages.properties @@ -80,6 +80,13 @@ brand.footer.links-label-4=Mailing List brand.footer.links-desc-4=Shibboleth.net open-source community mailing list brand.footer.copyright=Copyright \u00A9 Internet2 +brand.unicon=Unicon +brand.unicon-logo=Unicon Logo +brand.i2=Internet 2 +brand.i2-logo=Internet 2 Logo +brand.in-partnership-with=In partnership with +brand.and=and + heading.shibboleth=Shibboleth label.metadata-source=Metadata Source diff --git a/docs/CUSTOMIZATIONS.md b/docs/CUSTOMIZATIONS.md index 0423fbce9..103137ef5 100644 --- a/docs/CUSTOMIZATIONS.md +++ b/docs/CUSTOMIZATIONS.md @@ -45,4 +45,16 @@ If deploying a WAR, one would use the standard packaging for providing items to It is highly recommended that a WAR overlay be used to prevent changing the version fingerprint. Overlay methods exist for both Maven ([https://maven.apache.org/plugins/maven-war-plugin/overlays.html]) and -Gradle ([https://github.com/scalding/gradle-waroverlay-plugin]) projects. \ No newline at end of file +Gradle ([https://github.com/scalding/gradle-waroverlay-plugin]) projects. + +## Sample Customization + +For an example of a customization, refer to the `pac4j-module` in the project source. This module overrides the delivered +authentication method to provide a simple SAML authentication method. Along with code changes required, it also shows +how one would tie it together using a Docker image. + +## Use Cases and Strategies + +Some ideas and strategies for customizing the application. + +TBD \ No newline at end of file diff --git a/docs/DATABASE.md b/docs/DATABASE.md new file mode 100644 index 000000000..09c379ad1 --- /dev/null +++ b/docs/DATABASE.md @@ -0,0 +1,45 @@ +# Database configuration + +The Shibboleth UI application uses Spring Boot and Spring JPA for database configuration. Out of the box, it ships with +JDBC drivers for H2, MariaDB and Postgres. + +By default, it will use an in-memory H2 database. To change which database is used, one should make changes to the +`applications.properties` or `application.yml` file as appropriate. For further information, refer to the appropriate +JDBC driver documentation. + +```properties +# Database Credentials +spring.datasource.username=shibui +spring.datasource.password=shibui + +# Database Configuration H2 +spring.datasource.url=jdbc:h2:mem:shibui;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.platform=h2 +spring.datasource.driverClassName=org.h2.Driver +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.h2.console.enabled=true + + +# Database Configuration PostgreSQL +#spring.datasource.url=jdbc:postgresql://localhost:5432/shibui +#spring.datasource.driverClassName=org.postgresql.Driver +#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect + +#Maria/MySQL DB +#spring.datasource.url=jdbc:mariadb://localhost:3306/shibui +#spring.datasource.driverClassName=org.mariadb.jdbc.Driver +#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect + +# Liquibase properties +spring.liquibase.enabled=false +#spring.liquibase.change-log=classpath:edu/internet2/tier/shibboleth/admin/ui/database/masterchangelog.xml + +# Hibernate properties +# for production never ever use create, create-drop. It's BEST to use validate +spring.jpa.hibernate.ddl-auto=update +spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl +spring.jpa.show-sql=false +spring.jpa.properties.hibernate.format_sql=false + +spring.jpa.hibernate.use-new-id-generator-mappings=true +``` \ No newline at end of file diff --git a/docs/GETTINGSTARTED.md b/docs/GETTINGSTARTED.md new file mode 100644 index 000000000..65da98a2c --- /dev/null +++ b/docs/GETTINGSTARTED.md @@ -0,0 +1,55 @@ +# Getting Started + +## Requirements + +* Java 8 (note that ONLY Java 8 is supported at this time) + +## Running + +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 +``` + +In Apache HTTPD, you'll need something like: + +``` + + AllowEncodedSlashes NoDecode + ServerName shibui.unicon.net + ProxyPass / http://localhost:8080/ nocanon + ProxyPassReverse / http://localhost:8080/ + +``` + +Note the `AllowEncodedSlashes NoDecode`. + +### Running as an executable + +`java -jar shibui.war` + +For complete information on overriding default configuration, see [https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html]. + +### Deploying as a WAR + +The application can be deployed as a WAR file in a Java Servlet 3.0 container. Currently, the application must be run in the root context. + +To override default configuration, see [https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html]. +The easiest way to do this in a servlet container is through the use of system properties + +## Authentication + +Currently, the application is wired with very simple authentication. A password for the user `user` +can be set with the `shibui.default-password` property. If none is set, a default password +will be generated and logged: + +``` +Using default security password: a3d9ab96-9c63-414f-b199-26fcf59e1ffa +``` \ No newline at end of file diff --git a/docs/HOME.md b/docs/HOME.md new file mode 100644 index 000000000..523c3fa38 --- /dev/null +++ b/docs/HOME.md @@ -0,0 +1,6 @@ +# Shibboleth UI + +* [DATABASE] +* [CUSTOMIZATIONS] +* [INTERNATIONALIZATION] +* [DATABASE] \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 678f56673..60c4a518e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ name=shibui group=edu.internet2.tier.shibboleth.admin.ui -version=1.0.1-SNAPSHOT +version=1.5.0-SNAPSHOT shibboleth.version=3.4.0 opensaml.version=3.4.0 diff --git a/pac4j-module/build.gradle b/pac4j-module/build.gradle index 657add297..5133e4e48 100644 --- a/pac4j-module/build.gradle +++ b/pac4j-module/build.gradle @@ -11,9 +11,6 @@ sourceCompatibility = 1.8 targetCompatibility = 1.8 repositories { - maven () { - url 'https://oss.sonatype.org/content/groups/public' - } jcenter() maven { url 'https://build.shibboleth.net/nexus/content/groups/public' @@ -36,8 +33,8 @@ dependencies { compileOnly project(':backend') compile "org.pac4j:spring-security-pac4j:4.0.0" - compile "org.pac4j:pac4j-core:3.3.0-SNAPSHOT" - compile "org.pac4j:pac4j-saml:3.3.0-SNAPSHOT", { + compile "org.pac4j:pac4j-core:3.3.0" + compile "org.pac4j:pac4j-saml:3.3.0", { // opensaml libraries are provided exclude group: 'org.opensaml' } diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index 4d6ba9564..cd56856e3 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -63,27 +63,36 @@ diff --git a/ui/src/app/app.component.scss b/ui/src/app/app.component.scss index 769799707..dbe420760 100644 --- a/ui/src/app/app.component.scss +++ b/ui/src/app/app.component.scss @@ -5,4 +5,11 @@ nav.navbar { .dropdown-menu { min-width: 12rem; } -} \ No newline at end of file +} + +.logo a { + img { + display: inline-block; + max-height: 60px; + } +} diff --git a/ui/src/assets/logo_internet2.png b/ui/src/assets/logo_internet2.png new file mode 100644 index 000000000..a2b280e5b Binary files /dev/null and b/ui/src/assets/logo_internet2.png differ diff --git a/ui/src/assets/logo_unicon.png b/ui/src/assets/logo_unicon.png new file mode 100644 index 000000000..8d45bb447 Binary files /dev/null and b/ui/src/assets/logo_unicon.png differ