[java-identity-provider] branch main updated: IDP-1664 - Support Module service API

Scott Cantor cantor.2 at osu.edu
Wed Sep 2 15:42:18 UTC 2020


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch main
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=ef9b69d817dc10fa712625e84d119326e650bcd0

The following commit(s) were added to refs/heads/main by this push:
       new  ef9b69d81 IDP-1664 - Support Module service API
ef9b69d81 is described below

commit ef9b69d817dc10fa712625e84d119326e650bcd0
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Sep 2 11:41:53 2020 -0400

    IDP-1664 - Support Module service API
    
    https://issues.shibboleth.net/jira/browse/IDP-1664
    
    Redo file handling code and fix some bugs.
    First batch of unit tests.
---
 .../shibboleth/idp/module/AbstractIdPModule.java   |  66 ++++----
 .../java/net/shibboleth/idp/module/IdPModule.java  |  28 ----
 .../net/shibboleth/idp/module/ModuleContext.java   |  98 +++++++++++
 .../idp/module/PropertyDrivenIdPModule.java        |   3 +-
 .../net/shibboleth/idp/module/IdPModuleTest.java   | 181 ++++++++++++++++++---
 idp-admin-api/src/test/resources/logback-test.xml  |   2 +-
 .../net/shibboleth/idp/module/module.properties    |   9 +-
 .../resources/net/shibboleth/idp/module/test.vm    |   1 +
 .../resources/net/shibboleth/idp/module/test.xml   |   1 +
 9 files changed, 303 insertions(+), 86 deletions(-)

diff --git a/idp-admin-api/src/main/java/net/shibboleth/idp/module/AbstractIdPModule.java b/idp-admin-api/src/main/java/net/shibboleth/idp/module/AbstractIdPModule.java
index 9a68ab59e..2883d073a 100644
--- a/idp-admin-api/src/main/java/net/shibboleth/idp/module/AbstractIdPModule.java
+++ b/idp-admin-api/src/main/java/net/shibboleth/idp/module/AbstractIdPModule.java
@@ -19,14 +19,16 @@ package net.shibboleth.idp.module;
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
 import java.security.DigestOutputStream;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -191,7 +193,7 @@ public abstract class AbstractIdPModule implements IdPModule {
                             try (final OutputStream srcSink = OutputStream.nullOutputStream();
                                     final DigestOutputStream srcDigest = new DigestOutputStream(srcSink, digest)) {
                                 src.transferTo(srcDigest);
-                                return digest.digest().equals(destHash);
+                                return !Arrays.equals(destHash, digest.digest());
                             }
                         }
                         log.debug("Module {} resource {} does not exist at source", getId(), source);
