diff --git a/grouper-misc/grouper-test-plugin/pom.xml b/grouper-misc/grouper-test-plugin/pom.xml
new file mode 100644
index 000000000000..15eb0617c3f5
--- /dev/null
+++ b/grouper-misc/grouper-test-plugin/pom.xml
@@ -0,0 +1,109 @@
+
+
+
+
+ 4.0.0
+
+
+ edu.internet2.middleware.grouper
+ grouper-parent
+ 5.0.0-SNAPSHOT
+ ../../grouper-parent
+
+
+ Grouper Test plugin
+ A Grouper test plugin. This can also be used as a protype/sample for creating a plugin
+ grouper-test-plugin
+ bundle
+
+
+
+ org.osgi
+ osgi.core
+ provided
+
+
+ javax
+ javaee-web-api
+ 8.0.1
+ provided
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ true
+
+ ${project.name}
+ ${project.organization.name}
+ ${project.artifactId}
+ ${project.version}
+ ${project.url}
+ ${maven.build.timestamp}
+
+
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ true
+
+
+ ${project.groupId}.${project.artifactId}
+ ${project.artifactId}-bundle
+ ${project.version}
+ edu.internet2.middleware.grouper.plugin.test.TestPlugin
+ edu.internet2.middleware.grouper.plugin.test.filter
+ edu.internet2.middleware.grouper.plugin.*
+ *;scope=compile|runtime;inline=false
+ false
+
+ !*
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jarsigner-plugin
+ 3.0.0
+
+
+ sign
+
+ sign
+
+
+
+
+ ${project.basedir}/src/main/signing/signing.jks
+ signing
+ signing
+ signing
+
+
+
+
+
\ No newline at end of file
diff --git a/grouper-misc/grouper-test-plugin/src/main/java/edu/internet2/middleware/grouper/plugin/test/TestPlugin.java b/grouper-misc/grouper-test-plugin/src/main/java/edu/internet2/middleware/grouper/plugin/test/TestPlugin.java
new file mode 100644
index 000000000000..7cf14befcff1
--- /dev/null
+++ b/grouper-misc/grouper-test-plugin/src/main/java/edu/internet2/middleware/grouper/plugin/test/TestPlugin.java
@@ -0,0 +1,26 @@
+package edu.internet2.middleware.grouper.plugin.test;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import javax.servlet.ServletContainerInitializer;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+public class TestPlugin implements BundleActivator {
+ private Map registrationMap = new HashMap<>();
+
+ @Override
+ public void start(BundleContext bundleContext) throws Exception {
+ TestPluginServletContainerInitializer initializer = new TestPluginServletContainerInitializer();
+ ServiceRegistration registration = bundleContext.registerService(ServletContainerInitializer.class, initializer, new Hashtable<>());
+ registrationMap.put(TestPluginServletContainerInitializer.class.getCanonicalName(), registration);
+ }
+
+ @Override
+ public void stop(BundleContext bundleContext) throws Exception {
+ registrationMap.values().forEach(x -> { x.unregister(); } );
+ }
+}
diff --git a/grouper-misc/grouper-test-plugin/src/main/java/edu/internet2/middleware/grouper/plugin/test/TestPluginServletContainerInitializer.java b/grouper-misc/grouper-test-plugin/src/main/java/edu/internet2/middleware/grouper/plugin/test/TestPluginServletContainerInitializer.java
new file mode 100644
index 000000000000..f3b15000d3c7
--- /dev/null
+++ b/grouper-misc/grouper-test-plugin/src/main/java/edu/internet2/middleware/grouper/plugin/test/TestPluginServletContainerInitializer.java
@@ -0,0 +1,18 @@
+package edu.internet2.middleware.grouper.plugin.test;
+
+import edu.internet2.middleware.grouper.plugin.test.filter.TestFilter;
+
+import javax.servlet.FilterRegistration;
+import javax.servlet.ServletContainerInitializer;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import java.util.Set;
+
+public class TestPluginServletContainerInitializer implements ServletContainerInitializer {
+ @Override
+ public void onStartup(Set> set, ServletContext servletContext) throws ServletException {
+ TestFilter testFilter = new TestFilter();
+ FilterRegistration.Dynamic testFilterRegistration = servletContext.addFilter("testFilter", testFilter);
+ testFilterRegistration.addMappingForUrlPatterns(null, false, "/*");
+ }
+}
diff --git a/grouper-misc/grouper-test-plugin/src/main/java/edu/internet2/middleware/grouper/plugin/test/filter/TestFilter.java b/grouper-misc/grouper-test-plugin/src/main/java/edu/internet2/middleware/grouper/plugin/test/filter/TestFilter.java
new file mode 100644
index 000000000000..aec006c65236
--- /dev/null
+++ b/grouper-misc/grouper-test-plugin/src/main/java/edu/internet2/middleware/grouper/plugin/test/filter/TestFilter.java
@@ -0,0 +1,15 @@
+package edu.internet2.middleware.grouper.plugin.test.filter;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.io.IOException;
+
+public class TestFilter implements Filter {
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+}
diff --git a/grouper-misc/grouper-test-plugin/src/main/signing/README.md b/grouper-misc/grouper-test-plugin/src/main/signing/README.md
new file mode 100644
index 000000000000..ad9420d743c7
--- /dev/null
+++ b/grouper-misc/grouper-test-plugin/src/main/signing/README.md
@@ -0,0 +1 @@
+These files are strictly examples and for testing. Do not use them in your own projects!
\ No newline at end of file
diff --git a/grouper-misc/grouper-test-plugin/src/main/signing/signing.crt b/grouper-misc/grouper-test-plugin/src/main/signing/signing.crt
new file mode 100644
index 000000000000..1c6272ecf5ac
--- /dev/null
+++ b/grouper-misc/grouper-test-plugin/src/main/signing/signing.crt
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDczCCAlugAwIBAgIILehkAIY5xvQwDQYJKoZIhvcNAQELBQAwaDEQMA4GA1UE
+BhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQ
+MA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEMMAoGA1UEAwwDSmoh
+MB4XDTIzMTAxMjAwMjE1OFoXDTMzMTAxMTAwMjE1OFowaDEQMA4GA1UEBhMHVW5r
+bm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UE
+ChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEMMAoGA1UEAwwDSmohMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9Y8HkztTbh1gXW+lIB175CYiqR5U
+qPVaCe9efvLd89zzyKpO1wxdp7lbJwwHfepuFhsGPMl38aD6YXeQDRKj3m7u2m94
+I1PAiAYslRSN/3oWyv7RQwP3gn4XAegpR4kBCNJoXLTPK80oyYA62okVNtCH/dFq
+Z3k3A1O+OoeqcDBq5KzuPatViFAjEyyLCcV1g0mLQlCfzrWFw5OVp2U6q2D0qLKF
+5i2bFkIlwxcWY0tfvv0S8Lp7+JpPN/etXGByKigM4Cu9CnfLBUs0v9h4wH5wv/Zj
+/XGfViD+pemIgkR82BqPM31TeWU0ONOYapS0LS4RpZBOcLtXcwgBMIch2QIDAQAB
+oyEwHzAdBgNVHQ4EFgQU5cbJ7MBtBDej6XYd3iukheyNO5kwDQYJKoZIhvcNAQEL
+BQADggEBAMjtgjBespxleA5Po49A3kng/A1n24m0SuGDVRmtWm5OGi34e+A64GIo
+kgiux+r0HOSDHUaIfao1L84FIxIBGGSHW/hcZ8RJr1qSYvW7KnD9PfgAcghtokKx
+38fx3Q3RUDB6Gcz2vigTkJ7OzR1wkyzVvfnLEOL3kA1P49Eb/wyMKEmmLDQyij6n
+JG+T6+M4+QKiA8W9yIIAAjJFoNL0LBczsdVn6o34buwqLPhjhVoGazDbax5cXxkJ
+8TRdktqQNbBqDk+6mOto8MCJUtg7d+Run+HvtgjNsdYLI6J+6K9+51STLtXST90V
+voa3jXptx9Tl/BFoJK2sJWdoIwG3zug=
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/grouper-misc/grouper-test-plugin/src/main/signing/signing.jks b/grouper-misc/grouper-test-plugin/src/main/signing/signing.jks
new file mode 100644
index 000000000000..27e0a33e4752
Binary files /dev/null and b/grouper-misc/grouper-test-plugin/src/main/signing/signing.jks differ
diff --git a/grouper-misc/grouper-test-plugin/src/main/signing/test.jks b/grouper-misc/grouper-test-plugin/src/main/signing/test.jks
new file mode 100644
index 000000000000..d6cc98835617
Binary files /dev/null and b/grouper-misc/grouper-test-plugin/src/main/signing/test.jks differ
diff --git a/grouper-misc/webapp/grouper-ui-webapp/src/test/resources/grouper.properties b/grouper-misc/webapp/grouper-ui-webapp/src/test/resources/grouper.properties
deleted file mode 100644
index 2fa273ea373b..000000000000
--- a/grouper-misc/webapp/grouper-ui-webapp/src/test/resources/grouper.properties
+++ /dev/null
@@ -1,35 +0,0 @@
-# directory of plugins, default to /opt/grouper/grouperWebapp/WEB-INF/grouperPlugins
-# {valueType: "string", required: true, order: 1000}
-grouper.osgi.jar.dir = C:/Users/jj-unicon/workspace/grouper.se/grouper/grouper-misc/grouper-authentication/target
-
-# directory of felix cache of plugins, default to /opt/grouper/grouperWebapp/WEB-INF/grouperFelixCache
-# {valueType: "string", required: true, order: 2000}
-grouper.felix.cache.rootdir = c:/tmp/grouperFelixCache
-
-#grouperOsgiPlugin.extAuthPlugin.jarName = grouper-authentication-plugin-0.0.1-SNAPSHOT.jar
-#grouperOsgiPlugin.extAuthPlugin.numberOfImplementations = 2
-#grouperOsgiPlugin.extAuthPlugin.osgiImplementation.0.implementationClass = edu.internet2.middleware.grouper.authentication.plugin.filter.SecurityFilterDecorator
-#grouperOsgiPlugin.extAuthPlugin.osgiImplementation.0.implementsInterface = javax.servlet.Filter
-#grouperOsgiPlugin.extAuthPlugin.osgiImplementation.1.implementationClass = edu.internet2.middleware.grouper.authentication.plugin.filter.CallbackFilterDecorator
-#grouperOsgiPlugin.extAuthPlugin.osgiImplementation.1.implementsInterface = javax.servlet.Filter
-
-############################################
-## External Authentication plugin
-############################################
-# Enable external authorization security filters
-# {valueType: "boolean", defaultValue: "false"}
-grouper.is.extAuth.enabled=true
-
-# Name of the jar containing the external authorization plugin
-# {valueType: "string", required: true}
-grouper.extAuth.jarname=grouper-authentication-0.0.1-SNAPSHOT.jar
-
-# Callback filter implementation classname
-# ex: edu.internet2.middleware.grouper.plugins.testImplementation.SamplePluginProviderServiceImpl
-# ${valueType: "class", required: true}
-grouper.extAuth.filter.callback.implmentation.className=edu.internet2.middleware.grouper.authentication.plugin.filter.CallbackFilterDecorator
-
-# Security filter implementation classname
-# ex: edu.internet2.middleware.grouper.plugins.testImplementation.SamplePluginProviderServiceImpl
-# ${valueType: "class", required: true}
-grouper.extAuth.filter.security.implmentation.className=edu.internet2.middleware.grouper.authentication.plugin.filter.SecurityFilterDecorator
diff --git a/grouper-misc/webapp/grouper-ui-webapp/pom.xml b/grouper-misc/webapp/pom.xml
similarity index 54%
rename from grouper-misc/webapp/grouper-ui-webapp/pom.xml
rename to grouper-misc/webapp/pom.xml
index 8a8ec64fcbc1..ed2b984fde97 100644
--- a/grouper-misc/webapp/grouper-ui-webapp/pom.xml
+++ b/grouper-misc/webapp/pom.xml
@@ -25,11 +25,11 @@
edu.internet2.middleware.grouper
grouper-parent
5.0.0-SNAPSHOT
- ../../../grouper-parent
+ ../../grouper-parent
-
- Grouper UI webapp
- grouper-ui-webapp
+
+ Grouper webapp
+ grouper-webapp
war
@@ -38,12 +38,17 @@
grouper-ui
${project.version}
+
+ ${project.groupId}
+ grouper-ws
+ ${project.version}
+
- ../../../grouper/conf
+ ../../grouper/conf
@@ -55,19 +60,21 @@
false
- ../../../grouper-ui/webapp
+ ../../grouper-ui/webapp
- org.eclipse.jetty
- jetty-maven-plugin
- 9.4.27.v20200227
+ org.codehaus.cargo
+ cargo-maven3-plugin
+ 1.10.9
-
- /grouper
-
+
+
+ 8080
+
+
@@ -84,5 +91,33 @@
+
+ debug
+
+
+
+ org.codehaus.cargo
+ cargo-maven3-plugin
+ 1.10.9
+
+
+ 3600000
+
+
+
+ 8080
+
+ -Xdebug
+ -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005
+ -Xnoagent
+ -Djava.compiler=NONE
+
+
+
+
+
+
+
+
diff --git a/grouper-misc/webapp/grouper-ui-webapp/src/test/resources/grouper-loader.properties b/grouper-misc/webapp/src/test/resources/grouper-loader.properties
similarity index 100%
rename from grouper-misc/webapp/grouper-ui-webapp/src/test/resources/grouper-loader.properties
rename to grouper-misc/webapp/src/test/resources/grouper-loader.properties
diff --git a/grouper-misc/webapp/grouper-ui-webapp/src/test/resources/grouper-ui.properties b/grouper-misc/webapp/src/test/resources/grouper-ui.properties
similarity index 100%
rename from grouper-misc/webapp/grouper-ui-webapp/src/test/resources/grouper-ui.properties
rename to grouper-misc/webapp/src/test/resources/grouper-ui.properties
diff --git a/grouper-misc/webapp/grouper-ui-webapp/src/test/resources/grouper.cache.properties b/grouper-misc/webapp/src/test/resources/grouper.cache.properties
similarity index 100%
rename from grouper-misc/webapp/grouper-ui-webapp/src/test/resources/grouper.cache.properties
rename to grouper-misc/webapp/src/test/resources/grouper.cache.properties
diff --git a/grouper-misc/webapp/grouper-ui-webapp/src/test/resources/grouper.client.properties b/grouper-misc/webapp/src/test/resources/grouper.client.properties
similarity index 100%
rename from grouper-misc/webapp/grouper-ui-webapp/src/test/resources/grouper.client.properties
rename to grouper-misc/webapp/src/test/resources/grouper.client.properties
diff --git a/grouper-misc/webapp/grouper-ui-webapp/src/test/resources/grouper.hibernate.properties b/grouper-misc/webapp/src/test/resources/grouper.hibernate.properties
similarity index 100%
rename from grouper-misc/webapp/grouper-ui-webapp/src/test/resources/grouper.hibernate.properties
rename to grouper-misc/webapp/src/test/resources/grouper.hibernate.properties
diff --git a/grouper-misc/webapp/src/test/resources/grouper.properties b/grouper-misc/webapp/src/test/resources/grouper.properties
new file mode 100644
index 000000000000..6ce84329634c
--- /dev/null
+++ b/grouper-misc/webapp/src/test/resources/grouper.properties
@@ -0,0 +1,15 @@
+grouper.osgi.enable = true
+grouper.osgi.security.enable=false
+
+# directory of plugins, default to /opt/grouper/grouperWebapp/WEB-INF/grouperPlugins
+# {valueType: "string", required: true, order: 1000}
+grouper.osgi.jar.dir = /Users/jj/Documents/workspace/community/grouper-ext-auth/target
+
+# directory of felix cache of plugins, default to /opt/grouper/grouperWebapp/WEB-INF/grouperFelixCache
+# {valueType: "string", required: true, order: 2000}
+grouper.felix.cache.rootdir = /tmp/grouperFelixCache
+
+grouper.osgi.plugin.extauth.location=file:/Users/jj/Documents/workspace/community/grouper-ext-auth/target/grouper-authentication-plugin-0.0.1-SNAPSHOT.jar
+
+# grouper.osgi.framework.system.packages.extra=javax.*,org.osgi.*,org.osgi,org.apache.commons.logging,edu.internet2.middleware.grouperClient.config,edu.internet2.middleware.grouper.app.externalSystem,org.w3c.dom.*
+# grouper.osgi.framework.boot.delegation=javax.*,org.osgi.*,org.osgi,org.apache.commons.logging,edu.internet2.middleware.grouperClient.config,edu.internet2.middleware.grouper.app.externalSystem,org.w3c.dom.*
\ No newline at end of file
diff --git a/grouper-misc/webapp/grouper-ui-webapp/src/test/resources/morphString.properties b/grouper-misc/webapp/src/test/resources/morphString.properties
similarity index 100%
rename from grouper-misc/webapp/grouper-ui-webapp/src/test/resources/morphString.properties
rename to grouper-misc/webapp/src/test/resources/morphString.properties
diff --git a/grouper-misc/webapp/grouper-ui-webapp/src/test/resources/subject.properties b/grouper-misc/webapp/src/test/resources/subject.properties
similarity index 100%
rename from grouper-misc/webapp/grouper-ui-webapp/src/test/resources/subject.properties
rename to grouper-misc/webapp/src/test/resources/subject.properties
diff --git a/grouper-parent/pom.xml b/grouper-parent/pom.xml
index b16d60d3593d..d2951eadf4bc 100644
--- a/grouper-parent/pom.xml
+++ b/grouper-parent/pom.xml
@@ -37,7 +37,8 @@
../grouper-ui
../grouper-ws
../grouper-misc/grouper-installer
- ../grouper-misc/webapp/grouper-ui-webapp
+ ../grouper-misc/webapp
+ ../grouper-misc/grouper-test-plugin
@@ -95,7 +96,7 @@
0.9.5
0.2.15
3.3.0
- 7.0.3
+ 7.0.5
2.81
1.4.20
0.1.55
@@ -195,6 +196,9 @@
2.0.8
1.4
+
+
+ 8.0.0
@@ -343,11 +347,6 @@
cglib
${cglib.version}
-
- org.apache.felix
- org.apache.felix.framework
- ${felix.version}
-
com.thoughtworks.xstream
xstream
@@ -965,6 +964,18 @@
${amqp-client.version}
+
+
+ org.osgi
+ osgi.core
+ ${osgi.version}
+
+
+ org.apache.felix
+ org.apache.felix.framework
+ ${felix.version}
+
+
diff --git a/grouper-ui/webapp/index.jsp b/grouper-ui/webapp/index.jsp
index 88a71bf4ef57..40a5a981526c 100644
--- a/grouper-ui/webapp/index.jsp
+++ b/grouper-ui/webapp/index.jsp
@@ -2,7 +2,6 @@
<%@ include file="WEB-INF/grouperUi2/assetsJsp/commonTaglib.jsp"%>
<%
- GrouperRequestContainer.retrieveFromRequestOrCreate();
String location="grouperUi/app/UiV2Main.index?operation=UiV2Main.indexMain";
%>
diff --git a/grouper/pom.xml b/grouper/pom.xml
index c88404a1a901..0776740a2a90 100644
--- a/grouper/pom.xml
+++ b/grouper/pom.xml
@@ -96,10 +96,19 @@
cglib
cglib
+
+
org.apache.felix
org.apache.felix.framework
+
+ edu.internet2.middleware.grouper.plugins
+ felix-framework-security
+ 0.0.1
+ test
+
+
com.amazonaws
aws-java-sdk-core
@@ -568,6 +577,9 @@
src/test
+
+ src/main/resources
+
conf
@@ -591,6 +603,8 @@
**/*.jexl
edu/internet2/middleware/grouper/xml/export.properties
edu/internet2/middleware/grouper/xml/import.properties
+ edu/internet2/middleware/grouper/plugins/all.policy
+ edu/internet2/middleware/grouper/plugins/org.apache.felix.framework.security-2.8.4.jar
@@ -607,6 +621,9 @@
+
+
+ plugins/**/*
diff --git a/grouper/src/grouper/edu/internet2/middleware/grouper/plugins/BundleStarter.java b/grouper/src/grouper/edu/internet2/middleware/grouper/plugins/BundleStarter.java
index ee77b9ab43e7..e20863ddc331 100644
--- a/grouper/src/grouper/edu/internet2/middleware/grouper/plugins/BundleStarter.java
+++ b/grouper/src/grouper/edu/internet2/middleware/grouper/plugins/BundleStarter.java
@@ -1,8 +1,10 @@
package edu.internet2.middleware.grouper.plugins;
+import edu.internet2.middleware.grouper.Group;
import edu.internet2.middleware.grouper.cfg.GrouperConfig;
import edu.internet2.middleware.grouper.util.GrouperUtil;
import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
@@ -18,16 +20,21 @@
public class BundleStarter {
private static final Log LOG = GrouperUtil.getLog(BundleStarter.class);
+
+ public static final String GROUPER_OSGI_EXCEPTION_ON_PLUGIN_LOAD_ERROR = "grouper.osgi.exceptionOnPluginLoadError";
private final BundleContext bundleContext;
private final String bundleDirWithLastSlash;
+ private final Map installedBundles;
+
public BundleStarter(BundleContext bundleContext) {
this.bundleContext = bundleContext;
+ this.installedBundles = new HashMap<>();
try {
Path tmpDir = Files.createTempDirectory("grouper");
- bundleDirWithLastSlash = GrouperUtil.stripLastSlashIfExists(GrouperConfig.retrieveConfig().propertyValueString("grouper.osgi.jar.dir", tmpDir.toString())) + File.separator;
+ bundleDirWithLastSlash = GrouperUtil.stripLastSlashIfExists(GrouperConfig.retrieveConfig().propertyValueString("grouper.osgi.jar.dir", tmpDir.toString())) + "/";
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -36,16 +43,48 @@ public BundleStarter(BundleContext bundleContext) {
public void start() {
Map, Set>> pluginJarNameToInterfaceToImplementationClasses = new HashMap, Set>>();
- Pattern pattern = Pattern.compile("^grouperOsgiPlugin\\.([^.]+)\\.jarName$");
+ if (!GrouperConfig.retrieveConfig().containsKey(GROUPER_OSGI_EXCEPTION_ON_PLUGIN_LOAD_ERROR)) {
+ LOG.warn("DEPRECATION WARNING: You are currently using the default behavior for error handling on plugin load (log and continue). This behavior will change in the future. If you'd want to use future behavior, set `" + GROUPER_OSGI_EXCEPTION_ON_PLUGIN_LOAD_ERROR + "=true` in `grouper.properties`");
+ }
+ boolean exceptionOnLoad = GrouperConfig.retrieveConfig().propertyValueBoolean(GROUPER_OSGI_EXCEPTION_ON_PLUGIN_LOAD_ERROR, false);
+
+ // TODO: deprecate
+ {
+ Pattern pattern = Pattern.compile("^grouperOsgiPlugin\\.([^.]+)\\.jarName$");
+ Set configIds = GrouperConfig.retrieveConfig().propertyConfigIds(pattern);
+ if (GrouperUtil.length(configIds) > 0) {
+ for (String configId: configIds) {
+ LOG.warn("DEPRECATION WARNING: you are using older configuration in the namespace `grouperOsgiPlugin`; this will be removed in future versions. Update configuration to use `grouper.osgi.plugin`");
+ String jarName = GrouperConfig.retrieveConfig().propertyValueString("grouperOsgiPlugin." + configId + ".jarName");
+ Bundle bundle = null;
+ try {
+ bundle = bundleContext.installBundle("file:" + bundleDirWithLastSlash + jarName);
+ bundle.start();
+ installedBundles.put(configId, bundle);
+ } catch (BundleException e) {
+ if (exceptionOnLoad) {
+ throw new GrouperPluginException("Problem installing plugin: " + jarName, e);
+ }
+ LOG.error("Problem installing plugin: " + jarName, e);
+ }
+ }
+ }
+ }
+
+ Pattern pattern = Pattern.compile("^grouper\\.osgi\\.plugin\\.([^.]+)\\.location$");
Set configIds = GrouperConfig.retrieveConfig().propertyConfigIds(pattern);
if (GrouperUtil.length(configIds) > 0) {
for (String configId: configIds) {
- String jarName = GrouperConfig.retrieveConfig().propertyValueString("grouperOsgiPlugin." + configId + ".jarName");
+ String location = GrouperConfig.retrieveConfig().propertyValueString("grouper.osgi.plugin." + configId + ".location");
try {
- Bundle bundle = bundleContext.installBundle("file:" + bundleDirWithLastSlash + jarName);
+ Bundle bundle = bundleContext.installBundle(location);
bundle.start();
+ installedBundles.put(configId, bundle);
} catch (BundleException e) {
- LOG.error("Problem installing plugin: " + jarName, e);
+ if (exceptionOnLoad) {
+ throw new GrouperPluginException("Problem installing plugin: " + location, e);
+ }
+ LOG.error("Problem installing plugin: " + location, e);
}
}
}
diff --git a/grouper/src/grouper/edu/internet2/middleware/grouper/plugins/FrameworkStarter.java b/grouper/src/grouper/edu/internet2/middleware/grouper/plugins/FrameworkStarter.java
index 7d5213b60d77..af940fe6c5d0 100644
--- a/grouper/src/grouper/edu/internet2/middleware/grouper/plugins/FrameworkStarter.java
+++ b/grouper/src/grouper/edu/internet2/middleware/grouper/plugins/FrameworkStarter.java
@@ -1,9 +1,11 @@
package edu.internet2.middleware.grouper.plugins;
-import edu.internet2.middleware.grouper.app.externalSystem.GrouperExternalSystem;
import edu.internet2.middleware.grouper.cfg.GrouperConfig;
import edu.internet2.middleware.grouper.cfg.GrouperHibernateConfig;
+import edu.internet2.middleware.grouper.util.GrouperUtil;
import edu.internet2.middleware.grouperClient.config.ConfigPropertiesCascadeBase;
+import org.apache.commons.io.input.ReaderInputStream;
+import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
@@ -12,10 +14,20 @@
import org.osgi.framework.launch.Framework;
import org.osgi.framework.launch.FrameworkFactory;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.Arrays;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
@@ -23,6 +35,7 @@
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
+import java.util.regex.Pattern;
/**
* Class used to start up the OSGI framework
@@ -30,8 +43,18 @@
* @author jj
*/
public class FrameworkStarter {
+ private final static Log LOG = GrouperUtil.getLog(FrameworkStarter.class);
+
private final static FrameworkStarter frameworkStarter = new FrameworkStarter();
+ // properties
+ public final static String GROUPER_OSGI_ENABLE = "grouper.osgi.enable";
+ public final static String GROUPER_OSGI_SECURITY_ENABLE = "grouper.osgi.security.enable";
+ public final static String GROUPER_OSGI_FRAMEWORK_TRUST_REPOSITORIES = "grouper.osgi.framework.trust.repositories";
+ public final static String GROUPER_OSGI_FRAMEWORK_SYSTEM_PACKAGES_EXTRA = "grouper.osgi.framework.system.packages.extra";
+ public final static String GROUPER_OSGI_CACHE_ROOTDIR = "grouper.osgi.cache.rootdir";
+ public final static String GROUPER_OSGI_FRAMEWORK_BOOT_DELEGATION = "grouper.osgi.framework.boot.delegation";
+
private Framework framework;
private FrameworkStarter(){}
@@ -41,23 +64,35 @@ public static FrameworkStarter getInstance() {
}
public void start() {
- if (!GrouperConfig.retrieveConfig().propertyValueBoolean("grouper.osgi.enable", false)) {
+ if (!GrouperConfig.retrieveConfig().propertyValueBoolean(GROUPER_OSGI_ENABLE, false)) {
return;
}
Map configMap = new HashMap<>();
+ // setup osgi security if enabled
+ if (GrouperConfig.retrieveConfig().propertyValueBoolean(GROUPER_OSGI_SECURITY_ENABLE, false)) {
+ setupSecurity(configMap);
+ }
+
configMap.put(Constants.FRAMEWORK_BUNDLE_PARENT, Constants.FRAMEWORK_BUNDLE_PARENT_FRAMEWORK);
// if it caches modules, they might not ever get reloaded
configMap.put(Constants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
- //TODO: maybe make this more dynamic. currently we're very opinionated on what we export
- configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, GrouperConfig.retrieveConfig().propertyValueString("grouper.osgi.framework.system.packages.extra","javax.servlet,javax.servlet.http"));
+ Set frameworkSystemPackagesExtra = new HashSet<>();
+ // TODO: add any needed system packages here
+ if (null != GrouperConfig.retrieveConfig().propertyValueString(GROUPER_OSGI_FRAMEWORK_SYSTEM_PACKAGES_EXTRA)) {
+ LOG.warn("You are setting a value for `grouper.osgi.framework.system.packages.extra`. This generally is not needed and should not be used unless there is a good reason to do so");
+ frameworkSystemPackagesExtra.addAll(Arrays.asList(GrouperConfig.retrieveConfig().propertyValueString(GROUPER_OSGI_FRAMEWORK_SYSTEM_PACKAGES_EXTRA, "").split(",")));
+ }
+ if (!frameworkSystemPackagesExtra.isEmpty()) {
+ configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, String.join(",", frameworkSystemPackagesExtra));
+ }
try {
// set up cachedir
Path cacheDir = Files.createTempDirectory("osgi-cache");
- String grouperOsgiCacheDir = GrouperConfig.retrieveConfig().propertyValueString("grouper.osgi.cache.rootdir", cacheDir.toString());
+ String grouperOsgiCacheDir = GrouperConfig.retrieveConfig().propertyValueString(GROUPER_OSGI_CACHE_ROOTDIR, cacheDir.toString());
configMap.put(Constants.FRAMEWORK_STORAGE, grouperOsgiCacheDir);
} catch (IOException e) {
throw new RuntimeException("problem with setting up osgi cache directory", e);
@@ -65,18 +100,18 @@ public void start() {
// usually, this is a bad idea, but we have several classes that must be loaded from the framework classpath to work,
// e.g., logging, configuration
- String packagesForBootDelegationString;
- if (null != GrouperConfig.retrieveConfig().propertyValueString("grouper.osgi.framework.boot.delegation")) {
- packagesForBootDelegationString = GrouperConfig.retrieveConfig().propertyValueString("grouper.osgi.framework.boot.delegation");
+ Set packagesForBootDelegation = new HashSet<>();
+ if (null != GrouperConfig.retrieveConfig().propertyValueString(GROUPER_OSGI_FRAMEWORK_BOOT_DELEGATION)) {
+ LOG.warn("You are setting a value for `grouper.osgi.framework.boot.delegation`. This is generally not needed adn should not be used unless there is a good reason to do so");
+ packagesForBootDelegation.addAll(Arrays.asList(GrouperConfig.retrieveConfig().propertyValueString(GROUPER_OSGI_FRAMEWORK_BOOT_DELEGATION).split(",")));
} else {
- Set packagesForBootDelegation = new HashSet<>();
- packagesForBootDelegation.add(LogFactory.class.getPackage().getName());
- packagesForBootDelegation.add(ConfigPropertiesCascadeBase.class.getPackage().getName());
- // TODO: why oh why... need to fix this
- packagesForBootDelegation.add(GrouperExternalSystem.class.getPackage().getName());
- packagesForBootDelegationString = String.join(",", packagesForBootDelegation);
+ packagesForBootDelegation.add("org.osgi.*");
+ packagesForBootDelegation.add("javax.*");
+ packagesForBootDelegation.add("org.apache.commons.logging");
+ packagesForBootDelegation.add("edu.internet2.middleware.grouper.*");
+ packagesForBootDelegation.add("edu.internet2.middleware.grouperClient.*");
}
- configMap.put(Constants.FRAMEWORK_BOOTDELEGATION, packagesForBootDelegationString);
+ configMap.put(Constants.FRAMEWORK_BOOTDELEGATION, String.join(",", packagesForBootDelegation));
try {
FrameworkFactory frameworkFactory = ServiceLoader.load(FrameworkFactory.class).iterator().next();
@@ -85,6 +120,12 @@ public void start() {
this.registerConfigurationServices();
+ // install security plugin, if needed
+ // TODO: currently, we have a custom version of the plugin that fixes a problem with allowing plugins that are signed with untrusted certificates. this should change
+ if (GrouperConfig.retrieveConfig().propertyValueBoolean(GROUPER_OSGI_SECURITY_ENABLE, false)) {
+ framework.getBundleContext().installBundle(FrameworkStarter.class.getResource("/plugins/org.apache.felix.framework.security.jar").toString()).start();
+ }
+
BundleStarter bundleStarter = new BundleStarter(framework.getBundleContext());
bundleStarter.start();
} catch (BundleException e) {
@@ -92,6 +133,43 @@ public void start() {
}
}
+ // setup up osgi security cofiguration. This includes setting up a jvm security policy and setting up a truststore
+ private static void setupSecurity(Map configMap) {
+ // TODO: this seems very dangerous, but is also going to be deprecated. look into options, though for now there should be a bit of manual trust as a buffer
+ System.setProperty("java.security.policy", FrameworkStarter.class.getResource("/plugins/all.policy").toString());
+
+ configMap.put(Constants.FRAMEWORK_SECURITY, Constants.FRAMEWORK_SECURITY_OSGI);
+
+ // setup trust repositories
+ // TODO: look into making this more dynamic. Currently, according to spec, the only required way of setting the trust stores is to set a property with java.io.File.pathSeparator (`:` on unix, `;` on windows) separated list of keystores, with each implementation having the option to providing other mechanisms
+ Pattern pattern = Pattern.compile("^grouper\\.osgi\\.truststore\\.([^.]+)\\.certificate$");
+ Set trustCertificatesAliases = GrouperConfig.retrieveConfig().propertyConfigIds(pattern);
+ if (GrouperConfig.retrieveConfig().propertyValueString(GROUPER_OSGI_FRAMEWORK_TRUST_REPOSITORIES) != null) {
+ configMap.put(Constants.FRAMEWORK_TRUST_REPOSITORIES, GrouperConfig.retrieveConfig().propertyValueString(GROUPER_OSGI_FRAMEWORK_TRUST_REPOSITORIES));
+ } else if (!trustCertificatesAliases.isEmpty()) {
+ // we have certificates defined in configuration
+ try {
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ String keyStorePassword = "changeme";
+ keyStore.load(null, keyStorePassword.toCharArray());
+
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ for (String alias: trustCertificatesAliases) {
+ Certificate certificate = certificateFactory.generateCertificate(new ReaderInputStream(new StringReader(GrouperConfig.retrieveConfig().propertyValueString("grouper.osgi.truststore." + alias + ".certificate"))));
+ keyStore.setCertificateEntry(alias, certificate);
+ }
+
+ Path keystorePath = Files.createTempFile("keystore", ".jks");
+ try (OutputStream os = new FileOutputStream(keystorePath.toFile())) {
+ keyStore.store(os, keyStorePassword.toCharArray());
+ configMap.put(Constants.FRAMEWORK_TRUST_REPOSITORIES, keystorePath.toString());
+ }
+ } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
/**
* register various Grouper services
*/
@@ -125,6 +203,13 @@ private static Dictionary buildSimpleDictionary(String key, String value) {
public Framework getFramework() {
return this.framework;
}
+
+ public synchronized void stop() throws BundleException {
+ if (this.framework != null) {
+ this.framework.stop();
+ this.framework = null;
+ }
+ }
// public void stop() {
// if (!started) {
diff --git a/grouper/src/grouper/edu/internet2/middleware/grouper/plugins/GrouperPluginException.java b/grouper/src/grouper/edu/internet2/middleware/grouper/plugins/GrouperPluginException.java
new file mode 100644
index 000000000000..17db5b6a880f
--- /dev/null
+++ b/grouper/src/grouper/edu/internet2/middleware/grouper/plugins/GrouperPluginException.java
@@ -0,0 +1,22 @@
+package edu.internet2.middleware.grouper.plugins;
+
+public class GrouperPluginException extends RuntimeException{
+ public GrouperPluginException() {
+ }
+
+ public GrouperPluginException(String message) {
+ super(message);
+ }
+
+ public GrouperPluginException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public GrouperPluginException(Throwable cause) {
+ super(cause);
+ }
+
+ public GrouperPluginException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/grouper/src/main/resources/plugins/all.policy b/grouper/src/main/resources/plugins/all.policy
new file mode 100644
index 000000000000..eb7803737747
--- /dev/null
+++ b/grouper/src/main/resources/plugins/all.policy
@@ -0,0 +1,3 @@
+grant {
+ permission java.security.AllPermission;
+};
\ No newline at end of file
diff --git a/grouper/src/main/resources/plugins/org.apache.felix.framework.security.jar b/grouper/src/main/resources/plugins/org.apache.felix.framework.security.jar
new file mode 100644
index 000000000000..67fbc256bd75
Binary files /dev/null and b/grouper/src/main/resources/plugins/org.apache.felix.framework.security.jar differ
diff --git a/grouper/src/test/edu/internet2/middleware/grouper/plugins/FrameworkStarterTests.java b/grouper/src/test/edu/internet2/middleware/grouper/plugins/FrameworkStarterTests.java
new file mode 100644
index 000000000000..a03a28bf96ec
--- /dev/null
+++ b/grouper/src/test/edu/internet2/middleware/grouper/plugins/FrameworkStarterTests.java
@@ -0,0 +1,84 @@
+package edu.internet2.middleware.grouper.plugins;
+
+import edu.internet2.middleware.grouper.cfg.GrouperConfig;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.BundleException;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+public class FrameworkStarterTests {
+ @Before
+ public void setup() {
+ GrouperConfig.retrieveConfig().propertiesOverrideMap().clear();
+
+ // common properties
+ GrouperConfig.retrieveConfig().propertiesOverrideMap().put(FrameworkStarter.GROUPER_OSGI_ENABLE, "true");
+ GrouperConfig.retrieveConfig().propertiesOverrideMap().put(FrameworkStarter.GROUPER_OSGI_SECURITY_ENABLE, "true");
+ GrouperConfig.retrieveConfig().propertiesOverrideMap().put(BundleStarter.GROUPER_OSGI_EXCEPTION_ON_PLUGIN_LOAD_ERROR, "true");
+ }
+
+ @After
+ public void tearDown() throws BundleException {
+ FrameworkStarter.getInstance().stop();
+ }
+
+ @Test
+ public void testSecurityBasic() {
+ GrouperConfig.retrieveConfig().propertiesOverrideMap().put(FrameworkStarter.GROUPER_OSGI_FRAMEWORK_TRUST_REPOSITORIES, FrameworkStarterTests.class.getResource("/plugins/test.jks").getPath());
+ GrouperConfig.retrieveConfig().propertiesOverrideMap().put("grouper.osgi.plugin.test.location", FrameworkStarterTests.class.getResource("/plugins/grouper-test-plugin.jar").toString());
+
+ FrameworkStarter.getInstance().start();
+ }
+
+ @Test
+ public void testSecurityPropertyTruststore() {
+ GrouperConfig.retrieveConfig().propertiesOverrideMap().put("grouper.osgi.plugin.test.location", FrameworkStarterTests.class.getResource("/plugins/grouper-test-plugin.jar").toString());
+ GrouperConfig.retrieveConfig().propertiesOverrideMap().put("grouper.osgi.truststore.signing.certificate", """
+ -----BEGIN CERTIFICATE-----
+ MIIDczCCAlugAwIBAgIILehkAIY5xvQwDQYJKoZIhvcNAQELBQAwaDEQMA4GA1UE
+ BhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQ
+ MA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEMMAoGA1UEAwwDSmoh
+ MB4XDTIzMTAxMjAwMjE1OFoXDTMzMTAxMTAwMjE1OFowaDEQMA4GA1UEBhMHVW5r
+ bm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UE
+ ChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEMMAoGA1UEAwwDSmohMIIBIjAN
+ BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9Y8HkztTbh1gXW+lIB175CYiqR5U
+ qPVaCe9efvLd89zzyKpO1wxdp7lbJwwHfepuFhsGPMl38aD6YXeQDRKj3m7u2m94
+ I1PAiAYslRSN/3oWyv7RQwP3gn4XAegpR4kBCNJoXLTPK80oyYA62okVNtCH/dFq
+ Z3k3A1O+OoeqcDBq5KzuPatViFAjEyyLCcV1g0mLQlCfzrWFw5OVp2U6q2D0qLKF
+ 5i2bFkIlwxcWY0tfvv0S8Lp7+JpPN/etXGByKigM4Cu9CnfLBUs0v9h4wH5wv/Zj
+ /XGfViD+pemIgkR82BqPM31TeWU0ONOYapS0LS4RpZBOcLtXcwgBMIch2QIDAQAB
+ oyEwHzAdBgNVHQ4EFgQU5cbJ7MBtBDej6XYd3iukheyNO5kwDQYJKoZIhvcNAQEL
+ BQADggEBAMjtgjBespxleA5Po49A3kng/A1n24m0SuGDVRmtWm5OGi34e+A64GIo
+ kgiux+r0HOSDHUaIfao1L84FIxIBGGSHW/hcZ8RJr1qSYvW7KnD9PfgAcghtokKx
+ 38fx3Q3RUDB6Gcz2vigTkJ7OzR1wkyzVvfnLEOL3kA1P49Eb/wyMKEmmLDQyij6n
+ JG+T6+M4+QKiA8W9yIIAAjJFoNL0LBczsdVn6o34buwqLPhjhVoGazDbax5cXxkJ
+ 8TRdktqQNbBqDk+6mOto8MCJUtg7d+Run+HvtgjNsdYLI6J+6K9+51STLtXST90V
+ voa3jXptx9Tl/BFoJK2sJWdoIwG3zug=
+ -----END CERTIFICATE-----""");
+
+ FrameworkStarter.getInstance().start();
+ }
+
+ @Test
+ public void testSecurityUntrustedSigner() {
+ GrouperConfig.retrieveConfig().propertiesOverrideMap().put(FrameworkStarter.GROUPER_OSGI_FRAMEWORK_TRUST_REPOSITORIES, FrameworkStarterTests.class.getResource("/plugins/test-empty.jks").getPath());
+ GrouperConfig.retrieveConfig().propertiesOverrideMap().put("grouper.osgi.plugin.test.location", FrameworkStarterTests.class.getResource("/plugins/grouper-test-plugin.jar").toString());
+
+ Exception e = assertThrows(Exception.class, () -> FrameworkStarter.getInstance().start());
+ assert(e.getCause().getCause() instanceof IOException);
+ }
+
+ @Test
+ public void testSecurityCorruptJar() {
+ GrouperConfig.retrieveConfig().propertiesOverrideMap().put(FrameworkStarter.GROUPER_OSGI_FRAMEWORK_TRUST_REPOSITORIES, FrameworkStarterTests.class.getResource("/plugins/test.jks").getPath());
+ GrouperConfig.retrieveConfig().propertiesOverrideMap().put("grouper.osgi.plugin.test.location", FrameworkStarterTests.class.getResource("/plugins/grouper-test-plugin-corrupt.jar").toString());
+
+ Exception e = assertThrows(GrouperPluginException.class, () -> FrameworkStarter.getInstance().start());
+ assertTrue(e.getCause().getCause() instanceof IOException);
+ }
+}
diff --git a/grouper/src/test/plugins/grouper-test-plugin-corrupt.jar b/grouper/src/test/plugins/grouper-test-plugin-corrupt.jar
new file mode 100644
index 000000000000..8d465efd7f9a
Binary files /dev/null and b/grouper/src/test/plugins/grouper-test-plugin-corrupt.jar differ
diff --git a/grouper/src/test/plugins/grouper-test-plugin.jar b/grouper/src/test/plugins/grouper-test-plugin.jar
new file mode 100644
index 000000000000..70a5a9c4ea5b
Binary files /dev/null and b/grouper/src/test/plugins/grouper-test-plugin.jar differ
diff --git a/grouper/src/test/plugins/test-empty.jks b/grouper/src/test/plugins/test-empty.jks
new file mode 100644
index 000000000000..7a094b403274
Binary files /dev/null and b/grouper/src/test/plugins/test-empty.jks differ
diff --git a/grouper/src/test/plugins/test.jks b/grouper/src/test/plugins/test.jks
new file mode 100644
index 000000000000..d6cc98835617
Binary files /dev/null and b/grouper/src/test/plugins/test.jks differ