diff --git a/Dockerfile b/Dockerfile index ac08e98b..e5f412bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,9 +3,12 @@ FROM centos:centos7 as installing RUN yum update -y \ && yum install -y wget tar unzip dos2unix \ && yum clean all + +ARG GROUPER_CONTAINER_VERSION -ENV GROUPER_VERSION=2.3.0 \ - JAVA_HOME=/usr/lib/jvm/zulu-8/ +ENV GROUPER_VERSION=2.4.0 \ + JAVA_HOME=/usr/lib/jvm/zulu-8/ \ + GROUPER_CONTAINER_VERSION=$GROUPER_CONTAINER_VERSION # use Zulu package RUN rpm --import http://repos.azulsystems.com/RPM-GPG-KEY-azulsystems \ @@ -46,7 +49,7 @@ RUN echo 'Installing Grouper'; \ FROM centos:centos7 as cleanup -ENV GROUPER_VERSION=2.3.0 \ +ENV GROUPER_VERSION=2.4.0 \ TOMCAT_VERSION=8.5.12 \ TOMEE_VERSION=7.0.0 @@ -93,10 +96,13 @@ LABEL author="tier-packaging@internet2.edu " \ ImageType="Grouper" \ ImageName=$imagename \ ImageOS=centos7 + +ARG GROUPER_CONTAINER_VERSION ENV JAVA_HOME=/usr/lib/jvm/zulu-8/ \ PATH=$PATH:$JAVA_HOME/bin \ - GROUPER_HOME=/opt/grouper/grouper.apiBinary + GROUPER_HOME=/opt/grouper/grouper.apiBinary \ + GROUPER_CONTAINER_VERSION=$GROUPER_CONTAINER_VERSION RUN ln -sf /usr/share/zoneinfo/UTC /etc/localtime @@ -118,7 +124,8 @@ RUN groupadd -r tomcat \ && chown -R tomcat:tomcat /opt/tomee/logs/ /opt/tomee/temp/ /opt/tomee/work/ \ && ln -s $JAVA_HOME/bin/java /etc/alternatives/java -RUN rm /etc/shibboleth/sp-key.pem /etc/shibboleth/sp-cert.pem +# does shib sp3 not generate these files? +# RUN rm /etc/shibboleth/sp-key.pem /etc/shibboleth/sp-cert.pem COPY container_files/tier-support/ /opt/tier-support/ COPY container_files/usr-local-bin/ /usr/local/bin/ diff --git a/Jenkinsfile b/Jenkinsfile index f97f1526..3ed439e5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -27,6 +27,9 @@ pipeline { sh 'rm -rf ../bin/*' sh 'mv ./bin/* ../bin/.' } + // Build and test scripts expect that 'tag' is present in common.bash. This is necessary for both Jenkins and standalone testing. + // We don't care if there are more 'tag' assignments there. The latest one wins. + sh "echo >> common.bash ; echo \"tag=\\\"${tag}\\\"\" >> common.bash ; echo common.bash ; cat common.bash" } } } @@ -47,15 +50,55 @@ pipeline { stage('Build') { steps { script { - docker.withRegistry('https://registry.hub.docker.com/', "dockerhub-$maintainer") { - def baseImg = docker.build("$maintainer/$imagename", "--no-cache .") - // test the environment - sh 'cd test-compose && ./compose.sh' - // bring down after testing - sh 'cd test-compose && docker-compose down' - baseImg.push("$tag") - } - } + try{ + docker.withRegistry('https://registry.hub.docker.com/', "dockerhub-$maintainer") { + baseImg = docker.build("$maintainer/$imagename", "--build-arg GROUPER_CONTAINER_VERSION=$tag --no-cache .") + } + } catch(error) { + def error_details = readFile('./debug'); + def message = "BUILD ERROR: There was a problem building ${imagename}:${tag}. \n\n ${error_details}" + sh "rm -f ./debug" + handleError(message) + } + } + } + } + stage('Test') { + steps { + script { + try { + sh 'bin/test.sh 2>&1 | tee debug ; test ${PIPESTATUS[0]} -eq 0' + } catch (error) { + def error_details = readFile('./debug') + def message = "BUILD ERROR: There was a problem testing ${imagename}:${tag}. \n\n ${error_details}" + sh "rm -f ./debug" + handleError(message) + } + } + } + } + + stage('Push') { + steps { + script { + //// scan the image with clair + // sh 'docker run -p 5432:5432 -d --name clairdb arminc/clair-db:latest' + // sh 'docker run -p 6060:6060 --link clairdb:postgres -d --name clair arminc/clair-local-scan:v2.0.5' + // sh 'curl -L -o clair-scanner https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64' + // sh 'chmod 755 clair-scanner' + // sh "./clair-scanner --ip 172.17.0.1 -r test.out $maintainer/$imagename:latest" + //// test the environment + // sh 'docker kill clairdb' + // sh 'docker rm clairdb' + // sh 'docker kill clair' + // sh 'docker rm clair' + // sh 'cd test-compose && ./compose.sh' + //// bring down after testing + //sh 'cd test-compose && docker-compose down' + docker.withRegistry('https://registry.hub.docker.com/', "dockerhub-$maintainer") { + baseImg.push("$tag") + } + } } } stage('Notify') { diff --git a/README.md b/README.md index c00d0dd8..2c5c73cb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,16 @@ -[![Build Status](https://jenkins.testbed.tier.internet2.edu/job/docker/job/grouper/job/master/badge/icon)](https://jenkins.testbed.tier.internet2.edu/job/docker/job/grouper/job/master/) +[![Build Status](https://jenkins.testbed.tier.internet2.edu/buildStatus/icon?job=docker/grouper/2.4.0-a29-u14-w3-p2-20190217)](https://jenkins.testbed.tier.internet2.edu/buildStatus/icon?job=docker/grouper/2.4.0-a29-u14-w3-p2-20190217) + +# Upgrading from 2.3 to 2.4 + +If upgrading from Grouper version 2.3 to 2.4 and using LDAP, modifications will be needed in subject.properties and grouper-loaders.proprties. Further details about this can be found at the following URL: +https://spaces.at.internet2.edu/display/Grouper/vt-ldap+to+ldaptive+migration+for+LDAP+access + +In particular, in subject.properties, *.param.base.value should be adjusted to only contain the RDN (Relative Distinguished Name), not the full DN. For example, "OU=People", not "OU=People,DC=domain,DC=edu" + +Additional upgrade information can be found at the following URL: https://spaces.at.internet2.edu/display/Grouper/v2.4+Upgrade+Instructions+from+v2.3 + # Supported tags - latest diff --git a/container_files/grouper.installer.properties b/container_files/grouper.installer.properties index b2acf050..ee6acce7 100644 --- a/container_files/grouper.installer.properties +++ b/container_files/grouper.installer.properties @@ -1,7 +1,7 @@ # this should be before the version number download.server.url = https://software.internet2.edu/grouper # default version to install -grouper.version = 2.3.0 +grouper.version = 2.4.0 # print out autorun keys in prompts so you can easily see how to configure the autorun grouperInstaller.print.autorunKeys = true # default to install or upgrade (default is install) @@ -16,7 +16,8 @@ grouperInstaller.default.installOrUpgrade = install grouperInstaller.autorun.installAllPatches = false grouperInstaller.autorun.installPatchesUpToACertainPatchLevel = true -grouperInstaller.autorun.installPatchesUpToThesePatchLevels = grouper_v2_3_0_api_patch_109,grouper_v2_3_0_ui_patch_47,grouper_v2_3_0_ws_patch_13,grouper_v2_3_0_pspng_patch_24 +grouperInstaller.autorun.installPatchesUpToThesePatchLevels = grouper_v2_4_0_api_patch_31,grouper_v2_4_0_ui_patch_14,grouper_v2_4_0_ws_patch_3,grouper_v2_4_0_pspng_patch_3 + #### set this to true to try to use defaults for everything. Only things without default values will need to be set grouperInstaller.autorun.useDefaultsAsMuchAsAvailable = true @@ -30,10 +31,14 @@ grouperInstaller.autorun.addQuickstartData = f grouperInstaller.autorun.installClient = f grouperInstaller.autorun.installGrouperActiveMqMessaging = f -grouperInstaller.autorun.activeMqWhereInstalled = /opt/grouper/2.3.0/grouper.apiBinary-2.3.0/ +grouperInstaller.autorun.activeMqWhereInstalled = /opt/grouper/2.4.0/grouper.apiBinary-2.4.0/ grouperInstaller.autorun.installGrouperAwsSqsMessaging = t -grouperInstaller.autorun.AwsSqsWhereInstalled = /opt/grouper/2.3.0/grouper.apiBinary-2.3.0/ +grouperInstaller.autorun.AwsSqsWhereInstalled = /opt/grouper/2.4.0/grouper.apiBinary-2.4.0/ grouperInstaller.autorun.installGrouperRabbitMqMessaging = t -grouperInstaller.autorun.rabbitMqWhereInstalled = /opt/grouper/2.3.0/grouper.apiBinary-2.3.0/ +grouperInstaller.autorun.rabbitMqWhereInstalled = /opt/grouper/2.4.0/grouper.apiBinary-2.4.0/ + +# disable installing pspng, for now +grouperInstaller.autorun.installPspng = t +grouperInstaller.autorun.installPsp = f diff --git a/container_files/ui/web.xml b/container_files/ui/web.xml index 92d4125d..f3aa302f 100644 --- a/container_files/ui/web.xml +++ b/container_files/ui/web.xml @@ -1,203 +1,89 @@ - - - - - - - + + GrouperUi edu.internet2.middleware.grouper.ui.GrouperUiFilter - - - - Error Catcher - edu.internet2.middleware.grouper.ui.ErrorFilter - - - Login check - edu.internet2.middleware.grouper.ui.LoginCheckFilter - - failureUrl - /index.jsp - - - ignore - :/populateIndex.do:/callLogin.do:/error.do:/logout.do:/status: - - - grouperRole - * - - - - - Caller page - edu.internet2.middleware.grouper.ui.CallerPageFilter - - - + CSRFGuard org.owasp.csrfguard.CsrfGuardFilter - - - - GrouperUi - *.do - - - + GrouperUi *.jsp - - - Error Catcher - *.do - - - - Error Catcher - /gotoCallerPage - - - + GrouperUi /grouperUi/app/* - - + GrouperUi /grouperUi/appHtml/* - - + GrouperUi /grouperExternal/app/* - - - GrouperUi - /grouperExternal/appHtml/* - - - + GrouperUi /grouperExternal/public/UiV2Public.index - - + GrouperUi /grouperExternal/public/UiV2Public.postIndex - - - Caller page - /gotoCallerPage - - - - Login check - *.do - - - + CSRFGuard /* - - - - edu.internet2.middleware.grouper.ui.GrouperSessionAttributeListener - - - + + edu.internet2.middleware.grouper.ui.GrouperSessionAttributeListener + + org.owasp.csrfguard.CsrfGuardServletContextListener - - + org.owasp.csrfguard.CsrfGuardHttpSessionListener - - - + StatusServlet Status Servlet edu.internet2.middleware.grouper.j2ee.status.GrouperStatusServlet 1 - - - - + UiServlet edu.internet2.middleware.grouper.j2ee.GrouperUiRestServlet 1 - - - OwaspJavaScriptServlet - org.owasp.csrfguard.servlet.JavaScriptServlet - - - - action - org.apache.struts.action.ActionServlet - - config - /WEB-INF/struts-config.xml - - 2 + + OwaspJavaScriptServlet + org.owasp.csrfguard.servlet.JavaScriptServlet - - - + StatusServlet /status - - + UiServlet /grouperUi/app/* - - + UiServlet /grouperExternal/app/* - - + UiServlet /grouperExternal/public/UiV2Public.index - - + UiServlet /grouperExternal/public/UiV2Public.postIndex - - - OwaspJavaScriptServlet - /grouperExternal/public/OwaspJavaScriptServlet - - - - action - *.do + + OwaspJavaScriptServlet + /grouperExternal/public/OwaspJavaScriptServlet - - - - - - - - - - - diff --git a/test-compose/configs-and-secrets/grouper/grouper-loader.properties b/test-compose/configs-and-secrets/grouper/grouper-loader.properties index 07c4f56a..68bef05f 100644 --- a/test-compose/configs-and-secrets/grouper/grouper-loader.properties +++ b/test-compose/configs-and-secrets/grouper/grouper-loader.properties @@ -4,17 +4,18 @@ # 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://data:389/dc=example,dc=edu +ldap.demo.url = ldap://data:389/dc=internet2,dc=edu #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.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')} +#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 diff --git a/test-compose/configs-and-secrets/grouper/subject.properties b/test-compose/configs-and-secrets/grouper/subject.properties index fd5b25cd..c9329fdb 100644 --- a/test-compose/configs-and-secrets/grouper/subject.properties +++ b/test-compose/configs-and-secrets/grouper/subject.properties @@ -1,19 +1,25 @@ -subject.sources.xml.location = +#subject.sources.xml.location = + +subjectApi.source.ldap.param.ldapServerId.value = demo + 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://data: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')} + +## replaced with new LDAPTIVE lib +#subjectApi.source.ldap.param.INITIAL_CONTEXT_FACTORY.value = com.sun.jndi.ldap.LdapCtxFactory +#subjectApi.source.ldap.param.PROVIDER_URL.value = ldap://data: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.VTLDAP_VALIDATOR.value = ConnectLdapValidator + 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 @@ -55,7 +61,7 @@ subjectApi.source.ldap.param.searchAttribute0.value = searchAttribute0 # 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 +subjectApi.source.ldap.search.searchSubject.param.base.value = ou=people #searchSubjectByIdentifier: find a subject by identifier. Identifier is anything that uniquely # identifies the user, e.g. jsmith or jsmith@institution.edu. @@ -63,13 +69,13 @@ subjectApi.source.ldap.search.searchSubject.param.base.value = ou=people,dc=inte # 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 +subjectApi.source.ldap.search.searchSubjectByIdentifier.param.base.value = ou=people # 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.search.search.param.base.value = ou=people subjectApi.source.ldap.attributes = givenName, sn, uid, mail, employeeNumber subjectApi.source.ldap.internalAttributes = searchAttribute0 diff --git a/test-compose/data/container_files/conf/grouper-loader.properties b/test-compose/data/container_files/conf/grouper-loader.properties new file mode 100644 index 00000000..c7d0bcbc --- /dev/null +++ b/test-compose/data/container_files/conf/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://localhost:389/dc=internet2,dc=edu + +#optional, if authenticated +ldap.demo.user = cn=admin,dc=internet2,dc=edu +#ldap.demo.user = cn=admin + +#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/test-compose/data/container_files/conf/subject.properties b/test-compose/data/container_files/conf/subject.properties index a8231911..c9329fdb 100644 --- a/test-compose/data/container_files/conf/subject.properties +++ b/test-compose/data/container_files/conf/subject.properties @@ -1,19 +1,25 @@ -subject.sources.xml.location = +#subject.sources.xml.location = + +subjectApi.source.ldap.param.ldapServerId.value = demo + 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 + +## replaced with new LDAPTIVE lib +#subjectApi.source.ldap.param.INITIAL_CONTEXT_FACTORY.value = com.sun.jndi.ldap.LdapCtxFactory +#subjectApi.source.ldap.param.PROVIDER_URL.value = ldap://data: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.VTLDAP_VALIDATOR.value = ConnectLdapValidator + 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 @@ -55,7 +61,7 @@ subjectApi.source.ldap.param.searchAttribute0.value = searchAttribute0 # 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 +subjectApi.source.ldap.search.searchSubject.param.base.value = ou=people #searchSubjectByIdentifier: find a subject by identifier. Identifier is anything that uniquely # identifies the user, e.g. jsmith or jsmith@institution.edu. @@ -63,13 +69,13 @@ subjectApi.source.ldap.search.searchSubject.param.base.value = ou=people,dc=inte # 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 +subjectApi.source.ldap.search.searchSubjectByIdentifier.param.base.value = ou=people # 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.search.search.param.base.value = ou=people subjectApi.source.ldap.attributes = givenName, sn, uid, mail, employeeNumber subjectApi.source.ldap.internalAttributes = searchAttribute0 diff --git a/test-compose/data/container_files/seed-data/bootstrap.gsh b/test-compose/data/container_files/seed-data/bootstrap.gsh index e2a018aa..4f902129 100644 --- a/test-compose/data/container_files/seed-data/bootstrap.gsh +++ b/test-compose/data/container_files/seed-data/bootstrap.gsh @@ -1,3 +1,10 @@ gs = GrouperSession.startRootSession() +addStem("","app", "app") +addStem("","basis", "basis") +addStem("","bundle", "bundle") +addStem("","org", "org") +addStem("","ref", "ref") +addStem("","test", "test") + addMember("etc:sysadmingroup","banderson"); diff --git a/test-compose/docker-compose.yml b/test-compose/docker-compose.yml index bf11466c..c0e21ed3 100644 --- a/test-compose/docker-compose.yml +++ b/test-compose/docker-compose.yml @@ -198,7 +198,22 @@ services: ports: - "389:389" - "3306:3306" + secrets: + - 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/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_mysql:/var/lib/mysql - grouper_ldap:/var/lib/dirsrv diff --git a/tests/clairscan.sh b/tests/clairscan.sh new file mode 100755 index 00000000..99e36141 --- /dev/null +++ b/tests/clairscan.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +startsecs=$(date +'%s') +starttime=$(date +%H:%M:%S) + +echo 'starting:' ${starttime} + +#ensure clair-scanner +if [ ! -s ./clair-scanner ]; then + echo 'downloading curl-scanner...' + curl -s -L -o ./clair-scanner https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64 + chmod 755 clair-scanner +else + echo 'using existing clair-scanner...' +fi + +#ensure DB container +echo 'ensuring a fresh clair-db container...' +docker ps | grep clair-db &>/dev/null +if [ $? == "0" ]; then + echo 'removing existing clair-db container...' + docker kill db &>/dev/null + docker rm db &>/dev/null + docker run -p 5432:5432 -d --name db arminc/clair-db:latest &>/dev/null +else + docker run -p 5432:5432 -d --name db arminc/clair-db:latest &>/dev/null +fi +sleep 30 + +#ensure clair-scan container +echo 'ensuring a fresh clair-scan container...' +docker ps | grep clair-local-scan &>/dev/null +if [ $? == "0" ]; then + echo 'removing existing clair-scan container...' + docker kill clair &>/dev/null + docker rm clair &>/dev/null + docker run -p 6060:6060 --link db:postgres -d --name clair arminc/clair-local-scan:v2.0.5 &>/dev/null +else + docker run -p 6060:6060 --link db:postgres -d --name clair arminc/clair-local-scan:v2.0.5 &>/dev/null +fi +sleep 30 + +#get ip where clair-scanner will listen +clairip=$(/sbin/ifconfig docker0 | grep 'inet ' | sed 's/^[[:space:]]*//g' | cut -f 2 -d ' ' | sed 's/^[[:space:]]*//g') +echo 'sending ip addr' ${clairip} 'to clair-scan server...' + +#run scan +echo 'running scan...' +./clair-scanner --ip ${clairip} $1 +retcode=$? + +#eval results +if [ $retcode == '0' ]; then + echo 'scan found nothing.' +else + echo 'scan found issues.' +fi + +#cleanup +echo 'removing temporary containers...' +docker kill clair &>/dev/null +docker rm clair &>/dev/null +docker kill db &>/dev/null +docker rm db &>/dev/null + +endsecs=$(date +'%s') +endtime=$(date +%H:%M:%S) +echo 'finished:' $endtime ' ('$((endsecs - startsecs)) 'seconds)' +echo "" + +#pass along return code from scan +exit $retcode diff --git a/tests/main.bats b/tests/main.bats new file mode 100644 index 00000000..0c18d122 --- /dev/null +++ b/tests/main.bats @@ -0,0 +1,16 @@ +#!/usr/bin/env bats + +load ../common + +@test "010 Image is present and healthy" { + docker image inspect ${maintainer}/${imagename} +} + +@test "030 Test Compose the environment" { + cd test-compose && ./compose.sh && docker-compose down +} + + +@test "070 There are no known security vulnerabilities" { + ./tests/clairscan.sh ${maintainer}/${imagename}:latest +}