@@ -241,9 +243,10 @@ public abstract class AbstractIdPModule implements IdPModule {
         @Nullable private InputStream getDestinationStream(@Nonnull final ModuleContext moduleContext)
                 throws IOException {
             final File destFile = moduleContext.getIdPHome().resolve(destination).toFile();
-            if (destFile.exists()) {
+            if (destFile.exists() && destFile.isFile() && destFile.canRead()) {
                 return new FileInputStream(destFile);
             }
+            
             return null;
         }
 
@@ -264,33 +267,33 @@ public abstract class AbstractIdPModule implements IdPModule {
                     throw new IOException("Source stream was null");
                 }
 
-                
-                final File destFile;
+                final Path destPath;
                 
                 if (hasChanged) {
                     if (isReplace()) {
-                        final File renamedFile = moduleContext.getIdPHome().resolve(destination).toFile();
-                        if (renamedFile.renameTo(new File(renamedFile.getPath() + ".idpsave"))) {
-                            log.info("Module {} preserved {}", getId(), renamedFile);
-                        } else {
-                            throw new ModuleException("Unable to rename " + renamedFile);
-                        }
-                        destFile = moduleContext.getIdPHome().resolve(destination).toFile();
+                        destPath = moduleContext.getIdPHome().resolve(destination);
+                        Files.copy(destPath, destPath.resolveSibling(destPath.getFileName() + ".idpsave"),
+                                StandardCopyOption.REPLACE_EXISTING);
+                        log.info("Module {} preserved {}", getId(), destPath);
                     } else {
-                        destFile = new File(moduleContext.getIdPHome().resolve(destination).toString() + ".idpnew");
+                        final Path basePath = moduleContext.getIdPHome().resolve(destination);
+                        destPath = basePath.resolveSibling(basePath.getFileName() + ".idpnew");
                     }
                     
                 } else {
-                    destFile = moduleContext.getIdPHome().resolve(destination).toFile();
+                    destPath = moduleContext.getIdPHome().resolve(destination);
                 }
-
-                try (final OutputStream destStream = new FileOutputStream(destFile)) {
-                    srcStream.transferTo(destStream);
-                    log.info("Module {} created {}", getId(), destFile);
+                
+                if (!destPath.startsWith(moduleContext.getIdPHome())) {
+                    log.error("Module {} attempted to create file outside of IdP installation: {}", getId(), destPath);
+                    throw new ModuleException("Module asked to create file outside of IdP installation");
                 }
 
+                Files.copy(srcStream, destPath, StandardCopyOption.REPLACE_EXISTING);
+                log.info("Module {} created {}", getId(), destPath);
+                
             } catch (final IOException e) {
-                log.error("Module {} unable to enable resource {}", getId(), source, e);
+                log.error("Module {} unable to enable resource {}", getId(), source);
                 throw new ModuleException(e);
             }
         }
@@ -307,21 +310,22 @@ public abstract class AbstractIdPModule implements IdPModule {
             
             final Path resolved = moduleContext.getIdPHome().resolve(destination);
             log.debug("Module {} resolved resource destination {}", getId(), resolved);
-            final File file = resolved.toFile();
-            if (file.exists()) {
-                if (clean) {
-                    log.info("Module {} removing resource {}", getId(), file);
-                    if (!file.delete()) {
-                        throw new ModuleException("Unable to remove resource " + file);
-                    }
-                } else {
-                    log.info("Module {} moving aside resource {}", getId(), file);
-                    if (!file.renameTo(new File(file.toString() + ".idpsave"))) {
-                        throw new ModuleException("Unable to rename resource " + file);
+            if (Files.exists(resolved)) {
+                try {
+                    if (clean) {
+                        log.info("Module {} removing resource {}", getId(), resolved);
+                        Files.delete(resolved);
+                    } else {
+                        log.info("Module {} backing up resource {}", getId(), resolved);
+                        Files.move(resolved, resolved.resolveSibling(resolved.getFileName() + ".idpsave"),
+                                StandardCopyOption.REPLACE_EXISTING);
                     }
+                } catch (final IOException e) {
+                    log.error("Module {} failed to disable {}", getId(), resolved);
+                    throw new ModuleException(e);
                 }
             } else {
-                log.info("Module {} resource {} missing, ignoring", getId(), file);
+                log.info("Module {} resource {} missing, ignoring", getId(), resolved);
             }
         }
         
diff --git a/idp-admin-api/src/main/java/net/shibboleth/idp/module/IdPModule.java b/idp-admin-api/src/main/java/net/shibboleth/idp/module/IdPModule.java
index b89dbc3c7..4ef10f257 100644
--- a/idp-admin-api/src/main/java/net/shibboleth/idp/module/IdPModule.java
+++ b/idp-admin-api/src/main/java/net/shibboleth/idp/module/IdPModule.java
@@ -25,7 +25,6 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import org.apache.http.client.HttpClient;
-import org.opensaml.security.httpclient.HttpClientSecurityParameters;
 
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
@@ -109,33 +108,6 @@ public interface IdPModule extends IdentifiedComponent {
      * @throws ModuleException if not successful 
      */
     void disable(@Nonnull final ModuleContext moduleContext, final boolean clean) throws ModuleException;
-
-    /**
-     * Interface to information required to perform some module operations.
-     */
-    interface ModuleContext {
-
-        /**
-         * Gets software installation location.
-         * 
-         * @return install path
-         */
-        @Nonnull @NotEmpty Path getIdPHome();
-        
-        /**
-         * Gets an {@link HttpClient} instance to use if available.
-         * 
-         * @return HTTP client instance
-         */
-        @Nullable HttpClient getHttpClient();
-
-        /**
-         * Gets {@link HttpClient} security parameters, if any.
-         * 
-         * @return HTTP client security parameters to use
-         */
-        @Nullable HttpClientSecurityParameters getHttpClientSecurityParameters();
-    }
     
     /**
      * Interface to a resource managed by the module.
diff --git a/idp-admin-api/src/main/java/net/shibboleth/idp/module/ModuleContext.java b/idp-admin-api/src/main/java/net/shibboleth/idp/module/ModuleContext.java
new file mode 100644
index 000000000..69c031a17
--- /dev/null
+++ b/idp-admin-api/src/main/java/net/shibboleth/idp/module/ModuleContext.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.
+ */
+
+package net.shibboleth.idp.module;
+
+import java.nio.file.Path;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.http.client.HttpClient;
+import org.opensaml.security.httpclient.HttpClientSecurityParameters;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Information required to perform some module operations.
+ */
+public final class ModuleContext {
+
+    /** IdP installation root. */
+    @Nonnull private Path idpHome;
+    
+    /** HttpClient if needed. */
+    @Nullable private HttpClient httpClient;
+    
+    /** HTTP security parameters. */
+    @Nullable private HttpClientSecurityParameters httpClientSecurityParams;
+    
+    /**
+     * Constructor.
+     *
+     * @param home location of IdP install
+     */
+    public ModuleContext(@Nonnull final Path home) {
+        idpHome = Constraint.isNotNull(home, "IdP home path cannot be null");
+    }
+    
+    /**
+     * Gets software installation location.
+     * 
+     * @return install path
+     */
+    @Nonnull Path getIdPHome() {
+        return idpHome;
+    }
+    
+    /**
+     * Gets an {@link HttpClient} instance to use if available.
+     * 
+     * @return HTTP client instance
+     */
+    @Nullable HttpClient getHttpClient() {
+        return httpClient;
+    }
+    
+    /**
+     * Sets an {@link HttpClient} instance to use.
+     * 
+     * @param client client to use
+     */
+    public void setHttpClient(@Nullable final HttpClient client) {
+        httpClient = client;
+    }
+
+    /**
+     * Gets {@link HttpClient} security parameters, if any.
+     * 
+     * @return HTTP client security parameters to use
+     */
+    @Nullable HttpClientSecurityParameters getHttpClientSecurityParameters() {
+        return httpClientSecurityParams;
+    }
+
+    /**
+     * Sets {@link HttpClient} security parameters to use.
+     * 
+     * @param params security parameters
+     */
+    public void setHttpClientSecurityParameters(@Nullable final HttpClientSecurityParameters params) {
+        httpClientSecurityParams = params;
+    }
+
+}
\ No newline at end of file
diff --git a/idp-admin-api/src/main/java/net/shibboleth/idp/module/PropertyDrivenIdPModule.java b/idp-admin-api/src/main/java/net/shibboleth/idp/module/PropertyDrivenIdPModule.java
index 79e76f070..4fd2fd17c 100644
--- a/idp-admin-api/src/main/java/net/shibboleth/idp/module/PropertyDrivenIdPModule.java
+++ b/idp-admin-api/src/main/java/net/shibboleth/idp/module/PropertyDrivenIdPModule.java
@@ -148,8 +148,7 @@ public class PropertyDrivenIdPModule extends AbstractIdPModule {
                         moduleProperties.getProperty(getId() + renumstr + MODULE_REPLACE_PROPERTY, "false"));
                 
                 final Path destPath = Path.of(dest);
-                if (dest.contains("..") || destPath.isAbsolute() || destPath.startsWith("/") ||
-                        destPath.startsWith("\\")) {
+                if (dest.contains("..") || destPath.isAbsolute() || destPath.startsWith("/")) {
                     throw new ModuleException("Module contained a suspect resource destination");
                 }
                 
diff --git a/idp-admin-api/src/test/java/net/shibboleth/idp/module/IdPModuleTest.java b/idp-admin-api/src/test/java/net/shibboleth/idp/module/IdPModuleTest.java
index b267e4393..dbdffd0b7 100644
--- a/idp-admin-api/src/test/java/net/shibboleth/idp/module/IdPModuleTest.java
+++ b/idp-admin-api/src/test/java/net/shibboleth/idp/module/IdPModuleTest.java
@@ -16,7 +16,13 @@ package net.shibboleth.idp.module;
  * limitations under the License.
  */
 
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
 import java.util.Iterator;
 import java.util.Optional;
 import java.util.ServiceConfigurationError;
@@ -24,6 +30,8 @@ import java.util.ServiceLoader;
 import java.util.ServiceLoader.Provider;
 
 import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import net.shibboleth.idp.module.IdPModule.ModuleResource;
@@ -33,33 +41,55 @@ import net.shibboleth.idp.module.IdPModule.ModuleResource;
  */
 public class IdPModuleTest {
 
-    @Test
-    public void testModule() {
-        
+    private static final String XML_DATA = "<test>foo</test>\n";
+    private static final String XML_OTHER_DATA = "<test>bar</test>\n";
+    private static final String VEL_DATA = "## something\n";
+    private static final String VEL_OTHER_DATA = "## something else\n";
+    
+    private Path testHome;
+    private IdPModule testModule;
+    private ModuleContext context;
+    
+    @BeforeMethod
+    public void setUp() throws IOException {
         final ServiceLoader<IdPModule> loader = ServiceLoader.load(IdPModule.class);
         final Optional<Provider<IdPModule>> opt =
                 loader.stream().filter(p -> TestModule.class.equals(p.type())).findFirst();
-        
         Assert.assertTrue(opt.isPresent());
         
-        final IdPModule module = opt.get().get();
+        testModule = opt.get().get();
         
-        Assert.assertEquals(module.getId(), TestModule.class.getName());
-        Assert.assertEquals(module.getName(), "Test module");
-        Assert.assertEquals(module.getURL().toString(), "https://wiki.shibboleth.net/confluence/display/IDP4/Home");
-        
-        final Iterator<ModuleResource> resources = module.getResources().iterator();
-        Assert.assertEquals(module.getResources().size(), 2);
-        
-        ModuleResource resource = resources.next();
-        Assert.assertEquals(resource.getSource(), "net/shibboleth/idp/module/test.xml");
-        Assert.assertEquals(resource.getDestination(), Path.of("conf/test.xml"));
-        
-        resource = resources.next();
-        Assert.assertEquals(resource.getSource(), "net/shibboleth/idp/module/test.vm");
-        Assert.assertEquals(resource.getDestination(), Path.of("views/test.vm"));
+        testHome = Files.createTempDirectory("test-idp-home-");
+        context = new ModuleContext(testHome);
     }
-
+    
+    @AfterMethod
+    public void tearDown() throws IOException {
+        if (testHome != null) {
+            Files.walkFileTree(testHome, new SimpleFileVisitor<Path>() {
+                @Override
+                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+                    throws IOException
+                {
+                    Files.delete(file);
+                    return FileVisitResult.CONTINUE;
+                }
+                @Override
+                public FileVisitResult postVisitDirectory(Path dir, IOException e)
+                    throws IOException
+                {
+                    if (e == null) {
+                        Files.delete(dir);
+                        return FileVisitResult.CONTINUE;
+                    }
+                    // directory iteration failed
+                    throw e;
+                }
+            });
+            testHome = null;
+        }
+    }
+    
     @Test
     public void testBadModules() {
         
@@ -84,5 +114,116 @@ public class IdPModuleTest {
             Assert.assertTrue(e.getCause() instanceof ModuleException);
         }
     }
+    
+    @Test
+    public void testModule() {
+        
+        Assert.assertEquals(testModule.getId(), TestModule.class.getName());
+        Assert.assertEquals(testModule.getName(), "Test module");
+        Assert.assertEquals(testModule.getURL().toString(), "https://wiki.shibboleth.net/confluence/display/IDP4/Home");
+        
+        final Iterator<ModuleResource> resources = testModule.getResources().iterator();
+        Assert.assertEquals(testModule.getResources().size(), 2);
+        
+        ModuleResource resource = resources.next();
+        Assert.assertEquals(resource.getSource(), "/net/shibboleth/idp/module/test.xml");
+        Assert.assertEquals(resource.getDestination(), Path.of("conf/test.xml"));
+        
+        resource = resources.next();
+        Assert.assertEquals(resource.getSource(), "/net/shibboleth/idp/module/test.vm");
+        Assert.assertEquals(resource.getDestination(), Path.of("views/test.vm"));
+    }
+
+    @Test(expectedExceptions=ModuleException.class)
+    public void testEnableNoTree() throws ModuleException {
+        testModule.enable(context);
+    }
+
+    @Test
+    public void testDisableNoTree() throws ModuleException {
+        testModule.disable(context, true);
+        testModule.disable(context, false);
+    }
 
+    @Test
+    public void testEnableClean() throws ModuleException, IOException {
+        
+        Files.createDirectory(testHome.resolve("conf"));
+        Files.createDirectory(testHome.resolve("views"));
+        
+        testModule.enable(context);
+        
+        String xml = Files.readString(testHome.resolve("conf/test.xml"));
+        Assert.assertEquals(xml, XML_DATA);
+        
+        String vel = Files.readString(testHome.resolve("views/test.vm"));
+        Assert.assertEquals(vel, VEL_DATA);
+        
+        testModule.disable(context, true);
+        Assert.assertEquals(testHome.resolve("conf").toFile().listFiles().length, 0);
+        Assert.assertEquals(testHome.resolve("views").toFile().listFiles().length, 0);
+        
+        testModule.enable(context);
+        testModule.disable(context, false);
+        xml = Files.readString(testHome.resolve("conf/test.xml.idpsave"));
+        Assert.assertEquals(xml, XML_DATA);
+        
+        vel = Files.readString(testHome.resolve("views/test.vm.idpsave"));
+        Assert.assertEquals(vel, VEL_DATA);
+    }
+
+    @Test
+    public void testEnableExistingSame() throws IOException, ModuleException {
+        Files.createDirectory(testHome.resolve("conf"));
+        Files.createDirectory(testHome.resolve("views"));
+        
+        try (final OutputStream os =
+                Files.newOutputStream(Files.createFile(testHome.resolve("conf/test.xml")))) {
+            os.write(XML_DATA.getBytes());
+        }
+
+        try (final OutputStream os =
+                Files.newOutputStream(Files.createFile(testHome.resolve("views/test.vm")))) {
+            os.write(VEL_DATA.getBytes());
+        }
+
+        testModule.enable(context);
+
+        String xml = Files.readString(testHome.resolve("conf/test.xml"));
+        Assert.assertEquals(xml, XML_DATA);
+        Assert.assertFalse(testHome.resolve("conf/test.xml.idpsave").toFile().exists());
+        
+        String vel = Files.readString(testHome.resolve("views/test.vm"));
+        Assert.assertEquals(vel, VEL_DATA);
+        Assert.assertFalse(testHome.resolve("views/test.vm.idpnew").toFile().exists());
+    }
+
+    @Test
+    public void testEnableExistingDifferent() throws IOException, ModuleException {
+        Files.createDirectory(testHome.resolve("conf"));
+        Files.createDirectory(testHome.resolve("views"));
+        
+        try (final OutputStream os =
+                Files.newOutputStream(Files.createFile(testHome.resolve("conf/test.xml")))) {
+            os.write(XML_OTHER_DATA.getBytes());
+        }
+
+        try (final OutputStream os =
+                Files.newOutputStream(Files.createFile(testHome.resolve("views/test.vm")))) {
+            os.write(VEL_OTHER_DATA.getBytes());
+        }
+
+        testModule.enable(context);
+
+        String xml = Files.readString(testHome.resolve("conf/test.xml"));
+        Assert.assertEquals(xml, XML_DATA);
+        xml = Files.readString(testHome.resolve("conf/test.xml.idpsave"));
+        Assert.assertEquals(xml, XML_OTHER_DATA);
+        
+        String vel = Files.readString(testHome.resolve("views/test.vm"));
+        Assert.assertEquals(vel, VEL_OTHER_DATA);
+        vel = Files.readString(testHome.resolve("views/test.vm.idpnew"));
+        Assert.assertEquals(vel, VEL_DATA);
+    }
+    
 }
\ No newline at end of file
diff --git a/idp-admin-api/src/test/resources/logback-test.xml b/idp-admin-api/src/test/resources/logback-test.xml
index f3280e56b..8f891b483 100644
--- a/idp-admin-api/src/test/resources/logback-test.xml
+++ b/idp-admin-api/src/test/resources/logback-test.xml
@@ -10,7 +10,7 @@
     </appender>
 
     <root>
-        <level value="warn" />
+        <level value="DEBUG" />
         <appender-ref ref="STDOUT" />
     </root>
     
diff --git a/idp-admin-api/src/test/resources/net/shibboleth/idp/module/module.properties b/idp-admin-api/src/test/resources/net/shibboleth/idp/module/module.properties
index 50a9c3209..ad2b75892 100644
--- a/idp-admin-api/src/test/resources/net/shibboleth/idp/module/module.properties
+++ b/idp-admin-api/src/test/resources/net/shibboleth/idp/module/module.properties
@@ -6,20 +6,21 @@ net.shibboleth.idp.module.TestModule.name = Test module
 net.shibboleth.idp.module.TestModule.desc = Module for unit tests
 net.shibboleth.idp.module.TestModule.url = https://wiki.shibboleth.net/confluence/display/IDP4/Home
 
-net.shibboleth.idp.module.TestModule.1.src = net/shibboleth/idp/module/test.xml
+net.shibboleth.idp.module.TestModule.1.src = /net/shibboleth/idp/module/test.xml
 net.shibboleth.idp.module.TestModule.1.dest = conf/test.xml
+net.shibboleth.idp.module.TestModule.1.replace = true
 
-net.shibboleth.idp.module.TestModule.2.src = net/shibboleth/idp/module/test.vm
+net.shibboleth.idp.module.TestModule.2.src = /net/shibboleth/idp/module/test.vm
 net.shibboleth.idp.module.TestModule.2.dest = views/test.vm
 
 # Broken modules due to dangerous resources
 
 net.shibboleth.idp.module.BadModule.name = Bad module
 net.shibboleth.idp.module.BadModule.desc = Module for unit tests with error
-net.shibboleth.idp.module.BadModule.1.src = net/shibboleth/idp/module/test.xml
+net.shibboleth.idp.module.BadModule.1.src = /net/shibboleth/idp/module/test.xml
 net.shibboleth.idp.module.BadModule.1.dest = ../conf/test.xml
 
 net.shibboleth.idp.module.BadModule2.name = Bad module 2
 net.shibboleth.idp.module.BadModule2.desc = Module for unit tests with error
-net.shibboleth.idp.module.BadModule2.1.src = net/shibboleth/idp/module/test.xml
+net.shibboleth.idp.module.BadModule2.1.src = /net/shibboleth/idp/module/test.xml
 net.shibboleth.idp.module.BadModule2.1.dest = /conf/test.xml
diff --git a/idp-admin-api/src/test/resources/net/shibboleth/idp/module/test.vm b/idp-admin-api/src/test/resources/net/shibboleth/idp/module/test.vm
new file mode 100644
index 000000000..239c69ea1
--- /dev/null
+++ b/idp-admin-api/src/test/resources/net/shibboleth/idp/module/test.vm
@@ -0,0 +1 @@
+## something
diff --git a/idp-admin-api/src/test/resources/net/shibboleth/idp/module/test.xml b/idp-admin-api/src/test/resources/net/shibboleth/idp/module/test.xml
new file mode 100644
index 000000000..a544c98ec
--- /dev/null
+++ b/idp-admin-api/src/test/resources/net/shibboleth/idp/module/test.xml
@@ -0,0 +1 @@
+<test>foo</test>

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list