diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 783f86488..9907c0ff9 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -42,6 +42,7 @@ spring.jpa.properties.hibernate.format_sql=false spring.jpa.hibernate.use-new-id-generator-mappings=true +# Set the following property to periodically write out the generated metadata files. There is no default value; the following is just an example # shibui.metadata-dir=/opt/shibboleth-idp/metadata/generated shibui.logout-url=/dashboard @@ -59,5 +60,6 @@ shibui.entity-attributes-filters-ui-schema-location=classpath:entity-attributes- ### # metadata-providers.xml write configuration +# Set the following property to periodically write out metadata providers configuration. There is no default value; the following is just an example # shibui.metadataProviders.target=file:/opt/shibboleth-idp/conf/shibui-metadata-providers.xml # shibui.metadataProviders.taskRunRate=30000 diff --git a/backend/src/main/resources/i18n/messages_en.properties b/backend/src/main/resources/i18n/messages_en.properties index 3d4670252..bf1d807bf 100644 --- a/backend/src/main/resources/i18n/messages_en.properties +++ b/backend/src/main/resources/i18n/messages_en.properties @@ -215,6 +215,7 @@ label.entity-id=Entity ID label.service-provider-name=Service Provider Name label.organization=Organization label.contacts=Contacts +label.contact=Contact label.mdui=MDUI Information label.service-provider-sso-descriptor=Service Provider Sso Descriptor label.service-enabled=Service Enabled diff --git a/backend/src/main/resources/metadata-sources-ui-schema.json b/backend/src/main/resources/metadata-sources-ui-schema.json index c79a9ed0c..4d6ef830c 100644 --- a/backend/src/main/resources/metadata-sources-ui-schema.json +++ b/backend/src/main/resources/metadata-sources-ui-schema.json @@ -316,6 +316,7 @@ }, "definitions": { "Contact": { + "title": "label.contact", "type": "object", "required": [ "name", diff --git a/docs/DEFAULTPROPERTIES.md b/docs/DEFAULTPROPERTIES.md new file mode 100644 index 000000000..75916235a --- /dev/null +++ b/docs/DEFAULTPROPERTIES.md @@ -0,0 +1,72 @@ +# Default properties + +This is a reflection of the default `application.properties` file included in the distribution. Note that lines +beginning with `#` are commented out. + +Please refer to the Spring Boot documentation [https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html] +for more information. + +```properties +# Server Configuration +#server.port=8080 + +# Logging Configuration +#logging.config=classpath:log4j2.xml + +logging.level.org.springframework=INFO +logging.level.edu.internet2.tier.shibboleth.admin.ui=INFO + +# 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 + +# Set the following property to periodically write out the generated metadata files. There is no default value; the following is just an example +# shibui.metadata-dir=/opt/shibboleth-idp/metadata/generated +shibui.logout-url=/dashboard + +# spring.profiles.active=default + +#shibui.default-password= + +#Actuator endpoints (info) +# Un-comment to get full git details exposed like author, abbreviated SHA-1, commit message +#management.info.git.mode=full + +### +# metadata-providers.xml write configuration + +# Set the following property to periodically write out metadata providers configuration. There is no default value; the following is just an example +# shibui.metadataProviders.target=file:/opt/shibboleth-idp/conf/shibui-metadata-providers.xml +# shibui.metadataProviders.taskRunRate=30000 +``` \ No newline at end of file diff --git a/docs/METADATAPROVIDERS.md b/docs/METADATAPROVIDERS.md new file mode 100644 index 000000000..32d02149b --- /dev/null +++ b/docs/METADATAPROVIDERS.md @@ -0,0 +1,22 @@ +# Metadata providers + +The application can generate a `metadata-providers.xml` configuration appropriate for use in the Shibboleth IdP. +There are 2 ways to access this configuration: through a web endpoint or a file. + +1. Web endpoint + + A request can be made to the `${ui.baseUrl}/api/MetadataResolvers` to get the + current configuration + +2. File export + + A file can be periodically written to disk. Set the application property `shibui.metadataProviders.target`, + pointing to a spring file resource. Note that there is no value set by default, and nothing will be written + out by default. A file, once defined, will be written every 30 seconds by default. To change the rate, set the + `shibui.metadataProviders.taskRunRate` application property, in milliseconds. + +## Docker considerations + +If writing the file out, one should use a mount in the docker container for the destination. While a bind mount +might be easiest, if running on a Windows host, one might run into problems. This is easily avoided by using a +volume instead. Refer to [https://docs.docker.com/storage/] for more information. \ No newline at end of file diff --git a/docs/METADATASOURCES.md b/docs/METADATASOURCES.md new file mode 100644 index 000000000..2ec2563b6 --- /dev/null +++ b/docs/METADATASOURCES.md @@ -0,0 +1,21 @@ +# Metdata Sources + +Metadata sources in the UI are individual metadata artifacts describing single entities, typically +relying parties. There are 2 ways to access these artifacts. + +1. MDQ + + _To be written_ + +2. File export + + Files can be periodically written to disk. Define the application property `shibui.metadata-dir`, + and the files will be written out by default every 30 seconds. Note that there is no default value + set for this property and no file will be written by default. To change the run rate, set the + `shibui.taskRunRate` application property, in milliseconds. + +## Docker considerations + +If writing the files out, one should use a mount in the docker container for the destination. While a bind mount +might be easiest, if running on a Windows host, one might run into problems. This is easily avoided by using a +volume instead. Refer to [https://docs.docker.com/storage/] for more information. \ No newline at end of file diff --git a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.spec.ts b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.spec.ts index 4ecd91c66..a7e0dd900 100644 --- a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.spec.ts +++ b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.spec.ts @@ -127,7 +127,7 @@ describe('Dashboard Resolvers List Page', () => { it('should route to the wizard page', () => { spyOn(router, 'navigate'); instance.edit(draft); - expect(router.navigate).toHaveBeenCalledWith(['metadata', 'resolver', 'new'], { + expect(router.navigate).toHaveBeenCalledWith(['metadata', 'resolver', 'new', 'blank', 'org-info'], { queryParams: { id: '1' } }); }); diff --git a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts index 8f8caca24..e6577886d 100644 --- a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts +++ b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts @@ -72,7 +72,7 @@ export class DashboardResolversListComponent implements OnInit { edit(entity: MetadataEntity): void { if (entity.isDraft()) { - this.router.navigate(['metadata', 'resolver', 'new'], { + this.router.navigate(['metadata', 'resolver', 'new', 'blank', 'org-info'], { queryParams: { id: entity.getId() } diff --git a/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.ts b/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.ts index 0021727b2..566291404 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.ts +++ b/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.ts @@ -1,6 +1,6 @@ import { Component, OnDestroy } from '@angular/core'; import { Observable, Subject } from 'rxjs'; -import { withLatestFrom, map, distinctUntilChanged, skipWhile } from 'rxjs/operators'; +import { withLatestFrom, map, distinctUntilChanged, skipWhile, filter } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import * as fromResolver from '../reducer'; @@ -60,7 +60,7 @@ export class ResolverWizardStepComponent implements OnDestroy { }, definition })), - skipWhile(({ model, definition }) => !definition || !model), + filter(({ model, definition }) => (definition && model)), map(({ model, definition }) => definition.formatter(model)) ); @@ -76,7 +76,7 @@ export class ResolverWizardStepComponent implements OnDestroy { this.valueChangeEmitted$.pipe( withLatestFrom(this.definition$), - skipWhile(([ changes, definition ]) => !definition || !changes), + filter(([ changes, definition ]) => (!!definition && !!changes)), map(([ changes, definition ]) => definition.parser(changes.value)), withLatestFrom(this.store.select(fromResolver.getSelectedDraft)), map(([changes, original]) => ({ ...original, ...changes })) diff --git a/ui/src/app/metadata/resolver/container/resolver-wizard.component.spec.ts b/ui/src/app/metadata/resolver/container/resolver-wizard.component.spec.ts index 2851feb55..fdd16fd5f 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard.component.spec.ts +++ b/ui/src/app/metadata/resolver/container/resolver-wizard.component.spec.ts @@ -16,7 +16,7 @@ import { METADATA_SOURCE_WIZARD } from '../wizard-definition'; import { MetadataSourceWizard } from '../../domain/model/wizards/metadata-source-wizard'; import { initialState } from '../reducer/entity.reducer'; import { MockWizardModule } from '../../../../testing/wizard.stub'; -import { RouterStateSnapshot } from '@angular/router'; +import { RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router'; import { NgbModalStub } from '../../../../testing/modal.stub'; import { of } from 'rxjs'; import { MetadataResolver } from '../../domain/model'; @@ -115,9 +115,12 @@ describe('Resolver Wizard Component', () => { })); describe('canDeactivate method', () => { - it('should return true if moving to another edit page', async(() => { + it('should return true if moving to another page', async(() => { app.canDeactivate(null, null, { - url: 'wizard' + url: 'blank', + root: { + queryParams: { id: 'foo' } + } } as RouterStateSnapshot).subscribe((can) => { expect(can).toBe(true); }); diff --git a/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts b/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts index ba2221fda..d6cac6053 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts +++ b/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts @@ -26,9 +26,8 @@ import { SetDefinition, SetIndex, SetDisabled, ClearWizard } from '../../../wiza import * as fromWizard from '../../../wizard/reducer'; import { LoadSchemaRequest } from '../../../wizard/action/wizard.action'; import { UnsavedEntityComponent } from '../../domain/component/unsaved-entity.dialog'; -import { ModalService } from '../../../core/service/modal.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { UpdateChanges, Clear } from '../action/entity.action'; +import { Clear } from '../action/entity.action'; @Component({ selector: 'resolver-wizard-page', @@ -172,7 +171,7 @@ export class ResolverWizardComponent implements OnDestroy, CanComponentDeactivat currentState: RouterStateSnapshot, nextState: RouterStateSnapshot ): Observable { - if (nextState.url.match('blank')) { return of(true); } + if (nextState.url.match('blank') && !!nextState.root.queryParams.id) { return of(true); } if (Object.keys(this.changes).length > 0) { let modal = this.modalService.open(UnsavedEntityComponent); modal.componentInstance.message = 'resolver'; diff --git a/ui/src/app/metadata/resolver/effect/draft-collection.effects.ts b/ui/src/app/metadata/resolver/effect/draft-collection.effects.ts index 024c923eb..f6e203381 100644 --- a/ui/src/app/metadata/resolver/effect/draft-collection.effects.ts +++ b/ui/src/app/metadata/resolver/effect/draft-collection.effects.ts @@ -120,7 +120,7 @@ export class DraftCollectionEffects { ) ), tap(() => { - this.store.dispatch(new ClearWizard()); + // this.store.dispatch(new ClearWizard()); this.store.dispatch(new Clear()); }) ); diff --git a/ui/src/app/wizard/component/wizard.component.html b/ui/src/app/wizard/component/wizard.component.html index 4f5a6e5c6..c3612bec2 100644 --- a/ui/src/app/wizard/component/wizard.component.html +++ b/ui/src/app/wizard/component/wizard.component.html @@ -1,4 +1,4 @@ -