[java-identity-provider] 03/11: IDP-1499 New V4 Installer: Handle all user input
Rod Widdowson
rdw at steadingsoftware.com
Fri Oct 11 11:08:26 EDT 2019
This is an automated email from the git hooks/post-receive script.
rdw pushed a commit to branch master
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=f357b25070ee71e5ed03d1105e2670969a0ec809
commit f357b25070ee71e5ed03d1105e2670969a0ec809
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Sun Oct 6 11:37:41 2019 +0100
IDP-1499 New V4 Installer: Handle all user input
https://issues.shibboleth.net/jira/browse/IDP-1499
---
.../idp/installer/impl/InstallerProperties.java | 267 ++++++++++++++++++++-
1 file changed, 259 insertions(+), 8 deletions(-)
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/InstallerProperties.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/InstallerProperties.java
index 64d0ff1..8cacea3 100644
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/InstallerProperties.java
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/InstallerProperties.java
@@ -20,9 +20,14 @@ package net.shibboleth.idp.installer.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Collections;
import java.util.Properties;
+import java.util.function.Supplier;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -33,6 +38,7 @@ import org.apache.tools.ant.input.InputRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import net.shibboleth.idp.installer.ant.impl.PasswordHandler;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@@ -78,18 +84,36 @@ public class InstallerProperties extends AbstractInitializableComponent {
/** The base directory, inherited and shared with ant. */
public static final String ANT_BASE_DIR = "ant.home";
- /** The name of a property file to fill in some or all of the above. This file is deleted after processing. */
+ /** The name of a property file to fill in some or all of the above. This file is deleted after processing. */
public static final String PROPERTY_SOURCE_FILE = "idp.property.file";
- /** where to install to. Default is basedir */
+ /** Where to install to. Default is basedir */
public static final String TARGET_DIR = "idp.target.dir";
- /** where to install from (installs only). */
+ /** Where to install from (installs only). */
public static final String SOURCE_DIR = "idp.src.dir";
+ /** The entity ID. */
+ public static final String ENTITY_ID = "idp.entityID";
+
/** Do we cause a failure rather than a prompt. */
public static final String NO_PROMPT = "idp.noprompt";
+ /** What is the installer host name? */
+ public static final String HOST_NAME = "idp.host.name";
+
+ /** The scope to assert. */
+ public static final String SCOPE = "idp.scope";
+
+ /** The keystore password to use. */
+ public static final String KEY_STORE_PASSWORD = "idp.keystore.password";
+
+ /** The sealer password to use. */
+ public static final String SEALER_PASSWORD = "idp.sealer.password";
+
+ /** The sealer alias to use. */
+ public static final String SEALER_ALIAS = "idp.sealer.alias";
+
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(InstallerProperties.class);
@@ -114,6 +138,29 @@ public class InstallerProperties extends AbstractInitializableComponent {
/** Is a source Dir needed? */
private final boolean needSourceDir;
+ /** The entity ID. */
+ private String entityID;
+
+ /** Old Version. */
+ private String oldVersion;
+ /** Have done the work aroun the old version ?*/
+ private boolean oldVersionDefinitive;
+
+ /** Hostname. */
+ private String hostname;
+
+ /** scope. */
+ private String scope;
+
+ /** Keystore Password. */
+ private String keyStorePassword;
+
+ /** Sealer Password. */
+ private String sealerPassword;
+
+ /** Sealer Alias. */
+ private String sealerAlias;
+
/**
* Constructor.
* @param populateDone is this a war build (or a windows installer)? If no we don't need the source dir.
@@ -162,7 +209,8 @@ public class InstallerProperties extends AbstractInitializableComponent {
noPrompt = value != null;
if (needSourceDir) {
- value = getValue(SOURCE_DIR, "Source (Distribution) Directory (press <enter> to accept default):", antBase);
+ value = getValue(SOURCE_DIR, "Source (Distribution) Directory (press <enter> to accept default):",
+ () -> antBase);
srcDir = Path.of(value);
log.debug("Source directory {}", srcDir.toAbsolutePath());
}
@@ -171,12 +219,12 @@ public class InstallerProperties extends AbstractInitializableComponent {
/** Lookup a property. If it isn't defined then ask the user (if we are allowed)
* @param propertyName the property to lookup.
* @param prompt what to say to the user
- * @param defaultValue the default value
+ * @param defaultSupplier how to get the default value
* @throws BuildException of anything goes wrong
* @return the value
*/
protected String getValue(final String propertyName,
- final String prompt, final String defaultValue) throws BuildException {
+ final String prompt, final Supplier<String> defaultSupplier) throws BuildException {
String value = installerProperties.getProperty(propertyName);
if (value != null) {
return value;
@@ -185,7 +233,8 @@ public class InstallerProperties extends AbstractInitializableComponent {
throw new BuildException("No value for " + propertyName + " specified");
}
- final InputRequest request = new InputRequest("Installation Directory:");
+ final InputRequest request = new InputRequest(prompt);
+ final String defaultValue = defaultSupplier.get();
request.setDefaultValue(defaultValue);
new DefaultInputHandler().handleInput(request);
@@ -196,6 +245,28 @@ public class InstallerProperties extends AbstractInitializableComponent {
return value;
}
+ /** Lookup a property. If it isn't defined then ask the user (if we are allowed)
+ * @param propertyName the property to lookup.
+ * @param prompt what to say to the user
+ * @throws BuildException of anything goes wrong
+ * @return the value. this is not repeated to the screen
+ */
+ protected String getPassword(final String propertyName, final String prompt) throws BuildException {
+ final String value = installerProperties.getProperty(propertyName);
+ if (value != null) {
+ return value;
+ }
+ if (noPrompt) {
+ throw new BuildException("No value for " + propertyName + " specified");
+ }
+
+ final InputRequest request = new InputRequest(prompt);
+
+ new PasswordHandler().handleInput(request);
+ return request.getInput();
+ }
+
+
/** Get where we are installing/updating/building the war.
* @return the target directory
* @throws BuildException if something goes awry.
@@ -213,7 +284,7 @@ public class InstallerProperties extends AbstractInitializableComponent {
// build-war or Windows so "here" is also "where"
defTarget = baseDir.toAbsolutePath().toString();
}
- final String targetValue = getValue(TARGET_DIR, "Installation Directory", defTarget);
+ final String targetValue = getValue(TARGET_DIR, "Installation Directory:", () -> defTarget);
targetDir = Path.of(targetValue);
return targetDir;
}
@@ -224,4 +295,184 @@ public class InstallerProperties extends AbstractInitializableComponent {
@Nullable public Path getSourceDir() {
return srcDir;
}
+
+ /** What is the installer version.
+ * @return "3" for a V3 install, null for a new install or the value we write during last install.
+ */
+ @Nullable public String getInstallerVersion() {
+ if (oldVersionDefinitive) {
+ return oldVersion;
+ }
+ oldVersionDefinitive = true;
+ final Path conf = getTargetDir().resolve("conf");
+ if (!Files.exists(conf.resolve("relying-party.xml"))) {
+ // No relying party, no install
+ oldVersion = null;
+ return null;
+ }
+ if (!Files.exists(conf.resolve("idp.properties"))) {
+ throw new BuildException("V2 Installation detected");
+ }
+
+ final Path currentInstall = getTargetDir().resolve("dist").resolve(InstallerSupport.VERSION_NAME);
+ if (!Files.exists(currentInstall)) {
+ oldVersion= "3";
+ return oldVersion;
+ }
+ final Properties vers = new Properties(1);
+ try {
+ vers.load(new FileInputStream(currentInstall.toFile()));
+ } catch (final IOException e) {
+ log.error("Could not load {}", currentInstall.toAbsolutePath(), e);
+ throw new BuildException(e);
+ }
+ oldVersion = vers.getProperty(InstallerSupport.VERSION_NAME);
+ if (null == oldVersion) {
+ throw new BuildException("File " + InstallerSupport.VERSION_NAME +
+ " did not contain property " + InstallerSupport.VERSION_NAME);
+ }
+ return oldVersion;
+ }
+
+ /** Get the host name for this install. Defaults to information pulled from the network.
+ * @return the host name.*/
+ @Nonnull public String getEntityID() {
+ if (entityID == null) {
+ entityID = getValue(ENTITY_ID, "SAML EntityID:", () -> "https://" + getHostName() + "/idp/shibboleth");
+ }
+ return entityID;
+ }
+
+
+ /** Is this address named? Helper method for {@link #bestHostName()}
+ * @return true unless the name is the canonical name...
+ * @param addr what to look at
+ */
+ private boolean hasHostName(final InetAddress addr) {
+ return !addr.getHostAddress().equals(addr.getCanonicalHostName());
+ }
+
+ /** Find the most aposite network connector. Taken from Ant.
+ * @throws SocketException
+ * @return the best name we can work out
+ */
+ // CheckStyle: CyclomaticComplexity OFF
+ private String bestHostName() {
+ InetAddress bestSoFar = null;
+ try {
+ for (final NetworkInterface netInterface : Collections.list(NetworkInterface.getNetworkInterfaces())) {
+ for (final InetAddress address : Collections.list(netInterface.getInetAddresses())) {
+ if (bestSoFar == null) {
+ // none selected so far, so this one is better.
+ bestSoFar = address;
+ } else if (address == null || address.isLoopbackAddress()) {
+ // definitely not better than the previously selected address.
+ } else if (address.isLinkLocalAddress()) {
+ // link local considered better than loopback
+ if (bestSoFar.isLoopbackAddress()) {
+ bestSoFar = address;
+ }
+ } else if (address.isSiteLocalAddress()) {
+ // site local considered better than link local (and loopback)
+ // address with hostname resolved considered better than
+ // address without hostname
+ if (bestSoFar.isLoopbackAddress()
+ || bestSoFar.isLinkLocalAddress()
+ || (bestSoFar.isSiteLocalAddress() && !hasHostName(bestSoFar))) {
+ bestSoFar = address;
+ }
+ } else {
+ // current is a "Global address", considered better than
+ // site local (and better than link local, loopback)
+ // address with hostname resolved considered better than
+ // address without hostname
+ if (bestSoFar.isLoopbackAddress()
+ || bestSoFar.isLinkLocalAddress()
+ || bestSoFar.isSiteLocalAddress()
+ || !hasHostName(bestSoFar)) {
+ bestSoFar = address;
+ }
+ }
+ }
+ }
+ } catch (final SocketException e) {
+ log.error("Could not get host information", e);
+ }
+ if (bestSoFar == null) {
+ return "localhost.localdomain";
+ }
+ return bestSoFar.getCanonicalHostName();
+ }
+ // CheckStyle: CyclomaticComplexity ON
+
+ /** Get the host name for this install. Defaults to information pulled from the network.
+ * @return the host name.*/
+ @Nonnull public String getHostName() {
+ if (hostname == null) {
+ hostname = getValue(HOST_NAME, "Host Name:", () -> bestHostName());
+ }
+ return hostname;
+ }
+
+ /** Evaluate the default scope value.
+ * @return everything after the firsty '.' in the hostname
+ */
+ @Nonnull private String defaultScope() {
+ final String host = getHostName();
+ final int index = host.indexOf('.');
+ if (index > 1) {
+ return host.substring(index+1);
+ }
+ return "localdomain";
+ }
+
+ /** Get the scope for this installation.
+ * @return The scope.
+ */
+ @Nonnull public String getScope() {
+ if (scope == null) {
+ scope = getValue(SCOPE, "Attribute Scope:", () -> defaultScope());
+ }
+ return scope;
+ }
+
+ /** Get the SubjectAltName for the certificates.
+ * @return the SubjectAltName
+ */
+ @Nonnull public String getSubjectAltName() {
+ return "https://" + getHostName() + "/idp/shibboleth";
+ }
+
+ /** Get the password for the keystore for this installation.
+ * @return the password.
+ */
+ @Nonnull public String getKeyStorePassword() {
+ if (keyStorePassword == null) {
+ keyStorePassword = getPassword(KEY_STORE_PASSWORD, "Backchannel PKCS12 Password:");
+ }
+ return keyStorePassword;
+ }
+
+ /** Get the password for the sealer for this installation.
+ * @return the password.
+ */
+ @Nonnull public String getSealerPassword() {
+ if (sealerPassword == null) {
+ sealerPassword = getPassword(SEALER_PASSWORD, "Cookie Encryption Key Password:");
+ }
+ return sealerPassword;
+ }
+
+ /** Get the alias for the sealer key.
+ * @return the alias
+ */
+ @Nonnull public String getSealerAlias() {
+ if (sealerAlias == null) {
+ sealerAlias = installerProperties.getProperty(SEALER_ALIAS);
+ }
+ if (sealerAlias == null) {
+ sealerAlias = "secret";
+ }
+ return sealerAlias;
+ }
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list