diff --git a/demo/complex/.env b/demo/complex/.env
new file mode 100644
index 0000000..03f48af
--- /dev/null
+++ b/demo/complex/.env
@@ -0,0 +1,14 @@
+AUTHENTICATION=internal
+ENV=demo
+USERTOKEN=
+REPO_DATABASE_TYPE=mariadb
+REPO_JDBC_URL=default
+REPO_HOST=midpoint-data
+REPO_PORT=default
+REPO_DATABASE=midpoint
+REPO_USER=root
+REPO_PASSWORD_FILE=/run/secrets/m_database_password.txt
+KEYSTORE_PASSWORD_FILE=/run/secrets/m_keystore_password.txt
+MEM=2048m
+LOGOUT_URL=https://localhost:8443/Shibboleth.sso/Logout
+SSO_HEADER=uid
diff --git a/demo/complex/configs-and-secrets/grouper/application/database_password.txt b/demo/complex/configs-and-secrets/grouper/application/database_password.txt
new file mode 100644
index 0000000..e69de29
diff --git a/demo/complex/configs-and-secrets/grouper/application/grouper-loader.properties b/demo/complex/configs-and-secrets/grouper/application/grouper-loader.properties
new file mode 100644
index 0000000..d73a54a
--- /dev/null
+++ b/demo/complex/configs-and-secrets/grouper/application/grouper-loader.properties
@@ -0,0 +1,64 @@
+#################################
+## LDAP connections
+#################################
+# specify the ldap connection with user, pass, url
+# the string after "ldap." is the ID of the connection, and it should not have
+# spaces or other special chars in it. In this case is it "personLdap"
+
+#note the URL should start with ldap: or ldaps: if it is SSL.
+#It should contain the server and port (optional if not default), and baseDn,
+#e.g. ldaps://ldapserver.school.edu:636/dc=school,dc=edu
+#ldap.demo.url = ldap://directory:389/dc=internet2,dc=edu
+ldap.demo.url = ldap://directory:389
+
+#optional, if authenticated
+ldap.demo.user = cn=admin,dc=internet2,dc=edu
+
+#optional, if authenticated note the password can be stored encrypted in an external file
+#ldap.demo.pass = ${java.lang.System.getenv().get('SUBJECT_SOURCE_LDAP_PASSWORD_FILE') != null ? org.apache.commons.io.FileUtils.readFileToString(java.lang.System.getenv().get('SUBJECT_SOURCE_LDAP_PASSWORD_FILE'), "utf-8") : java.lang.System.getenv().get('SUBJECT_SOURCE_LDAP_PASSWORD')}
+ldap.demo.pass=password
+
+#optional, if you are using tls, set this to true. Generally you will not be using an SSL URL to use TLS...
+ldap.demo.tls = false
+
+#optional, if using sasl
+#ldap.personLdap.saslAuthorizationId =
+#ldap.personLdap.saslRealm =
+
+#optional (note, time limit is for search operations, timeout is for connection timeouts),
+#most of these default to vt-ldap defaults. times are in millis
+#validateOnCheckout defaults to true if all other validate methods are false
+#ldap.personLdap.batchSize =
+#ldap.personLdap.countLimit =
+#ldap.personLdap.timeLimit =
+#ldap.personLdap.timeout =
+#ldap.personLdap.minPoolSize =
+#ldap.personLdap.maxPoolSize =
+#ldap.personLdap.validateOnCheckIn =
+#ldap.personLdap.validateOnCheckOut =
+#ldap.personLdap.validatePeriodically =
+#ldap.personLdap.validateTimerPeriod =
+#ldap.personLdap.pruneTimerPeriod =
+#if connections expire after a certain amount of time, this is it, in millis, defaults to 300000 (5 minutes)
+#ldap.personLdap.expirationTime =
+
+#make the paths fully qualified and not relative to the loader group.
+loader.ldap.requireTopStemAsStemFromConfigGroup=false
+
+#####################################
+## Messaging integration with change log
+#####################################
+changeLog.consumer.rabbitMqMessagingSample.quartzCron = 0 * * * * ?
+
+# note, change "messagingSample" in key to be the name of the consumer. e.g. changeLog.consumer.someNameAnyName.class
+changeLog.consumer.rabbitMqMessagingSample.class = edu.internet2.middleware.grouper.changeLog.esb.consumer.EsbConsumer
+
+changeLog.consumer.rabbitMqMessagingSample.publisher.class = edu.internet2.middleware.grouper.changeLog.esb.consumer.EsbMessagingPublisher
+changeLog.consumer.rabbitMqMessagingSample.publisher.messagingSystemName = rabbitmq
+# note, routingKey property is valid only for rabbitmq. For other messaging systems, it is ignored.
+changeLog.consumer.rabbitMqMessagingSample.publisher.routingKey =
+## queue or topic
+changeLog.consumer.rabbitMqMessagingSample.publisher.messageQueueType = queue
+changeLog.consumer.rabbitMqMessagingSample.publisher.queueOrTopicName = sampleQueue
+## this is optional if not using "id" for subjectId, need to be a subject attribute in the sources.xml
+#changeLog.consumer.rabbitMqMessagingSample.publisher.addSubjectAttributes = email
diff --git a/demo/complex/configs-and-secrets/grouper/application/grouper.client.properties b/demo/complex/configs-and-secrets/grouper/application/grouper.client.properties
new file mode 100644
index 0000000..ee9895f
--- /dev/null
+++ b/demo/complex/configs-and-secrets/grouper/application/grouper.client.properties
@@ -0,0 +1,112 @@
+#
+# Copyright 2014 Internet2
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Grouper client configuration
+# $Id: grouper.client.example.properties,v 1.24 2009-12-30 04:23:02 mchyzer Exp $
+#
+
+# The grouper client uses Grouper Configuration Overlays (documented on wiki)
+# By default the configuration is read from grouper.client.base.properties
+# (which should not be edited), and the grouper.client.properties overlays
+# the base settings. See the grouper.client.base.properties for the possible
+# settings that can be applied to the grouper.client.properties
+
+########################################
+## LDAP connection settings
+########################################
+
+# url of directory, including the base DN (distinguished name)
+# e.g. ldap://server.school.edu/dc=school,dc=edu
+# e.g. ldaps://server.school.edu/dc=school,dc=edu
+grouperClient.ldap.url =
+
+# kerberos principal used to connect to ldap
+grouperClient.ldap.login =
+
+# password for shared secret authentication to ldap
+# or you can put a filename with an encrypted password
+grouperClient.ldap.password =
+
+########################################
+## Web service Connection settings
+########################################
+
+# url of web service, should include everything up to the first resource to access
+# e.g. http://groups.school.edu:8090/grouper-ws/servicesRest
+# e.g. https://groups.school.edu/grouper-ws/servicesRest
+grouperClient.webService.url = https://grouper-ws/grouper-ws/servicesRest
+
+# kerberos principal used to connect to web service
+grouperClient.webService.login = banderson
+
+# password for shared secret authentication to web service
+# or you can put a filename with an encrypted password
+grouperClient.webService.password.elConfig = ${java.lang.System.getenv().get('GROUPER_CLIENT_WEBSERVICE_PASSWORD_FILE') != null ? org.apache.commons.io.FileUtils.readFileToString(java.lang.System.getenv().get('GROUPER_CLIENT_WEBSERVICE_PASSWORD_FILE'), "utf-8") : java.lang.System.getenv().get('GROUPER_CLIENT_WEBSERVICE_PASSWORD') }
+
+
+################################
+## Grouper Messaging System
+################################
+
+# name of messaging system which is the default
+grouper.messaging.default.name.of.messaging.system = rabbitmq
+
+# name of a messaging system. note, "grouperBuiltinMessaging" can be arbitrary
+# grouper.messaging.system.grouperBuiltinMessaging.name = grouperBuiltinMessaging
+
+# class that implements edu.internet2.middleware.grouperClient.messaging.GrouperMessagingSystem
+# grouper.messaging.system.grouperBuiltinMessaging.class = edu.internet2.middleware.grouper.messaging.GrouperBuiltinMessagingSystem
+
+# name of a messaging system. note, "grouperBuiltinMessaging" can be arbitrary
+grouper.messaging.system.rabbitmqSystem.name = rabbitmqSystem
+
+# class that implements edu.internet2.middleware.grouperClient.messaging.GrouperMessagingSystem
+grouper.messaging.system.rabbitmqSystem.class = edu.internet2.middleware.grouperMessagingRabbitmq.GrouperMessagingRabbitmqSystem
+
+# host address of rabbitmq queue
+grouper.messaging.system.rabbitmqSystem.host = mq
+
+# virtual host of rabbitmq queue
+grouper.messaging.system.rabbitmqSystem.virtualhost =
+
+# port of rabbitmq queue
+grouper.messaging.system.rabbitmqSystem.port =
+
+grouper.messaging.system.rabbitmqSystem.defaultPageSize = 10
+
+grouper.messaging.system.rabbitmqSystem.maxPageSize = 50
+
+
+# name of a messaging system, required
+grouper.messaging.system.rabbitmq.name = rabbitmq
+
+# default system settings to this messaging system, note, there is only one level of inheritance
+grouper.messaging.system.rabbitmq.defaultSystemName = rabbitmqSystem
+
+grouper.messaging.system.rabbitmq.user = guest
+
+#pass
+grouper.messaging.system.rabbitmq.password.elConfig = ${java.lang.System.getenv().get('RABBITMQ_PASSWORD_FILE') != null ? org.apache.commons.io.FileUtils.readFileToString(java.lang.System.getenv().get('RABBITMQ_PASSWORD_FILE'), "utf-8") : java.lang.System.getenv().get('RABBITMQ_PASSWORD') }
+# set the following three properties if you want to use TLS connection to rabbitmq. All three need to be populated.
+# TLS Version
+#grouper.messaging.system.rabbitmqSystem.tlsVersion = TLSv1.1
+
+# path to trust store file
+#grouper.messaging.system.rabbitmqSystem.pathToTrustStore =
+
+# trust passphrase
+#grouper.messaging.system.rabbitmqSystem.trustPassphrase =
diff --git a/demo/complex/configs-and-secrets/grouper/application/grouper.hibernate.properties b/demo/complex/configs-and-secrets/grouper/application/grouper.hibernate.properties
new file mode 100644
index 0000000..f4849ba
--- /dev/null
+++ b/demo/complex/configs-and-secrets/grouper/application/grouper.hibernate.properties
@@ -0,0 +1,29 @@
+#
+# Grouper Hibernate Configuration
+# $Id: grouper.hibernate.example.properties,v 1.9 2009-08-11 20:18:09 mchyzer Exp $
+#
+
+# The grouper hibernate config uses Grouper Configuration Overlays (documented on wiki)
+# By default the configuration is read from grouper.hibernate.base.properties
+# (which should not be edited), and the grouper.hibernate.properties overlays
+# the base settings. See the grouper.hibernate.base.properties for the possible
+# settings that can be applied to the grouper.hibernate.properties
+
+########################################
+## DB settings
+########################################
+
+# e.g. mysql: jdbc:mysql://localhost:3306/grouper
+# e.g. p6spy (log sql): [use the URL that your DB requires]
+# e.g. oracle: jdbc:oracle:thin:@server.school.edu:1521:sid
+# e.g. hsqldb (a): jdbc:hsqldb:dist/run/grouper;create=true
+# e.g. hsqldb (b): jdbc:hsqldb:hsql://localhost:9001/grouper
+# e.g. postgres: jdbc:postgresql://localhost:5432/database
+# e.g. mssql: jdbc:sqlserver://localhost:3280;databaseName=grouper
+hibernate.connection.url = jdbc:mysql://grouper-data:3306/grouper?CharSet=utf8&useUnicode=true&characterEncoding=utf8
+
+hibernate.connection.username = root
+# If you are using an empty password, depending upon your version of
+# Java and Ant you may need to specify a password of "".
+# Note: you can keep passwords external and encrypted: https://bugs.internet2.edu/jira/browse/GRP-122
+hibernate.connection.password.elConfig = ${java.lang.System.getenv().get('GROUPER_DATABASE_PASSWORD_FILE') != null ? org.apache.commons.io.FileUtils.readFileToString(java.lang.System.getenv().get('GROUPER_DATABASE_PASSWORD_FILE'), "utf-8") : java.lang.System.getenv().get('GROUPER_DATABASE_PASSWORD') }
diff --git a/demo/complex/configs-and-secrets/grouper/application/grouper.properties b/demo/complex/configs-and-secrets/grouper/application/grouper.properties
new file mode 100644
index 0000000..c931287
--- /dev/null
+++ b/demo/complex/configs-and-secrets/grouper/application/grouper.properties
@@ -0,0 +1,25 @@
+#
+# Grouper Configuration
+# $Id: grouper.example.properties,v 1.48 2009-12-16 06:02:30 mchyzer Exp $
+#
+
+# Grouper uses Grouper Configuration Overlays (documented on wiki)
+# By default the configuration is read from grouper.base.properties
+# (which should not be edited), and the grouper.properties overlays
+# the base settings. See the grouper.base.properties for the possible
+# settings that can be applied to the grouper.properties
+
+#if groups like the wheel group should be auto-created for convenience (note: check config needs to be on)
+configuration.autocreate.system.groups = true
+
+# A wheel group allows you to enable non-GrouperSystem subjects to act
+# like a root user when interacting with the registry.
+groups.wheel.use = true
+
+# Set to the name of the group you want to treat as the wheel group.
+# The members of this group will be treated as root-like users.
+groups.wheel.group = etc:sysadmingroup
+
+# Used to allow Include Exclude groups
+grouperIncludeExclude.use = true
+grouperIncludeExclude.requireGroups.use = true
diff --git a/demo/complex/configs-and-secrets/grouper/application/rabbitmq_password.txt b/demo/complex/configs-and-secrets/grouper/application/rabbitmq_password.txt
new file mode 100644
index 0000000..158f675
--- /dev/null
+++ b/demo/complex/configs-and-secrets/grouper/application/rabbitmq_password.txt
@@ -0,0 +1 @@
+guest
\ No newline at end of file
diff --git a/demo/complex/configs-and-secrets/grouper/application/subject.properties b/demo/complex/configs-and-secrets/grouper/application/subject.properties
new file mode 100644
index 0000000..535e728
--- /dev/null
+++ b/demo/complex/configs-and-secrets/grouper/application/subject.properties
@@ -0,0 +1,75 @@
+subject.sources.xml.location =
+
+subjectApi.source.ldap.id = ldap
+subjectApi.source.ldap.name = EDU Ldap
+subjectApi.source.ldap.types = person
+subjectApi.source.ldap.adapterClass = edu.internet2.middleware.grouper.subj.GrouperJndiSourceAdapter
+subjectApi.source.ldap.param.INITIAL_CONTEXT_FACTORY.value = com.sun.jndi.ldap.LdapCtxFactory
+subjectApi.source.ldap.param.PROVIDER_URL.value = ldap://directory:389
+subjectApi.source.ldap.param.SECURITY_AUTHENTICATION.value = simple
+subjectApi.source.ldap.param.SECURITY_PRINCIPAL.value = cn=admin,dc=internet2,dc=edu
+subjectApi.source.ldap.param.SECURITY_CREDENTIALS.value.elConfig = ${java.lang.System.getenv().get('SUBJECT_SOURCE_LDAP_PASSWORD_FILE') != null ? org.apache.commons.io.FileUtils.readFileToString(java.lang.System.getenv().get('SUBJECT_SOURCE_LDAP_PASSWORD_FILE'), "utf-8") : java.lang.System.getenv().get('SUBJECT_SOURCE_LDAP_PASSWORD')}
+subjectApi.source.ldap.param.SubjectID_AttributeType.value = uid
+subjectApi.source.ldap.param.SubjectID_formatToLowerCase.value = false
+subjectApi.source.ldap.param.Name_AttributeType.value = cn
+subjectApi.source.ldap.param.Description_AttributeType.value = cn
+subjectApi.source.ldap.param.VTLDAP_VALIDATOR.value = ConnectLdapValidator
+subjectApi.source.ldap.param.subjectVirtualAttribute_0_searchAttribute0.value = ${subjectUtils.defaultIfBlank(subject.getAttributeValueOrCommaSeparated('uid'), "")},${subjectUtils.defaultIfBlank(subject.getAttributeValueOrCommaSeparated('cn'), "")},${subjectUtils.defaultIfBlank(subject.getAttributeValueOrCommaSeparated('exampleEduRegId'), "")}
+subjectApi.source.ldap.param.sortAttribute0.value = cn
+subjectApi.source.ldap.param.searchAttribute0.value = searchAttribute0
+
+# STATUS SECTION for searches to filter out inactives and allow
+# the user to filter by status with e.g. status=all
+# this is optional, and advanced
+#
+# field in database or ldap or endpoint that is the status field
+#subjectApi.source.example.param.statusDatastoreFieldName.value = status
+
+# search string from user which represents the status. e.g. status=active
+#subjectApi.source.example.param.statusLabel.value = status
+
+# available statuses from screen (if not specified, any will be allowed). comma separated list.
+# Note, this is optional and you probably dont want to configure it, it is mostly necessary
+# when you have multiple sources with statuses... if someone types an invalid status
+# and you have this configured, it will not filter by it
+#subjectApi.source.example.param.statusesFromUser.value = Active, Inactive, Pending, All
+
+# all label from the user
+#subjectApi.source.example.param.statusAllFromUser.value = All
+
+# if no status is specified, this will be used (e.g. for active only). Note, the value should be of the
+# form the user would type in
+#subjectApi.source.example.param.statusSearchDefault.value = status=active
+
+# translate between screen values of status, and the data store value. Increment the 0 to 1, 2, etc for more translations.
+# so the user could enter: status=active, and that could translate to status_col=A. The 'user' is what the user types in,
+# the 'datastore' is what is in the datastore. The user part is not case-sensitive. Note, this could be a many to one
+#subjectApi.source.example.param.statusTranslateUser0.value = active
+#subjectApi.source.example.param.statusTranslateDatastore0.value = A
+
+# subject identifier to store in grouper's member table. this is used to increase speed of loader and perhaps for provisioning
+# you can have up to max 1 subject identifier
+#subjectApi.source.example.param.subjectIdentifierAttribute0.value = uid
+
+#searchSubject: find a subject by ID. ID is generally an opaque and permanent identifier, e.g. 12345678.
+# Each subject has one and only on ID. Returns one result when searching for one ID.
+subjectApi.source.ldap.search.searchSubject.param.filter.value = (&(uid=%TERM%)(objectclass=person))
+subjectApi.source.ldap.search.searchSubject.param.scope.value = SUBTREE_SCOPE
+subjectApi.source.ldap.search.searchSubject.param.base.value = ou=people,dc=internet2,dc=edu
+
+#searchSubjectByIdentifier: find a subject by identifier. Identifier is anything that uniquely
+# identifies the user, e.g. jsmith or jsmith@institution.edu.
+# Subjects can have multiple identifiers. Note: it is nice to have if identifiers are unique
+# even across sources. Returns one result when searching for one identifier.
+subjectApi.source.ldap.search.searchSubjectByIdentifier.param.filter.value = (&(|(uid=%TERM%)(employeeNumber=%TERM%))(objectclass=person))
+subjectApi.source.ldap.search.searchSubjectByIdentifier.param.scope.value = SUBTREE_SCOPE
+subjectApi.source.ldap.search.searchSubjectByIdentifier.param.base.value = ou=people,dc=internet2,dc=edu
+
+# search: find subjects by free form search. Returns multiple results.
+
+subjectApi.source.ldap.search.search.param.filter.value = (&(|(|(uid=%TERM%)(cn=*%TERM%*))(uid=%TERM%*))(objectclass=person))
+subjectApi.source.ldap.search.search.param.scope.value = SUBTREE_SCOPE
+subjectApi.source.ldap.search.search.param.base.value = ou=people,dc=internet2,dc=edu
+
+subjectApi.source.ldap.attributes = givenName, sn, uid, mail, employeeNumber
+subjectApi.source.ldap.internalAttributes = searchAttribute0
diff --git a/demo/complex/configs-and-secrets/grouper/httpd/cachain-cer.pem b/demo/complex/configs-and-secrets/grouper/httpd/cachain-cer.pem
new file mode 100644
index 0000000..e69de29
diff --git a/demo/complex/configs-and-secrets/grouper/httpd/host-cert.pem b/demo/complex/configs-and-secrets/grouper/httpd/host-cert.pem
new file mode 100644
index 0000000..9cc228a
--- /dev/null
+++ b/demo/complex/configs-and-secrets/grouper/httpd/host-cert.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDPDCCAiQCCQDNZe8r0hVtuTANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCTUkxEjAQBgNVBAcMCUFubiBBcmJvcjEXMBUGA1UECgwOSW50
+ZXJuZXQyL1RJRVIxFzAVBgNVBAMMDnNwLmV4YW1wbGUub3JnMB4XDTE3MDkyMjE5
+NTAzNVoXDTI3MDkyMDE5NTAzNVowYDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1J
+MRIwEAYDVQQHDAlBbm4gQXJib3IxFzAVBgNVBAoMDkludGVybmV0Mi9USUVSMRcw
+FQYDVQQDDA5zcC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAMTNJmsNpTpR4NrDJwOgK/o3UYlNdi1c6xBflt+liLAsQc160QReV4dS
+SGK8LZvN58a/BTIsH8dLhQlUQ8qQUY2AfolVrNxb7Waumeh/POzYUTRylnoGpU3W
+bGMEPxE/AdgP5U/adYvyu4XI5epv7wjZJOTqcVag15SalY+aso+ZC/5l+UzRxmWB
+ZxKTsSL1y7PFehY4/Zl3Y3oGVsVl/zspt5lteoZQeeVxUX29S3Af11yHY4xpEp+7
+rvAzY/nlsTiHAsUoCFK/NFQ2evvSRx52B9Fk1cWP1MDVDm2QjQqD9xBGYSnX6bhQ
+ejVx7JUJHlblu2Q5p5XdW0BihgFluoECAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
+n/qhYnIviPs4tglCdrw+M7gbqKNWadDC3F9HDYzlJMFeS/ae2turhEUgQPbYPDQQ
+eO3oOILtvCXNFUPM58jf8V5YFRrOqrTgx44kexQDaHO5YYNft5tF5TdvBYE2gOVr
+GdYrH2iSP8WX+Yy7JH5uqkfwWzEntWHJdey39rCWKAUCCB35+/2b4N53Qmlv2+ug
+CpNJYFtXInd4YMmM5HjXLyoWXtjnKiwDqYUCeYPSwAajnCqRqRXUX0gYTFDRiwRP
+HbmO9We0nqoc/71nikmGGoSRMO/zWVMFjwmAx1fGiWdU61sjGX8sHifzmVyJVEBI
+Z75p+JrWYZJYrx/vpWxL8g==
+-----END CERTIFICATE-----
diff --git a/demo/complex/configs-and-secrets/grouper/httpd/host-key.pem b/demo/complex/configs-and-secrets/grouper/httpd/host-key.pem
new file mode 100644
index 0000000..1b0b579
--- /dev/null
+++ b/demo/complex/configs-and-secrets/grouper/httpd/host-key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEzSZrDaU6UeDa
+wycDoCv6N1GJTXYtXOsQX5bfpYiwLEHNetEEXleHUkhivC2bzefGvwUyLB/HS4UJ
+VEPKkFGNgH6JVazcW+1mrpnofzzs2FE0cpZ6BqVN1mxjBD8RPwHYD+VP2nWL8ruF
+yOXqb+8I2STk6nFWoNeUmpWPmrKPmQv+ZflM0cZlgWcSk7Ei9cuzxXoWOP2Zd2N6
+BlbFZf87KbeZbXqGUHnlcVF9vUtwH9dch2OMaRKfu67wM2P55bE4hwLFKAhSvzRU
+Nnr70kcedgfRZNXFj9TA1Q5tkI0Kg/cQRmEp1+m4UHo1ceyVCR5W5btkOaeV3VtA
+YoYBZbqBAgMBAAECggEAA/5t0ypZug9DUu0283niqpdIzlKGHXGPS6vE8hD37ytW
+wobFiyMm/5YJ5gcPnePV2lCyGEyQ8Ih10LSnE4tOPGLpLnxQn8A11ymf8fnzEJNr
+Qnc42o0b+bJqTLAfX4g5z1qzOqWiUQ7CA3sKP3G6FiHh/8tKNYnaFif09Q8cpJFb
+YDDkvm48NJgsrIoCgmaFIQIn+yDzGQKWwTNMIks+RByWpc67j1x1kiyQM1RfrEev
+Yyq/ZkP66IYZzmZKpFCWGs5qbRZdxyXNpq85DjwA99lAH7vxtMJHQM4z1h1eDH4L
+Ma5hEnmmHu4D5lF2GDQYflvuFdDGH5tThO6MV0IrSQKBgQD+kvEtNxJCMxLOVFyV
+NWF3pk/i2nkD+53t/VPXjMPtW7IesouEGzU82I/fT2wUTkNwFdkVpv37qoLypKZm
+npJFxr6abQNjiDh2Fsh8/iuJfvdZUFJbCEY6NS58qgjix8XCQKRD06EugK7uekIZ
+zJnttF3qVBBD8Z8Uwxz8i+jF1wKBgQDF51y/5XB6Bz47cdxw7P8NsfnTz2V3H0HU
+OnlEBANbhmBadjU8dqbM54Nxbn7VOdooXPuSnAKJ9vPDg1n5Y/GO+lgldNzfyK6g
+HnbldSu0zBvAaGvmAjLjetEtOkBqYkrHJlT6JAems/Kc/YX5uooAz9/jNJFXP9++
+KbjH3CzHZwKBgQC6ppxEDZPKi83nD/2NvMTIyFzcNFj0LaEepFW7vc7NkiSn0zrt
+0lEXWqUqEv5oaPWTEcHH2VdxFRTLuSL0LKGMnWqUqQcKDA9xrcSzuFvNhRTwHC81
+5XwwI1wBNV4sgFKj2WdW/6y2/szDt0oNxnC50zvkmlwOpPKBc4kmNaKmowKBgBmC
+uXIDIXyZcmw3QTNNWZNqXcnv8iRo4xN4dilOWyBxMfp3QmWI5feD4G2+0Jqr2nNZ
+iRRdB/bA3qtVQ0PinkDQBIzPg6lVNS1uv+TUNc4YgXtL+pyrq+Om8U/jMmqEQR9q
+0YltG49houSZyatnYGK6aSHgpNuaYD0jI66fsyYBAoGAMefyD0I/ncArjuf58hVQ
+zSjxfcvlja9okrC8ZgqsVluezcm4rQNcSjBnESGTCjJC7O29AofGLHkvnsBQDiGk
+hE38IRisd+okXdApr41ifWDhmtASud5q6wlhOpMmQxg+OALf1rTvFYhbnFEXV/KY
+e5A4iXLRIbxbmXZDa35Rebw=
+-----END PRIVATE KEY-----
diff --git a/demo/complex/configs-and-secrets/grouper/shibboleth/idp-metadata.xml b/demo/complex/configs-and-secrets/grouper/shibboleth/idp-metadata.xml
new file mode 100644
index 0000000..5a70824
--- /dev/null
+++ b/demo/complex/configs-and-secrets/grouper/shibboleth/idp-metadata.xml
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+
+ example.org
+
+
+
+
+
+
+
+MIIDEzCCAfugAwIBAgIUS9SuTXwsFVVG+LjOEAbLqqT/el0wDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMjZaFw0zNTEy
+MTEwMjIwMjZaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCMAoDHx8xCIfv/6QKqt9mcHYmEJ8y2dKprUbpdcOjH
+YvNPIl/lHPsUyrb+Nc+q2CDeiWjVk1mWYq0UpIwpBMuw1H6+oOqr4VQRi65pin0M
+SfE0MWIaFo5FPvpvoptkHD4gvREbm4swyXGMczcMRfqgalFXhUD2wz8W3XAM5Cq2
+03XeJbj6TwjvKatG5XPdeUe2FBGuOO2q54L1hcIGnLMCQrg7D31lR13PJbjnJ0No
+5C3k8TPuny6vJsBC03GNLNKfmrKVTdzr3VKp1uay1G3DL9314fgmbl8HA5iRQmy+
+XInUU6/8NXZSF59p3ITAOvZQeZsbJjg5gGDip5OZo9YlAgMBAAGjWzBZMB0GA1Ud
+DgQWBBRPlM4VkKZ0U4ec9GrIhFQl0hNbLDA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAIZ0a1ov3my3ljJG588I/PHx+TxAWONWmpKbO9c/qI3Drxk4oRIffiac
+ANxdvtabgIzrlk5gMMisD7oyqHJiWgKv5Bgctd8w3IS3lLl7wHX65mTKQRXniG98
+NIjkvfrhe2eeJxecOqnDI8GOhIGCIqZUn8ShdM/yHjhQ2Mh0Hj3U0LlKvnmfGSQl
+j0viGwbFCaNaIP3zc5UmCrdE5h8sWL3Fu7ILKM9RyFa2ILHrJScV9t623IcHffHP
+IeaY/WtuapsrqRFxuQL9QFWN0FsRIdLmjTq+00+B/XnnKRKFBuWfjhHLF/uu8f+E
+t6Lf23Kb8yD6ZR7dihMZAGHnYQ/hlhM=
+
+
+
+
+
+
+
+
+
+MIIDFDCCAfygAwIBAgIVAN3vv+b7KN5Se9m1RZsCllp/B/hdMA0GCSqGSIb3DQEB
+CwUAMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwHhcNMTUxMjExMDIyMDE0WhcNMzUx
+MjExMDIyMDE0WjAVMRMwEQYDVQQDDAppZHB0ZXN0YmVkMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAh91caeY0Q85uhaUyqFwP2bMjwMFxMzRlAoqBHd7g
+u6eo4duaeLz1BaoR2XTBpNNvFR5oHH+TkKahVDGeH5+kcnIpxI8JPdsZml1srvf2
+Z6dzJsulJZUdpqnngycTkGtZgEoC1vmYVky2BSAIIifmdh6s0epbHnMGLsHzMKfJ
+Cb/Q6dYzRWTCPtzE2VMuQqqWgeyMr7u14x/Vqr9RPEFsgY8GIu5jzB6AyUIwrLg+
+MNkv6aIdcHwxYTGL7ijfy6rSWrgBflQoYRYNEnseK0ZHgJahz4ovCag6wZAoPpBs
+uYlY7lEr89Ucb6NHx3uqGMsXlDFdE4QwfDLLhCYHPvJ0uwIDAQABo1swWTAdBgNV
+HQ4EFgQUAkOgED3iYdmvQEOMm6u/JmD/UTQwOAYDVR0RBDEwL4IKaWRwdGVzdGJl
+ZIYhaHR0cHM6Ly9pZHB0ZXN0YmVkL2lkcC9zaGliYm9sZXRoMA0GCSqGSIb3DQEB
+CwUAA4IBAQBIdd4YWlnvJjql8+zKKgmWgIY7U8DA8e6QcbAf8f8cdE33RSnjI63X
+sv/y9GfmbAVAD6RIAXPFFeRYJ08GOxGI9axfNaKdlsklJ9bk4ducHqgCSWYVer3s
+RQBjxyOfSTvk9YCJvdJVQRJLcCvxwKakFCsOSnV3t9OvN86Ak+fKPVB5j2fM/0fZ
+Kqjn3iqgdNPTLXPsuJLJO5lITRiBa4onmVelAiCstI9PQiaEck+oAHnMTnC9JE/B
+DHv3e4rwq3LznlqPw0GSd7xqNTdMDwNOWjkuOr3sGpWS8ms/ZHHXV1Vd22uPe70i
+s00xrv14zLifcc8oj5DYzOhYRifRXgHX
+
+
+
+
+
+
+
+
+
+MIIDEzCCAfugAwIBAgIUG6Nn1rlERS1vsi88tcdzSYX0oqAwDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMTRaFw0zNTEy
+MTEwMjIwMTRaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCBXv0o3fmT8iluyLjJ4lBAVCW+ZRVyEXPYQuRi7vfD
+cO4a6d1kxiJLsaK0W88VNxjFQRr8PgDkWr28vwoH1rgk4pLsszLD48DBzD942peJ
+l/S6FnsIJjmaHcBh4pbNhU4yowu63iKkvttrcZAEbpEro6Z8CziWEx8sywoaYEQG
+ifPkr9ORV6Cn3txq+9gMBePG41GrtZrUGIu+xrndL0Shh4Pq0eq/9MAsVlIIXEa8
+9WfH8J2kFcTOfoWtIc70b7TLZQsx4YnNcnrGLSUEcstFyPLX+Xtv5SNZF89OOIxX
+VNjNvgE5DbJb9hMM4UAFqI+1bo9QqtxwThjc/sOvIxzNAgMBAAGjWzBZMB0GA1Ud
+DgQWBBStTyogRPuAVG6q7yPyav1uvE+7pTA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAFMfoOv+oISGjvamq7+Y4G7ep5vxlAPeK3RATYPYvAmyH946qZXh98ni
+QXyuqZW5P5eEt86toY45IwDU5r09SKwHughEe99iiEkxh0mb2qo84qX9/qcg+kyN
+jeLd/OSyolpUCEFNwOFcog7pj7Eer+6AHbwTn1Mjb5TBsKwtDMJsaxPvdj0u7M5r
+xL/wHkFhn1rCo2QiojzjSlV3yLTh49iTyhE3cG+RxaNKDCxhp0jSSLX1BW/ZoPA8
++PMJEA+Q0QbyRD8aJOHN5O8jGxCa/ZzcOnYVL6AsEXoDiY3vAUYh1FUonOWw0m9H
+p+tGUbGS2l873J5PrsbpeKEVR/IIoKo=
+
+
+
+
+
+
+
+
+
+
+ urn:mace:shibboleth:1.0:nameIdentifier
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+
+
+
+
+
+
+
+
+
+
+
+
+ localhost
+
+
+
+
+
+
+MIIDEzCCAfugAwIBAgIUS9SuTXwsFVVG+LjOEAbLqqT/el0wDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMjZaFw0zNTEy
+MTEwMjIwMjZaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCMAoDHx8xCIfv/6QKqt9mcHYmEJ8y2dKprUbpdcOjH
+YvNPIl/lHPsUyrb+Nc+q2CDeiWjVk1mWYq0UpIwpBMuw1H6+oOqr4VQRi65pin0M
+SfE0MWIaFo5FPvpvoptkHD4gvREbm4swyXGMczcMRfqgalFXhUD2wz8W3XAM5Cq2
+03XeJbj6TwjvKatG5XPdeUe2FBGuOO2q54L1hcIGnLMCQrg7D31lR13PJbjnJ0No
+5C3k8TPuny6vJsBC03GNLNKfmrKVTdzr3VKp1uay1G3DL9314fgmbl8HA5iRQmy+
+XInUU6/8NXZSF59p3ITAOvZQeZsbJjg5gGDip5OZo9YlAgMBAAGjWzBZMB0GA1Ud
+DgQWBBRPlM4VkKZ0U4ec9GrIhFQl0hNbLDA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAIZ0a1ov3my3ljJG588I/PHx+TxAWONWmpKbO9c/qI3Drxk4oRIffiac
+ANxdvtabgIzrlk5gMMisD7oyqHJiWgKv5Bgctd8w3IS3lLl7wHX65mTKQRXniG98
+NIjkvfrhe2eeJxecOqnDI8GOhIGCIqZUn8ShdM/yHjhQ2Mh0Hj3U0LlKvnmfGSQl
+j0viGwbFCaNaIP3zc5UmCrdE5h8sWL3Fu7ILKM9RyFa2ILHrJScV9t623IcHffHP
+IeaY/WtuapsrqRFxuQL9QFWN0FsRIdLmjTq+00+B/XnnKRKFBuWfjhHLF/uu8f+E
+t6Lf23Kb8yD6ZR7dihMZAGHnYQ/hlhM=
+
+
+
+
+
+
+
+
+
+MIIDFDCCAfygAwIBAgIVAN3vv+b7KN5Se9m1RZsCllp/B/hdMA0GCSqGSIb3DQEB
+CwUAMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwHhcNMTUxMjExMDIyMDE0WhcNMzUx
+MjExMDIyMDE0WjAVMRMwEQYDVQQDDAppZHB0ZXN0YmVkMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAh91caeY0Q85uhaUyqFwP2bMjwMFxMzRlAoqBHd7g
+u6eo4duaeLz1BaoR2XTBpNNvFR5oHH+TkKahVDGeH5+kcnIpxI8JPdsZml1srvf2
+Z6dzJsulJZUdpqnngycTkGtZgEoC1vmYVky2BSAIIifmdh6s0epbHnMGLsHzMKfJ
+Cb/Q6dYzRWTCPtzE2VMuQqqWgeyMr7u14x/Vqr9RPEFsgY8GIu5jzB6AyUIwrLg+
+MNkv6aIdcHwxYTGL7ijfy6rSWrgBflQoYRYNEnseK0ZHgJahz4ovCag6wZAoPpBs
+uYlY7lEr89Ucb6NHx3uqGMsXlDFdE4QwfDLLhCYHPvJ0uwIDAQABo1swWTAdBgNV
+HQ4EFgQUAkOgED3iYdmvQEOMm6u/JmD/UTQwOAYDVR0RBDEwL4IKaWRwdGVzdGJl
+ZIYhaHR0cHM6Ly9pZHB0ZXN0YmVkL2lkcC9zaGliYm9sZXRoMA0GCSqGSIb3DQEB
+CwUAA4IBAQBIdd4YWlnvJjql8+zKKgmWgIY7U8DA8e6QcbAf8f8cdE33RSnjI63X
+sv/y9GfmbAVAD6RIAXPFFeRYJ08GOxGI9axfNaKdlsklJ9bk4ducHqgCSWYVer3s
+RQBjxyOfSTvk9YCJvdJVQRJLcCvxwKakFCsOSnV3t9OvN86Ak+fKPVB5j2fM/0fZ
+Kqjn3iqgdNPTLXPsuJLJO5lITRiBa4onmVelAiCstI9PQiaEck+oAHnMTnC9JE/B
+DHv3e4rwq3LznlqPw0GSd7xqNTdMDwNOWjkuOr3sGpWS8ms/ZHHXV1Vd22uPe70i
+s00xrv14zLifcc8oj5DYzOhYRifRXgHX
+
+
+
+
+
+
+
+
+
+MIIDEzCCAfugAwIBAgIUG6Nn1rlERS1vsi88tcdzSYX0oqAwDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMTRaFw0zNTEy
+MTEwMjIwMTRaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCBXv0o3fmT8iluyLjJ4lBAVCW+ZRVyEXPYQuRi7vfD
+cO4a6d1kxiJLsaK0W88VNxjFQRr8PgDkWr28vwoH1rgk4pLsszLD48DBzD942peJ
+l/S6FnsIJjmaHcBh4pbNhU4yowu63iKkvttrcZAEbpEro6Z8CziWEx8sywoaYEQG
+ifPkr9ORV6Cn3txq+9gMBePG41GrtZrUGIu+xrndL0Shh4Pq0eq/9MAsVlIIXEa8
+9WfH8J2kFcTOfoWtIc70b7TLZQsx4YnNcnrGLSUEcstFyPLX+Xtv5SNZF89OOIxX
+VNjNvgE5DbJb9hMM4UAFqI+1bo9QqtxwThjc/sOvIxzNAgMBAAGjWzBZMB0GA1Ud
+DgQWBBStTyogRPuAVG6q7yPyav1uvE+7pTA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAFMfoOv+oISGjvamq7+Y4G7ep5vxlAPeK3RATYPYvAmyH946qZXh98ni
+QXyuqZW5P5eEt86toY45IwDU5r09SKwHughEe99iiEkxh0mb2qo84qX9/qcg+kyN
+jeLd/OSyolpUCEFNwOFcog7pj7Eer+6AHbwTn1Mjb5TBsKwtDMJsaxPvdj0u7M5r
+xL/wHkFhn1rCo2QiojzjSlV3yLTh49iTyhE3cG+RxaNKDCxhp0jSSLX1BW/ZoPA8
++PMJEA+Q0QbyRD8aJOHN5O8jGxCa/ZzcOnYVL6AsEXoDiY3vAUYh1FUonOWw0m9H
+p+tGUbGS2l873J5PrsbpeKEVR/IIoKo=
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/complex/configs-and-secrets/grouper/shibboleth/shibboleth2.xml b/demo/complex/configs-and-secrets/grouper/shibboleth/shibboleth2.xml
new file mode 100644
index 0000000..0c38f82
--- /dev/null
+++ b/demo/complex/configs-and-secrets/grouper/shibboleth/shibboleth2.xml
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SAML2
+
+
+
+ SAML2 Local
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/complex/configs-and-secrets/grouper/shibboleth/sp-cert.pem b/demo/complex/configs-and-secrets/grouper/shibboleth/sp-cert.pem
new file mode 100644
index 0000000..9cc228a
--- /dev/null
+++ b/demo/complex/configs-and-secrets/grouper/shibboleth/sp-cert.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDPDCCAiQCCQDNZe8r0hVtuTANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCTUkxEjAQBgNVBAcMCUFubiBBcmJvcjEXMBUGA1UECgwOSW50
+ZXJuZXQyL1RJRVIxFzAVBgNVBAMMDnNwLmV4YW1wbGUub3JnMB4XDTE3MDkyMjE5
+NTAzNVoXDTI3MDkyMDE5NTAzNVowYDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1J
+MRIwEAYDVQQHDAlBbm4gQXJib3IxFzAVBgNVBAoMDkludGVybmV0Mi9USUVSMRcw
+FQYDVQQDDA5zcC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAMTNJmsNpTpR4NrDJwOgK/o3UYlNdi1c6xBflt+liLAsQc160QReV4dS
+SGK8LZvN58a/BTIsH8dLhQlUQ8qQUY2AfolVrNxb7Waumeh/POzYUTRylnoGpU3W
+bGMEPxE/AdgP5U/adYvyu4XI5epv7wjZJOTqcVag15SalY+aso+ZC/5l+UzRxmWB
+ZxKTsSL1y7PFehY4/Zl3Y3oGVsVl/zspt5lteoZQeeVxUX29S3Af11yHY4xpEp+7
+rvAzY/nlsTiHAsUoCFK/NFQ2evvSRx52B9Fk1cWP1MDVDm2QjQqD9xBGYSnX6bhQ
+ejVx7JUJHlblu2Q5p5XdW0BihgFluoECAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
+n/qhYnIviPs4tglCdrw+M7gbqKNWadDC3F9HDYzlJMFeS/ae2turhEUgQPbYPDQQ
+eO3oOILtvCXNFUPM58jf8V5YFRrOqrTgx44kexQDaHO5YYNft5tF5TdvBYE2gOVr
+GdYrH2iSP8WX+Yy7JH5uqkfwWzEntWHJdey39rCWKAUCCB35+/2b4N53Qmlv2+ug
+CpNJYFtXInd4YMmM5HjXLyoWXtjnKiwDqYUCeYPSwAajnCqRqRXUX0gYTFDRiwRP
+HbmO9We0nqoc/71nikmGGoSRMO/zWVMFjwmAx1fGiWdU61sjGX8sHifzmVyJVEBI
+Z75p+JrWYZJYrx/vpWxL8g==
+-----END CERTIFICATE-----
diff --git a/demo/complex/configs-and-secrets/grouper/shibboleth/sp-key.pem b/demo/complex/configs-and-secrets/grouper/shibboleth/sp-key.pem
new file mode 100644
index 0000000..1b0b579
--- /dev/null
+++ b/demo/complex/configs-and-secrets/grouper/shibboleth/sp-key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEzSZrDaU6UeDa
+wycDoCv6N1GJTXYtXOsQX5bfpYiwLEHNetEEXleHUkhivC2bzefGvwUyLB/HS4UJ
+VEPKkFGNgH6JVazcW+1mrpnofzzs2FE0cpZ6BqVN1mxjBD8RPwHYD+VP2nWL8ruF
+yOXqb+8I2STk6nFWoNeUmpWPmrKPmQv+ZflM0cZlgWcSk7Ei9cuzxXoWOP2Zd2N6
+BlbFZf87KbeZbXqGUHnlcVF9vUtwH9dch2OMaRKfu67wM2P55bE4hwLFKAhSvzRU
+Nnr70kcedgfRZNXFj9TA1Q5tkI0Kg/cQRmEp1+m4UHo1ceyVCR5W5btkOaeV3VtA
+YoYBZbqBAgMBAAECggEAA/5t0ypZug9DUu0283niqpdIzlKGHXGPS6vE8hD37ytW
+wobFiyMm/5YJ5gcPnePV2lCyGEyQ8Ih10LSnE4tOPGLpLnxQn8A11ymf8fnzEJNr
+Qnc42o0b+bJqTLAfX4g5z1qzOqWiUQ7CA3sKP3G6FiHh/8tKNYnaFif09Q8cpJFb
+YDDkvm48NJgsrIoCgmaFIQIn+yDzGQKWwTNMIks+RByWpc67j1x1kiyQM1RfrEev
+Yyq/ZkP66IYZzmZKpFCWGs5qbRZdxyXNpq85DjwA99lAH7vxtMJHQM4z1h1eDH4L
+Ma5hEnmmHu4D5lF2GDQYflvuFdDGH5tThO6MV0IrSQKBgQD+kvEtNxJCMxLOVFyV
+NWF3pk/i2nkD+53t/VPXjMPtW7IesouEGzU82I/fT2wUTkNwFdkVpv37qoLypKZm
+npJFxr6abQNjiDh2Fsh8/iuJfvdZUFJbCEY6NS58qgjix8XCQKRD06EugK7uekIZ
+zJnttF3qVBBD8Z8Uwxz8i+jF1wKBgQDF51y/5XB6Bz47cdxw7P8NsfnTz2V3H0HU
+OnlEBANbhmBadjU8dqbM54Nxbn7VOdooXPuSnAKJ9vPDg1n5Y/GO+lgldNzfyK6g
+HnbldSu0zBvAaGvmAjLjetEtOkBqYkrHJlT6JAems/Kc/YX5uooAz9/jNJFXP9++
+KbjH3CzHZwKBgQC6ppxEDZPKi83nD/2NvMTIyFzcNFj0LaEepFW7vc7NkiSn0zrt
+0lEXWqUqEv5oaPWTEcHH2VdxFRTLuSL0LKGMnWqUqQcKDA9xrcSzuFvNhRTwHC81
+5XwwI1wBNV4sgFKj2WdW/6y2/szDt0oNxnC50zvkmlwOpPKBc4kmNaKmowKBgBmC
+uXIDIXyZcmw3QTNNWZNqXcnv8iRo4xN4dilOWyBxMfp3QmWI5feD4G2+0Jqr2nNZ
+iRRdB/bA3qtVQ0PinkDQBIzPg6lVNS1uv+TUNc4YgXtL+pyrq+Om8U/jMmqEQR9q
+0YltG49houSZyatnYGK6aSHgpNuaYD0jI66fsyYBAoGAMefyD0I/ncArjuf58hVQ
+zSjxfcvlja9okrC8ZgqsVluezcm4rQNcSjBnESGTCjJC7O29AofGLHkvnsBQDiGk
+hE38IRisd+okXdApr41ifWDhmtASud5q6wlhOpMmQxg+OALf1rTvFYhbnFEXV/KY
+e5A4iXLRIbxbmXZDa35Rebw=
+-----END PRIVATE KEY-----
diff --git a/demo/complex/configs-and-secrets/midpoint/application/database_password.txt b/demo/complex/configs-and-secrets/midpoint/application/database_password.txt
new file mode 100644
index 0000000..11bac01
--- /dev/null
+++ b/demo/complex/configs-and-secrets/midpoint/application/database_password.txt
@@ -0,0 +1 @@
+456654
diff --git a/demo/complex/configs-and-secrets/midpoint/application/keystore_password.txt b/demo/complex/configs-and-secrets/midpoint/application/keystore_password.txt
new file mode 100644
index 0000000..1d40192
--- /dev/null
+++ b/demo/complex/configs-and-secrets/midpoint/application/keystore_password.txt
@@ -0,0 +1 @@
+changeit
diff --git a/demo/complex/configs-and-secrets/midpoint/httpd/host-cert.pem b/demo/complex/configs-and-secrets/midpoint/httpd/host-cert.pem
new file mode 100644
index 0000000..9b1021b
--- /dev/null
+++ b/demo/complex/configs-and-secrets/midpoint/httpd/host-cert.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDqDCCApCgAwIBAgIJAMOSkn4oS2aAMA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJNSTESMBAGA1UEBwwJQW5uIEFyYm9yMRcwFQYDVQQK
+DA5JbnRlcm5ldDIvVElFUjEgMB4GA1UEAwwXbWlkcG9pbnQuc3AuZXhhbXBsZS5v
+cmcwHhcNMTgwOTE0MDU1OTQ1WhcNMTkwOTE0MDU1OTQ1WjBpMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCTUkxEjAQBgNVBAcMCUFubiBBcmJvcjEXMBUGA1UECgwOSW50
+ZXJuZXQyL1RJRVIxIDAeBgNVBAMMF21pZHBvaW50LnNwLmV4YW1wbGUub3JnMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApj/b7MEUSfu3oXMfNgRwTse7
+a5UV7Jswf1M/ZN/ZZkAkIxNBevZgozjesvLPWrmsTgONi7XigJUJvCjdjmlW9eDM
+lri/rkD8HuOR1DQCVKL9nvoS2c3D7sq5Emda3V8Tlj82VqfEmePd3sajx7mcTfbH
+8jwAL9NhkC+WMib5IpjLGpG0FEAC0ha7Lxb+7jIiqHVJaqLXJGCyGN4mh6c1Q9S1
+f8RVTiW2a8x22G+9wnZYbkiA2Kxls177imHlhSz8EdvV4IpGw1amrEWhhuDEum7B
+vZ1xQDLatgRqh4qAKLIVYeRnJ8H1FelMa90qB4G08MIPifmTsQwqJyBYaEdgWQID
+AQABo1MwUTAdBgNVHQ4EFgQUqb9BteODF6wv5R57aEON/wGXMiowHwYDVR0jBBgw
+FoAUqb9BteODF6wv5R57aEON/wGXMiowDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
+9w0BAQsFAAOCAQEAAcKhxI+tSItrXmqC0PSmgWyAYpqbkz6W/cefTutXqhIgY09f
+h0LSv7ogTahoGpyiZk9vy6u3OE9bYwxapEfa4KBjO6HxBMIVBBb3RegVjoPzjElN
+BDwAx0VGFcZTXwMxDWycWdG8ql7rCZBvS50w04uTaIgnGmqXAdWWmBgfJ9cRbxW+
+JwO/mOl1QM1lR/5142NpvuUVWlmZSKEGydE5A1qPz2wpDbBR1ym1BQNS4NEqw6Kp
+GSB8jKyCS1Ve0v2wVze2038Wukz02dq9uKPTIO3T+B+ibZmxn6Op/kFCc1/kK5NS
+Q6JdO1B6KquGAYdGmKAcQ19mv+jqGktqWEEf0g==
+-----END CERTIFICATE-----
diff --git a/demo/complex/configs-and-secrets/midpoint/httpd/host-key.pem b/demo/complex/configs-and-secrets/midpoint/httpd/host-key.pem
new file mode 100644
index 0000000..5746e59
--- /dev/null
+++ b/demo/complex/configs-and-secrets/midpoint/httpd/host-key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCmP9vswRRJ+7eh
+cx82BHBOx7trlRXsmzB/Uz9k39lmQCQjE0F69mCjON6y8s9auaxOA42LteKAlQm8
+KN2OaVb14MyWuL+uQPwe45HUNAJUov2e+hLZzcPuyrkSZ1rdXxOWPzZWp8SZ493e
+xqPHuZxN9sfyPAAv02GQL5YyJvkimMsakbQUQALSFrsvFv7uMiKodUlqotckYLIY
+3iaHpzVD1LV/xFVOJbZrzHbYb73CdlhuSIDYrGWzXvuKYeWFLPwR29XgikbDVqas
+RaGG4MS6bsG9nXFAMtq2BGqHioAoshVh5GcnwfUV6Uxr3SoHgbTwwg+J+ZOxDCon
+IFhoR2BZAgMBAAECggEAEIRBpjjceiku6jRUwnoYaks/nIWYQwR8AfpUTwJKR/VR
+Yca097Fokm7A+UhUP3A45RtHQb0VPq8P44iv0kk24YCu8r5yFK7SHYOAZnOwU5ZJ
+2jSAEPF3aM7tKh3okhuzB3dKP7u1NZDE5zAW723KUJiW7sL1RcsbY0bHBj6G+9/H
+NplmsjuGt684vRBB0qOBfKF7EiG7mT69tHuNj4gRza9SMY31UtKbZdt2fNY6mp5V
+HscMba7egZP+Ke0pVX4+go9j7K8GG8hYaQDLjrzlPqrxZ2c5X9cC+CRDI/CHuL/s
+V/2yGZJ6n6UabwZoH83RdFrbQ94rU8Hkli6EvxXvMQKBgQDRpheNW5jDG5TfeJKh
+yfKTDQqH2Tk3BsBYYBN7Hf3m7vbkzlxnAKJAoSLmtRMuoeXvI5MrhzaHGsNIUS76
+LDIZnvB7DLUxhFUZsCPkpAA1QHuTWY96oR3PHnPjpk8lSUvtbOPwDLdzVApeFJgZ
+VqMNArZ7AHsK3Kkyi+f4WVQjbQKBgQDLAWiGb5dx6fAM2W6B6HjNmzjBWOuVEXa2
+76to9jzupBZmETfZgxtWUaWUDuNS+f7dtVUTE+p6v/w8clrHEhEZYkqunIOLo/UA
+LFPiuoTfEsWb1rh+nsCjCgy4uimixj/bSkf7NC6NyKTvCygA1mGnVVJUEPegYlDy
+LXCkaKWxHQKBgQCmyHSKL2lrJkEcOwakEU2acNCE3Gno/cT9SYmV83kvQ8JEqmrW
+QqnRsp9aXIljGscapPmKsmnNt5vNp1AxFAHTYh88NRLczsMIyZj0ZwgHVUI6KhC7
+5Psa78YQQBlMt2/g9TSsnuE+rYgF6mpKFiNm0Vasqeg47uzn2mdzqlUGTQKBgE04
+JutkTUY+h1pL5vYxWKpVDfy19z7H2tFxT1FowPrBneeLSyRI88Ac5I/yLdRlVeY9
+0LOmEr5Igwj3MsKgg7KVKfVLgdo/LrW3Jt2Kt3onKNXDkoBPoNUjwH0QC0Boiue+
+VK0gR0kVdm+bXccbxR+im+NwZNE0NLg6Qqu3RredAoGBALuVoqbPPmTCZXYG328H
+bzOs2aiR7BzPSVByV+qG6jW7w03RAnFPJZp7HMU+ViI5VY0wabUscMSvz5163+gM
+4KwY3v9ZjZzZGukIfLuudkdqtaiVOx/KeAC0n+nG21YU+wpZww8gkfHh1/sa2CME
+CWYCgOnmiTHcj83UaTqEXtmv
+-----END PRIVATE KEY-----
diff --git a/demo/complex/configs-and-secrets/midpoint/shibboleth/idp-metadata.xml b/demo/complex/configs-and-secrets/midpoint/shibboleth/idp-metadata.xml
new file mode 100644
index 0000000..35914b7
--- /dev/null
+++ b/demo/complex/configs-and-secrets/midpoint/shibboleth/idp-metadata.xml
@@ -0,0 +1,207 @@
+
+
+
+
+
+
+
+ example.org
+
+
+
+
+
+
+
+MIIDEzCCAfugAwIBAgIUS9SuTXwsFVVG+LjOEAbLqqT/el0wDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMjZaFw0zNTEy
+MTEwMjIwMjZaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCMAoDHx8xCIfv/6QKqt9mcHYmEJ8y2dKprUbpdcOjH
+YvNPIl/lHPsUyrb+Nc+q2CDeiWjVk1mWYq0UpIwpBMuw1H6+oOqr4VQRi65pin0M
+SfE0MWIaFo5FPvpvoptkHD4gvREbm4swyXGMczcMRfqgalFXhUD2wz8W3XAM5Cq2
+03XeJbj6TwjvKatG5XPdeUe2FBGuOO2q54L1hcIGnLMCQrg7D31lR13PJbjnJ0No
+5C3k8TPuny6vJsBC03GNLNKfmrKVTdzr3VKp1uay1G3DL9314fgmbl8HA5iRQmy+
+XInUU6/8NXZSF59p3ITAOvZQeZsbJjg5gGDip5OZo9YlAgMBAAGjWzBZMB0GA1Ud
+DgQWBBRPlM4VkKZ0U4ec9GrIhFQl0hNbLDA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAIZ0a1ov3my3ljJG588I/PHx+TxAWONWmpKbO9c/qI3Drxk4oRIffiac
+ANxdvtabgIzrlk5gMMisD7oyqHJiWgKv5Bgctd8w3IS3lLl7wHX65mTKQRXniG98
+NIjkvfrhe2eeJxecOqnDI8GOhIGCIqZUn8ShdM/yHjhQ2Mh0Hj3U0LlKvnmfGSQl
+j0viGwbFCaNaIP3zc5UmCrdE5h8sWL3Fu7ILKM9RyFa2ILHrJScV9t623IcHffHP
+IeaY/WtuapsrqRFxuQL9QFWN0FsRIdLmjTq+00+B/XnnKRKFBuWfjhHLF/uu8f+E
+t6Lf23Kb8yD6ZR7dihMZAGHnYQ/hlhM=
+
+
+
+
+
+
+
+
+
+MIIDFDCCAfygAwIBAgIVAN3vv+b7KN5Se9m1RZsCllp/B/hdMA0GCSqGSIb3DQEB
+CwUAMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwHhcNMTUxMjExMDIyMDE0WhcNMzUx
+MjExMDIyMDE0WjAVMRMwEQYDVQQDDAppZHB0ZXN0YmVkMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAh91caeY0Q85uhaUyqFwP2bMjwMFxMzRlAoqBHd7g
+u6eo4duaeLz1BaoR2XTBpNNvFR5oHH+TkKahVDGeH5+kcnIpxI8JPdsZml1srvf2
+Z6dzJsulJZUdpqnngycTkGtZgEoC1vmYVky2BSAIIifmdh6s0epbHnMGLsHzMKfJ
+Cb/Q6dYzRWTCPtzE2VMuQqqWgeyMr7u14x/Vqr9RPEFsgY8GIu5jzB6AyUIwrLg+
+MNkv6aIdcHwxYTGL7ijfy6rSWrgBflQoYRYNEnseK0ZHgJahz4ovCag6wZAoPpBs
+uYlY7lEr89Ucb6NHx3uqGMsXlDFdE4QwfDLLhCYHPvJ0uwIDAQABo1swWTAdBgNV
+HQ4EFgQUAkOgED3iYdmvQEOMm6u/JmD/UTQwOAYDVR0RBDEwL4IKaWRwdGVzdGJl
+ZIYhaHR0cHM6Ly9pZHB0ZXN0YmVkL2lkcC9zaGliYm9sZXRoMA0GCSqGSIb3DQEB
+CwUAA4IBAQBIdd4YWlnvJjql8+zKKgmWgIY7U8DA8e6QcbAf8f8cdE33RSnjI63X
+sv/y9GfmbAVAD6RIAXPFFeRYJ08GOxGI9axfNaKdlsklJ9bk4ducHqgCSWYVer3s
+RQBjxyOfSTvk9YCJvdJVQRJLcCvxwKakFCsOSnV3t9OvN86Ak+fKPVB5j2fM/0fZ
+Kqjn3iqgdNPTLXPsuJLJO5lITRiBa4onmVelAiCstI9PQiaEck+oAHnMTnC9JE/B
+DHv3e4rwq3LznlqPw0GSd7xqNTdMDwNOWjkuOr3sGpWS8ms/ZHHXV1Vd22uPe70i
+s00xrv14zLifcc8oj5DYzOhYRifRXgHX
+
+
+
+
+
+
+
+
+
+MIIDEzCCAfugAwIBAgIUG6Nn1rlERS1vsi88tcdzSYX0oqAwDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMTRaFw0zNTEy
+MTEwMjIwMTRaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCBXv0o3fmT8iluyLjJ4lBAVCW+ZRVyEXPYQuRi7vfD
+cO4a6d1kxiJLsaK0W88VNxjFQRr8PgDkWr28vwoH1rgk4pLsszLD48DBzD942peJ
+l/S6FnsIJjmaHcBh4pbNhU4yowu63iKkvttrcZAEbpEro6Z8CziWEx8sywoaYEQG
+ifPkr9ORV6Cn3txq+9gMBePG41GrtZrUGIu+xrndL0Shh4Pq0eq/9MAsVlIIXEa8
+9WfH8J2kFcTOfoWtIc70b7TLZQsx4YnNcnrGLSUEcstFyPLX+Xtv5SNZF89OOIxX
+VNjNvgE5DbJb9hMM4UAFqI+1bo9QqtxwThjc/sOvIxzNAgMBAAGjWzBZMB0GA1Ud
+DgQWBBStTyogRPuAVG6q7yPyav1uvE+7pTA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAFMfoOv+oISGjvamq7+Y4G7ep5vxlAPeK3RATYPYvAmyH946qZXh98ni
+QXyuqZW5P5eEt86toY45IwDU5r09SKwHughEe99iiEkxh0mb2qo84qX9/qcg+kyN
+jeLd/OSyolpUCEFNwOFcog7pj7Eer+6AHbwTn1Mjb5TBsKwtDMJsaxPvdj0u7M5r
+xL/wHkFhn1rCo2QiojzjSlV3yLTh49iTyhE3cG+RxaNKDCxhp0jSSLX1BW/ZoPA8
++PMJEA+Q0QbyRD8aJOHN5O8jGxCa/ZzcOnYVL6AsEXoDiY3vAUYh1FUonOWw0m9H
+p+tGUbGS2l873J5PrsbpeKEVR/IIoKo=
+
+
+
+
+
+
+ urn:mace:shibboleth:1.0:nameIdentifier
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+
+
+
+
+
+
+
+
+
+
+
+
+ localhost
+
+
+
+
+
+
+MIIDEzCCAfugAwIBAgIUS9SuTXwsFVVG+LjOEAbLqqT/el0wDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMjZaFw0zNTEy
+MTEwMjIwMjZaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCMAoDHx8xCIfv/6QKqt9mcHYmEJ8y2dKprUbpdcOjH
+YvNPIl/lHPsUyrb+Nc+q2CDeiWjVk1mWYq0UpIwpBMuw1H6+oOqr4VQRi65pin0M
+SfE0MWIaFo5FPvpvoptkHD4gvREbm4swyXGMczcMRfqgalFXhUD2wz8W3XAM5Cq2
+03XeJbj6TwjvKatG5XPdeUe2FBGuOO2q54L1hcIGnLMCQrg7D31lR13PJbjnJ0No
+5C3k8TPuny6vJsBC03GNLNKfmrKVTdzr3VKp1uay1G3DL9314fgmbl8HA5iRQmy+
+XInUU6/8NXZSF59p3ITAOvZQeZsbJjg5gGDip5OZo9YlAgMBAAGjWzBZMB0GA1Ud
+DgQWBBRPlM4VkKZ0U4ec9GrIhFQl0hNbLDA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAIZ0a1ov3my3ljJG588I/PHx+TxAWONWmpKbO9c/qI3Drxk4oRIffiac
+ANxdvtabgIzrlk5gMMisD7oyqHJiWgKv5Bgctd8w3IS3lLl7wHX65mTKQRXniG98
+NIjkvfrhe2eeJxecOqnDI8GOhIGCIqZUn8ShdM/yHjhQ2Mh0Hj3U0LlKvnmfGSQl
+j0viGwbFCaNaIP3zc5UmCrdE5h8sWL3Fu7ILKM9RyFa2ILHrJScV9t623IcHffHP
+IeaY/WtuapsrqRFxuQL9QFWN0FsRIdLmjTq+00+B/XnnKRKFBuWfjhHLF/uu8f+E
+t6Lf23Kb8yD6ZR7dihMZAGHnYQ/hlhM=
+
+
+
+
+
+
+
+
+
+MIIDFDCCAfygAwIBAgIVAN3vv+b7KN5Se9m1RZsCllp/B/hdMA0GCSqGSIb3DQEB
+CwUAMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwHhcNMTUxMjExMDIyMDE0WhcNMzUx
+MjExMDIyMDE0WjAVMRMwEQYDVQQDDAppZHB0ZXN0YmVkMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAh91caeY0Q85uhaUyqFwP2bMjwMFxMzRlAoqBHd7g
+u6eo4duaeLz1BaoR2XTBpNNvFR5oHH+TkKahVDGeH5+kcnIpxI8JPdsZml1srvf2
+Z6dzJsulJZUdpqnngycTkGtZgEoC1vmYVky2BSAIIifmdh6s0epbHnMGLsHzMKfJ
+Cb/Q6dYzRWTCPtzE2VMuQqqWgeyMr7u14x/Vqr9RPEFsgY8GIu5jzB6AyUIwrLg+
+MNkv6aIdcHwxYTGL7ijfy6rSWrgBflQoYRYNEnseK0ZHgJahz4ovCag6wZAoPpBs
+uYlY7lEr89Ucb6NHx3uqGMsXlDFdE4QwfDLLhCYHPvJ0uwIDAQABo1swWTAdBgNV
+HQ4EFgQUAkOgED3iYdmvQEOMm6u/JmD/UTQwOAYDVR0RBDEwL4IKaWRwdGVzdGJl
+ZIYhaHR0cHM6Ly9pZHB0ZXN0YmVkL2lkcC9zaGliYm9sZXRoMA0GCSqGSIb3DQEB
+CwUAA4IBAQBIdd4YWlnvJjql8+zKKgmWgIY7U8DA8e6QcbAf8f8cdE33RSnjI63X
+sv/y9GfmbAVAD6RIAXPFFeRYJ08GOxGI9axfNaKdlsklJ9bk4ducHqgCSWYVer3s
+RQBjxyOfSTvk9YCJvdJVQRJLcCvxwKakFCsOSnV3t9OvN86Ak+fKPVB5j2fM/0fZ
+Kqjn3iqgdNPTLXPsuJLJO5lITRiBa4onmVelAiCstI9PQiaEck+oAHnMTnC9JE/B
+DHv3e4rwq3LznlqPw0GSd7xqNTdMDwNOWjkuOr3sGpWS8ms/ZHHXV1Vd22uPe70i
+s00xrv14zLifcc8oj5DYzOhYRifRXgHX
+
+
+
+
+
+
+
+
+
+MIIDEzCCAfugAwIBAgIUG6Nn1rlERS1vsi88tcdzSYX0oqAwDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMTRaFw0zNTEy
+MTEwMjIwMTRaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCBXv0o3fmT8iluyLjJ4lBAVCW+ZRVyEXPYQuRi7vfD
+cO4a6d1kxiJLsaK0W88VNxjFQRr8PgDkWr28vwoH1rgk4pLsszLD48DBzD942peJ
+l/S6FnsIJjmaHcBh4pbNhU4yowu63iKkvttrcZAEbpEro6Z8CziWEx8sywoaYEQG
+ifPkr9ORV6Cn3txq+9gMBePG41GrtZrUGIu+xrndL0Shh4Pq0eq/9MAsVlIIXEa8
+9WfH8J2kFcTOfoWtIc70b7TLZQsx4YnNcnrGLSUEcstFyPLX+Xtv5SNZF89OOIxX
+VNjNvgE5DbJb9hMM4UAFqI+1bo9QqtxwThjc/sOvIxzNAgMBAAGjWzBZMB0GA1Ud
+DgQWBBStTyogRPuAVG6q7yPyav1uvE+7pTA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAFMfoOv+oISGjvamq7+Y4G7ep5vxlAPeK3RATYPYvAmyH946qZXh98ni
+QXyuqZW5P5eEt86toY45IwDU5r09SKwHughEe99iiEkxh0mb2qo84qX9/qcg+kyN
+jeLd/OSyolpUCEFNwOFcog7pj7Eer+6AHbwTn1Mjb5TBsKwtDMJsaxPvdj0u7M5r
+xL/wHkFhn1rCo2QiojzjSlV3yLTh49iTyhE3cG+RxaNKDCxhp0jSSLX1BW/ZoPA8
++PMJEA+Q0QbyRD8aJOHN5O8jGxCa/ZzcOnYVL6AsEXoDiY3vAUYh1FUonOWw0m9H
+p+tGUbGS2l873J5PrsbpeKEVR/IIoKo=
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/complex/configs-and-secrets/midpoint/shibboleth/shibboleth2.xml b/demo/complex/configs-and-secrets/midpoint/shibboleth/shibboleth2.xml
new file mode 100644
index 0000000..a644264
--- /dev/null
+++ b/demo/complex/configs-and-secrets/midpoint/shibboleth/shibboleth2.xml
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SAML2
+
+
+
+ SAML2 Local
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/complex/configs-and-secrets/midpoint/shibboleth/sp-cert.pem b/demo/complex/configs-and-secrets/midpoint/shibboleth/sp-cert.pem
new file mode 100644
index 0000000..0f5474e
--- /dev/null
+++ b/demo/complex/configs-and-secrets/midpoint/shibboleth/sp-cert.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDqDCCApCgAwIBAgIJAKUZrfriIt9cMA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJNSTESMBAGA1UEBwwJQW5uIEFyYm9yMRcwFQYDVQQK
+DA5JbnRlcm5ldDIvVElFUjEgMB4GA1UEAwwXZXZvbHZldW0uc3AuZXhhbXBsZS5v
+cmcwHhcNMTgwOTE0MDU0NjU3WhcNMTkwOTE0MDU0NjU3WjBpMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCTUkxEjAQBgNVBAcMCUFubiBBcmJvcjEXMBUGA1UECgwOSW50
+ZXJuZXQyL1RJRVIxIDAeBgNVBAMMF2V2b2x2ZXVtLnNwLmV4YW1wbGUub3JnMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw5v1zxlM94yaBssgNNbIUJwW
+XxbGxgSs2AWBeg2aEi/VQd2UE5ivZakNJlqWSJyHo2xE4kxeSyBBxinjSyhmpNao
+xIcqQsgW0gxo4SEHo3kUXWPo+of/pj6CslutsSJZWGTRV0dHITvaWX+NM8eXMfgu
+mJFwy3RMdLaWQhY1Dyi2jNoO+DZnfNgPyPeEZcmORaoeEID9QdZfHtcgTf2QfSHq
++xsTwHB6Ro5t7YD2ma8Krb/XcDTfsq3qJemd7LhPj5lGmhYSMgDbgwEkZgZ1kBOP
+lfsP2BvX5nipv7Vd1C5YXmv+NDR8V3yAWBC7ZAenxGmrnkaSVXnpUplUsGGm1QID
+AQABo1MwUTAdBgNVHQ4EFgQUuxSZwW6V1P/b0tsTM32OU/v/n+UwHwYDVR0jBBgw
+FoAUuxSZwW6V1P/b0tsTM32OU/v/n+UwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
+9w0BAQsFAAOCAQEAJWLXEfZkPeUyiGvsIUjczzdF3ptqXoP9aETS2pOV9sTri19R
+TsQZW6XQRHGtuEOsqEGH8yiTdGR5hbGC+ynH/xTJnK+tBn/R3KrgxLKyMvoUzAPl
+mhVq1dh+ZEtbsRpQRRubP6nm9kXNma0cXrkJSzuWM0W+l/xSOOYiSRRk3XWJfVjn
+9jQlcJRh5SOkKN08oZHrCYKxToEuOfV8PtRj3T80DhsBTv2SHqhg4cBhzQPb0Kjm
+9m4IkYOz8c5ZtuHDGnqMHw60Nyt+jyik4mMFP2frcOVP0W0sgwcfHllYzHoA/Khq
+Yk3TBVs1BjPuNDJWHct8Eo68YP2/ZvzqfVM87Q==
+-----END CERTIFICATE-----
diff --git a/demo/complex/configs-and-secrets/midpoint/shibboleth/sp-key.pem b/demo/complex/configs-and-secrets/midpoint/shibboleth/sp-key.pem
new file mode 100644
index 0000000..b4c7a68
--- /dev/null
+++ b/demo/complex/configs-and-secrets/midpoint/shibboleth/sp-key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDDm/XPGUz3jJoG
+yyA01shQnBZfFsbGBKzYBYF6DZoSL9VB3ZQTmK9lqQ0mWpZInIejbETiTF5LIEHG
+KeNLKGak1qjEhypCyBbSDGjhIQejeRRdY+j6h/+mPoKyW62xIllYZNFXR0chO9pZ
+f40zx5cx+C6YkXDLdEx0tpZCFjUPKLaM2g74Nmd82A/I94RlyY5Fqh4QgP1B1l8e
+1yBN/ZB9Ier7GxPAcHpGjm3tgPaZrwqtv9dwNN+yreol6Z3suE+PmUaaFhIyANuD
+ASRmBnWQE4+V+w/YG9fmeKm/tV3ULlhea/40NHxXfIBYELtkB6fEaaueRpJVeelS
+mVSwYabVAgMBAAECggEAeQxOdTaG9ro/puyUpZ40oA23mmiR4YCLJbNiiJFaQt7u
+7a+dLvNRt2uCA7YTQiP7nBooFpKD16HCkkrhqWT7AA8GqXero2AHHz0mugqim3sW
+KyTkzATVI5+TaaRqPC+xco4RLh2h2uMbID9atixRWE7pJuj6EC2MEr3bBlKPyyyj
+gYEi7PLZC5oEMHCZSteVik0yXI3kJJEneuVgFHTc+mwuz/qzqJP7f93i77c/FcRd
+pWCo1fg0Xuxh3M17Y/0e0ZzSBowbRVv7VkJDKZmdAYE+DaODdaIR8rrQVqkIEX4A
+4m/ufAS5j2Rck0MEY9mu5STQByo1EGiAsxTp8pVCkQKBgQD905GZiMIZ+yBM1aXa
+ZJj/BF2a/s/wf3N6YLtfobWGDAGXDIe07lr+jSK1+vYRcqHW/a6/H0JsVg+gD9Gs
+10p0ry8AVWqR+5GRRDUKBMBXVDxMFZybXvMzRnR67SJA6TVEirMHYeawtzEjzha0
+csIo0kCe3Ke22mR8aebFSFDonwKBgQDFSMTvC/y18k8n9NQ3Rsu2Z4itH3nZ3fM3
+e7+gFxQyCXJHR/kTaAPVb2HAsuA+lAemnEHJUhOjao0QiUilWkE44wSfx7LwFPWP
+XO89oOWGKhOclkzs2nvxOcC/pEjWm83cgOz/IPWqkw0/wefmh6RDcPz1JgwO2Jij
+P14FGG9YCwKBgQDPdmOlODBeB/Iomt+Kl3qtxaW0j9zp12JNfk7oJUY3IA0Uprss
+2T0qnbTnRQ5myGkbThHMSQKDhfwTeZqJSRakNKOYBDisxQ//yd4IKEYFmuYF0gxE
+ueDR4h5GcxfyMirFsRAPUqe5fKH/f+zy5HFp6B/FONkkDXDgd7aVPTxURQKBgF8q
+sUUXJTqnvqNpqQ1ZK1+8JeRNcLw2IuM64L9bIrTYdbYI9A/2dxL4fdE0+JN3WRF4
+AwwYeby044N8lfydwySr7kkdTiYeFi6oCAhUmyPTHE7G+iivZGaDE4Xeu/w6gF3E
+G48e2SMFeLiu0y0TdZvK31tqlRp8RXVKVhJjLSLtAoGBAP01ZMUJu5AU19c7xRTy
+kfVzE6PI+E8ZZ6qEWMs2Xt3NQL4Bffh94QQCmQYX3AY7xVD1gAQgbLXx4lEDQdzu
+i/kV4t+oeYwXSn1UEgOQj504qwR3gi4JZdfa0LbiMzhp9qyxofyk7Lre7Tc3Xmu1
+jyigTF7oAYoPBWk/mAthK1wO
+-----END PRIVATE KEY-----
diff --git a/demo/complex/directory/Dockerfile b/demo/complex/directory/Dockerfile
new file mode 100644
index 0000000..6e87782
--- /dev/null
+++ b/demo/complex/directory/Dockerfile
@@ -0,0 +1,30 @@
+FROM centos:centos7
+
+LABEL author="tier-packaging@internet2.edu "
+
+RUN yum install -y epel-release \
+ && yum update -y \
+ && yum install -y 389-ds-base 389-admin 389-adminutil \
+ && yum clean all \
+ && rm -rf /var/cache/yum
+
+COPY container_files/seed-data/ /seed-data/
+
+RUN useradd ldapadmin \
+ && rm -fr /var/lock /usr/lib/systemd/system \
+ # The 389-ds setup will fail because the hostname can't reliable be determined, so we'll bypass it and then install. \
+ && sed -i 's/checkHostname {/checkHostname {\nreturn();/g' /usr/lib64/dirsrv/perl/DSUtil.pm \
+ # Not doing SELinux \
+ && sed -i 's/updateSelinuxPolicy($inf);//g' /usr/lib64/dirsrv/perl/* \
+ # Do not restart at the end \
+ && sed -i '/if (@errs = startServer($inf))/,/}/d' /usr/lib64/dirsrv/perl/* \
+ && setup-ds.pl --silent --file /seed-data/ds-setup.inf \
+ && /usr/sbin/ns-slapd -D /etc/dirsrv/slapd-dir \
+ && while ! curl -s ldap://localhost:389 > /dev/null; do echo waiting for ldap to start; sleep 1; done; \
+ ldapadd -H ldap:/// -f /seed-data/users.ldif -x -D "cn=Directory Manager" -w password
+
+EXPOSE 389
+
+# temporary!
+
+CMD rm -rf /var/lock/dirsrv/slapd-dir/server/* && /usr/sbin/ns-slapd -D /etc/dirsrv/slapd-dir && sleep 100000000
diff --git a/demo/complex/directory/container_files/seed-data/ds-setup.inf b/demo/complex/directory/container_files/seed-data/ds-setup.inf
new file mode 100644
index 0000000..96c29a1
--- /dev/null
+++ b/demo/complex/directory/container_files/seed-data/ds-setup.inf
@@ -0,0 +1,28 @@
+[General]
+AdminDomain = internet2.edu
+ConfigDirectoryAdminID = admin
+ConfigDirectoryAdminPwd = admin
+ConfigDirectoryLdapURL = ldap://localhost:389/o=NetscapeRoot
+FullMachineName = localhost
+ServerRoot = /usr/lib64/dirsrv
+SuiteSpotGroup = nobody
+SuiteSpotUserID = nobody
+
+[admin]
+Port = 9830
+ServerAdminID = admin
+ServerAdminPwd = admin
+ServerIpAddress = 0.0.0.0
+SysUser = nobody
+
+[slapd]
+AddOrgEntries = No
+AddSampleEntries = No
+InstallLdifFile = suggest
+RootDN = cn=Directory Manager
+RootDNPwd = password
+ServerIdentifier = dir
+ServerPort = 389
+SlapdConfigForMC = yes
+Suffix = dc=internet2,dc=edu
+UseExistingMC = No
diff --git a/demo/complex/directory/container_files/seed-data/users.ldif b/demo/complex/directory/container_files/seed-data/users.ldif
new file mode 100644
index 0000000..5381f7d
--- /dev/null
+++ b/demo/complex/directory/container_files/seed-data/users.ldif
@@ -0,0 +1,20 @@
+dn: cn=admin,dc=internet2,dc=edu
+objectClass: simpleSecurityObject
+objectClass: organizationalRole
+cn: admin
+userPassword: password
+description: LDAP administrator
+
+dn: cn=users,ou=Groups,dc=internet2,dc=edu
+objectClass: groupOfUniqueNames
+objectClass: top
+uniqueMember: uid=banderson,ou=People,dc=internet2,dc=edu
+uniqueMember: uid=jsmith,ou=People,dc=internet2,dc=edu
+cn: users
+
+dn: ou=Courses,ou=Groups,dc=internet2,dc=edu
+objectClass: top
+objectClass: organizationalUnit
+ou: Courses
+
+
diff --git a/demo/complex/docker-compose.yml b/demo/complex/docker-compose.yml
new file mode 100644
index 0000000..4c0b206
--- /dev/null
+++ b/demo/complex/docker-compose.yml
@@ -0,0 +1,315 @@
+version: "3.3"
+
+services:
+ grouper-daemon:
+ build: ./grouper-daemon/
+ command: bash -c "while ! curl -s grouper-data:3306 > /dev/null; do echo waiting for mysql on grouper-data to start; sleep 3; done; while ! curl -s ldap://directory:389 > /dev/null; do echo waiting for ldap on directory to start; sleep 3; done; exec daemon"
+ depends_on:
+ - grouper-data
+ - directory
+ environment:
+ - ENV=demo
+ - GROUPER_CLIENT_WEBSERVICE_PASSWORD_FILE=password
+ - GROUPER_DATABASE_PASSWORD_FILE=/run/secrets/g_database_password.txt
+ - RABBITMQ_PASSWORD_FILE=/run/secrets/rabbitmq_password.txt
+ - SUBJECT_SOURCE_LDAP_PASSWORD=password
+ - USERTOKEN=build-2
+ networks:
+ - back
+ secrets:
+ - g_database_password.txt
+ - rabbitmq_password.txt
+ - source: grouper.hibernate.properties
+ target: grouper_grouper.hibernate.properties
+ - source: grouper-loader.properties
+ target: grouper_grouper-loader.properties
+ - source: subject.properties
+ target: grouper_subject.properties
+ volumes:
+ - type: bind
+ source: ./configs-and-secrets/grouper/application/grouper.properties
+ target: /opt/grouper/conf/grouper.properties
+ - type: bind
+ source: ./configs-and-secrets/grouper/application/grouper.client.properties
+ target: /opt/grouper/conf/grouper.client.properties
+
+
+ grouper-ui:
+ build: ./grouper-ui/
+ command: bash -c "while ! curl -s grouper-data:3306 > /dev/null; do echo waiting for mysql on grouper-data to start; sleep 3; done; while ! curl -s ldap://directory:389 > /dev/null; do echo waiting for ldap on directory to start; sleep 3; done; exec ui"
+ depends_on:
+ - grouper-data
+ - directory
+ environment:
+ - ENV=demo
+ - GROUPER_DATABASE_PASSWORD_FILE=/run/secrets/g_database_password.txt
+ - SUBJECT_SOURCE_LDAP_PASSWORD=password
+ - USERTOKEN=build-2
+ networks:
+ - front
+ - back
+ ports:
+ - "80:80"
+ - "443:443"
+ secrets:
+ - g_database_password.txt
+ - source: grouper.hibernate.properties
+ target: grouper_grouper.hibernate.properties
+ - source: grouper-loader.properties
+ target: grouper_grouper-loader.properties
+ - source: subject.properties
+ target: grouper_subject.properties
+ - source: g_sp-key.pem
+ target: shib_sp-key.pem
+ - source: g_host-key.pem
+ target: host-key.pem
+ volumes:
+ - type: bind
+ source: ./configs-and-secrets/grouper/application/grouper.properties
+ target: /opt/grouper/conf/grouper.properties
+ - type: bind
+ source: ./configs-and-secrets/grouper/application/grouper.client.properties
+ target: /opt/grouper/conf/grouper.client.properties
+ - type: bind
+ source: ./configs-and-secrets/grouper/shibboleth/sp-cert.pem
+ target: /etc/shibboleth/sp-cert.pem
+ - type: bind
+ source: ./configs-and-secrets/grouper/shibboleth/shibboleth2.xml
+ target: /etc/shibboleth/shibboleth2.xml
+ - type: bind
+ source: ./configs-and-secrets/grouper/shibboleth/idp-metadata.xml
+ target: /etc/shibboleth/idp-metadata.xml
+ - type: bind
+ source: ./configs-and-secrets/grouper/httpd/host-cert.pem
+ target: /etc/pki/tls/certs/host-cert.pem
+ - type: bind
+ source: ./configs-and-secrets/grouper/httpd/host-cert.pem
+ target: /etc/pki/tls/certs/cachain.pem
+
+
+# grouper-ws:
+# build: ./grouper-ws/
+# command: bash -c "while ! curl -s grouper-data:3306 > /dev/null; do echo waiting for mysql on grouper-data to start; sleep 3; done; while ! curl -s ldap://directory:389 > /dev/null; do echo waiting for ldap on directory to start; sleep 3; done; exec ws"
+# depends_on:
+# - grouper-data
+# - directory
+# environment:
+# - ENV=dev
+# - GROUPER_DATABASE_PASSWORD_FILE=/run/secrets/g_database_password.txt
+# - SUBJECT_SOURCE_LDAP_PASSWORD=password
+# - USERTOKEN=build-2
+# networks:
+# - front
+# - back
+# ports:
+# - "8443:443"
+# secrets:
+# - g_database_password.txt
+# - source: grouper.hibernate.properties
+# target: grouper_grouper.hibernate.properties
+# - source: grouper-loader.properties
+# target: grouper_grouper-loader.properties
+# - source: subject.properties
+# target: grouper_subject.properties
+# - source: sp-key.pem
+# target: shib_sp-key.pem
+# - source: host-key.pem
+# volumes:
+# - type: bind
+# source: ./configs-and-secrets/grouper/grouper.properties
+# target: /opt/grouper/conf/grouper.properties
+# - type: bind
+# source: ./configs-and-secrets/grouper/grouper.client.properties
+# target: /opt/grouper/conf/grouper.client.properties
+# - type: bind
+# source: ./configs-and-secrets/httpd/host-cert.pem
+# target: /etc/pki/tls/certs/host-cert.pem
+# - type: bind
+# source: ./configs-and-secrets/httpd/host-cert.pem
+# target: /etc/pki/tls/certs/cachain.pem
+#
+# gsh:
+# build: ./gsh/
+# depends_on:
+# - grouper-data
+# - directory
+# environment:
+# - ENV=dev
+# - GROUPER_DATABASE_PASSWORD_FILE=/run/secrets/g_database_password.txt
+# - SUBJECT_SOURCE_LDAP_PASSWORD=password
+# - USERTOKEN=build-2
+# networks:
+# - back
+# secrets:
+# - g_database_password.txt
+# - source: grouper.hibernate.properties
+# target: grouper_grouper.hibernate.properties
+# - source: grouper-loader.properties
+# target: grouper_grouper-loader.properties
+# - source: subject.properties
+# target: grouper_subject.properties
+# volumes:
+# - type: bind
+# source: ./configs-and-secrets/grouper/grouper.properties
+# target: /opt/grouper/conf/grouper.properties
+# - type: bind
+# source: ./configs-and-secrets/grouper/grouper.client.properties
+# target: /opt/grouper/conf/grouper.client.properties
+
+ grouper-data:
+ build: ./grouper-data/
+ networks:
+ - back
+ ports:
+ - "3306:3306"
+ volumes:
+ - grouper_data:/var/lib/mysql
+
+ directory:
+ build: ./directory/
+ ports:
+ - "389:389"
+ networks:
+ - back
+ volumes:
+ - ldap:/var/lib/dirsrv
+
+ sources:
+ build: ./sources/
+ ports:
+ - "13306:3306"
+ networks:
+ - back
+ volumes:
+ - source_data:/var/lib/mysql
+
+ targets:
+ build: ./targets/
+ ports:
+ - "23306:389"
+ networks:
+ - back
+ volumes:
+ - target_data:/var/lib/mysql
+
+ midpoint-data:
+ image: tier/midpoint-mariadb:latest
+ ports:
+ - "33306:3306"
+ networks:
+ - back
+ volumes:
+ - midpoint_data:/var/lib/mysql
+
+ midpoint-server:
+ build: ./midpoint-server/
+ depends_on:
+ - midpoint-data
+ ports:
+ - "8443:443"
+ environment:
+ - AUTHENTICATION
+ - ENV
+ - USERTOKEN
+ - REPO_DATABASE_TYPE
+ - REPO_JDBC_URL
+ - REPO_HOST
+ - REPO_PORT
+ - REPO_DATABASE
+ - REPO_USER
+ - REPO_PASSWORD_FILE
+ - KEYSTORE_PASSWORD_FILE
+ - MEM
+ - SSO_HEADER
+ - LOGOUT_URL
+ - TIER_BEACON_OPT_OUT
+ networks:
+ - front
+ - back
+ secrets:
+ - m_database_password.txt
+ - m_keystore_password.txt
+ - m_sp-key.pem
+ - m_host-key.pem
+ volumes:
+ - midpoint_home:/opt/midpoint/var
+ - type: bind
+ source: ./configs-and-secrets/midpoint/shibboleth/shibboleth2.xml
+ target: /etc/shibboleth/shibboleth2.xml
+ - type: bind
+ source: ./configs-and-secrets/midpoint/shibboleth/idp-metadata.xml
+ target: /etc/shibboleth/idp-metadata.xml
+ - type: bind
+ source: ./configs-and-secrets/midpoint/shibboleth/sp-cert.pem
+ target: /etc/shibboleth/sp-cert.pem
+ - type: bind
+ source: ./configs-and-secrets/midpoint/httpd/host-cert.pem
+ target: /etc/pki/tls/certs/host-cert.pem
+ - type: bind
+ source: ./configs-and-secrets/midpoint/httpd/host-cert.pem
+ target: /etc/pki/tls/certs/cachain.pem
+
+ idp:
+ build: ./idp/
+ depends_on:
+ - directory
+ environment:
+ - JETTY_MAX_HEAP=64m
+ - JETTY_BROWSER_SSL_KEYSTORE_PASSWORD=password
+ - JETTY_BACKCHANNEL_SSL_KEYSTORE_PASSWORD=password
+ networks:
+ - front
+ - back
+ ports:
+ - "4443:4443"
+
+ mq:
+ image: rabbitmq:management
+ environment:
+ - RABBITMQ_NODENAME=docker-rabbit
+ hostname: rabbitmq
+ networks:
+ - front
+ - back
+ ports:
+ - "15672:15672"
+
+networks:
+ front:
+ driver: bridge
+ back:
+ driver: bridge
+
+secrets:
+# grouper
+ g_host-key.pem:
+ file: ./configs-and-secrets/grouper/httpd/host-key.pem
+ g_sp-key.pem:
+ file: ./configs-and-secrets/grouper/shibboleth/sp-key.pem
+ g_database_password.txt:
+ file: ./configs-and-secrets/grouper/application/database_password.txt
+ rabbitmq_password.txt:
+ file: ./configs-and-secrets/grouper/application/rabbitmq_password.txt
+ grouper.hibernate.properties:
+ file: ./configs-and-secrets/grouper/application/grouper.hibernate.properties
+ grouper-loader.properties:
+ file: ./configs-and-secrets/grouper/application/grouper-loader.properties
+ subject.properties:
+ file: ./configs-and-secrets/grouper/application/subject.properties
+# midPoint
+ m_host-key.pem:
+ file: ./configs-and-secrets/midpoint/httpd/host-key.pem
+ m_sp-key.pem:
+ file: ./configs-and-secrets/midpoint/shibboleth/sp-key.pem
+ m_database_password.txt:
+ file: ./configs-and-secrets/midpoint/application/database_password.txt
+ m_keystore_password.txt:
+ file: ./configs-and-secrets/midpoint/application/keystore_password.txt
+
+volumes:
+ grouper_data:
+ source_data:
+ target_data:
+ ldap:
+ midpoint_data:
+ midpoint_home:
diff --git a/demo/complex/grouper-daemon/Dockerfile b/demo/complex/grouper-daemon/Dockerfile
new file mode 100644
index 0000000..f620350
--- /dev/null
+++ b/demo/complex/grouper-daemon/Dockerfile
@@ -0,0 +1,5 @@
+FROM tier/grouper:latest
+
+LABEL author="tier-packaging@internet2.edu "
+
+CMD ["daemon"]
diff --git a/demo/complex/grouper-data/Dockerfile b/demo/complex/grouper-data/Dockerfile
new file mode 100644
index 0000000..5ca6a12
--- /dev/null
+++ b/demo/complex/grouper-data/Dockerfile
@@ -0,0 +1,40 @@
+FROM tier/grouper:latest
+
+LABEL author="tier-packaging@internet2.edu "
+
+RUN yum install -y epel-release \
+ && yum update -y \
+ && yum install -y mariadb-server mariadb \
+ && yum clean all \
+ && rm -rf /var/cache/yum
+
+COPY container_files/seed-data/ /seed-data/
+COPY container_files/conf/ /opt/grouper/grouper.apiBinary/conf/
+
+RUN mysql_install_db \
+ && chown -R mysql:mysql /var/lib/mysql/ \
+ && sed -i 's/^\(bind-address\s.*\)/# \1/' /etc/my.cnf \
+ && sed -i 's/^\(log_error\s.*\)/# \1/' /etc/my.cnf \
+ && sed -i 's/\[mysqld\]/\[mysqld\]\ncharacter_set_server = utf8/' /etc/my.cnf \
+ && sed -i 's/\[mysqld\]/\[mysqld\]\ncollation_server = utf8_general_ci/' /etc/my.cnf \
+ && sed -i 's/\[mysqld\]/\[mysqld\]\nport = 3306/' /etc/my.cnf \
+ && cat /etc/my.cnf \
+ && echo "/usr/bin/mysqld_safe &" > /tmp/config \
+ && echo "mysqladmin --silent --wait=30 ping || exit 1" >> /tmp/config \
+ && echo "mysql -e 'GRANT ALL PRIVILEGES ON *.* TO \"root\"@\"%\" WITH GRANT OPTION;'" >> /tmp/config \
+ && echo "mysql -e 'CREATE DATABASE grouper CHARACTER SET utf8 COLLATE utf8_bin;'" >> /tmp/config \
+ && bash /tmp/config \
+ && rm -f /tmp/config
+
+RUN (mysqld_safe & ) \
+ && while ! curl -s localhost:3306 > /dev/null; do echo waiting for mysqld to start; sleep 1; done; \
+ bin/gsh -registry -check -runscript -noprompt && \
+ echo "Running demo.gsh" && \
+ sleep 10 && \
+ bin/gsh /seed-data/demo.gsh && \
+ echo "demo.gsh DONE" && \
+ rm /seed-data/demo.gsh
+
+EXPOSE 3306
+
+CMD mysqld_safe
diff --git a/demo/complex/grouper-data/container_files/conf/grouper.hibernate.properties b/demo/complex/grouper-data/container_files/conf/grouper.hibernate.properties
new file mode 100644
index 0000000..154b8eb
--- /dev/null
+++ b/demo/complex/grouper-data/container_files/conf/grouper.hibernate.properties
@@ -0,0 +1,29 @@
+#
+# Grouper Hibernate Configuration
+# $Id: grouper.hibernate.example.properties,v 1.9 2009-08-11 20:18:09 mchyzer Exp $
+#
+
+# The grouper hibernate config uses Grouper Configuration Overlays (documented on wiki)
+# By default the configuration is read from grouper.hibernate.base.properties
+# (which should not be edited), and the grouper.hibernate.properties overlays
+# the base settings. See the grouper.hibernate.base.properties for the possible
+# settings that can be applied to the grouper.hibernate.properties
+
+########################################
+## DB settings
+########################################
+
+# e.g. mysql: jdbc:mysql://localhost:3306/grouper
+# e.g. p6spy (log sql): [use the URL that your DB requires]
+# e.g. oracle: jdbc:oracle:thin:@server.school.edu:1521:sid
+# e.g. hsqldb (a): jdbc:hsqldb:dist/run/grouper;create=true
+# e.g. hsqldb (b): jdbc:hsqldb:hsql://localhost:9001/grouper
+# e.g. postgres: jdbc:postgresql://localhost:5432/database
+# e.g. mssql: jdbc:sqlserver://localhost:3280;databaseName=grouper
+hibernate.connection.url = jdbc:mysql://localhost:3306/grouper?CharSet=utf8&useUnicode=true&characterEncoding=utf8
+
+hibernate.connection.username = root
+# If you are using an empty password, depending upon your version of
+# Java and Ant you may need to specify a password of "".
+# Note: you can keep passwords external and encrypted: https://bugs.internet2.edu/jira/browse/GRP-122
+hibernate.connection.password =
diff --git a/demo/complex/grouper-data/container_files/conf/grouper.properties b/demo/complex/grouper-data/container_files/conf/grouper.properties
new file mode 100644
index 0000000..c931287
--- /dev/null
+++ b/demo/complex/grouper-data/container_files/conf/grouper.properties
@@ -0,0 +1,25 @@
+#
+# Grouper Configuration
+# $Id: grouper.example.properties,v 1.48 2009-12-16 06:02:30 mchyzer Exp $
+#
+
+# Grouper uses Grouper Configuration Overlays (documented on wiki)
+# By default the configuration is read from grouper.base.properties
+# (which should not be edited), and the grouper.properties overlays
+# the base settings. See the grouper.base.properties for the possible
+# settings that can be applied to the grouper.properties
+
+#if groups like the wheel group should be auto-created for convenience (note: check config needs to be on)
+configuration.autocreate.system.groups = true
+
+# A wheel group allows you to enable non-GrouperSystem subjects to act
+# like a root user when interacting with the registry.
+groups.wheel.use = true
+
+# Set to the name of the group you want to treat as the wheel group.
+# The members of this group will be treated as root-like users.
+groups.wheel.group = etc:sysadmingroup
+
+# Used to allow Include Exclude groups
+grouperIncludeExclude.use = true
+grouperIncludeExclude.requireGroups.use = true
diff --git a/demo/complex/grouper-data/container_files/conf/subject.properties b/demo/complex/grouper-data/container_files/conf/subject.properties
new file mode 100644
index 0000000..a823191
--- /dev/null
+++ b/demo/complex/grouper-data/container_files/conf/subject.properties
@@ -0,0 +1,75 @@
+subject.sources.xml.location =
+
+subjectApi.source.ldap.id = ldap
+subjectApi.source.ldap.name = EDU Ldap
+subjectApi.source.ldap.types = person
+subjectApi.source.ldap.adapterClass = edu.internet2.middleware.grouper.subj.GrouperJndiSourceAdapter
+subjectApi.source.ldap.param.INITIAL_CONTEXT_FACTORY.value = com.sun.jndi.ldap.LdapCtxFactory
+subjectApi.source.ldap.param.PROVIDER_URL.value = ldap://localhost:389
+subjectApi.source.ldap.param.SECURITY_AUTHENTICATION.value = simple
+subjectApi.source.ldap.param.SECURITY_PRINCIPAL.value = cn=admin,dc=internet2,dc=edu
+subjectApi.source.ldap.param.SECURITY_CREDENTIALS.value = password
+subjectApi.source.ldap.param.SubjectID_AttributeType.value = uid
+subjectApi.source.ldap.param.SubjectID_formatToLowerCase.value = false
+subjectApi.source.ldap.param.Name_AttributeType.value = cn
+subjectApi.source.ldap.param.Description_AttributeType.value = cn
+subjectApi.source.ldap.param.VTLDAP_VALIDATOR.value = ConnectLdapValidator
+subjectApi.source.ldap.param.subjectVirtualAttribute_0_searchAttribute0.value = ${subjectUtils.defaultIfBlank(subject.getAttributeValueOrCommaSeparated('uid'), "")},${subjectUtils.defaultIfBlank(subject.getAttributeValueOrCommaSeparated('cn'), "")},${subjectUtils.defaultIfBlank(subject.getAttributeValueOrCommaSeparated('exampleEduRegId'), "")}
+subjectApi.source.ldap.param.sortAttribute0.value = cn
+subjectApi.source.ldap.param.searchAttribute0.value = searchAttribute0
+
+# STATUS SECTION for searches to filter out inactives and allow
+# the user to filter by status with e.g. status=all
+# this is optional, and advanced
+#
+# field in database or ldap or endpoint that is the status field
+#subjectApi.source.example.param.statusDatastoreFieldName.value = status
+
+# search string from user which represents the status. e.g. status=active
+#subjectApi.source.example.param.statusLabel.value = status
+
+# available statuses from screen (if not specified, any will be allowed). comma separated list.
+# Note, this is optional and you probably dont want to configure it, it is mostly necessary
+# when you have multiple sources with statuses... if someone types an invalid status
+# and you have this configured, it will not filter by it
+#subjectApi.source.example.param.statusesFromUser.value = Active, Inactive, Pending, All
+
+# all label from the user
+#subjectApi.source.example.param.statusAllFromUser.value = All
+
+# if no status is specified, this will be used (e.g. for active only). Note, the value should be of the
+# form the user would type in
+#subjectApi.source.example.param.statusSearchDefault.value = status=active
+
+# translate between screen values of status, and the data store value. Increment the 0 to 1, 2, etc for more translations.
+# so the user could enter: status=active, and that could translate to status_col=A. The 'user' is what the user types in,
+# the 'datastore' is what is in the datastore. The user part is not case-sensitive. Note, this could be a many to one
+#subjectApi.source.example.param.statusTranslateUser0.value = active
+#subjectApi.source.example.param.statusTranslateDatastore0.value = A
+
+# subject identifier to store in grouper's member table. this is used to increase speed of loader and perhaps for provisioning
+# you can have up to max 1 subject identifier
+#subjectApi.source.example.param.subjectIdentifierAttribute0.value = uid
+
+#searchSubject: find a subject by ID. ID is generally an opaque and permanent identifier, e.g. 12345678.
+# Each subject has one and only on ID. Returns one result when searching for one ID.
+subjectApi.source.ldap.search.searchSubject.param.filter.value = (&(uid=%TERM%)(objectclass=person))
+subjectApi.source.ldap.search.searchSubject.param.scope.value = SUBTREE_SCOPE
+subjectApi.source.ldap.search.searchSubject.param.base.value = ou=people,dc=internet2,dc=edu
+
+#searchSubjectByIdentifier: find a subject by identifier. Identifier is anything that uniquely
+# identifies the user, e.g. jsmith or jsmith@institution.edu.
+# Subjects can have multiple identifiers. Note: it is nice to have if identifiers are unique
+# even across sources. Returns one result when searching for one identifier.
+subjectApi.source.ldap.search.searchSubjectByIdentifier.param.filter.value = (&(|(uid=%TERM%)(employeeNumber=%TERM%))(objectclass=person))
+subjectApi.source.ldap.search.searchSubjectByIdentifier.param.scope.value = SUBTREE_SCOPE
+subjectApi.source.ldap.search.searchSubjectByIdentifier.param.base.value = ou=people,dc=internet2,dc=edu
+
+# search: find subjects by free form search. Returns multiple results.
+
+subjectApi.source.ldap.search.search.param.filter.value = (&(|(|(uid=%TERM%)(cn=*%TERM%*))(uid=%TERM%*))(objectclass=person))
+subjectApi.source.ldap.search.search.param.scope.value = SUBTREE_SCOPE
+subjectApi.source.ldap.search.search.param.base.value = ou=people,dc=internet2,dc=edu
+
+subjectApi.source.ldap.attributes = givenName, sn, uid, mail, employeeNumber
+subjectApi.source.ldap.internalAttributes = searchAttribute0
diff --git a/demo/complex/grouper-data/container_files/seed-data/demo.gsh b/demo/complex/grouper-data/container_files/seed-data/demo.gsh
new file mode 100644
index 0000000..6e3c239
--- /dev/null
+++ b/demo/complex/grouper-data/container_files/seed-data/demo.gsh
@@ -0,0 +1,72 @@
+System.out.println("************** demo.gsh starting...");
+
+gs = GrouperSession.startRootSession();
+addRootStem("ref", "ref");
+addStem("ref", "course", "course")
+addStem("ref", "affiliation", "affiliation")
+
+group = new GroupSave(gs).assignName("etc:affiliationLoader").assignCreateParentStemsIfNotExist(true).save();
+group.getAttributeDelegate().assignAttribute(LoaderLdapUtils.grouperLoaderLdapAttributeDefName()).getAttributeAssign();
+attributeAssign = group.getAttributeDelegate().retrieveAssignment(null, LoaderLdapUtils.grouperLoaderLdapAttributeDefName(), false, true);
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapQuartzCronName(), "0 * * * * ?");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapTypeName(), "LDAP_GROUPS_FROM_ATTRIBUTES");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapServerIdName(), "demo");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapFilterName(), "(eduPersonAffiliation=*)");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSearchDnName(), "ou=People,dc=internet2,dc=edu");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSubjectAttributeName(), "uid");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSourceIdName(), "ldap");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapGroupAttributeName(), "eduPersonAffiliation");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSubjectIdTypeName(), "subjectId");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapGroupNameExpressionName(), 'ref:affiliation:${groupAttribute}_systemOfRecord');
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapGroupDisplayNameExpressionName(), '${groupAttribute} system of record');
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapGroupTypesName(), "addIncludeExclude");
+
+group = new GroupSave(gs).assignName("etc:deptLoader").assignCreateParentStemsIfNotExist(true).save();
+group.getAttributeDelegate().assignAttribute(LoaderLdapUtils.grouperLoaderLdapAttributeDefName()).getAttributeAssign();
+attributeAssign = group.getAttributeDelegate().retrieveAssignment(null, LoaderLdapUtils.grouperLoaderLdapAttributeDefName(), false, true);
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapQuartzCronName(), "0 * * * * ?");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapTypeName(), "LDAP_GROUPS_FROM_ATTRIBUTES");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapServerIdName(), "demo");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapFilterName(), "(businessCategory=*)");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSearchDnName(), "ou=People,dc=internet2,dc=edu");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSubjectAttributeName(), "uid");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSourceIdName(), "ldap");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapGroupAttributeName(), "businessCategory");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSubjectIdTypeName(), "subjectId");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapGroupNameExpressionName(), 'ref:dept:${groupAttribute}');
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapGroupDisplayNameExpressionName(), '${groupAttribute}');
+
+group = new GroupSave(gs).assignName("etc:coursesLoader").assignCreateParentStemsIfNotExist(true).save();
+group.getAttributeDelegate().assignAttribute(LoaderLdapUtils.grouperLoaderLdapAttributeDefName()).getAttributeAssign();
+attributeAssign = group.getAttributeDelegate().retrieveAssignment(null, LoaderLdapUtils.grouperLoaderLdapAttributeDefName(), false, true);
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapQuartzCronName(), "0 * * * * ?");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapTypeName(), "LDAP_GROUP_LIST");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapFilterName(), "(cn=*)");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSearchDnName(), "ou=Courses,ou=Groups,dc=internet2,dc=edu");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapServerIdName(), "demo");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSourceIdName(), "ldap");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSubjectExpressionName(), '${loaderLdapElUtils.convertDnToSpecificValue(subjectId)}');
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSubjectAttributeName(), "uniqueMember");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSubjectIdTypeName(), "subjectId");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapExtraAttributesName(), "cn");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapGroupNameExpressionName(), 'ref:course:${groupAttributes["cn"]}');
+
+group = GroupFinder.findByName(gs, "etc:sysadmingroup", true);
+group.getAttributeDelegate().assignAttribute(LoaderLdapUtils.grouperLoaderLdapAttributeDefName()).getAttributeAssign();
+attributeAssign = group.getAttributeDelegate().retrieveAssignment(null, LoaderLdapUtils.grouperLoaderLdapAttributeDefName(), false, true);
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapQuartzCronName(), "0 * * * * ?");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapTypeName(), "LDAP_SIMPLE");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapFilterName(), "(cn=sysadmingroup)");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSearchDnName(), "ou=Groups,dc=internet2,dc=edu");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapServerIdName(), "demo");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSourceIdName(), "ldap");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSubjectExpressionName(), '${loaderLdapElUtils.convertDnToSpecificValue(subjectId)}');
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSubjectAttributeName(), "uniqueMember");
+attributeAssign.getAttributeValueDelegate().assignValue(LoaderLdapUtils.grouperLoaderLdapSubjectIdTypeName(), "subjectId");
+
+testGroup = new GroupSave(gs).assignName("etc:testGroup").assignCreateParentStemsIfNotExist(true).save();
+
+exportedGroups = new GroupSave(gs).assignName("etc:exportedGroups").assignCreateParentStemsIfNotExist(true).save();
+
+s = SubjectFinder.findById(testGroup.getId(), 'group', 'g:gsa');
+exportedGroups.addMember(s, false);
diff --git a/demo/complex/grouper-ui/Dockerfile b/demo/complex/grouper-ui/Dockerfile
new file mode 100644
index 0000000..8fec2ae
--- /dev/null
+++ b/demo/complex/grouper-ui/Dockerfile
@@ -0,0 +1,7 @@
+FROM tier/grouper:latest
+
+LABEL author="tier-packaging@internet2.edu "
+
+#COPY in custom css, images, etc
+
+CMD ["ui"]
diff --git a/demo/complex/grouper-ui/container_files/shibboleth/shibd.logger b/demo/complex/grouper-ui/container_files/shibboleth/shibd.logger
new file mode 100644
index 0000000..2589b43
--- /dev/null
+++ b/demo/complex/grouper-ui/container_files/shibboleth/shibd.logger
@@ -0,0 +1,69 @@
+# set overall behavior
+log4j.rootCategory=DEBUG, shibd_log, warn_log
+
+# fairly verbose for DEBUG, so generally leave at DEBUG
+log4j.category.XMLTooling.XMLObject=DEBUG
+log4j.category.XMLTooling.KeyInfoResolver=DEBUG
+log4j.category.Shibboleth.IPRange=DEBUG
+log4j.category.Shibboleth.PropertySet=DEBUG
+
+# raise for low-level tracing of SOAP client HTTP/SSL behavior
+log4j.category.XMLTooling.libcurl=DEBUG
+
+# useful categories to tune independently:
+#
+# tracing of SAML messages and security policies
+#log4j.category.OpenSAML.MessageDecoder=DEBUG
+#log4j.category.OpenSAML.MessageEncoder=DEBUG
+#log4j.category.OpenSAML.SecurityPolicyRule=DEBUG
+#log4j.category.XMLTooling.SOAPClient=DEBUG
+# interprocess message remoting
+#log4j.category.Shibboleth.Listener=DEBUG
+# mapping of requests to applicationId
+#log4j.category.Shibboleth.RequestMapper=DEBUG
+# high level session cache operations
+#log4j.category.Shibboleth.SessionCache=DEBUG
+# persistent storage and caching
+#log4j.category.XMLTooling.StorageService=DEBUG
+
+# logs XML being signed or verified if set to DEBUG
+log4j.category.XMLTooling.Signature.Debugger=DEBUG, sig_log
+log4j.additivity.XMLTooling.Signature.Debugger=false
+
+# the tran log blocks the "default" appender(s) at runtime
+# Level should be left at DEBUG for this category
+log4j.category.Shibboleth-TRANSACTION=DEBUG, tran_log
+log4j.additivity.Shibboleth-TRANSACTION=false
+# uncomment to suppress particular event types
+#log4j.category.Shibboleth-TRANSACTION.AuthnRequest=WARN
+#log4j.category.Shibboleth-TRANSACTION.Login=WARN
+#log4j.category.Shibboleth-TRANSACTION.Logout=WARN
+
+# define the appenders
+
+log4j.appender.shibd_log=org.apache.log4j.RollingFileAppender
+log4j.appender.shibd_log.fileName=/var/log/shibboleth/shibd.log
+log4j.appender.shibd_log.maxFileSize=1000000
+log4j.appender.shibd_log.maxBackupIndex=10
+log4j.appender.shibd_log.layout=org.apache.log4j.PatternLayout
+log4j.appender.shibd_log.layout.ConversionPattern=%d{%Y-%m-%d %H:%M:%S} %p %c %x: %m%n
+
+log4j.appender.warn_log=org.apache.log4j.RollingFileAppender
+log4j.appender.warn_log.fileName=/var/log/shibboleth/shibd_warn.log
+log4j.appender.warn_log.maxFileSize=1000000
+log4j.appender.warn_log.maxBackupIndex=10
+log4j.appender.warn_log.layout=org.apache.log4j.PatternLayout
+log4j.appender.warn_log.layout.ConversionPattern=%d{%Y-%m-%d %H:%M:%S} %p %c %x: %m%n
+log4j.appender.warn_log.threshold=WARN
+
+log4j.appender.tran_log=org.apache.log4j.RollingFileAppender
+log4j.appender.tran_log.fileName=/var/log/shibboleth/transaction.log
+log4j.appender.tran_log.maxFileSize=1000000
+log4j.appender.tran_log.maxBackupIndex=20
+log4j.appender.tran_log.layout=org.apache.log4j.PatternLayout
+log4j.appender.tran_log.layout.ConversionPattern=%d{%Y-%m-%d %H:%M:%S} %p %c %x: %m%n
+
+log4j.appender.sig_log=org.apache.log4j.FileAppender
+log4j.appender.sig_log.fileName=/var/log/shibboleth/signature.log
+log4j.appender.sig_log.layout=org.apache.log4j.PatternLayout
+log4j.appender.sig_log.layout.ConversionPattern=%m
diff --git a/demo/complex/grouper-ws/Dockerfile b/demo/complex/grouper-ws/Dockerfile
new file mode 100644
index 0000000..f5c06b9
--- /dev/null
+++ b/demo/complex/grouper-ws/Dockerfile
@@ -0,0 +1,9 @@
+FROM tier/grouper:latest
+
+LABEL author="tier-packaging@internet2.edu "
+
+COPY container_files/web.xml /opt/grouper/grouper.ws/WEB-INF/
+COPY container_files/tomcat-users.xml /opt/tomcat/conf/
+COPY container_files/server.xml /opt/tomcat/conf/
+
+CMD ["ws"]
diff --git a/demo/complex/grouper-ws/container_files/server.xml b/demo/complex/grouper-ws/container_files/server.xml
new file mode 100644
index 0000000..20edd02
--- /dev/null
+++ b/demo/complex/grouper-ws/container_files/server.xml
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/complex/grouper-ws/container_files/tomcat-users.xml b/demo/complex/grouper-ws/container_files/tomcat-users.xml
new file mode 100644
index 0000000..f5d6945
--- /dev/null
+++ b/demo/complex/grouper-ws/container_files/tomcat-users.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/complex/grouper-ws/container_files/web.xml b/demo/complex/grouper-ws/container_files/web.xml
new file mode 100644
index 0000000..03d3deb
--- /dev/null
+++ b/demo/complex/grouper-ws/container_files/web.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+ Grouper service filter
+ edu.internet2.middleware.grouper.ws.GrouperServiceJ2ee
+
+
+
+
+ Grouper logging filter
+ edu.internet2.middleware.grouper.ws.j2ee.ServletFilterLogger
+
+
+
+
+
+ Grouper service filter
+ /services/*
+
+
+ Grouper service filter
+ /servicesRest/*
+
+
+ AxisServlet
+ Apache-Axis Servlet
+ edu.internet2.middleware.grouper.ws.GrouperServiceAxisServlet
+ 1
+
+
+
+
+ RestServlet
+ WS REST Servlet
+ edu.internet2.middleware.grouper.ws.rest.GrouperRestServlet
+ 1
+
+
+ StatusServlet
+ Status Servlet
+ edu.internet2.middleware.grouper.j2ee.status.GrouperStatusServlet
+ 1
+
+
+ StatusServlet
+ /status
+
+
+ AxisServlet
+ /services/*
+
+
+ RestServlet
+ /servicesRest/*
+
+
+
+
+ Web services
+ /services/*
+
+
+ *
+
+
+
+
+
+ Web services
+ /servicesRest/*
+
+
+
+ *
+
+
+
+
+
+ BASIC
+ Grouper Application
+
+
+
+
+
+ The role that is required to log in to web service
+
+ *
+
+
+
+ 1
+
+
+
diff --git a/demo/complex/gsh/Dockerfile b/demo/complex/gsh/Dockerfile
new file mode 100644
index 0000000..3302328
--- /dev/null
+++ b/demo/complex/gsh/Dockerfile
@@ -0,0 +1,5 @@
+FROM tier/grouper:latest
+
+MAINTAINER tier-packaging@internet2.edu
+
+CMD ["gsh"]
diff --git a/demo/complex/idp/Dockerfile b/demo/complex/idp/Dockerfile
new file mode 100644
index 0000000..7d0b512
--- /dev/null
+++ b/demo/complex/idp/Dockerfile
@@ -0,0 +1,5 @@
+FROM unicon/shibboleth-idp:latest
+
+LABEL author="tier-packaging@internet2.edu "
+
+COPY shibboleth-idp/ /opt/shibboleth-idp/
diff --git a/demo/complex/idp/shibboleth-idp/conf/attribute-filter.xml b/demo/complex/idp/shibboleth-idp/conf/attribute-filter.xml
new file mode 100644
index 0000000..21ffdb8
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/conf/attribute-filter.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/complex/idp/shibboleth-idp/conf/attribute-resolver.xml b/demo/complex/idp/shibboleth-idp/conf/attribute-resolver.xml
new file mode 100644
index 0000000..ee9519f
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/conf/attribute-resolver.xml
@@ -0,0 +1,293 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/complex/idp/shibboleth-idp/conf/idp.properties b/demo/complex/idp/shibboleth-idp/conf/idp.properties
new file mode 100644
index 0000000..4396f49
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/conf/idp.properties
@@ -0,0 +1,195 @@
+# Load any additional property resources from a comma-delimited list
+idp.additionalProperties= /conf/ldap.properties, /conf/saml-nameid.properties, /conf/services.properties
+
+# Set the entityID of the IdP
+idp.entityID= https://idptestbed/idp/shibboleth
+
+# Set the scope used in the attribute resolver for scoped attributes
+idp.scope= example.org
+
+# General cookie properties (maxAge only applies to persistent cookies)
+#idp.cookie.secure = false
+#idp.cookie.httpOnly = true
+#idp.cookie.domain =
+#idp.cookie.path =
+#idp.cookie.maxAge = 31536000
+
+# Set the location of user-supplied web flow definitions
+#idp.webflows = %{idp.home}/flows
+
+# Set the location of Velocity view templates
+#idp.views = %{idp.home}/views
+
+# Settings for internal AES encryption key
+#idp.sealer.storeType = JCEKS
+#idp.sealer.updateInterval = PT15M
+#idp.sealer.aliasBase = secret
+idp.sealer.storeResource= %{idp.home}/credentials/sealer.jks
+idp.sealer.versionResource= %{idp.home}/credentials/sealer.kver
+idp.sealer.storePassword= password
+idp.sealer.keyPassword= password
+
+# Settings for public/private signing and encryption key(s)
+# During decryption key rollover, point the ".2" properties at a second
+# keypair, uncomment in credentials.xml, then publish it in your metadata.
+idp.signing.key= %{idp.home}/credentials/idp-signing.key
+idp.signing.cert= %{idp.home}/credentials/idp-signing.crt
+idp.encryption.key= %{idp.home}/credentials/idp-encryption.key
+idp.encryption.cert= %{idp.home}/credentials/idp-encryption.crt
+#idp.encryption.key.2 = %{idp.home}/credentials/idp-encryption-old.key
+#idp.encryption.cert.2 = %{idp.home}/credentials/idp-encryption-old.crt
+
+# Sets the bean ID to use as a default security configuration set
+#idp.security.config = shibboleth.DefaultSecurityConfiguration
+
+# To default to SHA-1, set to shibboleth.SigningConfiguration.SHA1
+#idp.signing.config = shibboleth.SigningConfiguration.SHA256
+
+# Configures trust evaluation of keys used by services at runtime
+# Defaults to supporting both explicit key and PKIX using SAML metadata.
+#idp.trust.signatures = shibboleth.ChainingSignatureTrustEngine
+# To pick only one set to one of:
+# shibboleth.ExplicitKeySignatureTrustEngine, shibboleth.PKIXSignatureTrustEngine
+#idp.trust.certificates = shibboleth.ChainingX509TrustEngine
+# To pick only one set to one of:
+# shibboleth.ExplicitKeyX509TrustEngine, shibboleth.PKIXX509TrustEngine
+
+# If true, encryption will happen whenever a key to use can be located, but
+# failure to encrypt won't result in request failure.
+#idp.encryption.optional = false
+
+# Configuration of client- and server-side storage plugins
+#idp.storage.cleanupInterval = PT10M
+#idp.storage.htmlLocalStorage = false
+
+# Set to true to expose more detailed errors in responses to SPs
+#idp.errors.detailed = false
+# Set to false to skip signing of SAML response messages that signal errors
+#idp.errors.signed = true
+# Name of bean containing a list of Java exception classes to ignore
+#idp.errors.excludedExceptions = ExceptionClassListBean
+# Name of bean containing a property set mapping exception names to views
+#idp.errors.exceptionMappings = ExceptionToViewPropertyBean
+# Set if a different default view name for events and exceptions is needed
+#idp.errors.defaultView = error
+
+# Set to false to disable the IdP session layer
+#idp.session.enabled = true
+
+# Set to "shibboleth.StorageService" for server-side storage of user sessions
+#idp.session.StorageService = shibboleth.ClientSessionStorageService
+idp.session.StorageService = shibboleth.StorageService
+
+# Size of session IDs
+#idp.session.idSize = 32
+# Bind sessions to IP addresses
+#idp.session.consistentAddress = true
+# Inactivity timeout
+#idp.session.timeout = PT60M
+# Extra time to store sessions for logout
+#idp.session.slop = PT0S
+# Tolerate storage-related errors
+#idp.session.maskStorageFailure = false
+# Track information about SPs logged into
+#idp.session.trackSPSessions = false
+# Support lookup by SP for SAML logout
+#idp.session.secondaryServiceIndex = false
+# Length of time to track SP sessions
+#idp.session.defaultSPlifetime = PT2H
+
+# Regular expression matching login flows to enable, e.g. IPAddress|Password
+idp.authn.flows= Password
+
+# Regular expression of forced "initial" methods when no session exists,
+# usually in conjunction with the idp.authn.resolveAttribute property below.
+#idp.authn.flows.initial = Password
+
+# Set to an attribute ID to resolve prior to selecting authentication flows;
+# its values are used to filter the flows to allow.
+#idp.authn.resolveAttribute = eduPersonAssurance
+
+# Default lifetime and timeout of various authentication methods
+#idp.authn.defaultLifetime = PT60M
+#idp.authn.defaultTimeout = PT30M
+
+# Whether to prioritize "active" results when an SP requests more than
+# one possible matching login method (V2 behavior was to favor them)
+#idp.authn.favorSSO = true
+
+# Whether to fail requests when a user identity after authentication
+# doesn't match the identity in a pre-existing session.
+#idp.authn.identitySwitchIsError = false
+
+# Set to "shibboleth.StorageService" or custom bean for alternate storage of consent
+#idp.consent.StorageService = shibboleth.ClientPersistentStorageService
+
+# Set to "shibboleth.consent.AttributeConsentStorageKey" to use an attribute
+# to key user consent storage records (and set the attribute name)
+#idp.consent.userStorageKey = shibboleth.consent.PrincipalConsentStorageKey
+#idp.consent.userStorageKeyAttribute = uid
+
+# Flags controlling how built-in attribute consent feature operates
+#idp.consent.allowDoNotRemember = true
+#idp.consent.allowGlobal = true
+#idp.consent.allowPerAttribute = false
+
+# Whether attribute values and terms of use text are compared
+#idp.consent.compareValues = false
+# Maximum number of consent records for space-limited storage (e.g. cookies)
+#idp.consent.maxStoredRecords = 10
+# Maximum number of consent records for larger/server-side storage (0 = no limit)
+#idp.consent.expandedMaxStoredRecords = 0
+
+# Time in milliseconds to expire consent storage records.
+#idp.consent.storageRecordLifetime = P1Y
+
+# Whether to lookup metadata, etc. for every SP involved in a logout
+# for use by user interface logic; adds overhead so off by default.
+#idp.logout.elaboration = false
+
+# Whether to require logout requests be signed/authenticated.
+#idp.logout.authenticated = true
+
+# Message freshness and replay cache tuning
+#idp.policy.messageLifetime = PT3M
+#idp.policy.clockSkew = PT3M
+
+# Set to custom bean for alternate storage of replay cache
+#idp.replayCache.StorageService = shibboleth.StorageService
+
+# Toggles whether to allow outbound messages via SAML artifact
+#idp.artifact.enabled = true
+# Suppresses typical signing/encryption when artifact binding used
+#idp.artifact.secureChannel = true
+# May differ to direct SAML 2 artifact lookups to specific server nodes
+#idp.artifact.endpointIndex = 2
+# Set to custom bean for alternate storage of artifact map state
+#idp.artifact.StorageService = shibboleth.StorageService
+
+# Name of access control policy for various admin flows
+idp.status.accessPolicy= AccessByIPAddress
+idp.resolvertest.accessPolicy= AccessByIPAddress
+idp.reload.accessPolicy= AccessByIPAddress
+
+# Comma-delimited languages to use if not match can be found with the
+# browser-supported languages, defaults to an empty list.
+idp.ui.fallbackLanguages= en,fr,de
+
+# Storage service used by CAS protocol
+# Defaults to shibboleth.StorageService (in-memory)
+# MUST be server-side storage (e.g. in-memory, memcached, database)
+# NOTE that idp.session.StorageService requires server-side storage
+# when CAS protocol is enabled
+idp.cas.StorageService=shibboleth.StorageService
+
+# CAS service registry implementation class
+#idp.cas.serviceRegistryClass=net.shibboleth.idp.cas.service.PatternServiceRegistry
+
+# Profile flows in which the ProfileRequestContext should be exposed
+# in servlet request under the key "opensamlProfileRequestContext"
+#idp.profile.exposeProfileRequestContextInServletRequest = SAML2/POST/SSO,SAML2/Redirect/SSO
+
+# F-TICKS auditing - set salt to include hashed username
+#idp.fticks.federation=MyFederation
+#idp.fticks.algorithm=SHA-256
+#idp.fticks.salt=somethingsecret
diff --git a/demo/complex/idp/shibboleth-idp/conf/ldap.properties b/demo/complex/idp/shibboleth-idp/conf/ldap.properties
new file mode 100644
index 0000000..726f145
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/conf/ldap.properties
@@ -0,0 +1,58 @@
+# LDAP authentication configuration, see authn/ldap-authn-config.xml
+
+## Authenticator strategy, either anonSearchAuthenticator, bindSearchAuthenticator, directAuthenticator, adAuthenticator
+#idp.authn.LDAP.authenticator = anonSearchAuthenticator
+
+## Connection properties ##
+idp.authn.LDAP.ldapURL = ldap://directory:389
+idp.authn.LDAP.useStartTLS = false
+idp.authn.LDAP.useSSL = false
+#idp.authn.LDAP.connectTimeout = 3000
+
+## SSL configuration, either jvmTrust, certificateTrust, or keyStoreTrust
+#idp.authn.LDAP.sslConfig = certificateTrust
+## If using certificateTrust above, set to the trusted certificate's path
+idp.authn.LDAP.trustCertificates = %{idp.home}/credentials/ldap-server.crt
+## If using keyStoreTrust above, set to the truststore path
+idp.authn.LDAP.trustStore = %{idp.home}/credentials/ldap-server.truststore
+
+## Return attributes during authentication
+## NOTE: this is not used during attribute resolution; configure that directly in the
+## attribute-resolver.xml configuration via a DataConnector's element
+idp.authn.LDAP.returnAttributes = cn,businessCategory,mail
+
+## DN resolution properties ##
+
+# Search DN resolution, used by anonSearchAuthenticator, bindSearchAuthenticator
+# for AD: CN=Users,DC=example,DC=org
+idp.authn.LDAP.baseDN = ou=people,dc=internet2,dc=edu
+#idp.authn.LDAP.subtreeSearch = false
+idp.authn.LDAP.userFilter = (uid={user})
+# bind search configuration
+# for AD: idp.authn.LDAP.bindDN=adminuser@domain.com
+idp.authn.LDAP.bindDN = cn=admin,dc=internet2,dc=edu
+idp.authn.LDAP.bindDNCredential = password
+
+# Format DN resolution, used by directAuthenticator, adAuthenticator
+# for AD use idp.authn.LDAP.dnFormat=%s@domain.com
+idp.authn.LDAP.dnFormat = uid=%s,ou=people,dc=internet2,dc=edu
+
+# LDAP attribute configuration, see attribute-resolver.xml
+idp.attribute.resolver.LDAP.ldapURL = %{idp.authn.LDAP.ldapURL}
+idp.attribute.resolver.LDAP.baseDN = %{idp.authn.LDAP.baseDN}
+idp.attribute.resolver.LDAP.bindDN = %{idp.authn.LDAP.bindDN}
+idp.attribute.resolver.LDAP.bindDNCredential = %{idp.authn.LDAP.bindDNCredential}
+idp.attribute.resolver.LDAP.useStartTLS = %{idp.authn.LDAP.useStartTLS:true}
+idp.attribute.resolver.LDAP.trustCertificates = %{idp.authn.LDAP.trustCertificates}
+idp.attribute.resolver.LDAP.searchFilter = (uid=$requestContext.principalName)
+
+# LDAP pool configuration, used for both authn and DN resolution
+#idp.pool.LDAP.minSize = 3
+#idp.pool.LDAP.maxSize = 10
+#idp.pool.LDAP.validateOnCheckout = false
+#idp.pool.LDAP.validatePeriodically = true
+#idp.pool.LDAP.validatePeriod = 300
+#idp.pool.LDAP.prunePeriod = 300
+#idp.pool.LDAP.idleTime = 600
+#idp.pool.LDAP.blockWaitTime = 3000
+#idp.pool.LDAP.failFastInitialize = false
diff --git a/demo/complex/idp/shibboleth-idp/conf/metadata-providers.xml b/demo/complex/idp/shibboleth-idp/conf/metadata-providers.xml
new file mode 100644
index 0000000..f70135e
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/conf/metadata-providers.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/complex/idp/shibboleth-idp/credentials/idp-backchannel.crt b/demo/complex/idp/shibboleth-idp/credentials/idp-backchannel.crt
new file mode 100644
index 0000000..c1f8fab
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/credentials/idp-backchannel.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDEzCCAfugAwIBAgIUS9SuTXwsFVVG+LjOEAbLqqT/el0wDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMjZaFw0zNTEy
+MTEwMjIwMjZaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCMAoDHx8xCIfv/6QKqt9mcHYmEJ8y2dKprUbpdcOjH
+YvNPIl/lHPsUyrb+Nc+q2CDeiWjVk1mWYq0UpIwpBMuw1H6+oOqr4VQRi65pin0M
+SfE0MWIaFo5FPvpvoptkHD4gvREbm4swyXGMczcMRfqgalFXhUD2wz8W3XAM5Cq2
+03XeJbj6TwjvKatG5XPdeUe2FBGuOO2q54L1hcIGnLMCQrg7D31lR13PJbjnJ0No
+5C3k8TPuny6vJsBC03GNLNKfmrKVTdzr3VKp1uay1G3DL9314fgmbl8HA5iRQmy+
+XInUU6/8NXZSF59p3ITAOvZQeZsbJjg5gGDip5OZo9YlAgMBAAGjWzBZMB0GA1Ud
+DgQWBBRPlM4VkKZ0U4ec9GrIhFQl0hNbLDA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAIZ0a1ov3my3ljJG588I/PHx+TxAWONWmpKbO9c/qI3Drxk4oRIffiac
+ANxdvtabgIzrlk5gMMisD7oyqHJiWgKv5Bgctd8w3IS3lLl7wHX65mTKQRXniG98
+NIjkvfrhe2eeJxecOqnDI8GOhIGCIqZUn8ShdM/yHjhQ2Mh0Hj3U0LlKvnmfGSQl
+j0viGwbFCaNaIP3zc5UmCrdE5h8sWL3Fu7ILKM9RyFa2ILHrJScV9t623IcHffHP
+IeaY/WtuapsrqRFxuQL9QFWN0FsRIdLmjTq+00+B/XnnKRKFBuWfjhHLF/uu8f+E
+t6Lf23Kb8yD6ZR7dihMZAGHnYQ/hlhM=
+-----END CERTIFICATE-----
diff --git a/demo/complex/idp/shibboleth-idp/credentials/idp-backchannel.p12 b/demo/complex/idp/shibboleth-idp/credentials/idp-backchannel.p12
new file mode 100644
index 0000000..112540a
Binary files /dev/null and b/demo/complex/idp/shibboleth-idp/credentials/idp-backchannel.p12 differ
diff --git a/demo/complex/idp/shibboleth-idp/credentials/idp-browser.p12 b/demo/complex/idp/shibboleth-idp/credentials/idp-browser.p12
new file mode 100644
index 0000000..032be0b
Binary files /dev/null and b/demo/complex/idp/shibboleth-idp/credentials/idp-browser.p12 differ
diff --git a/demo/complex/idp/shibboleth-idp/credentials/idp-encryption.crt b/demo/complex/idp/shibboleth-idp/credentials/idp-encryption.crt
new file mode 100644
index 0000000..15d764f
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/credentials/idp-encryption.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDEzCCAfugAwIBAgIUG6Nn1rlERS1vsi88tcdzSYX0oqAwDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMTRaFw0zNTEy
+MTEwMjIwMTRaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCBXv0o3fmT8iluyLjJ4lBAVCW+ZRVyEXPYQuRi7vfD
+cO4a6d1kxiJLsaK0W88VNxjFQRr8PgDkWr28vwoH1rgk4pLsszLD48DBzD942peJ
+l/S6FnsIJjmaHcBh4pbNhU4yowu63iKkvttrcZAEbpEro6Z8CziWEx8sywoaYEQG
+ifPkr9ORV6Cn3txq+9gMBePG41GrtZrUGIu+xrndL0Shh4Pq0eq/9MAsVlIIXEa8
+9WfH8J2kFcTOfoWtIc70b7TLZQsx4YnNcnrGLSUEcstFyPLX+Xtv5SNZF89OOIxX
+VNjNvgE5DbJb9hMM4UAFqI+1bo9QqtxwThjc/sOvIxzNAgMBAAGjWzBZMB0GA1Ud
+DgQWBBStTyogRPuAVG6q7yPyav1uvE+7pTA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAFMfoOv+oISGjvamq7+Y4G7ep5vxlAPeK3RATYPYvAmyH946qZXh98ni
+QXyuqZW5P5eEt86toY45IwDU5r09SKwHughEe99iiEkxh0mb2qo84qX9/qcg+kyN
+jeLd/OSyolpUCEFNwOFcog7pj7Eer+6AHbwTn1Mjb5TBsKwtDMJsaxPvdj0u7M5r
+xL/wHkFhn1rCo2QiojzjSlV3yLTh49iTyhE3cG+RxaNKDCxhp0jSSLX1BW/ZoPA8
++PMJEA+Q0QbyRD8aJOHN5O8jGxCa/ZzcOnYVL6AsEXoDiY3vAUYh1FUonOWw0m9H
+p+tGUbGS2l873J5PrsbpeKEVR/IIoKo=
+-----END CERTIFICATE-----
diff --git a/demo/complex/idp/shibboleth-idp/credentials/idp-encryption.key b/demo/complex/idp/shibboleth-idp/credentials/idp-encryption.key
new file mode 100644
index 0000000..8bb5cc6
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/credentials/idp-encryption.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAgV79KN35k/Ipbsi4yeJQQFQlvmUVchFz2ELkYu73w3DuGund
+ZMYiS7GitFvPFTcYxUEa/D4A5Fq9vL8KB9a4JOKS7LMyw+PAwcw/eNqXiZf0uhZ7
+CCY5mh3AYeKWzYVOMqMLut4ipL7ba3GQBG6RK6OmfAs4lhMfLMsKGmBEBonz5K/T
+kVegp97cavvYDAXjxuNRq7Wa1BiLvsa53S9EoYeD6tHqv/TALFZSCFxGvPVnx/Cd
+pBXEzn6FrSHO9G+0y2ULMeGJzXJ6xi0lBHLLRcjy1/l7b+UjWRfPTjiMV1TYzb4B
+OQ2yW/YTDOFABaiPtW6PUKrccE4Y3P7DryMczQIDAQABAoIBAF/IflMllcUtw/Nb
+9USzpIscQh2nJaugtE5nqER/fT1cfU273Mjh0T6NtFMorjec5WAWBe6/0VVAwb3f
+C4QmO4xDnFhXjLxwAaT6nfvSi+O5d93XCxxLgNZUNL3ET7a2feELyoF+OdQT4sy3
+9dLyMdVHgtnQTQMAAVLeuQoyP+s+Zax4Gca6ln8QxIIvDoD7NITnpl8887Hghhzl
+CvKtRiPRtoI2JTXWgWuLI6xXfVsDvFT+Up+ki9TMLWLACcmMU1d+lUBOKIqhhQHG
++np9iKxVausJwYaLwwT3h8SItNon7ltbV6kcqyZxMAA+uN8CVgIb5UaUrlW7Nvze
+1iUNudkCgYEAxpnBg8YxdEHFSMTqjEOYapn18cs3n32EBPtvPaUcvw3mGC1+ZVx1
+9WqnVsgykBOWI0qSBVF7Kke8yOqgqWtYQUFqLiMgCC9e/QcXnrm/bzAmKDgLVcCR
+KzgqU2ECQDkNSS0qeODjLGX4SEabDbLhN59WykHKM0i/RcrbhuvT1BcCgYEApsMD
+TFQBaiaEmLVm252piZf8b5g3DrUHeqGktHkHXTW4Iyyn8zEknoiCosk/Tej73zga
+cTT3zQgEh63DMC9Ag8IbIJiDpYLMkt1QvZYtq95E/94GVEfRRok6/pyagGYB351R
+PXcykrDyy26FSofmtaXU37Wxaj3ow+WROaPgULsCgYEArFoFScG3a2gkuRlDX8TN
+wj2o5lTxCbWY2+YEzR+8icWbGQJqPbb3G6uaW8LTtpt44Vm2zWzAEZo+KLMOCNmC
+tub5Kd8Lzm6l5brA8dvLWcgUZTT2CU5b7YEJomB+3pNkh0vuHwczv3Ui+j5kE4hY
+0bezT0W3H7iTXhNFXprMs7MCgYEAlIZn75l6URLRUjluzPdVQoktei72CpFNgflp
++ps45dmskRd61mzUkqY+w8G+MiPqANu1IVLtyZz0e+tVRxsuuKsvAg8UYVtn3P5k
+pRaWwtaKWeFjfbkhOVOMSa0tJmK0FHfHHZmGX4ReGrXq3YDBCNQUDtOCmn9dSuyy
+NcYxSXUCgYB+yo6dg8nyHDSqKDdrQQiAKv7jNsbecQ/rYrt8l0n9FBiwn5R7v6kp
+afsimCVou5i06L2Cr5Xs+XSf11KVkDh+qM70ZFubWEsHCDrS1KrxUzfFbrQczKof
+qX7ZsBuOT72RwVEa8fpT6IZ6IpOOEPmUid/f2VM2aAcXgaF//vMjxA==
+-----END RSA PRIVATE KEY-----
diff --git a/demo/complex/idp/shibboleth-idp/credentials/idp-signing.crt b/demo/complex/idp/shibboleth-idp/credentials/idp-signing.crt
new file mode 100644
index 0000000..6a032c1
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/credentials/idp-signing.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDFDCCAfygAwIBAgIVAN3vv+b7KN5Se9m1RZsCllp/B/hdMA0GCSqGSIb3DQEB
+CwUAMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwHhcNMTUxMjExMDIyMDE0WhcNMzUx
+MjExMDIyMDE0WjAVMRMwEQYDVQQDDAppZHB0ZXN0YmVkMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAh91caeY0Q85uhaUyqFwP2bMjwMFxMzRlAoqBHd7g
+u6eo4duaeLz1BaoR2XTBpNNvFR5oHH+TkKahVDGeH5+kcnIpxI8JPdsZml1srvf2
+Z6dzJsulJZUdpqnngycTkGtZgEoC1vmYVky2BSAIIifmdh6s0epbHnMGLsHzMKfJ
+Cb/Q6dYzRWTCPtzE2VMuQqqWgeyMr7u14x/Vqr9RPEFsgY8GIu5jzB6AyUIwrLg+
+MNkv6aIdcHwxYTGL7ijfy6rSWrgBflQoYRYNEnseK0ZHgJahz4ovCag6wZAoPpBs
+uYlY7lEr89Ucb6NHx3uqGMsXlDFdE4QwfDLLhCYHPvJ0uwIDAQABo1swWTAdBgNV
+HQ4EFgQUAkOgED3iYdmvQEOMm6u/JmD/UTQwOAYDVR0RBDEwL4IKaWRwdGVzdGJl
+ZIYhaHR0cHM6Ly9pZHB0ZXN0YmVkL2lkcC9zaGliYm9sZXRoMA0GCSqGSIb3DQEB
+CwUAA4IBAQBIdd4YWlnvJjql8+zKKgmWgIY7U8DA8e6QcbAf8f8cdE33RSnjI63X
+sv/y9GfmbAVAD6RIAXPFFeRYJ08GOxGI9axfNaKdlsklJ9bk4ducHqgCSWYVer3s
+RQBjxyOfSTvk9YCJvdJVQRJLcCvxwKakFCsOSnV3t9OvN86Ak+fKPVB5j2fM/0fZ
+Kqjn3iqgdNPTLXPsuJLJO5lITRiBa4onmVelAiCstI9PQiaEck+oAHnMTnC9JE/B
+DHv3e4rwq3LznlqPw0GSd7xqNTdMDwNOWjkuOr3sGpWS8ms/ZHHXV1Vd22uPe70i
+s00xrv14zLifcc8oj5DYzOhYRifRXgHX
+-----END CERTIFICATE-----
diff --git a/demo/complex/idp/shibboleth-idp/credentials/idp-signing.key b/demo/complex/idp/shibboleth-idp/credentials/idp-signing.key
new file mode 100644
index 0000000..011c27c
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/credentials/idp-signing.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAh91caeY0Q85uhaUyqFwP2bMjwMFxMzRlAoqBHd7gu6eo4dua
+eLz1BaoR2XTBpNNvFR5oHH+TkKahVDGeH5+kcnIpxI8JPdsZml1srvf2Z6dzJsul
+JZUdpqnngycTkGtZgEoC1vmYVky2BSAIIifmdh6s0epbHnMGLsHzMKfJCb/Q6dYz
+RWTCPtzE2VMuQqqWgeyMr7u14x/Vqr9RPEFsgY8GIu5jzB6AyUIwrLg+MNkv6aId
+cHwxYTGL7ijfy6rSWrgBflQoYRYNEnseK0ZHgJahz4ovCag6wZAoPpBsuYlY7lEr
+89Ucb6NHx3uqGMsXlDFdE4QwfDLLhCYHPvJ0uwIDAQABAoIBAGpInLarQ0+X+ZXK
++aoC+tNO9DUiHq/z2OD4ALGhXSTvr4mgBWNWTkc0F+qJD8MlM8zNkJxaoNGTyLjY
+Z95NQJPXAx2k15HwCENdulvV1oiX0dkTjgmscRmj6FwNAZ0EnWtien45mxZHxEyW
+FkbB9+OHc6JzNvzG9ps1Vk1FtFtO8w4exXuJVATJeArQmMvAUHMJYPb7Qs+/NX9R
+RlDvFfXDzQv5eAzudT1SyFSa5W+Bsw6BtEoeiqdp+xQh4yc733nwN7KG2Z/TpGse
+jVe6akbULuCXOe9uPa7kv8hnQEUH38QVlIw8pK1SsgNq7S0U44WU5uF8gbDBYle/
+OoPBvSECgYEAvCwVEa8ryLzee5FaX4PBxk10lEl/Yp9jC88wPUQ+ZpfniIIQIfwl
+csRE9D3/dJOVAxw/Ac32F72SLVDzLabAhlBRINYLB0ZkVuJi1CIoDHIf9nfh/pOx
+b96VMUe/mpAL4hZnZkmBKjesX5URPEKtBD0aSeCw9aFqhORjRrxCJg0CgYEAuNaD
+LOuTPKsC6nxRtiL9r0CA5gCCdpALxwJA7wHAeh03i5xmy61i4iOMaunxKZhG+nzz
+PhcI8Uhwwk+l3tbYAf1rrtmMKNcyjy+UqWXGt4ZkWFlIyIungyLiH9L32IMhXNF0
+fBgOZNtFTmQBU18a78uIir9xASUbtaakzOtJ2+cCgYAgfawVpZ11x8bSp0Jng6SN
+zQn4IMiyCrtbaqb1rTbpGAmOdIa8l4EP0/vkAGB/jIwKQXJPqXR4nO8EjBmxJD3R
+80RO2yaEVw80QVq3Lj6kB4ClWgXXo0DcBB7Wp4DZ+01R+HRaIQ8AbySATIjxUsH1
+HWfQoc9sWja+Q4Ew0YjKcQKBgGLoPsdBw8b6B5RsM9lPvgoSbScmbKl/CR5TwWVj
+vZhanAd0CLnCrSAvP4tSZf8JAio1xH+cGefrCJOhxTOKKYpfDklBFjQge2iNYHKJ
+CJ3aJ0XzePP/bwLIHtJCtOdBvA+L8VYaFVG418xLzT3MrYBVnFoKeTDQp5Q7eQJC
+gYJPAoGBAKHcuXWzvXoHKnOg8Ljg2xZ6/SfjwNDIIrpXVTAQifmK3q4+Ua2Q+Cjq
+97tPMxF2bVRcbnCSNKpTMOTrsWs8Z3GpMyCh6XgYMSlclXusDVUkRkPpWj8hVTR4
+opm/rxS83hCrTsIX3Il3T8Fpb97kdF+unCiWEaxrPEurjW8lB506
+-----END RSA PRIVATE KEY-----
diff --git a/demo/complex/idp/shibboleth-idp/credentials/sealer.jks b/demo/complex/idp/shibboleth-idp/credentials/sealer.jks
new file mode 100644
index 0000000..89957e0
Binary files /dev/null and b/demo/complex/idp/shibboleth-idp/credentials/sealer.jks differ
diff --git a/demo/complex/idp/shibboleth-idp/credentials/sealer.kver b/demo/complex/idp/shibboleth-idp/credentials/sealer.kver
new file mode 100644
index 0000000..d64b0e4
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/credentials/sealer.kver
@@ -0,0 +1,2 @@
+#Fri Dec 11 02:20:32 UTC 2015
+CurrentVersion=1
diff --git a/demo/complex/idp/shibboleth-idp/metadata/grouper-sp.xml b/demo/complex/idp/shibboleth-idp/metadata/grouper-sp.xml
new file mode 100644
index 0000000..9bde5ef
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/metadata/grouper-sp.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sp.example.org
+
+ CN=sp.example.org,O=Internet2/TIER,L=Ann Arbor,ST=MI,C=US
+ MIIDPDCCAiQCCQDNZe8r0hVtuTANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCTUkxEjAQBgNVBAcMCUFubiBBcmJvcjEXMBUGA1UECgwOSW50
+ZXJuZXQyL1RJRVIxFzAVBgNVBAMMDnNwLmV4YW1wbGUub3JnMB4XDTE3MDkyMjE5
+NTAzNVoXDTI3MDkyMDE5NTAzNVowYDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1J
+MRIwEAYDVQQHDAlBbm4gQXJib3IxFzAVBgNVBAoMDkludGVybmV0Mi9USUVSMRcw
+FQYDVQQDDA5zcC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAMTNJmsNpTpR4NrDJwOgK/o3UYlNdi1c6xBflt+liLAsQc160QReV4dS
+SGK8LZvN58a/BTIsH8dLhQlUQ8qQUY2AfolVrNxb7Waumeh/POzYUTRylnoGpU3W
+bGMEPxE/AdgP5U/adYvyu4XI5epv7wjZJOTqcVag15SalY+aso+ZC/5l+UzRxmWB
+ZxKTsSL1y7PFehY4/Zl3Y3oGVsVl/zspt5lteoZQeeVxUX29S3Af11yHY4xpEp+7
+rvAzY/nlsTiHAsUoCFK/NFQ2evvSRx52B9Fk1cWP1MDVDm2QjQqD9xBGYSnX6bhQ
+ejVx7JUJHlblu2Q5p5XdW0BihgFluoECAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
+n/qhYnIviPs4tglCdrw+M7gbqKNWadDC3F9HDYzlJMFeS/ae2turhEUgQPbYPDQQ
+eO3oOILtvCXNFUPM58jf8V5YFRrOqrTgx44kexQDaHO5YYNft5tF5TdvBYE2gOVr
+GdYrH2iSP8WX+Yy7JH5uqkfwWzEntWHJdey39rCWKAUCCB35+/2b4N53Qmlv2+ug
+CpNJYFtXInd4YMmM5HjXLyoWXtjnKiwDqYUCeYPSwAajnCqRqRXUX0gYTFDRiwRP
+HbmO9We0nqoc/71nikmGGoSRMO/zWVMFjwmAx1fGiWdU61sjGX8sHifzmVyJVEBI
+Z75p+JrWYZJYrx/vpWxL8g==
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/complex/idp/shibboleth-idp/metadata/idp-metadata.xml b/demo/complex/idp/shibboleth-idp/metadata/idp-metadata.xml
new file mode 100644
index 0000000..5a70824
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/metadata/idp-metadata.xml
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+
+ example.org
+
+
+
+
+
+
+
+MIIDEzCCAfugAwIBAgIUS9SuTXwsFVVG+LjOEAbLqqT/el0wDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMjZaFw0zNTEy
+MTEwMjIwMjZaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCMAoDHx8xCIfv/6QKqt9mcHYmEJ8y2dKprUbpdcOjH
+YvNPIl/lHPsUyrb+Nc+q2CDeiWjVk1mWYq0UpIwpBMuw1H6+oOqr4VQRi65pin0M
+SfE0MWIaFo5FPvpvoptkHD4gvREbm4swyXGMczcMRfqgalFXhUD2wz8W3XAM5Cq2
+03XeJbj6TwjvKatG5XPdeUe2FBGuOO2q54L1hcIGnLMCQrg7D31lR13PJbjnJ0No
+5C3k8TPuny6vJsBC03GNLNKfmrKVTdzr3VKp1uay1G3DL9314fgmbl8HA5iRQmy+
+XInUU6/8NXZSF59p3ITAOvZQeZsbJjg5gGDip5OZo9YlAgMBAAGjWzBZMB0GA1Ud
+DgQWBBRPlM4VkKZ0U4ec9GrIhFQl0hNbLDA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAIZ0a1ov3my3ljJG588I/PHx+TxAWONWmpKbO9c/qI3Drxk4oRIffiac
+ANxdvtabgIzrlk5gMMisD7oyqHJiWgKv5Bgctd8w3IS3lLl7wHX65mTKQRXniG98
+NIjkvfrhe2eeJxecOqnDI8GOhIGCIqZUn8ShdM/yHjhQ2Mh0Hj3U0LlKvnmfGSQl
+j0viGwbFCaNaIP3zc5UmCrdE5h8sWL3Fu7ILKM9RyFa2ILHrJScV9t623IcHffHP
+IeaY/WtuapsrqRFxuQL9QFWN0FsRIdLmjTq+00+B/XnnKRKFBuWfjhHLF/uu8f+E
+t6Lf23Kb8yD6ZR7dihMZAGHnYQ/hlhM=
+
+
+
+
+
+
+
+
+
+MIIDFDCCAfygAwIBAgIVAN3vv+b7KN5Se9m1RZsCllp/B/hdMA0GCSqGSIb3DQEB
+CwUAMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwHhcNMTUxMjExMDIyMDE0WhcNMzUx
+MjExMDIyMDE0WjAVMRMwEQYDVQQDDAppZHB0ZXN0YmVkMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAh91caeY0Q85uhaUyqFwP2bMjwMFxMzRlAoqBHd7g
+u6eo4duaeLz1BaoR2XTBpNNvFR5oHH+TkKahVDGeH5+kcnIpxI8JPdsZml1srvf2
+Z6dzJsulJZUdpqnngycTkGtZgEoC1vmYVky2BSAIIifmdh6s0epbHnMGLsHzMKfJ
+Cb/Q6dYzRWTCPtzE2VMuQqqWgeyMr7u14x/Vqr9RPEFsgY8GIu5jzB6AyUIwrLg+
+MNkv6aIdcHwxYTGL7ijfy6rSWrgBflQoYRYNEnseK0ZHgJahz4ovCag6wZAoPpBs
+uYlY7lEr89Ucb6NHx3uqGMsXlDFdE4QwfDLLhCYHPvJ0uwIDAQABo1swWTAdBgNV
+HQ4EFgQUAkOgED3iYdmvQEOMm6u/JmD/UTQwOAYDVR0RBDEwL4IKaWRwdGVzdGJl
+ZIYhaHR0cHM6Ly9pZHB0ZXN0YmVkL2lkcC9zaGliYm9sZXRoMA0GCSqGSIb3DQEB
+CwUAA4IBAQBIdd4YWlnvJjql8+zKKgmWgIY7U8DA8e6QcbAf8f8cdE33RSnjI63X
+sv/y9GfmbAVAD6RIAXPFFeRYJ08GOxGI9axfNaKdlsklJ9bk4ducHqgCSWYVer3s
+RQBjxyOfSTvk9YCJvdJVQRJLcCvxwKakFCsOSnV3t9OvN86Ak+fKPVB5j2fM/0fZ
+Kqjn3iqgdNPTLXPsuJLJO5lITRiBa4onmVelAiCstI9PQiaEck+oAHnMTnC9JE/B
+DHv3e4rwq3LznlqPw0GSd7xqNTdMDwNOWjkuOr3sGpWS8ms/ZHHXV1Vd22uPe70i
+s00xrv14zLifcc8oj5DYzOhYRifRXgHX
+
+
+
+
+
+
+
+
+
+MIIDEzCCAfugAwIBAgIUG6Nn1rlERS1vsi88tcdzSYX0oqAwDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMTRaFw0zNTEy
+MTEwMjIwMTRaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCBXv0o3fmT8iluyLjJ4lBAVCW+ZRVyEXPYQuRi7vfD
+cO4a6d1kxiJLsaK0W88VNxjFQRr8PgDkWr28vwoH1rgk4pLsszLD48DBzD942peJ
+l/S6FnsIJjmaHcBh4pbNhU4yowu63iKkvttrcZAEbpEro6Z8CziWEx8sywoaYEQG
+ifPkr9ORV6Cn3txq+9gMBePG41GrtZrUGIu+xrndL0Shh4Pq0eq/9MAsVlIIXEa8
+9WfH8J2kFcTOfoWtIc70b7TLZQsx4YnNcnrGLSUEcstFyPLX+Xtv5SNZF89OOIxX
+VNjNvgE5DbJb9hMM4UAFqI+1bo9QqtxwThjc/sOvIxzNAgMBAAGjWzBZMB0GA1Ud
+DgQWBBStTyogRPuAVG6q7yPyav1uvE+7pTA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAFMfoOv+oISGjvamq7+Y4G7ep5vxlAPeK3RATYPYvAmyH946qZXh98ni
+QXyuqZW5P5eEt86toY45IwDU5r09SKwHughEe99iiEkxh0mb2qo84qX9/qcg+kyN
+jeLd/OSyolpUCEFNwOFcog7pj7Eer+6AHbwTn1Mjb5TBsKwtDMJsaxPvdj0u7M5r
+xL/wHkFhn1rCo2QiojzjSlV3yLTh49iTyhE3cG+RxaNKDCxhp0jSSLX1BW/ZoPA8
++PMJEA+Q0QbyRD8aJOHN5O8jGxCa/ZzcOnYVL6AsEXoDiY3vAUYh1FUonOWw0m9H
+p+tGUbGS2l873J5PrsbpeKEVR/IIoKo=
+
+
+
+
+
+
+
+
+
+
+ urn:mace:shibboleth:1.0:nameIdentifier
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+
+
+
+
+
+
+
+
+
+
+
+
+ localhost
+
+
+
+
+
+
+MIIDEzCCAfugAwIBAgIUS9SuTXwsFVVG+LjOEAbLqqT/el0wDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMjZaFw0zNTEy
+MTEwMjIwMjZaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCMAoDHx8xCIfv/6QKqt9mcHYmEJ8y2dKprUbpdcOjH
+YvNPIl/lHPsUyrb+Nc+q2CDeiWjVk1mWYq0UpIwpBMuw1H6+oOqr4VQRi65pin0M
+SfE0MWIaFo5FPvpvoptkHD4gvREbm4swyXGMczcMRfqgalFXhUD2wz8W3XAM5Cq2
+03XeJbj6TwjvKatG5XPdeUe2FBGuOO2q54L1hcIGnLMCQrg7D31lR13PJbjnJ0No
+5C3k8TPuny6vJsBC03GNLNKfmrKVTdzr3VKp1uay1G3DL9314fgmbl8HA5iRQmy+
+XInUU6/8NXZSF59p3ITAOvZQeZsbJjg5gGDip5OZo9YlAgMBAAGjWzBZMB0GA1Ud
+DgQWBBRPlM4VkKZ0U4ec9GrIhFQl0hNbLDA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAIZ0a1ov3my3ljJG588I/PHx+TxAWONWmpKbO9c/qI3Drxk4oRIffiac
+ANxdvtabgIzrlk5gMMisD7oyqHJiWgKv5Bgctd8w3IS3lLl7wHX65mTKQRXniG98
+NIjkvfrhe2eeJxecOqnDI8GOhIGCIqZUn8ShdM/yHjhQ2Mh0Hj3U0LlKvnmfGSQl
+j0viGwbFCaNaIP3zc5UmCrdE5h8sWL3Fu7ILKM9RyFa2ILHrJScV9t623IcHffHP
+IeaY/WtuapsrqRFxuQL9QFWN0FsRIdLmjTq+00+B/XnnKRKFBuWfjhHLF/uu8f+E
+t6Lf23Kb8yD6ZR7dihMZAGHnYQ/hlhM=
+
+
+
+
+
+
+
+
+
+MIIDFDCCAfygAwIBAgIVAN3vv+b7KN5Se9m1RZsCllp/B/hdMA0GCSqGSIb3DQEB
+CwUAMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwHhcNMTUxMjExMDIyMDE0WhcNMzUx
+MjExMDIyMDE0WjAVMRMwEQYDVQQDDAppZHB0ZXN0YmVkMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAh91caeY0Q85uhaUyqFwP2bMjwMFxMzRlAoqBHd7g
+u6eo4duaeLz1BaoR2XTBpNNvFR5oHH+TkKahVDGeH5+kcnIpxI8JPdsZml1srvf2
+Z6dzJsulJZUdpqnngycTkGtZgEoC1vmYVky2BSAIIifmdh6s0epbHnMGLsHzMKfJ
+Cb/Q6dYzRWTCPtzE2VMuQqqWgeyMr7u14x/Vqr9RPEFsgY8GIu5jzB6AyUIwrLg+
+MNkv6aIdcHwxYTGL7ijfy6rSWrgBflQoYRYNEnseK0ZHgJahz4ovCag6wZAoPpBs
+uYlY7lEr89Ucb6NHx3uqGMsXlDFdE4QwfDLLhCYHPvJ0uwIDAQABo1swWTAdBgNV
+HQ4EFgQUAkOgED3iYdmvQEOMm6u/JmD/UTQwOAYDVR0RBDEwL4IKaWRwdGVzdGJl
+ZIYhaHR0cHM6Ly9pZHB0ZXN0YmVkL2lkcC9zaGliYm9sZXRoMA0GCSqGSIb3DQEB
+CwUAA4IBAQBIdd4YWlnvJjql8+zKKgmWgIY7U8DA8e6QcbAf8f8cdE33RSnjI63X
+sv/y9GfmbAVAD6RIAXPFFeRYJ08GOxGI9axfNaKdlsklJ9bk4ducHqgCSWYVer3s
+RQBjxyOfSTvk9YCJvdJVQRJLcCvxwKakFCsOSnV3t9OvN86Ak+fKPVB5j2fM/0fZ
+Kqjn3iqgdNPTLXPsuJLJO5lITRiBa4onmVelAiCstI9PQiaEck+oAHnMTnC9JE/B
+DHv3e4rwq3LznlqPw0GSd7xqNTdMDwNOWjkuOr3sGpWS8ms/ZHHXV1Vd22uPe70i
+s00xrv14zLifcc8oj5DYzOhYRifRXgHX
+
+
+
+
+
+
+
+
+
+MIIDEzCCAfugAwIBAgIUG6Nn1rlERS1vsi88tcdzSYX0oqAwDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaWRwdGVzdGJlZDAeFw0xNTEyMTEwMjIwMTRaFw0zNTEy
+MTEwMjIwMTRaMBUxEzARBgNVBAMMCmlkcHRlc3RiZWQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCBXv0o3fmT8iluyLjJ4lBAVCW+ZRVyEXPYQuRi7vfD
+cO4a6d1kxiJLsaK0W88VNxjFQRr8PgDkWr28vwoH1rgk4pLsszLD48DBzD942peJ
+l/S6FnsIJjmaHcBh4pbNhU4yowu63iKkvttrcZAEbpEro6Z8CziWEx8sywoaYEQG
+ifPkr9ORV6Cn3txq+9gMBePG41GrtZrUGIu+xrndL0Shh4Pq0eq/9MAsVlIIXEa8
+9WfH8J2kFcTOfoWtIc70b7TLZQsx4YnNcnrGLSUEcstFyPLX+Xtv5SNZF89OOIxX
+VNjNvgE5DbJb9hMM4UAFqI+1bo9QqtxwThjc/sOvIxzNAgMBAAGjWzBZMB0GA1Ud
+DgQWBBStTyogRPuAVG6q7yPyav1uvE+7pTA4BgNVHREEMTAvggppZHB0ZXN0YmVk
+hiFodHRwczovL2lkcHRlc3RiZWQvaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQEL
+BQADggEBAFMfoOv+oISGjvamq7+Y4G7ep5vxlAPeK3RATYPYvAmyH946qZXh98ni
+QXyuqZW5P5eEt86toY45IwDU5r09SKwHughEe99iiEkxh0mb2qo84qX9/qcg+kyN
+jeLd/OSyolpUCEFNwOFcog7pj7Eer+6AHbwTn1Mjb5TBsKwtDMJsaxPvdj0u7M5r
+xL/wHkFhn1rCo2QiojzjSlV3yLTh49iTyhE3cG+RxaNKDCxhp0jSSLX1BW/ZoPA8
++PMJEA+Q0QbyRD8aJOHN5O8jGxCa/ZzcOnYVL6AsEXoDiY3vAUYh1FUonOWw0m9H
+p+tGUbGS2l873J5PrsbpeKEVR/IIoKo=
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/complex/idp/shibboleth-idp/metadata/midpoint-sp.xml b/demo/complex/idp/shibboleth-idp/metadata/midpoint-sp.xml
new file mode 100644
index 0000000..7266479
--- /dev/null
+++ b/demo/complex/idp/shibboleth-idp/metadata/midpoint-sp.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ midpoint.sp.example.org
+
+ CN=midpoint.sp.example.org,O=Internet2/TIER,L=Ann Arbor,ST=MI,C=US
+ MIIDqDCCApCgAwIBAgIJAKUZrfriIt9cMA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJNSTESMBAGA1UEBwwJQW5uIEFyYm9yMRcwFQYDVQQK
+DA5JbnRlcm5ldDIvVElFUjEgMB4GA1UEAwwXZXZvbHZldW0uc3AuZXhhbXBsZS5v
+cmcwHhcNMTgwOTE0MDU0NjU3WhcNMTkwOTE0MDU0NjU3WjBpMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCTUkxEjAQBgNVBAcMCUFubiBBcmJvcjEXMBUGA1UECgwOSW50
+ZXJuZXQyL1RJRVIxIDAeBgNVBAMMF2V2b2x2ZXVtLnNwLmV4YW1wbGUub3JnMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw5v1zxlM94yaBssgNNbIUJwW
+XxbGxgSs2AWBeg2aEi/VQd2UE5ivZakNJlqWSJyHo2xE4kxeSyBBxinjSyhmpNao
+xIcqQsgW0gxo4SEHo3kUXWPo+of/pj6CslutsSJZWGTRV0dHITvaWX+NM8eXMfgu
+mJFwy3RMdLaWQhY1Dyi2jNoO+DZnfNgPyPeEZcmORaoeEID9QdZfHtcgTf2QfSHq
++xsTwHB6Ro5t7YD2ma8Krb/XcDTfsq3qJemd7LhPj5lGmhYSMgDbgwEkZgZ1kBOP
+lfsP2BvX5nipv7Vd1C5YXmv+NDR8V3yAWBC7ZAenxGmrnkaSVXnpUplUsGGm1QID
+AQABo1MwUTAdBgNVHQ4EFgQUuxSZwW6V1P/b0tsTM32OU/v/n+UwHwYDVR0jBBgw
+FoAUuxSZwW6V1P/b0tsTM32OU/v/n+UwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
+9w0BAQsFAAOCAQEAJWLXEfZkPeUyiGvsIUjczzdF3ptqXoP9aETS2pOV9sTri19R
+TsQZW6XQRHGtuEOsqEGH8yiTdGR5hbGC+ynH/xTJnK+tBn/R3KrgxLKyMvoUzAPl
+mhVq1dh+ZEtbsRpQRRubP6nm9kXNma0cXrkJSzuWM0W+l/xSOOYiSRRk3XWJfVjn
+9jQlcJRh5SOkKN08oZHrCYKxToEuOfV8PtRj3T80DhsBTv2SHqhg4cBhzQPb0Kjm
+9m4IkYOz8c5ZtuHDGnqMHw60Nyt+jyik4mMFP2frcOVP0W0sgwcfHllYzHoA/Khq
+Yk3TBVs1BjPuNDJWHct8Eo68YP2/ZvzqfVM87Q==
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/complex/midpoint-objects-manual/tasks/task-import-grouper.xml b/demo/complex/midpoint-objects-manual/tasks/task-import-grouper.xml
new file mode 100644
index 0000000..3da3f02
--- /dev/null
+++ b/demo/complex/midpoint-objects-manual/tasks/task-import-grouper.xml
@@ -0,0 +1,26 @@
+
+ Import from Grouper
+
+ account
+ ri:AccountObjectClass
+
+ 1535468542646-0-1
+
+ runnable
+ ImportingAccounts
+ http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/import/handler-3
+
+ single
+ loose
+
diff --git a/demo/complex/midpoint-objects-manual/tasks/task-import-sis-courses.xml b/demo/complex/midpoint-objects-manual/tasks/task-import-sis-courses.xml
new file mode 100644
index 0000000..99d395b
--- /dev/null
+++ b/demo/complex/midpoint-objects-manual/tasks/task-import-sis-courses.xml
@@ -0,0 +1,28 @@
+
+ Import from SIS courses
+
+ account
+ ri:AccountObjectClass
+
+ 1535408076153-0-1
+
+ runnable
+ ImportingAccounts
+ http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/import/handler-3
+
+ single
+ loose
+
diff --git a/demo/complex/midpoint-objects-manual/tasks/task-import-sis-persons.xml b/demo/complex/midpoint-objects-manual/tasks/task-import-sis-persons.xml
new file mode 100644
index 0000000..d345eee
--- /dev/null
+++ b/demo/complex/midpoint-objects-manual/tasks/task-import-sis-persons.xml
@@ -0,0 +1,28 @@
+
+ Import from SIS persons
+
+ account
+ ri:AccountObjectClass
+
+ 1535407239440-0-1
+
+ runnable
+ ImportingAccounts
+ http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/import/handler-3
+
+ single
+ loose
+
diff --git a/demo/complex/midpoint-objects-manual/tasks/task-livesync-grouper.xml b/demo/complex/midpoint-objects-manual/tasks/task-livesync-grouper.xml
new file mode 100644
index 0000000..23e4b4c
--- /dev/null
+++ b/demo/complex/midpoint-objects-manual/tasks/task-livesync-grouper.xml
@@ -0,0 +1,32 @@
+
+ LiveSync from Grouper
+
+ account
+ ri:AccountObjectClass
+
+ 1535465478027-0-1
+
+ runnable
+ LiveSynchronization
+ http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/live-sync/handler-3
+
+ recurring
+
+ 60
+
+ loose
+
diff --git a/demo/complex/midpoint-objects/objectTemplates/template-org-course.xml b/demo/complex/midpoint-objects/objectTemplates/template-org-course.xml
new file mode 100644
index 0000000..4eafd08
--- /dev/null
+++ b/demo/complex/midpoint-objects/objectTemplates/template-org-course.xml
@@ -0,0 +1,18 @@
+
+
+ template-org-course
+
+ strong
+
+
+ RoleType
+ 8aa99e7b-f7d3-4585-9800-14bab4d26a43
+
+
+
+ assignment
+
+
+
+
\ No newline at end of file
diff --git a/demo/complex/midpoint-objects/objectTemplates/template-org-department.xml b/demo/complex/midpoint-objects/objectTemplates/template-org-department.xml
new file mode 100644
index 0000000..df9b223
--- /dev/null
+++ b/demo/complex/midpoint-objects/objectTemplates/template-org-department.xml
@@ -0,0 +1,18 @@
+
+
+ template-org-department
+
+ strong
+
+
+ RoleType
+ ffa9eaec-9539-4d15-97aa-24cd5b92ca5b
+
+
+
+ assignment
+
+
+
+
\ No newline at end of file
diff --git a/demo/complex/midpoint-objects/objectTemplates/template-role-affiliation.xml b/demo/complex/midpoint-objects/objectTemplates/template-role-affiliation.xml
new file mode 100644
index 0000000..3a9c726
--- /dev/null
+++ b/demo/complex/midpoint-objects/objectTemplates/template-role-affiliation.xml
@@ -0,0 +1,17 @@
+
+
+ template-role-affiliation
+
+ strong
+
+
+ RoleType
+ fecae27b-d1d3-40ae-95fa-8f7e44e2ee70
+
+
+
+ assignment
+
+
+
\ No newline at end of file
diff --git a/demo/complex/midpoint-objects/objectTemplates/template-role-generic-group.xml b/demo/complex/midpoint-objects/objectTemplates/template-role-generic-group.xml
new file mode 100644
index 0000000..1205f6d
--- /dev/null
+++ b/demo/complex/midpoint-objects/objectTemplates/template-role-generic-group.xml
@@ -0,0 +1,17 @@
+
+
+ template-role-generic-group
+
+ strong
+
+
+ RoleType
+ c691e15a-f30b-4e15-8445-532db07ceeeb
+
+
+
+ assignment
+
+
+
\ No newline at end of file
diff --git a/demo/complex/midpoint-objects/orgs/org-courses.xml b/demo/complex/midpoint-objects/orgs/org-courses.xml
new file mode 100644
index 0000000..71d1f7e
--- /dev/null
+++ b/demo/complex/midpoint-objects/orgs/org-courses.xml
@@ -0,0 +1,6 @@
+
+
+ courses
+ Courses
+
\ No newline at end of file
diff --git a/demo/complex/midpoint-objects/orgs/org-departments.xml b/demo/complex/midpoint-objects/orgs/org-departments.xml
new file mode 100644
index 0000000..5320c1e
--- /dev/null
+++ b/demo/complex/midpoint-objects/orgs/org-departments.xml
@@ -0,0 +1,6 @@
+
+
+ departments
+ Departments
+
\ No newline at end of file
diff --git a/demo/complex/midpoint-objects/resources/ldap-main.xml b/demo/complex/midpoint-objects/resources/ldap-main.xml
new file mode 100644
index 0000000..f826cc7
--- /dev/null
+++ b/demo/complex/midpoint-objects/resources/ldap-main.xml
@@ -0,0 +1,453 @@
+
+
+
+
+
+
+ OpenLDAP (directory)
+
+
+
+
+ c:connectorType
+ com.evolveum.polygon.connector.ldap.LdapConnector
+
+
+
+
+
+
+ 389
+ directory
+ dc=internet2,dc=edu
+ cn=Directory Manager
+
+ password
+
+ nsUniqueId
+ auto
+ uid
+ memberOf
+ createTimestamp
+ nsAccountLock
+
+
+
+
+ false
+ false
+ false
+
+
+
+
+
+ ri:inetOrgPerson
+ ri:eduPerson
+ ri:groupOfUniqueNames
+ ri:groupOfNames
+ ri:organizationalUnit
+
+
+
+
+
+ account
+ Normal Account
+ true
+ ri:inetOrgPerson
+ ri:eduPerson
+
+ [ri:dn]
+ Distinguished Name
+
+ 0
+
+ false
+ mr:stringIgnoreCase
+
+ strong
+
+ name
+
+
+
+
+
+
+
+ [ri:cn]
+ Common Name
+
+ 0
+
+ false
+
+ strong
+
+ fullName
+
+
+
+
+ [ri:sn]
+ Surname
+
+ 0
+
+ false
+
+ strong
+
+ familyName
+
+
+
+
+ [ri:givenName]
+ Given Name
+
+ 0
+
+ false
+
+ strong
+
+ givenName
+
+
+
+
+ [ri:uid]
+ Login Name
+ false
+ mr:stringIgnoreCase
+
+ strong
+
+ name
+
+
+
+
+ [ri:mail]
+ Mail
+ mr:stringIgnoreCase
+ false
+
+ strong
+
+ emailAddress
+
+
+
+
+ [ri:employeeNumber]
+ false
+
+ strong
+
+ employeeNumber
+
+
+
+
+ [ri:businessCategory]
+ false
+
+
+ [ri:eduPersonAffiliation]
+ false
+
+
+ [ri:eduPersonEntitlement]
+ false
+
+
+ extension/grouper_group
+
+
+
+
+ false
+ [ri:group]
+ entitlement
+ course-group
+ generic-group
+ objectToSubject
+ ri:uniqueMember
+ ri:dn
+
+
+
+
+ http://prism.evolveum.com/xml/ns/public/matching-rule-3#stringIgnoreCase
+ attributes/ri:dn
+ cn=root,dc=internet2,dc=edu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ entitlement
+ course-group
+ LDAP Group for courses
+ ri:groupOfUniqueNames
+
+ [ri:uniqueMember]
+ mr:distinguishedName
+ minimal
+
+
+ [ri:dn]
+ mr:stringIgnoreCase
+
+ strong
+
+ identifier
+
+
+
+
+
+
+
+ [ri:cn]
+ mr:stringIgnoreCase
+
+ weak
+
+ identifier
+
+
+
+
+
+ entitlement
+ generic-group
+ LDAP Group
+ ri:groupOfUniqueNames
+
+ [ri:uniqueMember]
+ mr:distinguishedName
+ minimal
+
+
+ [ri:dn]
+ mr:stringIgnoreCase
+
+ strong
+ true
+
+ identifier
+
+
+
+
+
+
+
+ [ri:cn]
+ mr:stringIgnoreCase
+
+ weak
+
+ identifier
+
+
+
+
+
+
+
+
+ true
+
+
+ name
+
+
+ declare namespace ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance-3";
+ $account/attributes/ri:uid
+
+
+
+
+
+ linked
+ true
+
+
+ deleted
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink
+
+
+
+ unlinked
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#link
+
+
+
+ unmatched
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#addFocus
+
+
+
+
+ course-group sync
+ ri:groupOfUniqueNames
+ entitlement
+ course-group
+ OrgType
+ true
+
+
+
+
+
+
+ identifier
+
+ $shadow/attributes/ri:cn
+
+
+
+ subtype
+ course
+
+
+
+
+ linked
+ true
+
+
+ deleted
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink
+
+
+
+ unlinked
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#link
+
+
+
+ unmatched
+
+
+
+ generic-group sync
+ ri:groupOfUniqueNames
+ entitlement
+ generic-group
+ RoleType
+ true
+
+
+
+
+
+
+ identifier
+
+ $shadow/attributes/ri:cn
+
+
+
+ subtype
+ generic-group
+
+
+
+
+ linked
+ true
+
+
+ deleted
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink
+
+
+
+ unlinked
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#link
+
+
+
+ unmatched
+
+
+
+
+ true
+
+
diff --git a/demo/complex/midpoint-objects/resources/scriptedsql-grouper2.xml b/demo/complex/midpoint-objects/resources/scriptedsql-grouper2.xml
new file mode 100644
index 0000000..ddd0c4b
--- /dev/null
+++ b/demo/complex/midpoint-objects/resources/scriptedsql-grouper2.xml
@@ -0,0 +1,152 @@
+
+
+
+
+ Grouper SQL/MQ
+
+
+
+
+ connectorType
+ net.tirasa.connid.bundles.db.scriptedsql.ScriptedSQLConnector
+
+
+
+
+
+
+
+ grouper-data
+ 3306
+
+ root
+
+
+
+ grouper
+
+ GROOVY
+
+ /opt/midpoint/var/res/grouper2/SearchScript.groovy
+ /opt/midpoint/var/res/grouper2/TestScript.groovy
+ /opt/midpoint/var/res/grouper2/SchemaScript.groovy
+ /opt/midpoint/var/res/grouper2/SyncScript.groovy
+ true
+
+
+
+
+
+ org.mariadb.jdbc.Driver
+ jdbc:mysql://%h:%p/%d?useUnicode=true&characterEncoding=utf8&connectionCollation=utf8_bin
+ true
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+ account
+ Normal Account
+ true
+ ri:AccountObjectClass
+
+ [ri:subject_id]
+ Subject ID
+
+
+ [ri:name]
+ Name
+
+
+ [ri:group]
+ Subject Groups
+
+ strong
+
+ extension/grouper_group
+
+
+
+
+
+ entitlement
+ group
+ Group
+ ri:GroupObjectClass
+
+
+
+
+
+
+
+ true
+
+
+
+ name
+
+
+ declare namespace ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance-3";
+ $account/attributes/icfs:name
+
+
+
+
+
+
+ linked
+ true
+
+
+ deleted
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink
+
+
+
+ unlinked
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#link
+
+
+
+ unmatched
+ true
+
+
+
+
+
+
diff --git a/demo/complex/midpoint-objects/resources/scriptedsql-sis-courses.xml b/demo/complex/midpoint-objects/resources/scriptedsql-sis-courses.xml
new file mode 100644
index 0000000..5b34689
--- /dev/null
+++ b/demo/complex/midpoint-objects/resources/scriptedsql-sis-courses.xml
@@ -0,0 +1,212 @@
+
+
+
+
+ SQL SIS courses (sources)
+
+
+
+
+ connectorType
+ net.tirasa.connid.bundles.db.scriptedsql.ScriptedSQLConnector
+
+
+
+
+
+
+
+ sources
+ 3306
+
+ root
+
+
+
+ sis
+
+ GROOVY
+
+ /opt/midpoint/var/res/sis/SearchScript.groovy
+ /opt/midpoint/var/res/sis/TestScript.groovy
+ /opt/midpoint/var/res/sis/SchemaScript.groovy
+
+ true
+
+
+
+
+
+ org.mariadb.jdbc.Driver
+ jdbc:mysql://%h:%p/%d?useUnicode=true&characterEncoding=utf8&connectionCollation=utf8_bin
+ true
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+ account
+ Normal Account
+ true
+ ri:AccountObjectClass
+
+ [ri:uid]
+ UID
+
+
+ [ri:courseId]
+ Course ID
+
+ strong
+
+
+ OrgType
+
+
+ name
+
+
+
+
+
+ true
+
+
+
+
+
+
+ name
+
+
+
+
+
+
+
+ displayName
+
+
+
+
+
+ OrgType
+ 225e9360-0639-40ba-8a31-7f31bef067be
+
+
+
+ assignment
+
+
+
+
+
+
+
+ identifier
+
+
+
+
+ course
+
+
+ subtype
+
+
+
+
+ course
+
+
+
+
+ assignment
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+ name
+
+
+ declare namespace ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance-3";
+ $account/attributes/ri:uid
+
+
+
+
+
+
+ linked
+ true
+
+
+ deleted
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink
+
+
+
+
+ unlinked
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#link
+
+
+
+ unmatched
+ true
+
+
+
+
+
+
+
diff --git a/demo/complex/midpoint-objects/resources/scriptedsql-sis-persons.xml b/demo/complex/midpoint-objects/resources/scriptedsql-sis-persons.xml
new file mode 100644
index 0000000..567039b
--- /dev/null
+++ b/demo/complex/midpoint-objects/resources/scriptedsql-sis-persons.xml
@@ -0,0 +1,379 @@
+
+
+
+
+ SQL SIS persons (sources)
+
+
+
+
+ connectorType
+ net.tirasa.connid.bundles.db.scriptedsql.ScriptedSQLConnector
+
+
+
+
+
+
+
+ sources
+ 3306
+
+ root
+
+
+
+ sis
+
+ GROOVY
+
+ /opt/midpoint/var/res/sis-persons/SearchScript.groovy
+ /opt/midpoint/var/res/sis-persons/TestScript.groovy
+ /opt/midpoint/var/res/sis-persons/SchemaScript.groovy
+
+ true
+
+
+
+
+
+ org.mariadb.jdbc.Driver
+ jdbc:mysql://%h:%p/%d?useUnicode=true&characterEncoding=utf8&connectionCollation=utf8_bin
+ true
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+ account
+ Normal Account
+ true
+ ri:AccountObjectClass
+
+ [ri:uid]
+ UID
+
+
+ name
+
+
+
+ strong
+
+
+ RoleType
+ c89f31dd-8d4f-4e0a-82cb-58ff9d8c1b2f
+
+ grouper-basic
+
+
+
+
+ assignment
+
+
+
+
+
+
+
+
+
+ [ri:fullName]
+ Full Name
+
+
+ fullName
+
+
+
+
+ [ri:surname]
+ Surname
+
+
+ familyName
+
+
+
+
+ [ri:givenName]
+ Given Name
+
+
+ givenName
+
+
+
+
+ [ri:mail]
+ Mail
+ mr:stringIgnoreCase
+
+
+ emailAddress
+
+
+
+
+
+ [ri:department]
+
+ strong
+
+
+ OrgType
+
+
+ name
+
+
+
+
+
+ true
+
+
+
+
+
+
+ name
+
+
+
+
+
+
+
+ displayName
+
+
+
+
+
+ OrgType
+ bee44c51-2469-411d-bac7-695728e9c241
+
+
+
+ assignment
+
+
+
+
+
+
+
+ identifier
+
+
+
+
+ department
+
+
+ subtype
+
+
+
+
+ department
+
+
+
+
+ assignment
+
+
+
+
+
+
+
+
+
+ [ri:affiliation]
+
+ strong
+
+
+ RoleType
+
+
+ name
+
+
+
+
+
+ true
+
+
+
+
+
+
+ name
+
+
+
+
+
+
+
+ displayName
+
+
+
+
+
+
+
+ identifier
+
+
+
+
+ affiliation
+
+
+ subtype
+
+
+
+
+ affiliation
+
+
+
+
+ assignment
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+ name
+
+
+ declare namespace ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance-3";
+ $account/attributes/ri:uid
+
+
+
+
+
+
+ linked
+ true
+
+
+ deleted
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink
+
+
+
+
+ unlinked
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#link
+
+
+
+ unmatched
+ true
+
+ http://midpoint.evolveum.com/xml/ns/public/model/action-3#addFocus
+
+
+
+
+
+
+
diff --git a/demo/complex/midpoint-objects/roles/metarole-affiliation.xml b/demo/complex/midpoint-objects/roles/metarole-affiliation.xml
new file mode 100644
index 0000000..be64d72
--- /dev/null
+++ b/demo/complex/midpoint-objects/roles/metarole-affiliation.xml
@@ -0,0 +1,29 @@
+
+ metarole-affiliation
+
+
+
+
+ [ri:eduPersonAffiliation]
+
+ strong
+
+
+
+
+
+
+ 2
+
+
diff --git a/demo/complex/midpoint-objects/roles/metarole-course.xml b/demo/complex/midpoint-objects/roles/metarole-course.xml
new file mode 100644
index 0000000..3e26105
--- /dev/null
+++ b/demo/complex/midpoint-objects/roles/metarole-course.xml
@@ -0,0 +1,38 @@
+
+ metarole-course
+
+
+
+ entitlement
+ course-group
+
+
+
+
+
+
+ ri:group
+
+
+
+
+ entitlement
+ course-group
+
+
+
+
+
+
+ 2
+
+
diff --git a/demo/complex/midpoint-objects/roles/metarole-department.xml b/demo/complex/midpoint-objects/roles/metarole-department.xml
new file mode 100644
index 0000000..96947e3
--- /dev/null
+++ b/demo/complex/midpoint-objects/roles/metarole-department.xml
@@ -0,0 +1,29 @@
+
+ metarole-department
+
+
+
+
+ [ri:businessCategory]
+
+ strong
+
+
+
+
+
+
+ 2
+
+
diff --git a/demo/complex/midpoint-objects/roles/metarole-generic-group.xml b/demo/complex/midpoint-objects/roles/metarole-generic-group.xml
new file mode 100644
index 0000000..f0e93c5
--- /dev/null
+++ b/demo/complex/midpoint-objects/roles/metarole-generic-group.xml
@@ -0,0 +1,38 @@
+
+ metarole-generic-group
+
+
+
+ entitlement
+ generic-group
+
+
+
+
+
+
+ ri:group
+
+
+
+
+ entitlement
+ generic-group
+
+
+
+
+
+
+ 2
+
+
diff --git a/demo/complex/midpoint-objects/roles/role-grouper-sysadmin.xml b/demo/complex/midpoint-objects/roles/role-grouper-sysadmin.xml
new file mode 100644
index 0000000..5b559f8
--- /dev/null
+++ b/demo/complex/midpoint-objects/roles/role-grouper-sysadmin.xml
@@ -0,0 +1,17 @@
+
+ role-grouper-sysadmin
+
+
+
+ generic-group
+ sysadmingroup
+
diff --git a/demo/complex/midpoint-objects/roles/role-ldap-basic.xml b/demo/complex/midpoint-objects/roles/role-ldap-basic.xml
new file mode 100644
index 0000000..3efa968
--- /dev/null
+++ b/demo/complex/midpoint-objects/roles/role-ldap-basic.xml
@@ -0,0 +1,18 @@
+
+ role-ldap-basic
+
+
+
+
+ 1
+
+
diff --git a/demo/complex/midpoint-objects/systemConfigurations/SystemConfiguration.xml b/demo/complex/midpoint-objects/systemConfigurations/SystemConfiguration.xml
new file mode 100644
index 0000000..146ee38
--- /dev/null
+++ b/demo/complex/midpoint-objects/systemConfigurations/SystemConfiguration.xml
@@ -0,0 +1,167 @@
+
+ SystemConfiguration
+
+ 2018-08-15T13:30:55.282+02:00
+ 2018-08-15T13:30:55.372+02:00
+ http://midpoint.evolveum.com/xml/ns/public/gui/channels-3#init
+
+
+ 2018-08-15T13:30:55.562+02:00
+
+
+ add
+ c:SystemConfigurationType
+
+
+ com.evolveum.midpoint.model.impl.lens.ChangeExecutor.executeDelta
+ handled_error
+ 1000000000000000009
+ Object of type 'SystemConfigurationType' with oid '00000000-0000-0000-0000-000000000001' was not found.
+
+ SystemConfiguration
+
+ handled_error
+ http://midpoint.evolveum.com/xml/ns/public/gui/channels-3#init
+
+
+
+
+ ERROR
+ ro.isdc.wro.extensions.processor.css.Less4jProcessor
+
+
+ OFF
+ org.hibernate.engine.jdbc.spi.SqlExceptionHelper
+
+
+ OFF
+ org.hibernate.engine.jdbc.batch.internal.BatchingBatch
+
+
+ WARN
+ org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl
+
+
+ OFF
+ org.hibernate.internal.ExceptionMapperStandardImpl
+
+
+ OFF
+ net.sf.jasperreports.engine.fill.JRFillDataset
+
+
+ WARN
+ org.apache.wicket.resource.PropertiesFactory
+
+
+ ERROR
+ org.springframework.context.support.ResourceBundleMessageSource
+
+
+ INFO
+ com.evolveum.midpoint.model.impl.lens.projector.Projector
+
+
+ INFO
+ com.evolveum.midpoint.model.impl.lens.Clockwork
+
+
+ %date [%X{subsystem}] [%thread] %level \(%logger\): %msg%n
+ MIDPOINT_LOG
+ ${midpoint.home}/log/midpoint.log
+ ${midpoint.home}/log/midpoint-%d{yyyy-MM-dd}.%i.log
+ 10
+ 100MB
+ true
+
+
+ %date %level: %msg%n
+ MIDPOINT_PROFILE_LOG
+ ${midpoint.home}/log/midpoint-profile.log
+ ${midpoint.home}/log/midpoint-profile-%d{yyyy-MM-dd}.%i.log
+ 10
+ 100MB
+ true
+
+ MIDPOINT_LOG
+ INFO
+
+ false
+ false
+
+
+
+ RoleType
+ affiliation
+
+
+
+ OrgType
+ department
+
+
+
+ OrgType
+ course
+
+
+
+ RoleType
+ generic-group
+
+
+
+
+ P3M
+
+
+ P1M
+
+
+
+ true
+
+
+
+ /self/profile
+
+ View/edit your profile
+
+ fa fa-user
+
+ green
+ http://midpoint.evolveum.com/xml/ns/public/security/authorization-ui-3#selfProfile
+ http://midpoint.evolveum.com/xml/ns/public/security/authorization-ui-3#selfAll
+
+
+ /self/credentials
+
+ View/edit your credentials
+
+ fa fa-shield
+
+ blue
+ http://midpoint.evolveum.com/xml/ns/public/security/authorization-ui-3#selfCredentials
+ http://midpoint.evolveum.com/xml/ns/public/security/authorization-ui-3#selfAll
+
+
+ /admin/users
+
+
+ fa fa-users
+
+ red
+ http://midpoint.evolveum.com/xml/ns/public/security/authorization-ui-3#users
+
+
+ /admin/resources
+
+
+ fa fa-database
+
+ purple
+ http://midpoint.evolveum.com/xml/ns/public/security/authorization-ui-3#resources
+
+ true
+
+
diff --git a/demo/complex/midpoint-server/Dockerfile b/demo/complex/midpoint-server/Dockerfile
new file mode 100644
index 0000000..49e5fb4
--- /dev/null
+++ b/demo/complex/midpoint-server/Dockerfile
@@ -0,0 +1,7 @@
+FROM tier/midpoint
+
+MAINTAINER info@evolveum.com
+
+ENV MP_DIR /opt/midpoint
+
+COPY container_files/mp-home/ ${MP_DIR}/var/
diff --git a/demo/complex/midpoint-server/container_files/mp-home/icf-connectors/net.tirasa.connid.bundles.db.scriptedsql-2.2.6-SNAPSHOT.jar b/demo/complex/midpoint-server/container_files/mp-home/icf-connectors/net.tirasa.connid.bundles.db.scriptedsql-2.2.6-SNAPSHOT.jar
new file mode 100644
index 0000000..88fcb54
Binary files /dev/null and b/demo/complex/midpoint-server/container_files/mp-home/icf-connectors/net.tirasa.connid.bundles.db.scriptedsql-2.2.6-SNAPSHOT.jar differ
diff --git a/demo/complex/midpoint-server/container_files/mp-home/lib/amqp-client-5.3.0.jar b/demo/complex/midpoint-server/container_files/mp-home/lib/amqp-client-5.3.0.jar
new file mode 100644
index 0000000..27c6942
Binary files /dev/null and b/demo/complex/midpoint-server/container_files/mp-home/lib/amqp-client-5.3.0.jar differ
diff --git a/demo/complex/midpoint-server/container_files/mp-home/res/grouper/SchemaScript.groovy b/demo/complex/midpoint-server/container_files/mp-home/res/grouper/SchemaScript.groovy
new file mode 100644
index 0000000..857e6c1
--- /dev/null
+++ b/demo/complex/midpoint-server/container_files/mp-home/res/grouper/SchemaScript.groovy
@@ -0,0 +1,91 @@
+/*
+ * ====================
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 ForgeRock. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common Development
+ * and Distribution License("CDDL") (the "License"). You may not use this file
+ * except in compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://opensource.org/licenses/cddl1.php
+ * See the License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * When distributing the Covered Code, include this CDDL Header Notice in each file
+ * and include the License file at http://opensource.org/licenses/cddl1.php.
+ * If applicable, add the following below this CDDL Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ * ====================
+ * Portions Copyrighted 2013 ConnId.
+ */
+import org.identityconnectors.framework.common.objects.AttributeInfo;
+import org.identityconnectors.framework.common.objects.AttributeInfo.Flags;
+import org.identityconnectors.framework.common.objects.AttributeInfoBuilder;
+import org.identityconnectors.framework.common.objects.ObjectClassInfo;
+import org.identityconnectors.framework.common.objects.ObjectClassInfoBuilder;
+
+// Parameters:
+// The connector sends the following:
+// action: a string describing the action ("SCHEMA" here)
+// log: a handler to the Log facility
+// builder: SchemaBuilder instance for the connector
+//
+// The connector will make the final call to builder.build()
+// so the scipt just need to declare the different object types.
+
+// This sample shows how to create 3 basic ObjectTypes: __ACCOUNT__, __GROUP__ and organization.
+// Each of them contains one required attribute and normal String attributes
+
+
+log.info("Entering "+action+" Script");
+
+// Declare the __ACCOUNT__ attributes
+// Make the uid required
+uidAIB = new AttributeInfoBuilder("uid",String.class);
+uidAIB.setRequired(true);
+
+accAttrsInfo = new HashSet();
+accAttrsInfo.add(uidAIB.build());
+accAttrsInfo.add(AttributeInfoBuilder.build("subject_id", String.class));
+accAttrsInfo.add(AttributeInfoBuilder.build("subject_identifier0", String.class));
+accAttrsInfo.add(AttributeInfoBuilder.build("sort_string0", String.class));
+accAttrsInfo.add(AttributeInfoBuilder.build("search_string0", String.class));
+accAttrsInfo.add(AttributeInfoBuilder.build("name", String.class));
+accAttrsInfo.add(AttributeInfoBuilder.build("description", String.class));
+accAttrsInfo.add(AttributeInfoBuilder.build("group", String.class, [Flags.MULTIVALUED] as Set));
+// Create the __ACCOUNT__ Object class
+final ObjectClassInfo ociAccount = new ObjectClassInfoBuilder().setType("__ACCOUNT__").addAllAttributeInfo(accAttrsInfo).build();
+builder.defineObjectClass(ociAccount);
+
+/*
+// Declare the __GROUP__ attributes
+// Make the gid required
+gidAIB = new AttributeInfoBuilder("gid",String.class);
+gidAIB.setRequired(true);
+
+grpAttrsInfo = new HashSet();
+grpAttrsInfo.add(gidAIB.build());
+grpAttrsInfo.add(AttributeInfoBuilder.build("name", String.class));
+grpAttrsInfo.add(AttributeInfoBuilder.build("description", String.class));
+// Create the __GROUP__ Object class
+final ObjectClassInfo ociGroup = new ObjectClassInfoBuilder().setType("__GROUP__").addAllAttributeInfo(grpAttrsInfo).build();
+builder.defineObjectClass(ociGroup);
+
+
+// Declare the organization attributes
+// Make the name required
+nAIB = new AttributeInfoBuilder("name",String.class);
+nAIB.setRequired(true);
+
+orgAttrsInfo = new HashSet();
+orgAttrsInfo.add(nAIB.build());
+orgAttrsInfo.add(AttributeInfoBuilder.build("description", String.class));
+// Create the organization Object class
+final ObjectClassInfo ociOrg = new ObjectClassInfoBuilder().setType("organization").addAllAttributeInfo(orgAttrsInfo).build();
+builder.defineObjectClass(ociOrg);
+*/
+
+log.info("Schema script done");
diff --git a/demo/complex/midpoint-server/container_files/mp-home/res/grouper/SearchScript.groovy b/demo/complex/midpoint-server/container_files/mp-home/res/grouper/SearchScript.groovy
new file mode 100644
index 0000000..801bfe1
--- /dev/null
+++ b/demo/complex/midpoint-server/container_files/mp-home/res/grouper/SearchScript.groovy
@@ -0,0 +1,101 @@
+/*
+ * ====================
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 ForgeRock. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common Development
+ * and Distribution License("CDDL") (the "License"). You may not use this file
+ * except in compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://opensource.org/licenses/cddl1.php
+ * See the License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * When distributing the Covered Code, include this CDDL Header Notice in each file
+ * and include the License file at http://opensource.org/licenses/cddl1.php.
+ * If applicable, add the following below this CDDL Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ * ====================
+ * Portions Copyrighted 2013 ConnId.
+ */
+import groovy.sql.Sql;
+import groovy.sql.DataSet;
+
+// Parameters:
+// The connector sends the following:
+// connection: handler to the SQL connection
+// objectClass: a String describing the Object class (__ACCOUNT__ / __GROUP__ / other)
+// action: a string describing the action ("SEARCH" here)
+// log: a handler to the Log facility
+// options: a handler to the OperationOptions Map
+// query: a handler to the Query Map
+//
+// The Query map describes the filter used.
+//
+// query = [ operation: "CONTAINS", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "ENDSWITH", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "STARTSWITH", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "EQUALS", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "GREATERTHAN", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "GREATERTHANOREQUAL", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "LESSTHAN", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "LESSTHANOREQUAL", left: attribute, right: "value", not: true/false ]
+// query = null : then we assume we fetch everything
+//
+// AND and OR filter just embed a left/right couple of queries.
+// query = [ operation: "AND", left: query1, right: query2 ]
+// query = [ operation: "OR", left: query1, right: query2 ]
+//
+// Returns: A list of Maps. Each map describing one row.
+// !!!! Each Map must contain a '__UID__' and '__NAME__' attribute.
+// This is required to build a ConnectorObject.
+
+log.info("Entering "+action+" Script");
+
+def sql = new Sql(connection);
+def result = []
+def where = "";
+
+switch ( objectClass ) {
+ case "__ACCOUNT__":
+ sql.eachRow("\
+select m.id, m.name, m.subject_id, m.subject_identifier0, m.sort_string0, m.search_string0, m.description, m.subject_source, group_concat(distinct g.name) as groups \
+from \
+ grouper_members m \
+ left join grouper_memberships_all_v gm on m.id=gm.member_id and gm.owner_id in \
+ (select m.subject_id \
+ from grouper_memberships gm join grouper_members m on gm.member_id=m.id \
+ where gm.owner_id = (select subject_id from grouper_members where name='etc:exportedGroups' and subject_type='group')) \
+ left join grouper_groups g on gm.owner_id=g.id \
+group by m.id \
+having \
+ subject_source = 'ldap';",
+ {result.add(
+ [__UID__:it.id,
+ __NAME__:it.subject_id,
+ uid:it.id,
+ subject_id:it.subject_id,
+ subject_identifier0:it.subject_identifier0,
+ sort_string0:it.sort_string0,
+ search_string0:it.search_string0,
+ name:it.name,
+ description:it.description,
+ group:it.groups?.tokenize(',')])} );
+ break
+
+/* case "__GROUP__":
+ sql.eachRow("SELECT * FROM Groups" + where, {result.add([__UID__:it.name, __NAME__:it.name, gid:it.gid, ,description:it.description])} );
+ break
+
+ case "organization":
+ sql.eachRow("SELECT * FROM Organizations" + where, {result.add([__UID__:it.name, __NAME__:it.name, description:it.description])} );
+ break */
+
+ default:
+ result;
+}
+
+return result;
diff --git a/demo/complex/midpoint-server/container_files/mp-home/res/grouper/TestScript.groovy b/demo/complex/midpoint-server/container_files/mp-home/res/grouper/TestScript.groovy
new file mode 100644
index 0000000..a232c15
--- /dev/null
+++ b/demo/complex/midpoint-server/container_files/mp-home/res/grouper/TestScript.groovy
@@ -0,0 +1,38 @@
+/*
+ * ====================
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 ForgeRock. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common Development
+ * and Distribution License("CDDL") (the "License"). You may not use this file
+ * except in compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://opensource.org/licenses/cddl1.php
+ * See the License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * When distributing the Covered Code, include this CDDL Header Notice in each file
+ * and include the License file at http://opensource.org/licenses/cddl1.php.
+ * If applicable, add the following below this CDDL Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ * ====================
+ * Portions Copyrighted 2013 ConnId.
+ */
+import groovy.sql.Sql;
+import groovy.sql.DataSet;
+
+// Parameters:
+// The connector sends the following:
+// connection: handler to the SQL connection
+// action: a string describing the action ("TEST" here)
+// log: a handler to the Log facility
+
+log.info("Entering "+action+" Script");
+def sql = new Sql(connection);
+
+sql.eachRow("select * from grouper_members limit 10", { println it.subject_id } );
+
+
diff --git a/demo/complex/midpoint-server/container_files/mp-home/res/grouper2/SchemaScript.groovy b/demo/complex/midpoint-server/container_files/mp-home/res/grouper2/SchemaScript.groovy
new file mode 100644
index 0000000..495615d
--- /dev/null
+++ b/demo/complex/midpoint-server/container_files/mp-home/res/grouper2/SchemaScript.groovy
@@ -0,0 +1,84 @@
+/*
+ * ====================
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 ForgeRock. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common Development
+ * and Distribution License("CDDL") (the "License"). You may not use this file
+ * except in compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://opensource.org/licenses/cddl1.php
+ * See the License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * When distributing the Covered Code, include this CDDL Header Notice in each file
+ * and include the License file at http://opensource.org/licenses/cddl1.php.
+ * If applicable, add the following below this CDDL Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ * ====================
+ * Portions Copyrighted 2013 ConnId.
+ */
+import org.identityconnectors.framework.common.objects.AttributeInfo;
+import org.identityconnectors.framework.common.objects.AttributeInfo.Flags;
+import org.identityconnectors.framework.common.objects.AttributeInfoBuilder;
+import org.identityconnectors.framework.common.objects.ObjectClassInfo;
+import org.identityconnectors.framework.common.objects.ObjectClassInfoBuilder;
+
+// Parameters:
+// The connector sends the following:
+// action: a string describing the action ("SCHEMA" here)
+// log: a handler to the Log facility
+// builder: SchemaBuilder instance for the connector
+//
+// The connector will make the final call to builder.build()
+// so the scipt just need to declare the different object types.
+
+// This sample shows how to create 3 basic ObjectTypes: __ACCOUNT__, __GROUP__ and organization.
+// Each of them contains one required attribute and normal String attributes
+
+
+log.info("Entering "+action+" Script");
+
+// __UID__ = grouper_members.id
+// __NAME__ = grouper_members.subject_id
+accAttrsInfo = new HashSet();
+accAttrsInfo.add(AttributeInfoBuilder.build("subject_id", String.class));
+accAttrsInfo.add(AttributeInfoBuilder.build("subject_identifier0", String.class));
+accAttrsInfo.add(AttributeInfoBuilder.build("sort_string0", String.class));
+accAttrsInfo.add(AttributeInfoBuilder.build("search_string0", String.class));
+accAttrsInfo.add(AttributeInfoBuilder.build("name", String.class));
+accAttrsInfo.add(AttributeInfoBuilder.build("description", String.class));
+accAttrsInfo.add(AttributeInfoBuilder.build("group", String.class, [Flags.MULTIVALUED] as Set));
+ociAccount = new ObjectClassInfoBuilder().setType("__ACCOUNT__").addAllAttributeInfo(accAttrsInfo).build();
+builder.defineObjectClass(ociAccount);
+
+// __UID__ = grouper_groups.id
+// __NAME__ = grouper_groups.name
+grpAttrsInfo = new HashSet();
+grpAttrsInfo.add(AttributeInfoBuilder.build("displayName", String.class));
+grpAttrsInfo.add(AttributeInfoBuilder.build("extension", String.class));
+grpAttrsInfo.add(AttributeInfoBuilder.build("displayExtension", String.class));
+grpAttrsInfo.add(AttributeInfoBuilder.build("description", String.class));
+grpAttrsInfo.add(AttributeInfoBuilder.build("type", String.class));
+ociGroup = new ObjectClassInfoBuilder().setType("__GROUP__").addAllAttributeInfo(grpAttrsInfo).build();
+builder.defineObjectClass(ociGroup);
+
+
+/*
+// Declare the organization attributes
+// Make the name required
+nAIB = new AttributeInfoBuilder("name",String.class);
+nAIB.setRequired(true);
+
+orgAttrsInfo = new HashSet();
+orgAttrsInfo.add(nAIB.build());
+orgAttrsInfo.add(AttributeInfoBuilder.build("description", String.class));
+// Create the organization Object class
+final ObjectClassInfo ociOrg = new ObjectClassInfoBuilder().setType("organization").addAllAttributeInfo(orgAttrsInfo).build();
+builder.defineObjectClass(ociOrg);
+*/
+
+log.info("Schema script done");
diff --git a/demo/complex/midpoint-server/container_files/mp-home/res/grouper2/SearchScript.groovy b/demo/complex/midpoint-server/container_files/mp-home/res/grouper2/SearchScript.groovy
new file mode 100644
index 0000000..ea61cdd
--- /dev/null
+++ b/demo/complex/midpoint-server/container_files/mp-home/res/grouper2/SearchScript.groovy
@@ -0,0 +1,112 @@
+/*
+ * ====================
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 ForgeRock. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common Development
+ * and Distribution License("CDDL") (the "License"). You may not use this file
+ * except in compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://opensource.org/licenses/cddl1.php
+ * See the License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * When distributing the Covered Code, include this CDDL Header Notice in each file
+ * and include the License file at http://opensource.org/licenses/cddl1.php.
+ * If applicable, add the following below this CDDL Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ * ====================
+ * Portions Copyrighted 2013 ConnId.
+ */
+import groovy.sql.Sql;
+import groovy.sql.DataSet;
+
+// Parameters:
+// The connector sends the following:
+// connection: handler to the SQL connection
+// objectClass: a String describing the Object class (__ACCOUNT__ / __GROUP__ / other)
+// action: a string describing the action ("SEARCH" here)
+// log: a handler to the Log facility
+// options: a handler to the OperationOptions Map
+// query: a handler to the Query Map
+//
+// The Query map describes the filter used.
+//
+// query = [ operation: "CONTAINS", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "ENDSWITH", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "STARTSWITH", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "EQUALS", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "GREATERTHAN", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "GREATERTHANOREQUAL", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "LESSTHAN", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "LESSTHANOREQUAL", left: attribute, right: "value", not: true/false ]
+// query = null : then we assume we fetch everything
+//
+// AND and OR filter just embed a left/right couple of queries.
+// query = [ operation: "AND", left: query1, right: query2 ]
+// query = [ operation: "OR", left: query1, right: query2 ]
+//
+// Returns: A list of Maps. Each map describing one row.
+// !!!! Each Map must contain a '__UID__' and '__NAME__' attribute.
+// This is required to build a ConnectorObject.
+
+log.info("Entering "+action+" Script");
+
+def sql = new Sql(connection);
+def result = []
+def where = "";
+
+switch ( objectClass ) {
+ case "__ACCOUNT__":
+ sql.eachRow("\
+select m.id, m.name, m.subject_id, m.subject_identifier0, m.sort_string0, m.search_string0, m.description, m.subject_source, m.subject_type, group_concat(distinct g.name) as groups \
+from \
+ grouper_members m \
+ left join grouper_memberships_all_v gm on m.id=gm.member_id and gm.owner_id in \
+ (select m.subject_id \
+ from grouper_memberships gm join grouper_members m on gm.member_id=m.id \
+ where gm.owner_id = (select subject_id from grouper_members where name='etc:exportedGroups' and subject_type='group')) \
+ left join grouper_groups g on gm.owner_id=g.id \
+group by m.id \
+having \
+ subject_source = 'ldap' and subject_type = 'person'",
+ {result.add(
+ [__UID__:it.id,
+ __NAME__:it.subject_id,
+ subject_id:it.subject_id,
+ subject_identifier0:it.subject_identifier0,
+ sort_string0:it.sort_string0,
+ search_string0:it.search_string0,
+ name:it.name,
+ description:it.description,
+ group:it.groups?.tokenize(',')])} );
+ break
+
+ case "__GROUP__":
+ sql.eachRow("SELECT id, name, display_name, extension, display_extension, description, type_of_group FROM grouper_groups WHERE id in \
+ (select m.subject_id \
+ from grouper_memberships gm join grouper_members m on gm.member_id=m.id \
+ where gm.owner_id = (select subject_id from grouper_members where name='etc:exportedGroups' and subject_type='group'))",
+ {result.add([
+ __UID__:it.id,
+ __NAME__:it.name,
+ displayName:it.display_name,
+ extension:it.extension,
+ displayExtension:it.display_extension,
+ description:it.description,
+ type:it.type_of_group])} );
+ break
+
+/*
+ case "organization":
+ sql.eachRow("SELECT * FROM Organizations" + where, {result.add([__UID__:it.name, __NAME__:it.name, description:it.description])} );
+ break */
+
+ default:
+ result;
+}
+
+return result;
diff --git a/demo/complex/midpoint-server/container_files/mp-home/res/grouper2/SyncScript.groovy b/demo/complex/midpoint-server/container_files/mp-home/res/grouper2/SyncScript.groovy
new file mode 100644
index 0000000..35062a9
--- /dev/null
+++ b/demo/complex/midpoint-server/container_files/mp-home/res/grouper2/SyncScript.groovy
@@ -0,0 +1,205 @@
+/*
+ * ====================
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 ForgeRock. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common Development
+ * and Distribution License("CDDL") (the "License"). You may not use this file
+ * except in compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://opensource.org/licenses/cddl1.php
+ * See the License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * When distributing the Covered Code, include this CDDL Header Notice in each file
+ * and include the License file at http://opensource.org/licenses/cddl1.php.
+ * If applicable, add the following below this CDDL Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ * ====================
+ * Portions Copyrighted 2013 ConnId.
+ */
+import groovy.sql.Sql
+import groovy.sql.DataSet
+import com.rabbitmq.client.*
+
+// Parameters:
+// The connector sends the following:
+// connection: handler to the SQL connection
+// objectClass: a String describing the Object class (__ACCOUNT__ / __GROUP__ / other)
+// action: a string describing the action ("SYNC" or "GET_LATEST_SYNC_TOKEN" here)
+// log: a handler to the Log facility
+// options: a handler to the OperationOptions Map (null if action = "GET_LATEST_SYNC_TOKEN")
+// token: a handler to an Object representing the sync token (null if action = "GET_LATEST_SYNC_TOKEN")
+//
+//
+// Returns:
+// if action = "GET_LATEST_SYNC_TOKEN", it must return an object representing the last known
+// sync token for the corresponding ObjectClass
+//
+// if action = "SYNC":
+// A list of Maps . Each map describing one update:
+// Map should look like the following:
+//
+// [
+// "token":