[java-shib-profile] 02/03: IDP-2121 Future Proofing the Module Plugin infrastructure for Future SP use

Rod Widdowson rdw at steadingsoftware.com
Fri Jun 9 09:35:29 UTC 2023


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

rdw pushed a commit to branch main
in repository java-shib-profile.

View the commit online:
http://git.shibboleth.net/view/?p=java-shib-profile.git;a=commit;h=97ea210aeb927e1d026ad53deaca193b2899893a

commit 97ea210aeb927e1d026ad53deaca193b2899893a
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Thu Jun 8 19:32:33 2023 +0100

    IDP-2121 Future Proofing the Module Plugin infrastructure for Future SP use
    
    https://shibboleth.atlassian.net/browse/IDP-2121
    
    Push the Property driven Module down to shib-profile
---
 .../profile/module/PropertyDrivenModule.java       | 356 +++++++++++++++++++++
 1 file changed, 356 insertions(+)

diff --git a/shib-profile-api/src/main/java/net/shibboleth/profile/module/PropertyDrivenModule.java b/shib-profile-api/src/main/java/net/shibboleth/profile/module/PropertyDrivenModule.java
new file mode 100644
index 0000000..0396759
--- /dev/null
+++ b/shib-profile-api/src/main/java/net/shibboleth/profile/module/PropertyDrivenModule.java
@@ -0,0 +1,356 @@
+/*
+ * 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.profile.module;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+
+import com.google.common.base.Strings;
+
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.ConstraintViolationException;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
+
+/**
+ * Implementation of {@link Module} relying on Java {@link Properties}.
+ * 
+ * @since 9.0.0
+ */
+public abstract class PropertyDrivenModule extends AbstractModule implements Module {
+
+    /** Default name of module properties resource. */
+    @Nonnull @NotEmpty public static final String DEFAULT_RESOURCE = "module.properties";
+
+    /** Suffix of property for module name. */
+    @Nonnull @NotEmpty public static final String MODULE_NAME_PROPERTY = ".name";
+
+    /** Suffix of property for module description. */
+    @Nonnull @NotEmpty public static final String MODULE_DESC_PROPERTY = ".desc";
+
+    /** Suffix of property for module URL. */
+    @Nonnull @NotEmpty public static final String MODULE_URL_PROPERTY = ".url";
+
+    /** Suffix of property for module plugin owner. */
+    @Nonnull @NotEmpty public static final String MODULE_PLUGIN_PROPERTY = ".plugin";
+
+    /** Suffix of property for resource source. */
+    @Nonnull @NotEmpty public static final String MODULE_SRC_PROPERTY = ".src";
+
+    /** Suffix of property for resource destination. */
+    @Nonnull @NotEmpty public static final String MODULE_DEST_PROPERTY = ".dest";
+
+    /** Suffix of property for resource replacement. */
+    @Nonnull @NotEmpty public static final String MODULE_REPLACE_PROPERTY = ".replace";
+
+    /** Suffix of property for resource optionality. */
+    @Nonnull @NotEmpty public static final String MODULE_OPTIONAL_PROPERTY = ".optional";
+
+    /** Suffix of property for resource executability. */
+    @Nonnull @NotEmpty public static final String MODULE_EXEC_PROPERTY = ".exec";
+
+    /** Suffix of property for resource Windows applicability. */
+    @Nonnull @NotEmpty public static final String MODULE_WINDOWS_PROPERTY = ".windows";
+
+    /** Suffix of property for resource non-Windows applicability. */
+    @Nonnull @NotEmpty public static final String MODULE_NONWINDOWS_PROPERTY = ".nonwindows";
+
+    /** Suffix of property for module post-enable message. */
+    @Nonnull @NotEmpty public static final String MODULE_POSTENABLE_PROPERTY = ".postenable";
+
+    /** Suffix of property for module post-disable message. */
+    @Nonnull @NotEmpty public static final String MODULE_POSTDISABLE_PROPERTY = ".postdisable";
+
+    /** Suffix of property for module languages. */
+    @Nonnull @NotEmpty public static final String MODULE_LANGS_PROPERTY = ".langs";
+
+    /** Class logger. */
+    @Nonnull private Logger log = LoggerFactory.getLogger(PropertyDrivenModule.class);
+
+    /** Properties describing module. */
+    @Nonnull private final Properties moduleProperties;
+
+    /** Module ID. */
+    @Nonnull @NotEmpty private String moduleId;
+
+    /** Module name. */
+    @Nonnull @NotEmpty private String moduleName;
+
+    /** Module URL. */
+    @Nullable @NotEmpty private String moduleURL;
+    
+    /** Plugin ID. */
+    @Nullable @NotEmpty private String pluginId;
+    
+    /** Available message locales. */
+    @Nonnull @NonnullElements private List<String> locales;
+    
+    /** Whether to require an HTTP client. */
+    private boolean requireHttpClient;
+
+    /**
+     * Constructor.
+     *
+     * @param claz type of object used to locate default module.properties resource
+     * @param version version of the containing application (or null if none available)
+     * 
+     * @throws IOException if unable to read file
+     * @throws ModuleException if the module is not in a valid state
+     */
+    public PropertyDrivenModule(@Nullable String version, @Nonnull final Class<? extends Module> claz) throws IOException, ModuleException {
+        this(version, claz.getResourceAsStream(DEFAULT_RESOURCE));
+    }
+    
+    /**
+     * Constructor.
+     *
+     * @param inputStream property stream
+     * @param version version of the containing application (or null if none available)
+     * 
+     * @throws IOException if unable to read file
+     * @throws ModuleException if the module is not in a valid state
+     */
+    public PropertyDrivenModule(@Nullable String version, @Nonnull final InputStream inputStream)
+            throws IOException, ModuleException {
+        super(version);
+        locales = CollectionSupport.emptyList();
+        moduleProperties = new Properties();
+        moduleProperties.load(inputStream);
+        moduleId = "";
+        moduleName = "";
+        load();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param properties property set
+     * @param version version of the containing application (or null if none available)
+     * 
+     * @throws ModuleException if the module is not in a valid state
+     */
+    public PropertyDrivenModule(@Nullable String version, @Nonnull final Properties properties) throws ModuleException {
+        super(version);
+        locales = CollectionSupport.emptyList();
+        moduleProperties = Constraint.isNotNull(properties, "Properties cannot be null");
+        moduleId = "";
+        moduleName = "";
+        load();
+    }
+
+// Checkstyle: CyclomaticComplexity OFF
+    /**
+     * Load module information from properties.
+     * 
+     * @throws ModuleException on errors
+     */
+    protected void load() throws ModuleException {
+        try {
+            moduleId = Constraint.isNotNull(
+                    StringSupport.trimOrNull(moduleProperties.getProperty(getClass().getName())),
+                    "Module ID missing from properties");
+            
+            moduleName = Constraint.isNotNull(
+                    StringSupport.trimOrNull(moduleProperties.getProperty(getId() + MODULE_NAME_PROPERTY)),
+                    "Module name missing from properties");
+            moduleURL = StringSupport.trimOrNull(moduleProperties.getProperty(getId() + MODULE_URL_PROPERTY));
+            pluginId = StringSupport.trimOrNull(moduleProperties.getProperty(getId() + MODULE_PLUGIN_PROPERTY));
+            
+            locales = StringSupport.stringToList(
+                    moduleProperties.getProperty(getId() + MODULE_LANGS_PROPERTY, ""), ", ");
+            
+            final Collection<BasicModuleResource> resources = new ArrayList<>();
+            
+            for (Integer rnum = 1; ; ++rnum) {
+                
+                final String renumstr = "." + rnum.toString();
+                
+                final String src = moduleProperties.getProperty(getId() + renumstr + MODULE_SRC_PROPERTY);
+                final String dest = moduleProperties.getProperty(getId() + renumstr + MODULE_DEST_PROPERTY);
+                if (src == null || dest == null) {
+                    break;
+                }
+                
+                final Boolean replace = Boolean.valueOf(
+                        moduleProperties.getProperty(getId() + renumstr + MODULE_REPLACE_PROPERTY, "false"));
+                
+                final Boolean optional = Boolean.valueOf(
+                        moduleProperties.getProperty(getId() + renumstr + MODULE_OPTIONAL_PROPERTY, "false"));
+                
+                final Boolean exec = Boolean.valueOf(
+                        moduleProperties.getProperty(getId() + renumstr + MODULE_EXEC_PROPERTY, "false"));
+
+                final Boolean windows = Boolean.valueOf(
+                        moduleProperties.getProperty(getId() + renumstr + MODULE_WINDOWS_PROPERTY, "true"));
+
+                final Boolean nonwindows = Boolean.valueOf(
+                        moduleProperties.getProperty(getId() + renumstr + MODULE_NONWINDOWS_PROPERTY, "true"));
+
+                final Path destPath = Path.of(dest);
+                if (dest.contains("..") || destPath.isAbsolute() || destPath.startsWith("/")) {
+                    throw new ModuleException("Module contained a suspect resource destination");
+                }
+                
+                if (!requireHttpClient) {
+                    requireHttpClient = src.startsWith("https://") || src.startsWith("http://");
+                }
+                
+                resources.add(new BasicModuleResource(src, destPath, replace, optional, exec, windows, nonwindows));
+            }
+            
+            setResources(resources);
+            
+            log.debug("Module {} loaded", getId());
+            resources.forEach(
+                    r -> log.debug("Module {}: Resource {} -> {} ({})",
+                            getId(), r.getSource(), r.getDestination(), r.isReplace() ? "replace" : "noreplace"));
+        } catch (final ConstraintViolationException e) {
+            throw new ModuleException(e);
+        }
+    }
+// Checkstyle: CyclomaticComplexity ON
+    
+    /** {@inheritDoc} */
+    @Nonnull @NotEmpty public String getId() {
+        return moduleId;
+    }
+
+    /** {@inheritDoc} */
+    @Nonnull @NotEmpty public String getName(@Nullable final ModuleContext moduleContext) {
+        
+        if (moduleContext != null) {
+            final String best = Locale.lookupTag(moduleContext.getLanguageRanges(), locales);
+            if (best != null && !best.equals(locales.get(0))) {
+                return moduleProperties.getProperty(getId() + MODULE_NAME_PROPERTY + "." + best, moduleName);
+            }
+        }
+        return moduleName;
+    }
+    
+    /** {@inheritDoc} */
+    @Nullable @NotEmpty public String getDescription(@Nullable final ModuleContext moduleContext) {
+
+        if (moduleContext != null) {
+            final String best = Locale.lookupTag(moduleContext.getLanguageRanges(), locales);
+            if (best != null && !best.equals(locales.get(0))) {
+                final String desc = moduleProperties.getProperty(getId() + MODULE_DESC_PROPERTY + "." + best);
+                if (!Strings.isNullOrEmpty(desc)) {
+                    return desc;
+                }
+            }
+        }
+        
+        return StringSupport.trimOrNull(moduleProperties.getProperty(getId() + MODULE_DESC_PROPERTY));
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @NotEmpty public String getURL() {
+        return moduleURL;
+    }
+    
+    /**
+     * Set the module URL.
+     * 
+     * @param url URL to set
+     */
+    public void setURL(@Nullable @NotEmpty final String url) {
+        moduleURL = StringSupport.trimOrNull(url);
+    }
+    
+    /** {@inheritDoc} */
+    @Nullable @NotEmpty public String getOwnerId() {
+        return pluginId;
+    }
+    
+    /** {@inheritDoc} */
+    public boolean isHttpClientRequired() {
+        return requireHttpClient;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull @NonnullElements public Map<ModuleResource,ResourceResult> enable(
+            @Nonnull final ModuleContext moduleContext) throws ModuleException {
+        final Map<ModuleResource,ResourceResult> results = super.enable(moduleContext);
+        
+        final PrintStream msgStream = moduleContext.getMessageStream();
+        if (msgStream != null) {
+            
+            String msg = null;
+            
+            final String best = Locale.lookupTag(moduleContext.getLanguageRanges(), locales);
+            if (best != null && !best.equals(locales.get(0))) {
+                msg = moduleProperties.getProperty(getId() + MODULE_POSTENABLE_PROPERTY + "." + best);
+            }
+            
+            if (msg == null) {
+                msg = moduleProperties.getProperty(getId() + MODULE_POSTENABLE_PROPERTY);
+            }
+            
+            if (msg != null) {
+                msgStream.println(msg);
+            }
+        }
+        
+        return results;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull @NonnullElements public Map<ModuleResource,ResourceResult> disable(
+            @Nonnull final ModuleContext moduleContext, final boolean clean) throws ModuleException {
+        final Map<ModuleResource,ResourceResult> results = super.disable(moduleContext, clean);
+
+        final PrintStream msgStream = moduleContext.getMessageStream();
+        if (msgStream != null) {
+            
+            String msg = null;
+            
+            final String best = Locale.lookupTag(moduleContext.getLanguageRanges(), locales);
+            if (best != null && !best.equals(locales.get(0))) {
+                msg = moduleProperties.getProperty(getId() + MODULE_POSTDISABLE_PROPERTY + "." + best);
+            }
+            
+            if (msg == null) {
+                msg = moduleProperties.getProperty(getId() + MODULE_POSTDISABLE_PROPERTY);
+            }
+
+            if (msg != null) {
+                msgStream.println(msg);
+            }
+        }
+        
+        return results;
+    }
+}
\ No newline at end of file

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


More information about the commits mailing list