From 1a25a48bbaad4015eb3ebed57332d6c34be764f0 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 00:07:39 +0200 Subject: [PATCH 01/17] Updates --- pom.xml | 9 ++++---- .../sec/dispatcher/DefaultSecDispatcher.java | 21 ++++------------- .../sec/dispatcher/PasswordDecryptor.java | 2 +- .../dispatcher/SecDispatcherException.java | 7 +----- .../components/sec/dispatcher/SecUtil.java | 2 +- src/main/mdo/settings-security.mdo | 23 ++++++++++--------- .../sec/dispatcher/SecUtilTest.java | 17 +++++++------- 7 files changed, 33 insertions(+), 48 deletions(-) diff --git a/pom.xml b/pom.xml index 3d02fa4..88cd2f7 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ plexus-sec-dispatcher - 2.1.0-SNAPSHOT + 3.0.0-SNAPSHOT Plexus Security Dispatcher Component @@ -45,12 +45,12 @@ org.codehaus.plexus plexus-utils - 4.0.1 + 4.0.2 org.codehaus.plexus plexus-cipher - 2.0 + 3.0.0-SNAPSHOT @@ -76,7 +76,7 @@ modello-maven-plugin 2.4.0 - 1.0.0 + 3.0.0 src/main/mdo/settings-security.mdo @@ -86,6 +86,7 @@ standard java + xsd xpp3-reader xpp3-writer diff --git a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/DefaultSecDispatcher.java b/src/main/java/org/sonatype/plexus/components/sec/dispatcher/DefaultSecDispatcher.java index 2421787..32eebc4 100644 --- a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/DefaultSecDispatcher.java +++ b/src/main/java/org/sonatype/plexus/components/sec/dispatcher/DefaultSecDispatcher.java @@ -86,11 +86,7 @@ public String decrypt(String str) throws SecDispatcherException { try { bare = _cipher.unDecorate(str); - } catch (PlexusCipherException e1) { - throw new SecDispatcherException(e1); - } - try { Map attr = stripAttributes(bare); String res; @@ -114,22 +110,20 @@ public String decrypt(String str) throws SecDispatcherException { if (dispatcher == null) throw new SecDispatcherException("no dispatcher for hint " + type); - String pass = attr == null ? bare : strip(bare); + String pass = strip(bare); return dispatcher.decrypt(pass, attr, conf); } return res; - } catch (Exception e) { - throw new SecDispatcherException(e); + } catch (PlexusCipherException e) { + throw new SecDispatcherException(e.getMessage(), e); } } private String strip(String str) { int pos = str.indexOf(ATTR_STOP); - if (pos == str.length()) return null; - if (pos != -1) return str.substring(pos + 1); return str; @@ -143,7 +137,7 @@ private Map stripAttributes(String str) { String attrs = str.substring(start + 1, stop).trim(); - if (attrs.length() < 1) return null; + if (attrs.isEmpty()) return null; Map res = null; @@ -160,11 +154,6 @@ private Map stripAttributes(String str) { String key = pair.substring(0, pos).trim(); - if (pos == pair.length()) { - res.put(key, null); - continue; - } - String val = pair.substring(pos + 1); res.put(key, val.trim()); @@ -210,7 +199,7 @@ private String getMaster(SettingsSecurity sec) throws SecDispatcherException { try { return _cipher.decryptDecorated(master, SYSTEM_PROPERTY_SEC_LOCATION); } catch (PlexusCipherException e) { - throw new SecDispatcherException(e); + throw new SecDispatcherException(e.getMessage(), e); } } // --------------------------------------------------------------- diff --git a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/PasswordDecryptor.java b/src/main/java/org/sonatype/plexus/components/sec/dispatcher/PasswordDecryptor.java index 2d6a434..5b3bc33 100644 --- a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/PasswordDecryptor.java +++ b/src/main/java/org/sonatype/plexus/components/sec/dispatcher/PasswordDecryptor.java @@ -33,5 +33,5 @@ public interface PasswordDecryptor { * * @throws SecDispatcherException */ - String decrypt(String str, Map attributes, Map config) throws SecDispatcherException; + String decrypt(String str, Map attributes, Map config) throws SecDispatcherException; } diff --git a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecDispatcherException.java b/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecDispatcherException.java index c7b8ad1..03471c1 100644 --- a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecDispatcherException.java +++ b/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecDispatcherException.java @@ -13,15 +13,10 @@ package org.sonatype.plexus.components.sec.dispatcher; -public class SecDispatcherException extends Exception { +public class SecDispatcherException extends RuntimeException { public SecDispatcherException(String message) { super(message); } - - public SecDispatcherException(Throwable cause) { - super(cause); - } - public SecDispatcherException(String message, Throwable cause) { super(message, cause); } diff --git a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecUtil.java b/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecUtil.java index ccd32da..761956b 100644 --- a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecUtil.java +++ b/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecUtil.java @@ -55,7 +55,7 @@ public static SettingsSecurity read(String location, boolean cycle) throws SecDi return sec; } catch (Exception e) { - throw new SecDispatcherException(e); + throw new SecDispatcherException(e.getMessage(), e); } } // --------------------------------------------------------------------------------------------------------------- diff --git a/src/main/mdo/settings-security.mdo b/src/main/mdo/settings-security.mdo index 436af7e..e5d4751 100644 --- a/src/main/mdo/settings-security.mdo +++ b/src/main/mdo/settings-security.mdo @@ -13,7 +13,8 @@ */ --> - + settings-security SecurityConfiguration @@ -30,26 +31,26 @@ SettingsSecurity - 1.0.0 + 1.0.0+ master - 1.0.0 + 1.0.0+ String encrypted master password relocation - 1.0.0 + 1.0.0+ String reference to the location of the security file configurations - 1.0.0 + 1.0.0+ named configurations Config @@ -62,7 +63,7 @@ Config - 1.0.0 + 1.0.0+ Named configuration @@ -70,13 +71,13 @@ name String true - 1.0.0 + 1.0.0+ name of this configuration properties - 1.0.0 + 1.0.0+ properties ConfigProperty @@ -89,7 +90,7 @@ ConfigProperty - 1.0.0 + 1.0.0+ generic property - name/value pair @@ -98,7 +99,7 @@ name String true - 1.0.0 + 1.0.0+ name of this property @@ -106,7 +107,7 @@ value String true - 1.0.0 + 1.0.0+ value of this property diff --git a/src/test/java/org/sonatype/plexus/components/sec/dispatcher/SecUtilTest.java b/src/test/java/org/sonatype/plexus/components/sec/dispatcher/SecUtilTest.java index be9dc58..293310b 100644 --- a/src/test/java/org/sonatype/plexus/components/sec/dispatcher/SecUtilTest.java +++ b/src/test/java/org/sonatype/plexus/components/sec/dispatcher/SecUtilTest.java @@ -35,11 +35,10 @@ * */ public class SecUtilTest { - String _pw = "{1wQaa6S/o8MH7FnaTNL53XmhT5O0SEGXQi3gC49o6OY=}"; - - String _clear = "testtest"; - - String _encrypted = "{BteqUEnqHecHM7MZfnj9FwLcYbdInWxou1C929Txa0A=}"; + String masterPassword = "masterPw"; + String masterPasswordEncrypted = "{zKIp99JSqcMP383jVX4zaomd8/gXhXxY0k1ZTKgIY81yzWesjdM0SZPnlI9fEJYp}"; + String password = "somePassword"; + String passwordEncrypted = "{Gfsw+RhB7REL9DE4+T73MdNRbF8zHW4Dt3YbhooZVVJVa70IjqGFz9hXOs7AH1Hi}"; String _confName = "cname"; @@ -60,7 +59,7 @@ public void prepare() throws Exception { new SecurityConfigurationXpp3Writer().write(new FileWriter("./target/sec.xml"), sec); sec.setRelocation(null); - sec.setMaster(_pw); + sec.setMaster(masterPasswordEncrypted); ConfigProperty cp = new ConfigProperty(); cp.setName(_propName); @@ -81,7 +80,7 @@ void testRead() throws Exception { assertNotNull(sec); - assertEquals(_pw, sec.getMaster()); + assertEquals(masterPasswordEncrypted, sec.getMaster()); Map conf = SecUtil.getConfig(sec, _confName); @@ -96,10 +95,10 @@ void testRead() throws Exception { void testDecrypt() throws Exception { DefaultSecDispatcher sd = new DefaultSecDispatcher(new DefaultPlexusCipher()); - String pass = sd.decrypt(_encrypted); + String pass = sd.decrypt(masterPasswordEncrypted); assertNotNull(pass); - assertEquals(_clear, pass); + assertEquals(masterPassword, pass); } } From bd232481a522fd7b37632a7dfd2ea4cf667ed5f8 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 02:27:50 +0200 Subject: [PATCH 02/17] Some more updates --- .github/workflows/maven.yml | 2 + pom.xml | 20 +- .../secdispatcher}/SecDispatcher.java | 6 +- .../SecDispatcherException.java | 3 +- .../internal}/DefaultSecDispatcher.java | 101 +++++----- .../internal/MasterPasswordSource.java | 33 ++++ .../internal}/PasswordDecryptor.java | 7 +- .../secdispatcher/internal}/SecUtil.java | 11 +- .../decryptor/StaticPasswordDecryptor.java | 35 ++++ .../sources/EnvMasterPasswordSource.java | 48 +++++ .../sources/GpgAgentMasterPasswordSource.java | 120 ++++++++++++ .../sources/MatchingMasterPasswordSource.java | 40 ++++ .../MemoizingMasterPasswordSource.java | 37 ++++ .../sources/StaticMasterPasswordSource.java | 34 ++++ .../SystemPropertyMasterPasswordSource.java | 48 +++++ src/main/mdo/settings-security.mdo | 11 +- .../secdispatcher/internal/SecUtilTest.java | 181 ++++++++++++++++++ .../sec/dispatcher/SecUtilTest.java | 104 ---------- 18 files changed, 666 insertions(+), 175 deletions(-) rename src/main/java/org/{sonatype/plexus/components/sec/dispatcher => codehaus/plexus/components/secdispatcher}/SecDispatcher.java (77%) rename src/main/java/org/{sonatype/plexus/components/sec/dispatcher => codehaus/plexus/components/secdispatcher}/SecDispatcherException.java (94%) rename src/main/java/org/{sonatype/plexus/components/sec/dispatcher => codehaus/plexus/components/secdispatcher/internal}/DefaultSecDispatcher.java (71%) create mode 100644 src/main/java/org/codehaus/plexus/components/secdispatcher/internal/MasterPasswordSource.java rename src/main/java/org/{sonatype/plexus/components/sec/dispatcher => codehaus/plexus/components/secdispatcher/internal}/PasswordDecryptor.java (84%) rename src/main/java/org/{sonatype/plexus/components/sec/dispatcher => codehaus/plexus/components/secdispatcher/internal}/SecUtil.java (88%) create mode 100644 src/main/java/org/codehaus/plexus/components/secdispatcher/internal/decryptor/StaticPasswordDecryptor.java create mode 100644 src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java create mode 100644 src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/GpgAgentMasterPasswordSource.java create mode 100644 src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MatchingMasterPasswordSource.java create mode 100644 src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MemoizingMasterPasswordSource.java create mode 100644 src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/StaticMasterPasswordSource.java create mode 100644 src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java create mode 100644 src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java delete mode 100644 src/test/java/org/sonatype/plexus/components/sec/dispatcher/SecUtilTest.java diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 09feae4..571e41b 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -23,6 +23,8 @@ jobs: build: name: Build it uses: codehaus-plexus/.github/.github/workflows/maven.yml@master + with: + jdk-matrix: '[ "23", "21", "17" ]' deploy: name: Deploy diff --git a/pom.xml b/pom.xml index 88cd2f7..9c2c31e 100644 --- a/pom.xml +++ b/pom.xml @@ -33,10 +33,17 @@ + 17 2023-05-22T22:22:22Z + + org.codehaus.plexus + plexus-cipher + 3.0.0-SNAPSHOT + + org.codehaus.plexus plexus-xml @@ -47,17 +54,20 @@ plexus-utils 4.0.2 - - org.codehaus.plexus - plexus-cipher - 3.0.0-SNAPSHOT - javax.inject javax.inject 1 + provided + + org.eclipse.sisu + org.eclipse.sisu.inject + ${sisuMavenPluginVersion} + provided + + org.junit.jupiter junit-jupiter diff --git a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java similarity index 77% rename from src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecDispatcher.java rename to src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java index 1557a69..eb873ad 100644 --- a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java @@ -11,7 +11,7 @@ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. */ -package org.sonatype.plexus.components.sec.dispatcher; +package org.codehaus.plexus.components.secdispatcher; /** * This component decrypts a string, passed to it @@ -19,10 +19,6 @@ * @author Oleg Gusakov */ public interface SecDispatcher { - String[] SYSTEM_PROPERTY_MASTER_PASSWORD = new String[] {"settings.master.password", "settings-master-password"}; - - String[] SYSTEM_PROPERTY_SERVER_PASSWORD = new String[] {"settings.server.password", "settings-server-password"}; - /** * decrypt given encrypted string * diff --git a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecDispatcherException.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcherException.java similarity index 94% rename from src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecDispatcherException.java rename to src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcherException.java index 03471c1..a83c70e 100644 --- a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecDispatcherException.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcherException.java @@ -11,12 +11,13 @@ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. */ -package org.sonatype.plexus.components.sec.dispatcher; +package org.codehaus.plexus.components.secdispatcher; public class SecDispatcherException extends RuntimeException { public SecDispatcherException(String message) { super(message); } + public SecDispatcherException(String message, Throwable cause) { super(message, cause); } diff --git a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/DefaultSecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java similarity index 71% rename from src/main/java/org/sonatype/plexus/components/sec/dispatcher/DefaultSecDispatcher.java rename to src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java index 32eebc4..80f8121 100644 --- a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/DefaultSecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java @@ -11,7 +11,7 @@ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. */ -package org.sonatype.plexus.components.sec.dispatcher; +package org.codehaus.plexus.components.secdispatcher.internal; import javax.inject.Inject; import javax.inject.Named; @@ -19,14 +19,21 @@ import java.io.BufferedReader; import java.io.InputStreamReader; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; -import org.sonatype.plexus.components.cipher.DefaultPlexusCipher; -import org.sonatype.plexus.components.cipher.PlexusCipher; -import org.sonatype.plexus.components.cipher.PlexusCipherException; -import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity; +import org.codehaus.plexus.components.cipher.PlexusCipher; +import org.codehaus.plexus.components.cipher.PlexusCipherException; +import org.codehaus.plexus.components.cipher.internal.DefaultPlexusCipher; +import org.codehaus.plexus.components.secdispatcher.SecDispatcher; +import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; +import org.codehaus.plexus.components.secdispatcher.model.SettingsSecurity; + +import static java.util.Objects.requireNonNull; /** * @author Oleg Gusakov @@ -34,46 +41,29 @@ @Singleton @Named public class DefaultSecDispatcher implements SecDispatcher { - private static final String DEFAULT_CONFIGURATION = "~/.settings-security.xml"; + public static final String DEFAULT_CONFIGURATION = "~/.m2/settings-security.xml"; public static final String SYSTEM_PROPERTY_SEC_LOCATION = "settings.security"; public static final String TYPE_ATTR = "type"; - public static final char ATTR_START = '['; - public static final char ATTR_STOP = ']'; - /** - * DefaultHandler - */ - protected final PlexusCipher _cipher; - - /** - * All available dispatchers - */ - protected final Map _decryptors; - - /** - * Configuration file - */ - protected String _configurationFile; + protected final PlexusCipher cipher; + protected final Map masterPasswordSources; + protected final Map decryptors; + protected String configurationFile; @Inject public DefaultSecDispatcher( - final PlexusCipher _cipher, - final Map _decryptors, - @Named("${_configurationFile:-" + DEFAULT_CONFIGURATION + "}") final String _configurationFile) { - this._cipher = _cipher; - this._decryptors = _decryptors; - this._configurationFile = _configurationFile; - } - - /** - * Ctor to be used in tests and other simplified cases (no decryptors and config). - */ - public DefaultSecDispatcher(final PlexusCipher _cipher) { - this(_cipher, new HashMap<>(), DEFAULT_CONFIGURATION); + PlexusCipher cipher, + Map masterPasswordSources, + Map decryptors, + @Named("${configurationFile:-" + DEFAULT_CONFIGURATION + "}") final String configurationFile) { + this.cipher = cipher; + this.masterPasswordSources = masterPasswordSources; + this.decryptors = decryptors; + this.configurationFile = configurationFile; } // --------------------------------------------------------------- @@ -85,7 +75,7 @@ public String decrypt(String str) throws SecDispatcherException { String bare; try { - bare = _cipher.unDecorate(str); + bare = cipher.unDecorate(str); Map attr = stripAttributes(bare); @@ -96,17 +86,17 @@ public String decrypt(String str) throws SecDispatcherException { if (attr == null || attr.get("type") == null) { String master = getMaster(sec); - res = _cipher.decrypt(bare, master); + res = cipher.decrypt(bare, master); } else { String type = attr.get(TYPE_ATTR); - if (_decryptors == null) + if (decryptors == null) throw new SecDispatcherException( "plexus container did not supply any required dispatchers - cannot lookup " + type); Map conf = SecUtil.getConfig(sec, type); - PasswordDecryptor dispatcher = _decryptors.get(type); + PasswordDecryptor dispatcher = decryptors.get(type); if (dispatcher == null) throw new SecDispatcherException("no dispatcher for hint " + type); @@ -141,7 +131,7 @@ private Map stripAttributes(String str) { Map res = null; - StringTokenizer st = new StringTokenizer(attrs, ", "); + StringTokenizer st = new StringTokenizer(attrs, ","); while (st.hasMoreTokens()) { if (res == null) res = new HashMap<>(st.countTokens()); @@ -170,7 +160,7 @@ private Map stripAttributes(String str) { private boolean isEncryptedString(String str) { if (str == null) return false; - return _cipher.isEncryptedString(str); + return cipher.isEncryptedString(str); } // ---------------------------------------------------------------------------- @@ -192,23 +182,25 @@ private SettingsSecurity getSec() throws SecDispatcherException { // ---------------------------------------------------------------------------- private String getMaster(SettingsSecurity sec) throws SecDispatcherException { - String master = sec.getMaster(); - - if (master == null) throw new SecDispatcherException("master password is not set"); - + String masterSource = requireNonNull(sec.getMasterSource(), "masterSource is null"); try { - return _cipher.decryptDecorated(master, SYSTEM_PROPERTY_SEC_LOCATION); - } catch (PlexusCipherException e) { - throw new SecDispatcherException(e.getMessage(), e); + URI masterSourceUri = new URI(masterSource); + for (MasterPasswordSource masterPasswordSource : masterPasswordSources.values()) { + String master = masterPasswordSource.handle(masterSourceUri); + if (master != null) return master; + } + } catch (URISyntaxException e) { + throw new SecDispatcherException("Invalid master source URI", e); } + throw new SecDispatcherException("master password could not be fetched"); } // --------------------------------------------------------------- public String getConfigurationFile() { - return _configurationFile; + return configurationFile; } public void setConfigurationFile(String file) { - _configurationFile = file; + configurationFile = file; } // --------------------------------------------------------------- @@ -241,6 +233,12 @@ private static void usage() { // --------------------------------------------------------------- + private static final String[] SYSTEM_PROPERTY_MASTER_PASSWORD = + new String[] {"settings.master.password", "settings-master-password"}; + + private static final String[] SYSTEM_PROPERTY_SERVER_PASSWORD = + new String[] {"settings.server.password", "settings-server-password"}; + public static void main(String[] args) throws Exception { if (args == null || args.length < 1) { usage(); @@ -267,7 +265,8 @@ private static void show(boolean showMaster) throws Exception { System.out.println("\n"); DefaultPlexusCipher dc = new DefaultPlexusCipher(); - DefaultSecDispatcher dd = new DefaultSecDispatcher(dc); + DefaultSecDispatcher dd = + new DefaultSecDispatcher(dc, Collections.emptyMap(), Collections.emptyMap(), DEFAULT_CONFIGURATION); if (showMaster) System.out.println(dc.encryptAndDecorate(pass, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION)); diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/MasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/MasterPasswordSource.java new file mode 100644 index 0000000..365a147 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/MasterPasswordSource.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2008 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ + +package org.codehaus.plexus.components.secdispatcher.internal; + +import java.net.URI; + +import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; + +/** + * Source of master password. + */ +public interface MasterPasswordSource { + /** + * Handles the URI to get master password. Implementation may do one of the following things: + *
    + *
  • if the URI cannot be handled by given source, return {@code null}
  • + *
  • if master password retrieval was attempted, but failed throw {@link SecDispatcherException}
  • + *
  • happy path: return the master password.
  • + *
+ */ + String handle(URI uri) throws SecDispatcherException; +} diff --git a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/PasswordDecryptor.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/PasswordDecryptor.java similarity index 84% rename from src/main/java/org/sonatype/plexus/components/sec/dispatcher/PasswordDecryptor.java rename to src/main/java/org/codehaus/plexus/components/secdispatcher/internal/PasswordDecryptor.java index 5b3bc33..3736ed2 100644 --- a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/PasswordDecryptor.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/PasswordDecryptor.java @@ -11,10 +11,12 @@ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. */ -package org.sonatype.plexus.components.sec.dispatcher; +package org.codehaus.plexus.components.secdispatcher.internal; import java.util.Map; +import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; + /** * * @@ -33,5 +35,6 @@ public interface PasswordDecryptor { * * @throws SecDispatcherException */ - String decrypt(String str, Map attributes, Map config) throws SecDispatcherException; + String decrypt(String str, Map attributes, Map config) + throws SecDispatcherException; } diff --git a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecUtil.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtil.java similarity index 88% rename from src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecUtil.java rename to src/main/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtil.java index 761956b..99faf93 100644 --- a/src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecUtil.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtil.java @@ -11,7 +11,7 @@ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. */ -package org.sonatype.plexus.components.sec.dispatcher; +package org.codehaus.plexus.components.secdispatcher.internal; import java.io.IOException; import java.io.InputStream; @@ -22,10 +22,11 @@ import java.util.List; import java.util.Map; -import org.sonatype.plexus.components.sec.dispatcher.model.Config; -import org.sonatype.plexus.components.sec.dispatcher.model.ConfigProperty; -import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity; -import org.sonatype.plexus.components.sec.dispatcher.model.io.xpp3.SecurityConfigurationXpp3Reader; +import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; +import org.codehaus.plexus.components.secdispatcher.model.Config; +import org.codehaus.plexus.components.secdispatcher.model.ConfigProperty; +import org.codehaus.plexus.components.secdispatcher.model.SettingsSecurity; +import org.codehaus.plexus.components.secdispatcher.model.io.xpp3.SecurityConfigurationXpp3Reader; /** * diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/decryptor/StaticPasswordDecryptor.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/decryptor/StaticPasswordDecryptor.java new file mode 100644 index 0000000..ad8325b --- /dev/null +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/decryptor/StaticPasswordDecryptor.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2008 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ + +package org.codehaus.plexus.components.secdispatcher.internal.decryptor; + +import java.util.Map; + +import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; +import org.codehaus.plexus.components.secdispatcher.internal.PasswordDecryptor; + +import static java.util.Objects.requireNonNull; + +public class StaticPasswordDecryptor implements PasswordDecryptor { + private final String decrypted; + + public StaticPasswordDecryptor(String decrypted) { + this.decrypted = requireNonNull(decrypted); + } + + @Override + public String decrypt(String str, Map attributes, Map config) + throws SecDispatcherException { + return decrypted; + } +} diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java new file mode 100644 index 0000000..92eecfa --- /dev/null +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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 org.codehaus.plexus.components.secdispatcher.internal.sources; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.net.URI; + +import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; +import org.codehaus.plexus.components.secdispatcher.internal.MasterPasswordSource; + +/** + * Password source that uses env. + */ +@Singleton +@Named(EnvMasterPasswordSource.NAME) +public final class EnvMasterPasswordSource implements MasterPasswordSource { + public static final String NAME = "env"; + + @Override + public String handle(URI uri) throws SecDispatcherException { + if (!NAME.equals(uri.getScheme())) { + return null; + } + String value = System.getenv(uri.getSchemeSpecificPart()); + if (value == null) { + throw new SecDispatcherException("Environment variable '" + uri.getPath() + "' not found"); + } + return value; + } +} diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/GpgAgentMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/GpgAgentMasterPasswordSource.java new file mode 100644 index 0000000..6838b3e --- /dev/null +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/GpgAgentMasterPasswordSource.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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 org.codehaus.plexus.components.secdispatcher.internal.sources; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.StandardProtocolFamily; +import java.net.URI; +import java.net.UnixDomainSocketAddress; +import java.nio.channels.Channels; +import java.nio.channels.SocketChannel; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HexFormat; + +import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; +import org.codehaus.plexus.components.secdispatcher.internal.MasterPasswordSource; + +/** + * Password source that uses GnuPG Agent. + */ +@Singleton +@Named(GpgAgentMasterPasswordSource.NAME) +public final class GpgAgentMasterPasswordSource implements MasterPasswordSource { + public static final String NAME = "gpg-agent"; + + @Override + public String handle(URI uri) throws SecDispatcherException { + if (!NAME.equals(uri.getScheme())) { + return null; + } + String socketLocation = uri.getPath(); + boolean interactive = uri.getQuery() == null || !uri.getQuery().contains("non-interactive"); + try { + Path socketLocationPath = Paths.get(socketLocation); + if (!socketLocationPath.isAbsolute()) { + socketLocationPath = Paths.get(System.getProperty("user.home")) + .resolve(socketLocationPath) + .toAbsolutePath(); + } + return load(socketLocationPath, interactive); + } catch (IOException e) { + throw new SecDispatcherException(e.getMessage(), e); + } + } + + private String load(Path socketPath, boolean interactive) throws IOException { + try (SocketChannel sock = SocketChannel.open(StandardProtocolFamily.UNIX)) { + sock.connect(UnixDomainSocketAddress.of(socketPath)); + try (BufferedReader in = new BufferedReader(new InputStreamReader(Channels.newInputStream(sock))); + OutputStream os = Channels.newOutputStream(sock)) { + + expectOK(in); + String display = System.getenv("DISPLAY"); + if (display != null) { + os.write(("OPTION display=" + display + "\n").getBytes()); + os.flush(); + expectOK(in); + } + String term = System.getenv("TERM"); + if (term != null) { + os.write(("OPTION ttytype=" + term + "\n").getBytes()); + os.flush(); + expectOK(in); + } + // https://unix.stackexchange.com/questions/71135/how-can-i-find-out-what-keys-gpg-agent-has-cached-like-how-ssh-add-l-shows-yo + String instruction = "GET_PASSPHRASE " + + (!interactive ? "--no-ask " : "") + + "plexus:secDispatcherMasterPassword" + + " " + + "X " + + "Maven+Master+Password " + + "Please+enter+your+Maven+master+password" + + "+to+use+it+for+decrypting+Maven+Settings\n"; + os.write((instruction).getBytes()); + os.flush(); + return mayExpectOK(in); + } + } + } + + private void expectOK(BufferedReader in) throws IOException { + String response = in.readLine(); + if (!response.startsWith("OK")) { + throw new IOException("Expected OK but got this instead: " + response); + } + } + + private String mayExpectOK(BufferedReader in) throws IOException { + String response = in.readLine(); + if (response.startsWith("ERR")) { + return null; + } else if (!response.startsWith("OK")) { + throw new IOException("Expected OK/ERR but got this instead: " + response); + } + return new String(HexFormat.of() + .parseHex(response.substring(Math.min(response.length(), 3)).trim())); + } +} diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MatchingMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MatchingMasterPasswordSource.java new file mode 100644 index 0000000..5d02395 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MatchingMasterPasswordSource.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2008 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ + +package org.codehaus.plexus.components.secdispatcher.internal.sources; + +import java.net.URI; +import java.util.function.Predicate; + +import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; +import org.codehaus.plexus.components.secdispatcher.internal.MasterPasswordSource; + +import static java.util.Objects.requireNonNull; + +public class MatchingMasterPasswordSource implements MasterPasswordSource { + private final Predicate matcher; + private final MasterPasswordSource masterPasswordSource; + + public MatchingMasterPasswordSource(Predicate matcher, MasterPasswordSource masterPasswordSource) { + this.matcher = requireNonNull(matcher); + this.masterPasswordSource = requireNonNull(masterPasswordSource); + } + + @Override + public String handle(URI uri) throws SecDispatcherException { + if (matcher.test(uri)) { + return masterPasswordSource.handle(uri); + } + return null; + } +} diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MemoizingMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MemoizingMasterPasswordSource.java new file mode 100644 index 0000000..e4bc7c9 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MemoizingMasterPasswordSource.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2008 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ + +package org.codehaus.plexus.components.secdispatcher.internal.sources; + +import java.net.URI; +import java.util.concurrent.ConcurrentHashMap; + +import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; +import org.codehaus.plexus.components.secdispatcher.internal.MasterPasswordSource; + +import static java.util.Objects.requireNonNull; + +public class MemoizingMasterPasswordSource implements MasterPasswordSource { + private final MasterPasswordSource masterPasswordSource; + private final ConcurrentHashMap memo; + + public MemoizingMasterPasswordSource(MasterPasswordSource masterPasswordSource) { + this.masterPasswordSource = requireNonNull(masterPasswordSource); + this.memo = new ConcurrentHashMap<>(); + } + + @Override + public String handle(URI uri) throws SecDispatcherException { + return memo.computeIfAbsent(uri, k -> masterPasswordSource.handle(uri)); + } +} diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/StaticMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/StaticMasterPasswordSource.java new file mode 100644 index 0000000..34c34e9 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/StaticMasterPasswordSource.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2008 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ + +package org.codehaus.plexus.components.secdispatcher.internal.sources; + +import java.net.URI; + +import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; +import org.codehaus.plexus.components.secdispatcher.internal.MasterPasswordSource; + +import static java.util.Objects.requireNonNull; + +public class StaticMasterPasswordSource implements MasterPasswordSource { + private final String masterPassword; + + public StaticMasterPasswordSource(String masterPassword) { + this.masterPassword = requireNonNull(masterPassword); + } + + @Override + public String handle(URI uri) throws SecDispatcherException { + return masterPassword; + } +} diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java new file mode 100644 index 0000000..f4c0b3e --- /dev/null +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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 org.codehaus.plexus.components.secdispatcher.internal.sources; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.net.URI; + +import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; +import org.codehaus.plexus.components.secdispatcher.internal.MasterPasswordSource; + +/** + * Password source that uses env. + */ +@Singleton +@Named(SystemPropertyMasterPasswordSource.NAME) +public final class SystemPropertyMasterPasswordSource implements MasterPasswordSource { + public static final String NAME = "system-property"; + + @Override + public String handle(URI uri) throws SecDispatcherException { + if (!NAME.equals(uri.getScheme())) { + return null; + } + String value = System.getProperty(uri.getSchemeSpecificPart()); + if (value == null) { + throw new SecDispatcherException("System property '" + uri.getPath() + "' not found"); + } + return value; + } +} diff --git a/src/main/mdo/settings-security.mdo b/src/main/mdo/settings-security.mdo index e5d4751..d6cefa0 100644 --- a/src/main/mdo/settings-security.mdo +++ b/src/main/mdo/settings-security.mdo @@ -23,7 +23,7 @@ package - org.sonatype.plexus.components.sec.dispatcher.model + org.codehaus.plexus.components.secdispatcher.model @@ -36,11 +36,18 @@ master - 1.0.0+ + 1.0.0/2.1.0 String encrypted master password + + masterSource + 3.0.0+ + String + The URI describing the source of the master password + + relocation 1.0.0+ diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java new file mode 100644 index 0000000..21d41d3 --- /dev/null +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2008 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ + +package org.codehaus.plexus.components.secdispatcher.internal; + +import java.io.FileWriter; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; + +import org.codehaus.plexus.components.cipher.internal.DefaultPlexusCipher; +import org.codehaus.plexus.components.secdispatcher.internal.decryptor.StaticPasswordDecryptor; +import org.codehaus.plexus.components.secdispatcher.internal.sources.EnvMasterPasswordSource; +import org.codehaus.plexus.components.secdispatcher.internal.sources.GpgAgentMasterPasswordSource; +import org.codehaus.plexus.components.secdispatcher.internal.sources.StaticMasterPasswordSource; +import org.codehaus.plexus.components.secdispatcher.internal.sources.SystemPropertyMasterPasswordSource; +import org.codehaus.plexus.components.secdispatcher.model.Config; +import org.codehaus.plexus.components.secdispatcher.model.ConfigProperty; +import org.codehaus.plexus.components.secdispatcher.model.SettingsSecurity; +import org.codehaus.plexus.components.secdispatcher.model.io.xpp3.SecurityConfigurationXpp3Writer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * + * + * @author Oleg Gusakov + * @version $Id$ + * + */ +public class SecUtilTest { + String masterPassword = "masterPw"; + String password = "somePassword"; + String passwordEncrypted = "{a/8OtPCGPvQLUVF+n6+UDTD3SeRCdqb0tPJLF71cs29M9Ms81MAb3Y1XG/TS4C4f}"; + + String _confName = "cname"; + + String _propName = "pname"; + + String _propVal = "pval"; + + private void saveSec(String masterSource) throws Exception { + SettingsSecurity sec = new SettingsSecurity(); + + sec.setRelocation(null); + sec.setMasterSource(masterSource); + + ConfigProperty cp = new ConfigProperty(); + cp.setName(_propName); + cp.setValue(_propVal); + + Config conf = new Config(); + conf.setName(_confName); + conf.addProperty(cp); + + sec.addConfiguration(conf); + + new SecurityConfigurationXpp3Writer().write(new FileWriter("./target/sec1.xml"), sec); + } + + @BeforeEach + public void prepare() throws Exception { + System.setProperty(DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION, "./target/sec.xml"); + + SettingsSecurity sec = new SettingsSecurity(); + + sec.setRelocation("./target/sec1.xml"); + new SecurityConfigurationXpp3Writer().write(new FileWriter("./target/sec.xml"), sec); + + saveSec("magic:mighty"); + } + + @Test + void testRead() throws Exception { + SettingsSecurity sec = SecUtil.read("./target/sec.xml", true); + + assertNotNull(sec); + + assertEquals("magic:mighty", sec.getMasterSource()); + + Map conf = SecUtil.getConfig(sec, _confName); + + assertNotNull(conf); + + assertNotNull(conf.get(_propName)); + + assertEquals(_propVal, conf.get(_propName)); + } + + @Test + void testDecrypt() throws Exception { + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of("static", new StaticMasterPasswordSource(masterPassword)), + Map.of("magic", new StaticPasswordDecryptor("magic")), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + + String pass = sd.decrypt(passwordEncrypted); + + assertNotNull(pass); + + assertEquals(password, pass); + } + + @Test + void testDecryptSystemProperty() throws Exception { + System.setProperty("foobar", masterPassword); + saveSec("system-property:foobar"); + // /run/user/1000/gnupg/S.gpg-agent + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of( + "prop", + new SystemPropertyMasterPasswordSource(), + "env", + new EnvMasterPasswordSource(), + "gpg", + new GpgAgentMasterPasswordSource()), + Map.of("magic", new StaticPasswordDecryptor("magic")), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + + String pass = sd.decrypt(passwordEncrypted); + + assertNotNull(pass); + + assertEquals(password, pass); + } + + @Disabled("triggers GPG agent: remove this and type in master pw") + @Test + void testDecryptGpg() throws Exception { + saveSec("gpg-agent:/run/user/1000/gnupg/S.gpg-agent"); + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of( + "prop", + new SystemPropertyMasterPasswordSource(), + "env", + new EnvMasterPasswordSource(), + "gpg", + new GpgAgentMasterPasswordSource()), + Map.of("magic", new StaticPasswordDecryptor("magic")), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + + String pass = sd.decrypt(passwordEncrypted); + + assertNotNull(pass); + + assertEquals(password, pass); + } + + @Test + void testDecryptWithDecryptor() throws Exception { + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of("static", new StaticMasterPasswordSource(masterPassword)), + Map.of("magic", new StaticPasswordDecryptor("magic")), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + + String pass = sd.decrypt("{" + Base64.getEncoder().encodeToString("whatever".getBytes(StandardCharsets.UTF_8)) + + "[a=b,type=magic]}"); + + assertNotNull(pass); + + assertEquals("magic", pass); + } +} diff --git a/src/test/java/org/sonatype/plexus/components/sec/dispatcher/SecUtilTest.java b/src/test/java/org/sonatype/plexus/components/sec/dispatcher/SecUtilTest.java deleted file mode 100644 index 293310b..0000000 --- a/src/test/java/org/sonatype/plexus/components/sec/dispatcher/SecUtilTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2008 Sonatype, Inc. All rights reserved. - * - * This program is licensed to you under the Apache License Version 2.0, - * and you may not use this file except in compliance with the Apache License Version 2.0. - * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the Apache License Version 2.0 is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. - */ - -package org.sonatype.plexus.components.sec.dispatcher; - -import java.io.FileWriter; -import java.util.Map; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.sonatype.plexus.components.cipher.DefaultPlexusCipher; -import org.sonatype.plexus.components.sec.dispatcher.model.Config; -import org.sonatype.plexus.components.sec.dispatcher.model.ConfigProperty; -import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity; -import org.sonatype.plexus.components.sec.dispatcher.model.io.xpp3.SecurityConfigurationXpp3Writer; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -/** - * - * - * @author Oleg Gusakov - * @version $Id$ - * - */ -public class SecUtilTest { - String masterPassword = "masterPw"; - String masterPasswordEncrypted = "{zKIp99JSqcMP383jVX4zaomd8/gXhXxY0k1ZTKgIY81yzWesjdM0SZPnlI9fEJYp}"; - String password = "somePassword"; - String passwordEncrypted = "{Gfsw+RhB7REL9DE4+T73MdNRbF8zHW4Dt3YbhooZVVJVa70IjqGFz9hXOs7AH1Hi}"; - - String _confName = "cname"; - - String _propName = "pname"; - - String _propVal = "pval"; - - @BeforeEach - public void prepare() throws Exception { - System.setProperty(DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION, "./target/sec.xml"); - - // DefaultPlexusCipher c = new DefaultPlexusCipher(); - // System.out.println(_clear+" -> "+c.encrypt( _clear, "testtest" )); - - SettingsSecurity sec = new SettingsSecurity(); - - sec.setRelocation("./target/sec1.xml"); - new SecurityConfigurationXpp3Writer().write(new FileWriter("./target/sec.xml"), sec); - - sec.setRelocation(null); - sec.setMaster(masterPasswordEncrypted); - - ConfigProperty cp = new ConfigProperty(); - cp.setName(_propName); - cp.setValue(_propVal); - - Config conf = new Config(); - conf.setName(_confName); - conf.addProperty(cp); - - sec.addConfiguration(conf); - - new SecurityConfigurationXpp3Writer().write(new FileWriter("./target/sec1.xml"), sec); - } - - @Test - void testRead() throws Exception { - SettingsSecurity sec = SecUtil.read("./target/sec.xml", true); - - assertNotNull(sec); - - assertEquals(masterPasswordEncrypted, sec.getMaster()); - - Map conf = SecUtil.getConfig(sec, _confName); - - assertNotNull(conf); - - assertNotNull(conf.get(_propName)); - - assertEquals(_propVal, conf.get(_propName)); - } - - @Test - void testDecrypt() throws Exception { - DefaultSecDispatcher sd = new DefaultSecDispatcher(new DefaultPlexusCipher()); - - String pass = sd.decrypt(masterPasswordEncrypted); - - assertNotNull(pass); - - assertEquals(masterPassword, pass); - } -} From 1913822123cd937c318ac9f362788ac3cbfcf77d Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 02:32:39 +0200 Subject: [PATCH 03/17] Fix errors --- .../secdispatcher/internal/sources/EnvMasterPasswordSource.java | 2 +- .../internal/sources/SystemPropertyMasterPasswordSource.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java index 92eecfa..4e4af98 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java @@ -41,7 +41,7 @@ public String handle(URI uri) throws SecDispatcherException { } String value = System.getenv(uri.getSchemeSpecificPart()); if (value == null) { - throw new SecDispatcherException("Environment variable '" + uri.getPath() + "' not found"); + throw new SecDispatcherException("Environment variable '" + uri.getSchemeSpecificPart() + "' not found"); } return value; } diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java index f4c0b3e..1d2e608 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java @@ -41,7 +41,7 @@ public String handle(URI uri) throws SecDispatcherException { } String value = System.getProperty(uri.getSchemeSpecificPart()); if (value == null) { - throw new SecDispatcherException("System property '" + uri.getPath() + "' not found"); + throw new SecDispatcherException("System property '" + uri.getSchemeSpecificPart() + "' not found"); } return value; } From b687a83a7c3ad78c0f700730c12cd61364b255ef Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 02:39:51 +0200 Subject: [PATCH 04/17] Fixes --- pom.xml | 9 ++++++ .../sources/EnvMasterPasswordSource.java | 4 +-- .../SystemPropertyMasterPasswordSource.java | 4 +-- .../secdispatcher/internal/SecUtilTest.java | 31 ++++++++++++++++--- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 9c2c31e..08ff765 100644 --- a/pom.xml +++ b/pom.xml @@ -103,6 +103,15 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + masterPw + + + diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java index 4e4af98..3891954 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java @@ -39,9 +39,9 @@ public String handle(URI uri) throws SecDispatcherException { if (!NAME.equals(uri.getScheme())) { return null; } - String value = System.getenv(uri.getSchemeSpecificPart()); + String value = System.getenv(uri.getPath().substring(1)); if (value == null) { - throw new SecDispatcherException("Environment variable '" + uri.getSchemeSpecificPart() + "' not found"); + throw new SecDispatcherException("Environment variable '" + uri.getPath() + "' not found"); } return value; } diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java index 1d2e608..71e52b5 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java @@ -39,9 +39,9 @@ public String handle(URI uri) throws SecDispatcherException { if (!NAME.equals(uri.getScheme())) { return null; } - String value = System.getProperty(uri.getSchemeSpecificPart()); + String value = System.getProperty(uri.getPath().substring(1)); if (value == null) { - throw new SecDispatcherException("System property '" + uri.getSchemeSpecificPart() + "' not found"); + throw new SecDispatcherException("System property '" + uri.getPath() + "' not found"); } return value; } diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java index 21d41d3..4f2c92d 100644 --- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java @@ -106,7 +106,7 @@ void testDecrypt() throws Exception { DefaultSecDispatcher sd = new DefaultSecDispatcher( new DefaultPlexusCipher(), Map.of("static", new StaticMasterPasswordSource(masterPassword)), - Map.of("magic", new StaticPasswordDecryptor("magic")), + Map.of(), DefaultSecDispatcher.DEFAULT_CONFIGURATION); String pass = sd.decrypt(passwordEncrypted); @@ -119,7 +119,7 @@ void testDecrypt() throws Exception { @Test void testDecryptSystemProperty() throws Exception { System.setProperty("foobar", masterPassword); - saveSec("system-property:foobar"); + saveSec("system-property:/foobar"); // /run/user/1000/gnupg/S.gpg-agent DefaultSecDispatcher sd = new DefaultSecDispatcher( new DefaultPlexusCipher(), @@ -130,7 +130,30 @@ void testDecryptSystemProperty() throws Exception { new EnvMasterPasswordSource(), "gpg", new GpgAgentMasterPasswordSource()), - Map.of("magic", new StaticPasswordDecryptor("magic")), + Map.of(), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + + String pass = sd.decrypt(passwordEncrypted); + + assertNotNull(pass); + + assertEquals(password, pass); + } + + @Test + void testDecryptEnv() throws Exception { + saveSec("env:/MASTER_PASSWORD"); + // /run/user/1000/gnupg/S.gpg-agent + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of( + "prop", + new SystemPropertyMasterPasswordSource(), + "env", + new EnvMasterPasswordSource(), + "gpg", + new GpgAgentMasterPasswordSource()), + Map.of(), DefaultSecDispatcher.DEFAULT_CONFIGURATION); String pass = sd.decrypt(passwordEncrypted); @@ -153,7 +176,7 @@ void testDecryptGpg() throws Exception { new EnvMasterPasswordSource(), "gpg", new GpgAgentMasterPasswordSource()), - Map.of("magic", new StaticPasswordDecryptor("magic")), + Map.of(), DefaultSecDispatcher.DEFAULT_CONFIGURATION); String pass = sd.decrypt(passwordEncrypted); From 981f2231bbc896eb2c396d12968066b33117f10f Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 02:42:44 +0200 Subject: [PATCH 05/17] Remove this nonsense --- .../secdispatcher/SecDispatcher.java | 3 + .../internal/DefaultSecDispatcher.java | 59 +------------------ .../secdispatcher/internal/SecUtilTest.java | 2 +- 3 files changed, 5 insertions(+), 59 deletions(-) diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java index eb873ad..9b8bd6e 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java @@ -19,6 +19,9 @@ * @author Oleg Gusakov */ public interface SecDispatcher { + String DEFAULT_CONFIGURATION = "~/.m2/settings-security.xml"; + String SYSTEM_PROPERTY_CONFIGURATION_LOCATION = "settings.security"; + /** * decrypt given encrypted string * diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java index 80f8121..2fe302a 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java @@ -17,18 +17,14 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.io.BufferedReader; -import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import org.codehaus.plexus.components.cipher.PlexusCipher; import org.codehaus.plexus.components.cipher.PlexusCipherException; -import org.codehaus.plexus.components.cipher.internal.DefaultPlexusCipher; import org.codehaus.plexus.components.secdispatcher.SecDispatcher; import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; import org.codehaus.plexus.components.secdispatcher.model.SettingsSecurity; @@ -41,10 +37,6 @@ @Singleton @Named public class DefaultSecDispatcher implements SecDispatcher { - public static final String DEFAULT_CONFIGURATION = "~/.m2/settings-security.xml"; - - public static final String SYSTEM_PROPERTY_SEC_LOCATION = "settings.security"; - public static final String TYPE_ATTR = "type"; public static final char ATTR_START = '['; public static final char ATTR_STOP = ']'; @@ -166,7 +158,7 @@ private boolean isEncryptedString(String str) { // ---------------------------------------------------------------------------- private SettingsSecurity getSec() throws SecDispatcherException { - String location = System.getProperty(SYSTEM_PROPERTY_SEC_LOCATION, getConfigurationFile()); + String location = System.getProperty(SYSTEM_PROPERTY_CONFIGURATION_LOCATION, getConfigurationFile()); String realLocation = location.charAt(0) == '~' ? System.getProperty("user.home") + location.substring(1) : location; @@ -226,53 +218,4 @@ private static boolean propertyExists(String[] values, String[] av) { return false; } - - private static void usage() { - System.out.println("usage: java -jar ...jar [-m|-p]\n-m: encrypt master password\n-p: encrypt password"); - } - - // --------------------------------------------------------------- - - private static final String[] SYSTEM_PROPERTY_MASTER_PASSWORD = - new String[] {"settings.master.password", "settings-master-password"}; - - private static final String[] SYSTEM_PROPERTY_SERVER_PASSWORD = - new String[] {"settings.server.password", "settings-server-password"}; - - public static void main(String[] args) throws Exception { - if (args == null || args.length < 1) { - usage(); - return; - } - - if ("-m".equals(args[0]) || propertyExists(SYSTEM_PROPERTY_MASTER_PASSWORD, args)) show(true); - else if ("-p".equals(args[0]) || propertyExists(SYSTEM_PROPERTY_SERVER_PASSWORD, args)) show(false); - else usage(); - } - - // --------------------------------------------------------------- - - private static void show(boolean showMaster) throws Exception { - if (showMaster) System.out.print("\nsettings master password\n"); - else System.out.print("\nsettings server password\n"); - - System.out.print("enter password: "); - - BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); - - String pass = r.readLine(); - - System.out.println("\n"); - - DefaultPlexusCipher dc = new DefaultPlexusCipher(); - DefaultSecDispatcher dd = - new DefaultSecDispatcher(dc, Collections.emptyMap(), Collections.emptyMap(), DEFAULT_CONFIGURATION); - - if (showMaster) - System.out.println(dc.encryptAndDecorate(pass, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION)); - else { - SettingsSecurity sec = dd.getSec(); - System.out.println(dc.encryptAndDecorate(pass, dd.getMaster(sec))); - } - } } diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java index 4f2c92d..b3ff665 100644 --- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java @@ -74,7 +74,7 @@ private void saveSec(String masterSource) throws Exception { @BeforeEach public void prepare() throws Exception { - System.setProperty(DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION, "./target/sec.xml"); + System.setProperty(DefaultSecDispatcher.SYSTEM_PROPERTY_CONFIGURATION_LOCATION, "./target/sec.xml"); SettingsSecurity sec = new SettingsSecurity(); From 3198bdc1af36503748a3b699c888c28f9cb4f3b0 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 10:59:06 +0200 Subject: [PATCH 06/17] More stuff --- .../secdispatcher/SecDispatcher.java | 19 ++- .../internal/DefaultSecDispatcher.java | 113 +++++++----------- ...PasswordDecryptor.java => Dispatcher.java} | 23 ++-- .../StaticDispatcher.java} | 16 ++- .../secdispatcher/internal/SecUtilTest.java | 39 +++++- 5 files changed, 122 insertions(+), 88 deletions(-) rename src/main/java/org/codehaus/plexus/components/secdispatcher/internal/{PasswordDecryptor.java => Dispatcher.java} (65%) rename src/main/java/org/codehaus/plexus/components/secdispatcher/internal/{decryptor/StaticPasswordDecryptor.java => dispatcher/StaticDispatcher.java} (67%) diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java index 9b8bd6e..ae6550b 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java @@ -13,6 +13,8 @@ package org.codehaus.plexus.components.secdispatcher; +import java.util.Map; + /** * This component decrypts a string, passed to it * @@ -21,13 +23,24 @@ public interface SecDispatcher { String DEFAULT_CONFIGURATION = "~/.m2/settings-security.xml"; String SYSTEM_PROPERTY_CONFIGURATION_LOCATION = "settings.security"; + String TYPE_ATTR = "type"; + + /** + * encrypt given plaintext string + * + * @param str the plaintext to encrypt + * @param attr the attributes, may be {@code null} + * @return encrypted string + * @throws SecDispatcherException in case of problem + */ + String encrypt(String str, Map attr) throws SecDispatcherException; /** * decrypt given encrypted string * - * @param str - * @return decrypted string - * @throws SecDispatcherException + * @param str the encrypted string + * @return plaintext string + * @throws SecDispatcherException in case of problem */ String decrypt(String str) throws SecDispatcherException; } diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java index 2fe302a..a6808b4 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; +import java.util.stream.Collectors; import org.codehaus.plexus.components.cipher.PlexusCipher; import org.codehaus.plexus.components.cipher.PlexusCipherException; @@ -37,67 +38,74 @@ @Singleton @Named public class DefaultSecDispatcher implements SecDispatcher { - public static final String TYPE_ATTR = "type"; - public static final char ATTR_START = '['; - public static final char ATTR_STOP = ']'; + public static final String ATTR_START = "["; + public static final String ATTR_STOP = "]"; protected final PlexusCipher cipher; protected final Map masterPasswordSources; - protected final Map decryptors; - protected String configurationFile; + protected final Map dispatchers; + protected final String configurationFile; @Inject public DefaultSecDispatcher( PlexusCipher cipher, Map masterPasswordSources, - Map decryptors, + Map dispatchers, @Named("${configurationFile:-" + DEFAULT_CONFIGURATION + "}") final String configurationFile) { - this.cipher = cipher; - this.masterPasswordSources = masterPasswordSources; - this.decryptors = decryptors; - this.configurationFile = configurationFile; + this.cipher = requireNonNull(cipher); + this.masterPasswordSources = requireNonNull(masterPasswordSources); + this.dispatchers = requireNonNull(dispatchers); + this.configurationFile = requireNonNull(configurationFile); } // --------------------------------------------------------------- @Override - public String decrypt(String str) throws SecDispatcherException { - if (!isEncryptedString(str)) return str; - - String bare; + public String encrypt(String str, Map attr) throws SecDispatcherException { + if (isEncryptedString(str)) return str; try { - bare = cipher.unDecorate(str); - - Map attr = stripAttributes(bare); - String res; - SettingsSecurity sec = getSec(); - - if (attr == null || attr.get("type") == null) { + if (attr == null || attr.get(TYPE_ATTR) == null) { String master = getMaster(sec); - - res = cipher.decrypt(bare, master); + res = cipher.encrypt(str, master); } else { String type = attr.get(TYPE_ATTR); - - if (decryptors == null) - throw new SecDispatcherException( - "plexus container did not supply any required dispatchers - cannot lookup " + type); - Map conf = SecUtil.getConfig(sec, type); + Dispatcher dispatcher = dispatchers.get(type); + if (dispatcher == null) throw new SecDispatcherException("no dispatcher for type " + type); + res = dispatcher.encrypt(str, attr, conf); + res += ATTR_START + + attr.entrySet().stream() + .map(e -> e.getKey() + "=" + e.getValue()) + .collect(Collectors.joining(",")) + + ATTR_STOP; + } + return cipher.decorate(res); + } catch (PlexusCipherException e) { + throw new SecDispatcherException(e.getMessage(), e); + } + } - PasswordDecryptor dispatcher = decryptors.get(type); - - if (dispatcher == null) throw new SecDispatcherException("no dispatcher for hint " + type); - + @Override + public String decrypt(String str) throws SecDispatcherException { + if (!isEncryptedString(str)) return str; + try { + String bare = cipher.unDecorate(str); + Map attr = stripAttributes(bare); + SettingsSecurity sec = getSec(); + if (attr == null || attr.get(TYPE_ATTR) == null) { + String master = getMaster(sec); + return cipher.decrypt(bare, master); + } else { + String type = attr.get(TYPE_ATTR); + Map conf = SecUtil.getConfig(sec, type); + Dispatcher dispatcher = dispatchers.get(type); + if (dispatcher == null) throw new SecDispatcherException("no dispatcher for type " + type); String pass = strip(bare); - return dispatcher.decrypt(pass, attr, conf); } - - return res; } catch (PlexusCipherException e) { throw new SecDispatcherException(e.getMessage(), e); } @@ -105,9 +113,7 @@ public String decrypt(String str) throws SecDispatcherException { private String strip(String str) { int pos = str.indexOf(ATTR_STOP); - if (pos != -1) return str.substring(pos + 1); - return str; } @@ -151,7 +157,6 @@ private Map stripAttributes(String str) { private boolean isEncryptedString(String str) { if (str == null) return false; - return cipher.isEncryptedString(str); } @@ -171,8 +176,6 @@ private SettingsSecurity getSec() throws SecDispatcherException { return sec; } - // ---------------------------------------------------------------------------- - private String getMaster(SettingsSecurity sec) throws SecDispatcherException { String masterSource = requireNonNull(sec.getMasterSource(), "masterSource is null"); try { @@ -186,36 +189,8 @@ private String getMaster(SettingsSecurity sec) throws SecDispatcherException { } throw new SecDispatcherException("master password could not be fetched"); } - // --------------------------------------------------------------- + public String getConfigurationFile() { return configurationFile; } - - public void setConfigurationFile(String file) { - configurationFile = file; - } - - // --------------------------------------------------------------- - - private static boolean propertyExists(String[] values, String[] av) { - if (values != null) { - for (String item : values) { - String p = System.getProperty(item); - - if (p != null) { - return true; - } - } - - if (av != null) - for (String value : values) - for (String s : av) { - if (("--" + value).equals(s)) { - return true; - } - } - } - - return false; - } } diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/PasswordDecryptor.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/Dispatcher.java similarity index 65% rename from src/main/java/org/codehaus/plexus/components/secdispatcher/internal/PasswordDecryptor.java rename to src/main/java/org/codehaus/plexus/components/secdispatcher/internal/Dispatcher.java index 3736ed2..d4ebf02 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/PasswordDecryptor.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/Dispatcher.java @@ -18,22 +18,31 @@ import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; /** - * + * Dispatcher. * * @author Oleg Gusakov * @version $Id$ * */ -public interface PasswordDecryptor { +public interface Dispatcher { + /** + * encrypt given plaintext string + * + * @param str string to encrypt + * @param attributes attributes, never {@code null} + * @param config configuration from settings-security.xml, may be {@code null} + * @return encrypted string + */ + String encrypt(String str, Map attributes, Map config) + throws SecDispatcherException; + /** * decrypt given encrypted string * - * @param str - string to decrypt - * @param attributes - string attributes - * @param config - configuration from settings-security.xml, if any + * @param str string to decrypt + * @param attributes attributes, never {@code null} + * @param config configuration from settings-security.xml, may be {@code null} * @return decrypted string - * - * @throws SecDispatcherException */ String decrypt(String str, Map attributes, Map config) throws SecDispatcherException; diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/decryptor/StaticPasswordDecryptor.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/dispatcher/StaticDispatcher.java similarity index 67% rename from src/main/java/org/codehaus/plexus/components/secdispatcher/internal/decryptor/StaticPasswordDecryptor.java rename to src/main/java/org/codehaus/plexus/components/secdispatcher/internal/dispatcher/StaticDispatcher.java index ad8325b..4088212 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/decryptor/StaticPasswordDecryptor.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/dispatcher/StaticDispatcher.java @@ -11,20 +11,28 @@ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. */ -package org.codehaus.plexus.components.secdispatcher.internal.decryptor; +package org.codehaus.plexus.components.secdispatcher.internal.dispatcher; import java.util.Map; import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; -import org.codehaus.plexus.components.secdispatcher.internal.PasswordDecryptor; +import org.codehaus.plexus.components.secdispatcher.internal.Dispatcher; import static java.util.Objects.requireNonNull; -public class StaticPasswordDecryptor implements PasswordDecryptor { +public class StaticDispatcher implements Dispatcher { private final String decrypted; + private final String encrypted; - public StaticPasswordDecryptor(String decrypted) { + public StaticDispatcher(String decrypted, String encrypted) { this.decrypted = requireNonNull(decrypted); + this.encrypted = requireNonNull(encrypted); + } + + @Override + public String encrypt(String str, Map attributes, Map config) + throws SecDispatcherException { + return encrypted; } @Override diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java index b3ff665..80ead1a 100644 --- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java @@ -19,7 +19,7 @@ import java.util.Map; import org.codehaus.plexus.components.cipher.internal.DefaultPlexusCipher; -import org.codehaus.plexus.components.secdispatcher.internal.decryptor.StaticPasswordDecryptor; +import org.codehaus.plexus.components.secdispatcher.internal.dispatcher.StaticDispatcher; import org.codehaus.plexus.components.secdispatcher.internal.sources.EnvMasterPasswordSource; import org.codehaus.plexus.components.secdispatcher.internal.sources.GpgAgentMasterPasswordSource; import org.codehaus.plexus.components.secdispatcher.internal.sources.StaticMasterPasswordSource; @@ -45,7 +45,7 @@ public class SecUtilTest { String masterPassword = "masterPw"; String password = "somePassword"; - String passwordEncrypted = "{a/8OtPCGPvQLUVF+n6+UDTD3SeRCdqb0tPJLF71cs29M9Ms81MAb3Y1XG/TS4C4f}"; + String passwordEncrypted = "{TT2NQZ4iAdoHqsSfYUab3s6X2IHl5qaf4vx/F8DvtSI=}"; String _confName = "cname"; @@ -101,6 +101,20 @@ void testRead() throws Exception { assertEquals(_propVal, conf.get(_propName)); } + @Test + void testEncrypt() throws Exception { + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of("static", new StaticMasterPasswordSource(masterPassword)), + Map.of(), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + + String enc = sd.encrypt(password, null); + assertNotNull(enc); + String password1 = sd.decrypt(enc); + assertEquals(password, password1); + } + @Test void testDecrypt() throws Exception { DefaultSecDispatcher sd = new DefaultSecDispatcher( @@ -187,11 +201,26 @@ void testDecryptGpg() throws Exception { } @Test - void testDecryptWithDecryptor() throws Exception { + void testEncryptWithDispatcher() throws Exception { + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of("static", new StaticMasterPasswordSource(masterPassword)), + Map.of("magic", new StaticDispatcher("decrypted", "encrypted")), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + + String enc = sd.encrypt("whatever", Map.of("type", "magic", "a", "b")); + assertNotNull(enc); + System.out.println(enc); + String password1 = sd.decrypt(enc); + assertEquals("decrypted", password1); + } + + @Test + void testDecryptWithDispatcher() throws Exception { DefaultSecDispatcher sd = new DefaultSecDispatcher( new DefaultPlexusCipher(), Map.of("static", new StaticMasterPasswordSource(masterPassword)), - Map.of("magic", new StaticPasswordDecryptor("magic")), + Map.of("magic", new StaticDispatcher("decrypted", "encrypted")), DefaultSecDispatcher.DEFAULT_CONFIGURATION); String pass = sd.decrypt("{" + Base64.getEncoder().encodeToString("whatever".getBytes(StandardCharsets.UTF_8)) @@ -199,6 +228,6 @@ void testDecryptWithDecryptor() throws Exception { assertNotNull(pass); - assertEquals("magic", pass); + assertEquals("decrypted", pass); } } From d012053494ded1072e45d3927a5c586baec230e7 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 11:04:28 +0200 Subject: [PATCH 07/17] Move off p-u --- pom.xml | 15 ++------------- .../secdispatcher/internal/SecUtil.java | 4 ++-- .../secdispatcher/internal/SecUtilTest.java | 12 +++++++++--- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index 08ff765..2d68c00 100644 --- a/pom.xml +++ b/pom.xml @@ -44,17 +44,6 @@ 3.0.0-SNAPSHOT
- - org.codehaus.plexus - plexus-xml - 3.0.1 - - - org.codehaus.plexus - plexus-utils - 4.0.2 - - javax.inject javax.inject @@ -97,8 +86,8 @@ java xsd - xpp3-reader - xpp3-writer + stax-reader + stax-writer diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtil.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtil.java index 99faf93..8bb0f66 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtil.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtil.java @@ -26,7 +26,7 @@ import org.codehaus.plexus.components.secdispatcher.model.Config; import org.codehaus.plexus.components.secdispatcher.model.ConfigProperty; import org.codehaus.plexus.components.secdispatcher.model.SettingsSecurity; -import org.codehaus.plexus.components.secdispatcher.model.io.xpp3.SecurityConfigurationXpp3Reader; +import org.codehaus.plexus.components.secdispatcher.model.io.stax.SecurityConfigurationStaxReader; /** * @@ -49,7 +49,7 @@ public static SettingsSecurity read(String location, boolean cycle) throws SecDi try { try (InputStream in = toStream(location)) { - sec = new SecurityConfigurationXpp3Reader().read(in); + sec = new SecurityConfigurationStaxReader().read(in); } if (cycle && sec.getRelocation() != null) return read(sec.getRelocation(), true); diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java index 80ead1a..e6003ae 100644 --- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java @@ -27,7 +27,7 @@ import org.codehaus.plexus.components.secdispatcher.model.Config; import org.codehaus.plexus.components.secdispatcher.model.ConfigProperty; import org.codehaus.plexus.components.secdispatcher.model.SettingsSecurity; -import org.codehaus.plexus.components.secdispatcher.model.io.xpp3.SecurityConfigurationXpp3Writer; +import org.codehaus.plexus.components.secdispatcher.model.io.stax.SecurityConfigurationStaxWriter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -69,7 +69,10 @@ private void saveSec(String masterSource) throws Exception { sec.addConfiguration(conf); - new SecurityConfigurationXpp3Writer().write(new FileWriter("./target/sec1.xml"), sec); + try (FileWriter fw = new FileWriter("./target/sec1.xml")) { + new SecurityConfigurationStaxWriter().write(fw, sec); + fw.flush(); + } } @BeforeEach @@ -79,7 +82,10 @@ public void prepare() throws Exception { SettingsSecurity sec = new SettingsSecurity(); sec.setRelocation("./target/sec1.xml"); - new SecurityConfigurationXpp3Writer().write(new FileWriter("./target/sec.xml"), sec); + try (FileWriter fw = new FileWriter("./target/sec.xml")) { + new SecurityConfigurationStaxWriter().write(fw, sec); + fw.flush(); + } saveSec("magic:mighty"); } From e0d3d2058e1cea48f9f0ad287bcce98b8ee607fe Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 13:46:10 +0200 Subject: [PATCH 08/17] Cleanup and add support classes --- .../secdispatcher/SecDispatcher.java | 2 +- .../internal/DefaultSecDispatcher.java | 21 +++----- .../internal/MasterPasswordSource.java | 4 +- .../sources/EnvMasterPasswordSource.java | 18 +++---- .../sources/GpgAgentMasterPasswordSource.java | 20 +++++--- .../sources/MasterPasswordSourceSupport.java | 50 +++++++++++++++++++ .../sources/MatchingMasterPasswordSource.java | 11 ++-- .../MemoizingMasterPasswordSource.java | 7 ++- .../PrefixMasterPasswordSourceSupport.java | 43 ++++++++++++++++ .../sources/StaticMasterPasswordSource.java | 4 +- .../SystemPropertyMasterPasswordSource.java | 20 ++++---- .../secdispatcher/internal/SecUtilTest.java | 14 ++++-- 12 files changed, 149 insertions(+), 65 deletions(-) create mode 100644 src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MasterPasswordSourceSupport.java create mode 100644 src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/PrefixMasterPasswordSourceSupport.java diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java index ae6550b..50cee88 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java @@ -23,7 +23,7 @@ public interface SecDispatcher { String DEFAULT_CONFIGURATION = "~/.m2/settings-security.xml"; String SYSTEM_PROPERTY_CONFIGURATION_LOCATION = "settings.security"; - String TYPE_ATTR = "type"; + String DISPATCHER_NAME_ATTR = "dispatcher.name"; /** * encrypt given plaintext string diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java index a6808b4..d8966b8 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java @@ -17,8 +17,6 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.net.URI; -import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; @@ -67,11 +65,11 @@ public String encrypt(String str, Map attr) throws SecDispatcher try { String res; SettingsSecurity sec = getSec(); - if (attr == null || attr.get(TYPE_ATTR) == null) { + if (attr == null || attr.get(DISPATCHER_NAME_ATTR) == null) { String master = getMaster(sec); res = cipher.encrypt(str, master); } else { - String type = attr.get(TYPE_ATTR); + String type = attr.get(DISPATCHER_NAME_ATTR); Map conf = SecUtil.getConfig(sec, type); Dispatcher dispatcher = dispatchers.get(type); if (dispatcher == null) throw new SecDispatcherException("no dispatcher for type " + type); @@ -95,11 +93,11 @@ public String decrypt(String str) throws SecDispatcherException { String bare = cipher.unDecorate(str); Map attr = stripAttributes(bare); SettingsSecurity sec = getSec(); - if (attr == null || attr.get(TYPE_ATTR) == null) { + if (attr == null || attr.get(DISPATCHER_NAME_ATTR) == null) { String master = getMaster(sec); return cipher.decrypt(bare, master); } else { - String type = attr.get(TYPE_ATTR); + String type = attr.get(DISPATCHER_NAME_ATTR); Map conf = SecUtil.getConfig(sec, type); Dispatcher dispatcher = dispatchers.get(type); if (dispatcher == null) throw new SecDispatcherException("no dispatcher for type " + type); @@ -178,14 +176,9 @@ private SettingsSecurity getSec() throws SecDispatcherException { private String getMaster(SettingsSecurity sec) throws SecDispatcherException { String masterSource = requireNonNull(sec.getMasterSource(), "masterSource is null"); - try { - URI masterSourceUri = new URI(masterSource); - for (MasterPasswordSource masterPasswordSource : masterPasswordSources.values()) { - String master = masterPasswordSource.handle(masterSourceUri); - if (master != null) return master; - } - } catch (URISyntaxException e) { - throw new SecDispatcherException("Invalid master source URI", e); + for (MasterPasswordSource masterPasswordSource : masterPasswordSources.values()) { + String master = masterPasswordSource.handle(masterSource); + if (master != null) return master; } throw new SecDispatcherException("master password could not be fetched"); } diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/MasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/MasterPasswordSource.java index 365a147..a36c7f3 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/MasterPasswordSource.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/MasterPasswordSource.java @@ -13,8 +13,6 @@ package org.codehaus.plexus.components.secdispatcher.internal; -import java.net.URI; - import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; /** @@ -29,5 +27,5 @@ public interface MasterPasswordSource { *
  • happy path: return the master password.
  • * */ - String handle(URI uri) throws SecDispatcherException; + String handle(String uri) throws SecDispatcherException; } diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java index 3891954..ede5ce1 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/EnvMasterPasswordSource.java @@ -21,27 +21,25 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.net.URI; - import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; -import org.codehaus.plexus.components.secdispatcher.internal.MasterPasswordSource; /** * Password source that uses env. */ @Singleton @Named(EnvMasterPasswordSource.NAME) -public final class EnvMasterPasswordSource implements MasterPasswordSource { +public final class EnvMasterPasswordSource extends PrefixMasterPasswordSourceSupport { public static final String NAME = "env"; + public EnvMasterPasswordSource() { + super(NAME + ":"); + } + @Override - public String handle(URI uri) throws SecDispatcherException { - if (!NAME.equals(uri.getScheme())) { - return null; - } - String value = System.getenv(uri.getPath().substring(1)); + protected String doHandle(String transformed) throws SecDispatcherException { + String value = System.getenv(transformed); if (value == null) { - throw new SecDispatcherException("Environment variable '" + uri.getPath() + "' not found"); + throw new SecDispatcherException("Environment variable '" + transformed + "' not found"); } return value; } diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/GpgAgentMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/GpgAgentMasterPasswordSource.java index 6838b3e..afe2ffa 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/GpgAgentMasterPasswordSource.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/GpgAgentMasterPasswordSource.java @@ -26,7 +26,6 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.net.StandardProtocolFamily; -import java.net.URI; import java.net.UnixDomainSocketAddress; import java.nio.channels.Channels; import java.nio.channels.SocketChannel; @@ -35,23 +34,28 @@ import java.util.HexFormat; import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; -import org.codehaus.plexus.components.secdispatcher.internal.MasterPasswordSource; /** * Password source that uses GnuPG Agent. */ @Singleton @Named(GpgAgentMasterPasswordSource.NAME) -public final class GpgAgentMasterPasswordSource implements MasterPasswordSource { +public final class GpgAgentMasterPasswordSource extends PrefixMasterPasswordSourceSupport { public static final String NAME = "gpg-agent"; + public GpgAgentMasterPasswordSource() { + super(NAME + ":"); + } + @Override - public String handle(URI uri) throws SecDispatcherException { - if (!NAME.equals(uri.getScheme())) { - return null; + protected String doHandle(String transformed) throws SecDispatcherException { + String extra = ""; + if (transformed.contains("?")) { + extra = transformed.substring(transformed.indexOf("?")); + transformed = transformed.substring(0, transformed.indexOf("?")); } - String socketLocation = uri.getPath(); - boolean interactive = uri.getQuery() == null || !uri.getQuery().contains("non-interactive"); + String socketLocation = transformed; + boolean interactive = !extra.contains("non-interactive"); try { Path socketLocationPath = Paths.get(socketLocation); if (!socketLocationPath.isAbsolute()) { diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MasterPasswordSourceSupport.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MasterPasswordSourceSupport.java new file mode 100644 index 0000000..7b19876 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MasterPasswordSourceSupport.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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 org.codehaus.plexus.components.secdispatcher.internal.sources; + +import java.util.function.Function; +import java.util.function.Predicate; + +import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; +import org.codehaus.plexus.components.secdispatcher.internal.MasterPasswordSource; + +import static java.util.Objects.requireNonNull; + +/** + * Master password source support class. + */ +public abstract class MasterPasswordSourceSupport implements MasterPasswordSource { + private final Predicate matcher; + private final Function transformer; + + public MasterPasswordSourceSupport(Predicate matcher, Function transformer) { + this.matcher = requireNonNull(matcher); + this.transformer = requireNonNull(transformer); + } + + @Override + public String handle(String masterSource) throws SecDispatcherException { + if (matcher.test(masterSource)) { + return doHandle(transformer.apply(masterSource)); + } + return null; + } + + protected abstract String doHandle(String transformed) throws SecDispatcherException; +} diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MatchingMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MatchingMasterPasswordSource.java index 5d02395..438f49c 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MatchingMasterPasswordSource.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MatchingMasterPasswordSource.java @@ -13,7 +13,6 @@ package org.codehaus.plexus.components.secdispatcher.internal.sources; -import java.net.URI; import java.util.function.Predicate; import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; @@ -22,18 +21,18 @@ import static java.util.Objects.requireNonNull; public class MatchingMasterPasswordSource implements MasterPasswordSource { - private final Predicate matcher; + private final Predicate matcher; private final MasterPasswordSource masterPasswordSource; - public MatchingMasterPasswordSource(Predicate matcher, MasterPasswordSource masterPasswordSource) { + public MatchingMasterPasswordSource(Predicate matcher, MasterPasswordSource masterPasswordSource) { this.matcher = requireNonNull(matcher); this.masterPasswordSource = requireNonNull(masterPasswordSource); } @Override - public String handle(URI uri) throws SecDispatcherException { - if (matcher.test(uri)) { - return masterPasswordSource.handle(uri); + public String handle(String masterSource) throws SecDispatcherException { + if (matcher.test(masterSource)) { + return masterPasswordSource.handle(masterSource); } return null; } diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MemoizingMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MemoizingMasterPasswordSource.java index e4bc7c9..5460062 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MemoizingMasterPasswordSource.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/MemoizingMasterPasswordSource.java @@ -13,7 +13,6 @@ package org.codehaus.plexus.components.secdispatcher.internal.sources; -import java.net.URI; import java.util.concurrent.ConcurrentHashMap; import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; @@ -23,7 +22,7 @@ public class MemoizingMasterPasswordSource implements MasterPasswordSource { private final MasterPasswordSource masterPasswordSource; - private final ConcurrentHashMap memo; + private final ConcurrentHashMap memo; public MemoizingMasterPasswordSource(MasterPasswordSource masterPasswordSource) { this.masterPasswordSource = requireNonNull(masterPasswordSource); @@ -31,7 +30,7 @@ public MemoizingMasterPasswordSource(MasterPasswordSource masterPasswordSource) } @Override - public String handle(URI uri) throws SecDispatcherException { - return memo.computeIfAbsent(uri, k -> masterPasswordSource.handle(uri)); + public String handle(String masterSource) throws SecDispatcherException { + return memo.computeIfAbsent(masterSource, k -> masterPasswordSource.handle(masterSource)); } } diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/PrefixMasterPasswordSourceSupport.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/PrefixMasterPasswordSourceSupport.java new file mode 100644 index 0000000..3d2d6b3 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/PrefixMasterPasswordSourceSupport.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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 org.codehaus.plexus.components.secdispatcher.internal.sources; + +import java.util.function.Function; +import java.util.function.Predicate; + +import static java.util.Objects.requireNonNull; + +/** + * Master password source support class for simple "prefix" use case. + */ +public abstract class PrefixMasterPasswordSourceSupport extends MasterPasswordSourceSupport { + public PrefixMasterPasswordSourceSupport(String prefix) { + super(prefixMatcher(prefix), prefixRemover(prefix)); + } + + private static Predicate prefixMatcher(String prefix) { + requireNonNull(prefix, "prefix cannot be null"); + return s -> s != null && s.startsWith(prefix); + } + + private static Function prefixRemover(String prefix) { + requireNonNull(prefix, "prefix cannot be null"); + return s -> s.substring(prefix.length()); + } +} diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/StaticMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/StaticMasterPasswordSource.java index 34c34e9..7ef6d89 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/StaticMasterPasswordSource.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/StaticMasterPasswordSource.java @@ -13,8 +13,6 @@ package org.codehaus.plexus.components.secdispatcher.internal.sources; -import java.net.URI; - import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; import org.codehaus.plexus.components.secdispatcher.internal.MasterPasswordSource; @@ -28,7 +26,7 @@ public StaticMasterPasswordSource(String masterPassword) { } @Override - public String handle(URI uri) throws SecDispatcherException { + public String handle(String masterSource) throws SecDispatcherException { return masterPassword; } } diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java index 71e52b5..58b08b8 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/SystemPropertyMasterPasswordSource.java @@ -21,27 +21,25 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.net.URI; - import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; -import org.codehaus.plexus.components.secdispatcher.internal.MasterPasswordSource; /** * Password source that uses env. */ @Singleton @Named(SystemPropertyMasterPasswordSource.NAME) -public final class SystemPropertyMasterPasswordSource implements MasterPasswordSource { - public static final String NAME = "system-property"; +public final class SystemPropertyMasterPasswordSource extends PrefixMasterPasswordSourceSupport { + public static final String NAME = "prop"; + + public SystemPropertyMasterPasswordSource() { + super(NAME + ":"); + } @Override - public String handle(URI uri) throws SecDispatcherException { - if (!NAME.equals(uri.getScheme())) { - return null; - } - String value = System.getProperty(uri.getPath().substring(1)); + protected String doHandle(String transformed) throws SecDispatcherException { + String value = System.getProperty(transformed); if (value == null) { - throw new SecDispatcherException("System property '" + uri.getPath() + "' not found"); + throw new SecDispatcherException("System property '" + transformed + "' not found"); } return value; } diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java index e6003ae..2362f7e 100644 --- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java @@ -19,6 +19,7 @@ import java.util.Map; import org.codehaus.plexus.components.cipher.internal.DefaultPlexusCipher; +import org.codehaus.plexus.components.secdispatcher.SecDispatcher; import org.codehaus.plexus.components.secdispatcher.internal.dispatcher.StaticDispatcher; import org.codehaus.plexus.components.secdispatcher.internal.sources.EnvMasterPasswordSource; import org.codehaus.plexus.components.secdispatcher.internal.sources.GpgAgentMasterPasswordSource; @@ -34,6 +35,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @@ -139,7 +141,7 @@ void testDecrypt() throws Exception { @Test void testDecryptSystemProperty() throws Exception { System.setProperty("foobar", masterPassword); - saveSec("system-property:/foobar"); + saveSec("prop:foobar"); // /run/user/1000/gnupg/S.gpg-agent DefaultSecDispatcher sd = new DefaultSecDispatcher( new DefaultPlexusCipher(), @@ -162,7 +164,7 @@ void testDecryptSystemProperty() throws Exception { @Test void testDecryptEnv() throws Exception { - saveSec("env:/MASTER_PASSWORD"); + saveSec("env:MASTER_PASSWORD"); // /run/user/1000/gnupg/S.gpg-agent DefaultSecDispatcher sd = new DefaultSecDispatcher( new DefaultPlexusCipher(), @@ -183,7 +185,7 @@ void testDecryptEnv() throws Exception { assertEquals(password, pass); } - @Disabled("triggers GPG agent: remove this and type in master pw") + @Disabled("triggers GPG agent: remove this and type in 'masterPw'") @Test void testDecryptGpg() throws Exception { saveSec("gpg-agent:/run/user/1000/gnupg/S.gpg-agent"); @@ -214,8 +216,10 @@ void testEncryptWithDispatcher() throws Exception { Map.of("magic", new StaticDispatcher("decrypted", "encrypted")), DefaultSecDispatcher.DEFAULT_CONFIGURATION); - String enc = sd.encrypt("whatever", Map.of("type", "magic", "a", "b")); + String enc = sd.encrypt("whatever", Map.of(SecDispatcher.DISPATCHER_NAME_ATTR, "magic", "a", "b")); assertNotNull(enc); + assertTrue(enc.contains("encrypted")); + assertTrue(enc.contains(SecDispatcher.DISPATCHER_NAME_ATTR + "=magic")); System.out.println(enc); String password1 = sd.decrypt(enc); assertEquals("decrypted", password1); @@ -230,7 +234,7 @@ void testDecryptWithDispatcher() throws Exception { DefaultSecDispatcher.DEFAULT_CONFIGURATION); String pass = sd.decrypt("{" + Base64.getEncoder().encodeToString("whatever".getBytes(StandardCharsets.UTF_8)) - + "[a=b,type=magic]}"); + + "[a=b," + SecDispatcher.DISPATCHER_NAME_ATTR + "=magic]}"); assertNotNull(pass); From 8dc15976551d733f1086a2190b00b5499c3c78bb Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 14:06:50 +0200 Subject: [PATCH 09/17] Cleanup --- .../secdispatcher/SecDispatcher.java | 8 +- .../internal/DefaultSecDispatcher.java | 6 +- .../internal/DefaultSecDispatcherTest.java | 172 ++++++++++++++++++ .../secdispatcher/internal/SecUtilTest.java | 160 +--------------- .../internal/dispatcher/StaticDispatcher.java | 0 5 files changed, 185 insertions(+), 161 deletions(-) create mode 100644 src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java rename src/{main => test}/java/org/codehaus/plexus/components/secdispatcher/internal/dispatcher/StaticDispatcher.java (100%) diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java index 50cee88..b16c7b5 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java @@ -14,6 +14,7 @@ package org.codehaus.plexus.components.secdispatcher; import java.util.Map; +import java.util.Set; /** * This component decrypts a string, passed to it @@ -23,7 +24,12 @@ public interface SecDispatcher { String DEFAULT_CONFIGURATION = "~/.m2/settings-security.xml"; String SYSTEM_PROPERTY_CONFIGURATION_LOCATION = "settings.security"; - String DISPATCHER_NAME_ATTR = "dispatcher.name"; + String DISPATCHER_NAME_ATTR = "name"; + + /** + * Returns the set of available dispatcher names, never {@code null}. + */ + Set availableDispatchers(); /** * encrypt given plaintext string diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java index d8966b8..ab6e42b 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java @@ -19,6 +19,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.StringTokenizer; import java.util.stream.Collectors; @@ -56,7 +57,10 @@ public DefaultSecDispatcher( this.configurationFile = requireNonNull(configurationFile); } - // --------------------------------------------------------------- + @Override + public Set availableDispatchers() { + return Set.copyOf(dispatchers.keySet()); + } @Override public String encrypt(String str, Map attr) throws SecDispatcherException { diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java new file mode 100644 index 0000000..196e732 --- /dev/null +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2008 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ + +package org.codehaus.plexus.components.secdispatcher.internal; + +import java.io.FileWriter; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; + +import org.codehaus.plexus.components.cipher.internal.DefaultPlexusCipher; +import org.codehaus.plexus.components.secdispatcher.SecDispatcher; +import org.codehaus.plexus.components.secdispatcher.internal.dispatcher.StaticDispatcher; +import org.codehaus.plexus.components.secdispatcher.internal.sources.EnvMasterPasswordSource; +import org.codehaus.plexus.components.secdispatcher.internal.sources.GpgAgentMasterPasswordSource; +import org.codehaus.plexus.components.secdispatcher.internal.sources.StaticMasterPasswordSource; +import org.codehaus.plexus.components.secdispatcher.internal.sources.SystemPropertyMasterPasswordSource; +import org.codehaus.plexus.components.secdispatcher.model.SettingsSecurity; +import org.codehaus.plexus.components.secdispatcher.model.io.stax.SecurityConfigurationStaxWriter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class DefaultSecDispatcherTest { + String masterPassword = "masterPw"; + String password = "somePassword"; + String passwordEncrypted = "{TT2NQZ4iAdoHqsSfYUab3s6X2IHl5qaf4vx/F8DvtSI=}"; + + private void saveSec(String masterSource) throws Exception { + SettingsSecurity sec = new SettingsSecurity(); + sec.setMasterSource(masterSource); + + try (FileWriter fw = new FileWriter("./target/sec.xml")) { + new SecurityConfigurationStaxWriter().write(fw, sec); + fw.flush(); + } + System.setProperty(DefaultSecDispatcher.SYSTEM_PROPERTY_CONFIGURATION_LOCATION, "./target/sec.xml"); + } + + @BeforeEach + public void prepare() throws Exception { + saveSec("magic:might"); + } + + @Test + void testEncrypt() throws Exception { + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of("static", new StaticMasterPasswordSource(masterPassword)), + Map.of(), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + String enc = sd.encrypt(password, null); + assertNotNull(enc); + String password1 = sd.decrypt(enc); + assertEquals(password, password1); + } + + @Test + void testDecrypt() throws Exception { + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of("static", new StaticMasterPasswordSource(masterPassword)), + Map.of(), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + String pass = sd.decrypt(passwordEncrypted); + assertNotNull(pass); + assertEquals(password, pass); + } + + @Test + void testDecryptSystemProperty() throws Exception { + System.setProperty("foobar", masterPassword); + saveSec("prop:foobar"); + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of( + "prop", + new SystemPropertyMasterPasswordSource(), + "env", + new EnvMasterPasswordSource(), + "gpg", + new GpgAgentMasterPasswordSource()), + Map.of(), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + String pass = sd.decrypt(passwordEncrypted); + assertNotNull(pass); + assertEquals(password, pass); + } + + @Test + void testDecryptEnv() throws Exception { + saveSec("env:MASTER_PASSWORD"); + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of( + "prop", + new SystemPropertyMasterPasswordSource(), + "env", + new EnvMasterPasswordSource(), + "gpg", + new GpgAgentMasterPasswordSource()), + Map.of(), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + String pass = sd.decrypt(passwordEncrypted); + assertNotNull(pass); + assertEquals(password, pass); + } + + @Disabled("triggers GPG agent: remove this and type in 'masterPw'") + @Test + void testDecryptGpg() throws Exception { + saveSec("gpg-agent:/run/user/1000/gnupg/S.gpg-agent"); + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of( + "prop", + new SystemPropertyMasterPasswordSource(), + "env", + new EnvMasterPasswordSource(), + "gpg", + new GpgAgentMasterPasswordSource()), + Map.of(), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + String pass = sd.decrypt(passwordEncrypted); + assertNotNull(pass); + assertEquals(password, pass); + } + + @Test + void testEncryptWithDispatcher() throws Exception { + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of("static", new StaticMasterPasswordSource(masterPassword)), + Map.of("magic", new StaticDispatcher("decrypted", "encrypted")), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + + String enc = sd.encrypt("whatever", Map.of(SecDispatcher.DISPATCHER_NAME_ATTR, "magic", "a", "b")); + assertNotNull(enc); + assertTrue(enc.contains("encrypted")); + assertTrue(enc.contains(SecDispatcher.DISPATCHER_NAME_ATTR + "=magic")); + String password1 = sd.decrypt(enc); + assertEquals("decrypted", password1); + } + + @Test + void testDecryptWithDispatcher() throws Exception { + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of("static", new StaticMasterPasswordSource(masterPassword)), + Map.of("magic", new StaticDispatcher("decrypted", "encrypted")), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + + String pass = sd.decrypt("{" + Base64.getEncoder().encodeToString("whatever".getBytes(StandardCharsets.UTF_8)) + + "[a=b," + SecDispatcher.DISPATCHER_NAME_ATTR + "=magic]}"); + assertNotNull(pass); + assertEquals("decrypted", pass); + } +} diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java index 2362f7e..67a4dcb 100644 --- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java @@ -14,28 +14,17 @@ package org.codehaus.plexus.components.secdispatcher.internal; import java.io.FileWriter; -import java.nio.charset.StandardCharsets; -import java.util.Base64; import java.util.Map; -import org.codehaus.plexus.components.cipher.internal.DefaultPlexusCipher; -import org.codehaus.plexus.components.secdispatcher.SecDispatcher; -import org.codehaus.plexus.components.secdispatcher.internal.dispatcher.StaticDispatcher; -import org.codehaus.plexus.components.secdispatcher.internal.sources.EnvMasterPasswordSource; -import org.codehaus.plexus.components.secdispatcher.internal.sources.GpgAgentMasterPasswordSource; -import org.codehaus.plexus.components.secdispatcher.internal.sources.StaticMasterPasswordSource; -import org.codehaus.plexus.components.secdispatcher.internal.sources.SystemPropertyMasterPasswordSource; import org.codehaus.plexus.components.secdispatcher.model.Config; import org.codehaus.plexus.components.secdispatcher.model.ConfigProperty; import org.codehaus.plexus.components.secdispatcher.model.SettingsSecurity; import org.codehaus.plexus.components.secdispatcher.model.io.stax.SecurityConfigurationStaxWriter; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; /** * @@ -45,14 +34,8 @@ * */ public class SecUtilTest { - String masterPassword = "masterPw"; - String password = "somePassword"; - String passwordEncrypted = "{TT2NQZ4iAdoHqsSfYUab3s6X2IHl5qaf4vx/F8DvtSI=}"; - String _confName = "cname"; - String _propName = "pname"; - String _propVal = "pval"; private void saveSec(String masterSource) throws Exception { @@ -80,164 +63,23 @@ private void saveSec(String masterSource) throws Exception { @BeforeEach public void prepare() throws Exception { System.setProperty(DefaultSecDispatcher.SYSTEM_PROPERTY_CONFIGURATION_LOCATION, "./target/sec.xml"); - SettingsSecurity sec = new SettingsSecurity(); - sec.setRelocation("./target/sec1.xml"); try (FileWriter fw = new FileWriter("./target/sec.xml")) { new SecurityConfigurationStaxWriter().write(fw, sec); fw.flush(); } - saveSec("magic:mighty"); } @Test - void testRead() throws Exception { + void testReadWithRelocation() throws Exception { SettingsSecurity sec = SecUtil.read("./target/sec.xml", true); - assertNotNull(sec); - assertEquals("magic:mighty", sec.getMasterSource()); - Map conf = SecUtil.getConfig(sec, _confName); - assertNotNull(conf); - assertNotNull(conf.get(_propName)); - assertEquals(_propVal, conf.get(_propName)); } - - @Test - void testEncrypt() throws Exception { - DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), - Map.of("static", new StaticMasterPasswordSource(masterPassword)), - Map.of(), - DefaultSecDispatcher.DEFAULT_CONFIGURATION); - - String enc = sd.encrypt(password, null); - assertNotNull(enc); - String password1 = sd.decrypt(enc); - assertEquals(password, password1); - } - - @Test - void testDecrypt() throws Exception { - DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), - Map.of("static", new StaticMasterPasswordSource(masterPassword)), - Map.of(), - DefaultSecDispatcher.DEFAULT_CONFIGURATION); - - String pass = sd.decrypt(passwordEncrypted); - - assertNotNull(pass); - - assertEquals(password, pass); - } - - @Test - void testDecryptSystemProperty() throws Exception { - System.setProperty("foobar", masterPassword); - saveSec("prop:foobar"); - // /run/user/1000/gnupg/S.gpg-agent - DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), - Map.of( - "prop", - new SystemPropertyMasterPasswordSource(), - "env", - new EnvMasterPasswordSource(), - "gpg", - new GpgAgentMasterPasswordSource()), - Map.of(), - DefaultSecDispatcher.DEFAULT_CONFIGURATION); - - String pass = sd.decrypt(passwordEncrypted); - - assertNotNull(pass); - - assertEquals(password, pass); - } - - @Test - void testDecryptEnv() throws Exception { - saveSec("env:MASTER_PASSWORD"); - // /run/user/1000/gnupg/S.gpg-agent - DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), - Map.of( - "prop", - new SystemPropertyMasterPasswordSource(), - "env", - new EnvMasterPasswordSource(), - "gpg", - new GpgAgentMasterPasswordSource()), - Map.of(), - DefaultSecDispatcher.DEFAULT_CONFIGURATION); - - String pass = sd.decrypt(passwordEncrypted); - - assertNotNull(pass); - - assertEquals(password, pass); - } - - @Disabled("triggers GPG agent: remove this and type in 'masterPw'") - @Test - void testDecryptGpg() throws Exception { - saveSec("gpg-agent:/run/user/1000/gnupg/S.gpg-agent"); - DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), - Map.of( - "prop", - new SystemPropertyMasterPasswordSource(), - "env", - new EnvMasterPasswordSource(), - "gpg", - new GpgAgentMasterPasswordSource()), - Map.of(), - DefaultSecDispatcher.DEFAULT_CONFIGURATION); - - String pass = sd.decrypt(passwordEncrypted); - - assertNotNull(pass); - - assertEquals(password, pass); - } - - @Test - void testEncryptWithDispatcher() throws Exception { - DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), - Map.of("static", new StaticMasterPasswordSource(masterPassword)), - Map.of("magic", new StaticDispatcher("decrypted", "encrypted")), - DefaultSecDispatcher.DEFAULT_CONFIGURATION); - - String enc = sd.encrypt("whatever", Map.of(SecDispatcher.DISPATCHER_NAME_ATTR, "magic", "a", "b")); - assertNotNull(enc); - assertTrue(enc.contains("encrypted")); - assertTrue(enc.contains(SecDispatcher.DISPATCHER_NAME_ATTR + "=magic")); - System.out.println(enc); - String password1 = sd.decrypt(enc); - assertEquals("decrypted", password1); - } - - @Test - void testDecryptWithDispatcher() throws Exception { - DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), - Map.of("static", new StaticMasterPasswordSource(masterPassword)), - Map.of("magic", new StaticDispatcher("decrypted", "encrypted")), - DefaultSecDispatcher.DEFAULT_CONFIGURATION); - - String pass = sd.decrypt("{" + Base64.getEncoder().encodeToString("whatever".getBytes(StandardCharsets.UTF_8)) - + "[a=b," + SecDispatcher.DISPATCHER_NAME_ATTR + "=magic]}"); - - assertNotNull(pass); - - assertEquals("decrypted", pass); - } } diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/dispatcher/StaticDispatcher.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/dispatcher/StaticDispatcher.java similarity index 100% rename from src/main/java/org/codehaus/plexus/components/secdispatcher/internal/dispatcher/StaticDispatcher.java rename to src/test/java/org/codehaus/plexus/components/secdispatcher/internal/dispatcher/StaticDispatcher.java From ab14caae86a1f6f50b20a651264ddb8601357f1f Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 14:10:56 +0200 Subject: [PATCH 10/17] Add javadoc --- .../components/secdispatcher/SecDispatcher.java | 16 ++++++++++++++++ .../internal/DefaultSecDispatcherTest.java | 3 +++ 2 files changed, 19 insertions(+) diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java index b16c7b5..4e88a46 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java @@ -22,8 +22,24 @@ * @author Oleg Gusakov */ public interface SecDispatcher { + /** + * The default path of configuration. + *

    + * The character {@code ~} (tilde) may be present as first character ONLY and is + * interpreted as "user home". + */ String DEFAULT_CONFIGURATION = "~/.m2/settings-security.xml"; + + /** + * Java System Property that may be set, to override configuration path. + */ String SYSTEM_PROPERTY_CONFIGURATION_LOCATION = "settings.security"; + + /** + * Attribute that selects a dispatcher. + * + * @see #availableDispatchers() + */ String DISPATCHER_NAME_ATTR = "name"; /** diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java index 196e732..5a3837a 100644 --- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java @@ -17,6 +17,7 @@ import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.Map; +import java.util.Set; import org.codehaus.plexus.components.cipher.internal.DefaultPlexusCipher; import org.codehaus.plexus.components.secdispatcher.SecDispatcher; @@ -148,6 +149,7 @@ void testEncryptWithDispatcher() throws Exception { Map.of("magic", new StaticDispatcher("decrypted", "encrypted")), DefaultSecDispatcher.DEFAULT_CONFIGURATION); + assertEquals(Set.of("magic"), sd.availableDispatchers()); String enc = sd.encrypt("whatever", Map.of(SecDispatcher.DISPATCHER_NAME_ATTR, "magic", "a", "b")); assertNotNull(enc); assertTrue(enc.contains("encrypted")); @@ -164,6 +166,7 @@ void testDecryptWithDispatcher() throws Exception { Map.of("magic", new StaticDispatcher("decrypted", "encrypted")), DefaultSecDispatcher.DEFAULT_CONFIGURATION); + assertEquals(Set.of("magic"), sd.availableDispatchers()); String pass = sd.decrypt("{" + Base64.getEncoder().encodeToString("whatever".getBytes(StandardCharsets.UTF_8)) + "[a=b," + SecDispatcher.DISPATCHER_NAME_ATTR + "=magic]}"); assertNotNull(pass); From c02fedd03a2aeed837029356a2d3834d7d10155c Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 14:21:30 +0200 Subject: [PATCH 11/17] Attributes may be prefix only --- .../internal/DefaultSecDispatcher.java | 20 +++++++++---------- .../internal/DefaultSecDispatcherTest.java | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java index ab6e42b..4c83c9b 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java @@ -76,13 +76,13 @@ public String encrypt(String str, Map attr) throws SecDispatcher String type = attr.get(DISPATCHER_NAME_ATTR); Map conf = SecUtil.getConfig(sec, type); Dispatcher dispatcher = dispatchers.get(type); - if (dispatcher == null) throw new SecDispatcherException("no dispatcher for type " + type); - res = dispatcher.encrypt(str, attr, conf); - res += ATTR_START + if (dispatcher == null) throw new SecDispatcherException("no dispatcher for name " + type); + res = ATTR_START + attr.entrySet().stream() .map(e -> e.getKey() + "=" + e.getValue()) .collect(Collectors.joining(",")) + ATTR_STOP; + res += dispatcher.encrypt(str, attr, conf); } return cipher.decorate(res); } catch (PlexusCipherException e) { @@ -104,7 +104,7 @@ public String decrypt(String str) throws SecDispatcherException { String type = attr.get(DISPATCHER_NAME_ATTR); Map conf = SecUtil.getConfig(sec, type); Dispatcher dispatcher = dispatchers.get(type); - if (dispatcher == null) throw new SecDispatcherException("no dispatcher for type " + type); + if (dispatcher == null) throw new SecDispatcherException("no dispatcher for name " + type); String pass = strip(bare); return dispatcher.decrypt(pass, attr, conf); } @@ -114,8 +114,11 @@ public String decrypt(String str) throws SecDispatcherException { } private String strip(String str) { - int pos = str.indexOf(ATTR_STOP); - if (pos != -1) return str.substring(pos + 1); + int start = str.indexOf(ATTR_START); + int stop = str.indexOf(ATTR_STOP); + if (start != -1 && stop != -1 && stop > start) { + return str.substring(stop + 1); + } return str; } @@ -123,6 +126,7 @@ private Map stripAttributes(String str) { int start = str.indexOf(ATTR_START); int stop = str.indexOf(ATTR_STOP); if (start != -1 && stop != -1 && stop > start) { + if (start != 0) throw new SecDispatcherException("Attributes can be prefix only"); if (stop == start + 1) return null; String attrs = str.substring(start + 1, stop).trim(); @@ -155,15 +159,11 @@ private Map stripAttributes(String str) { return null; } - // ---------------------------------------------------------------------------- - private boolean isEncryptedString(String str) { if (str == null) return false; return cipher.isEncryptedString(str); } - // ---------------------------------------------------------------------------- - private SettingsSecurity getSec() throws SecDispatcherException { String location = System.getProperty(SYSTEM_PROPERTY_CONFIGURATION_LOCATION, getConfigurationFile()); String realLocation = diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java index 5a3837a..265b761 100644 --- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java @@ -167,8 +167,8 @@ void testDecryptWithDispatcher() throws Exception { DefaultSecDispatcher.DEFAULT_CONFIGURATION); assertEquals(Set.of("magic"), sd.availableDispatchers()); - String pass = sd.decrypt("{" + Base64.getEncoder().encodeToString("whatever".getBytes(StandardCharsets.UTF_8)) - + "[a=b," + SecDispatcher.DISPATCHER_NAME_ATTR + "=magic]}"); + String pass = sd.decrypt("{" + "[a=b," + SecDispatcher.DISPATCHER_NAME_ATTR + "=magic]" + + Base64.getEncoder().encodeToString("whatever".getBytes(StandardCharsets.UTF_8)) + "}"); assertNotNull(pass); assertEquals("decrypted", pass); } From 06aaedff7899d45734a28727463a5c4c8e5c2fc9 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 14:24:56 +0200 Subject: [PATCH 12/17] Make parsing stricter --- .../internal/DefaultSecDispatcher.java | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java index 4c83c9b..1db3518 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java @@ -128,34 +128,21 @@ private Map stripAttributes(String str) { if (start != -1 && stop != -1 && stop > start) { if (start != 0) throw new SecDispatcherException("Attributes can be prefix only"); if (stop == start + 1) return null; - String attrs = str.substring(start + 1, stop).trim(); - if (attrs.isEmpty()) return null; - Map res = null; - StringTokenizer st = new StringTokenizer(attrs, ","); - while (st.hasMoreTokens()) { if (res == null) res = new HashMap<>(st.countTokens()); - String pair = st.nextToken(); - int pos = pair.indexOf('='); - - if (pos == -1) continue; - + if (pos == -1) throw new SecDispatcherException("Attribute malformed: " + pair); String key = pair.substring(0, pos).trim(); - - String val = pair.substring(pos + 1); - - res.put(key, val.trim()); + String val = pair.substring(pos + 1).trim(); + res.put(key, val); } - return res; } - return null; } From e986fff340190cdeeae3a5434f58e25e3a6999ae Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 15:17:03 +0200 Subject: [PATCH 13/17] Make sure Dispatcher gets non-null attributes and conf Also, pass on masterPassword (if possible, exists) to Dispatcher. --- .../internal/DefaultSecDispatcher.java | 59 ++++++++++------ .../secdispatcher/internal/Dispatcher.java | 10 ++- .../internal/MasterPasswordSource.java | 6 +- .../secdispatcher/internal/SecUtil.java | 68 +++++++++---------- .../internal/DefaultSecDispatcherTest.java | 37 ++++++++++ 5 files changed, 118 insertions(+), 62 deletions(-) diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java index 1db3518..c5335aa 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java @@ -68,13 +68,12 @@ public String encrypt(String str, Map attr) throws SecDispatcher try { String res; - SettingsSecurity sec = getSec(); if (attr == null || attr.get(DISPATCHER_NAME_ATTR) == null) { - String master = getMaster(sec); + SettingsSecurity sec = getConfiguration(true); + String master = getMasterPassword(sec, true); res = cipher.encrypt(str, master); } else { String type = attr.get(DISPATCHER_NAME_ATTR); - Map conf = SecUtil.getConfig(sec, type); Dispatcher dispatcher = dispatchers.get(type); if (dispatcher == null) throw new SecDispatcherException("no dispatcher for name " + type); res = ATTR_START @@ -82,7 +81,7 @@ public String encrypt(String str, Map attr) throws SecDispatcher .map(e -> e.getKey() + "=" + e.getValue()) .collect(Collectors.joining(",")) + ATTR_STOP; - res += dispatcher.encrypt(str, attr, conf); + res += dispatcher.encrypt(str, attr, prepareDispatcherConfig(type)); } return cipher.decorate(res); } catch (PlexusCipherException e) { @@ -96,23 +95,35 @@ public String decrypt(String str) throws SecDispatcherException { try { String bare = cipher.unDecorate(str); Map attr = stripAttributes(bare); - SettingsSecurity sec = getSec(); if (attr == null || attr.get(DISPATCHER_NAME_ATTR) == null) { - String master = getMaster(sec); + SettingsSecurity sec = getConfiguration(true); + String master = getMasterPassword(sec, true); return cipher.decrypt(bare, master); } else { String type = attr.get(DISPATCHER_NAME_ATTR); - Map conf = SecUtil.getConfig(sec, type); Dispatcher dispatcher = dispatchers.get(type); if (dispatcher == null) throw new SecDispatcherException("no dispatcher for name " + type); - String pass = strip(bare); - return dispatcher.decrypt(pass, attr, conf); + return dispatcher.decrypt(strip(bare), attr, prepareDispatcherConfig(type)); } } catch (PlexusCipherException e) { throw new SecDispatcherException(e.getMessage(), e); } } + private Map prepareDispatcherConfig(String type) { + HashMap dispatcherConf = new HashMap<>(); + SettingsSecurity sec = getConfiguration(false); + String master = getMasterPassword(sec, false); + if (master != null) { + dispatcherConf.put(Dispatcher.CONF_MASTER_PASSWORD, master); + } + Map conf = SecUtil.getConfig(sec, type); + if (conf != null) { + dispatcherConf.putAll(conf); + } + return dispatcherConf; + } + private String strip(String str) { int start = str.indexOf(ATTR_START); int stop = str.indexOf(ATTR_STOP); @@ -151,27 +162,31 @@ private boolean isEncryptedString(String str) { return cipher.isEncryptedString(str); } - private SettingsSecurity getSec() throws SecDispatcherException { + private SettingsSecurity getConfiguration(boolean mandatory) throws SecDispatcherException { String location = System.getProperty(SYSTEM_PROPERTY_CONFIGURATION_LOCATION, getConfigurationFile()); - String realLocation = - location.charAt(0) == '~' ? System.getProperty("user.home") + location.substring(1) : location; - - SettingsSecurity sec = SecUtil.read(realLocation, true); - - if (sec == null) - throw new SecDispatcherException( - "cannot retrieve master password. Please check that " + realLocation + " exists and has data"); + location = location.charAt(0) == '~' ? System.getProperty("user.home") + location.substring(1) : location; + SettingsSecurity sec = SecUtil.read(location, true); + if (mandatory && sec == null) + throw new SecDispatcherException("Please check that configuration file on path " + location + " exists"); return sec; } - private String getMaster(SettingsSecurity sec) throws SecDispatcherException { + private String getMasterPassword(SettingsSecurity sec, boolean mandatory) throws SecDispatcherException { + if (sec == null && !mandatory) { + return null; + } + requireNonNull(sec, "configuration is null"); String masterSource = requireNonNull(sec.getMasterSource(), "masterSource is null"); for (MasterPasswordSource masterPasswordSource : masterPasswordSources.values()) { - String master = masterPasswordSource.handle(masterSource); - if (master != null) return master; + String masterPassword = masterPasswordSource.handle(masterSource); + if (masterPassword != null) return masterPassword; + } + if (mandatory) { + throw new SecDispatcherException("master password could not be fetched"); + } else { + return null; } - throw new SecDispatcherException("master password could not be fetched"); } public String getConfigurationFile() { diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/Dispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/Dispatcher.java index d4ebf02..5865e5f 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/Dispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/Dispatcher.java @@ -25,12 +25,18 @@ * */ public interface Dispatcher { + /** + * Configuration key for masterPassword. It may be present, if SecDispatcher could + * obtain it, but presence is not optional. + */ + String CONF_MASTER_PASSWORD = "masterPassword"; + /** * encrypt given plaintext string * * @param str string to encrypt * @param attributes attributes, never {@code null} - * @param config configuration from settings-security.xml, may be {@code null} + * @param config configuration from settings-security.xml, never {@code null} * @return encrypted string */ String encrypt(String str, Map attributes, Map config) @@ -41,7 +47,7 @@ String encrypt(String str, Map attributes, Map c * * @param str string to decrypt * @param attributes attributes, never {@code null} - * @param config configuration from settings-security.xml, may be {@code null} + * @param config configuration from settings-security.xml, never {@code null} * @return decrypted string */ String decrypt(String str, Map attributes, Map config) diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/MasterPasswordSource.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/MasterPasswordSource.java index a36c7f3..e5704fd 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/MasterPasswordSource.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/MasterPasswordSource.java @@ -26,6 +26,10 @@ public interface MasterPasswordSource { *

  • if master password retrieval was attempted, but failed throw {@link SecDispatcherException}
  • *
  • happy path: return the master password.
  • * + * + * @param masterSource the source of master password, and opaque string. + * @return the master password, or {@code null} if implementation does not handle this masterSource + * @throws SecDispatcherException If implementation does handle this masterSource, but cannot obtain it */ - String handle(String uri) throws SecDispatcherException; + String handle(String masterSource) throws SecDispatcherException; } diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtil.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtil.java index 8bb0f66..ebf400e 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtil.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtil.java @@ -13,10 +13,13 @@ package org.codehaus.plexus.components.secdispatcher.internal; +import javax.xml.stream.XMLStreamException; + import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Paths; import java.util.HashMap; import java.util.List; @@ -28,6 +31,8 @@ import org.codehaus.plexus.components.secdispatcher.model.SettingsSecurity; import org.codehaus.plexus.components.secdispatcher.model.io.stax.SecurityConfigurationStaxReader; +import static java.util.Objects.requireNonNull; + /** * * @@ -44,68 +49,57 @@ public class SecUtil { public static SettingsSecurity read(String location, boolean cycle) throws SecDispatcherException { if (location == null) throw new SecDispatcherException("location to read from is null"); - SettingsSecurity sec; - try { try (InputStream in = toStream(location)) { sec = new SecurityConfigurationStaxReader().read(in); } - if (cycle && sec.getRelocation() != null) return read(sec.getRelocation(), true); - return sec; - } catch (Exception e) { - throw new SecDispatcherException(e.getMessage(), e); + } catch (NoSuchFileException e) { + return null; + } catch (IOException e) { + throw new SecDispatcherException("IO Problem", e); + } catch (XMLStreamException e) { + throw new SecDispatcherException("Parsing error", e); } } - // --------------------------------------------------------------------------------------------------------------- - private static InputStream toStream(String resource) throws IOException { - if (resource == null) return null; + private static InputStream toStream(String resource) throws IOException { + requireNonNull(resource, "resource is null"); int ind = resource.indexOf(PROTOCOL_DELIM); - if (ind > 1) { String protocol = resource.substring(0, ind); resource = resource.substring(ind + PROTOCOL_DELIM_LEN); - for (String p : URL_PROTOCOLS) { if (protocol.regionMatches(true, 0, p, 0, p.length())) { return new URL(p + PROTOCOL_DELIM + resource).openStream(); } } } - return Files.newInputStream(Paths.get(resource)); } - // --------------------------------------------------------------------------------------------------------------- - public static Map getConfig(SettingsSecurity sec, String name) { - if (name == null) return null; - List cl = sec.getConfigurations(); - - if (cl == null || cl.isEmpty()) return null; - - for (Config cf : cl) { - if (!name.equals(cf.getName())) { - continue; - } - - List pl = cf.getProperties(); - - if (pl == null || pl.isEmpty()) { - return null; - } - - Map res = new HashMap<>(pl.size()); - - for (ConfigProperty p : pl) { - res.put(p.getName(), p.getValue()); + public static Map getConfig(SettingsSecurity sec, String name) { + if (sec != null && name != null) { + List cl = sec.getConfigurations(); + if (!cl.isEmpty()) { + for (Config cf : cl) { + if (!name.equals(cf.getName())) { + continue; + } + List pl = cf.getProperties(); + if (pl.isEmpty()) { + break; + } + Map res = new HashMap<>(pl.size()); + for (ConfigProperty p : pl) { + res.put(p.getName(), p.getValue()); + } + return res; + } } - - return res; } - return null; } } diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java index 265b761..460a168 100644 --- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java @@ -21,6 +21,7 @@ import org.codehaus.plexus.components.cipher.internal.DefaultPlexusCipher; import org.codehaus.plexus.components.secdispatcher.SecDispatcher; +import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; import org.codehaus.plexus.components.secdispatcher.internal.dispatcher.StaticDispatcher; import org.codehaus.plexus.components.secdispatcher.internal.sources.EnvMasterPasswordSource; import org.codehaus.plexus.components.secdispatcher.internal.sources.GpgAgentMasterPasswordSource; @@ -172,4 +173,40 @@ void testDecryptWithDispatcher() throws Exception { assertNotNull(pass); assertEquals("decrypted", pass); } + + @Test + void testDecryptWithDispatcherConf() throws Exception { + String bare = Base64.getEncoder().encodeToString("whatever".getBytes(StandardCharsets.UTF_8)); + DefaultSecDispatcher sd = new DefaultSecDispatcher( + new DefaultPlexusCipher(), + Map.of("static", new StaticMasterPasswordSource(masterPassword)), + Map.of("magic", new Dispatcher() { + @Override + public String encrypt(String str, Map attributes, Map config) + throws SecDispatcherException { + throw new IllegalStateException("should not be called"); + } + + @Override + public String decrypt(String str, Map attributes, Map config) + throws SecDispatcherException { + assertEquals(bare, str); + assertEquals(2, attributes.size()); + assertEquals("magic", attributes.get(SecDispatcher.DISPATCHER_NAME_ATTR)); + assertEquals("value", attributes.get("key")); + + assertEquals(1, config.size()); + assertEquals(masterPassword, config.get(Dispatcher.CONF_MASTER_PASSWORD)); + + return "magic"; + } + }), + DefaultSecDispatcher.DEFAULT_CONFIGURATION); + + assertEquals(Set.of("magic"), sd.availableDispatchers()); + String pass = sd.decrypt("{" + "[key=value," + SecDispatcher.DISPATCHER_NAME_ATTR + "=magic]" + + Base64.getEncoder().encodeToString("whatever".getBytes(StandardCharsets.UTF_8)) + "}"); + assertNotNull(pass); + assertEquals("magic", pass); + } } From 3c5f3cb38d93fb2054210742fde0f177597e8494 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 15:20:16 +0200 Subject: [PATCH 14/17] Javadoc --- .../plexus/components/secdispatcher/internal/Dispatcher.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/Dispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/Dispatcher.java index 5865e5f..de030a8 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/Dispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/Dispatcher.java @@ -27,7 +27,8 @@ public interface Dispatcher { /** * Configuration key for masterPassword. It may be present, if SecDispatcher could - * obtain it, but presence is not optional. + * obtain it, but presence is optional. Still, dispatcher may throw and fail the operation + * if it requires it. */ String CONF_MASTER_PASSWORD = "masterPassword"; From 6dc32947eef73c88e4a054633e4feda17a8f2eb7 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 20:56:05 +0200 Subject: [PATCH 15/17] Update to released plexus-cipher, adopt --- pom.xml | 2 +- .../internal/DefaultSecDispatcher.java | 9 ++++-- src/main/mdo/settings-security.mdo | 7 +++++ .../internal/DefaultSecDispatcherTest.java | 31 +++++++++++-------- src/test/resources/test-sec.xml | 4 --- 5 files changed, 33 insertions(+), 20 deletions(-) delete mode 100644 src/test/resources/test-sec.xml diff --git a/pom.xml b/pom.xml index 2d68c00..af06ec5 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ org.codehaus.plexus plexus-cipher - 3.0.0-SNAPSHOT + 3.0.0 diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java index c5335aa..25dd6fc 100644 --- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java +++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java @@ -71,7 +71,7 @@ public String encrypt(String str, Map attr) throws SecDispatcher if (attr == null || attr.get(DISPATCHER_NAME_ATTR) == null) { SettingsSecurity sec = getConfiguration(true); String master = getMasterPassword(sec, true); - res = cipher.encrypt(str, master); + res = cipher.encrypt(getMasterCipher(sec), str, master); } else { String type = attr.get(DISPATCHER_NAME_ATTR); Dispatcher dispatcher = dispatchers.get(type); @@ -98,7 +98,7 @@ public String decrypt(String str) throws SecDispatcherException { if (attr == null || attr.get(DISPATCHER_NAME_ATTR) == null) { SettingsSecurity sec = getConfiguration(true); String master = getMasterPassword(sec, true); - return cipher.decrypt(bare, master); + return cipher.decrypt(getMasterCipher(sec), bare, master); } else { String type = attr.get(DISPATCHER_NAME_ATTR); Dispatcher dispatcher = dispatchers.get(type); @@ -189,6 +189,11 @@ private String getMasterPassword(SettingsSecurity sec, boolean mandatory) throws } } + private String getMasterCipher(SettingsSecurity sec) throws SecDispatcherException { + requireNonNull(sec, "configuration is null"); + return requireNonNull(sec.getMasterCipher(), "masterCipher is null"); + } + public String getConfigurationFile() { return configurationFile; } diff --git a/src/main/mdo/settings-security.mdo b/src/main/mdo/settings-security.mdo index d6cefa0..391c0c7 100644 --- a/src/main/mdo/settings-security.mdo +++ b/src/main/mdo/settings-security.mdo @@ -48,6 +48,13 @@ The URI describing the source of the master password + + masterCipher + 3.0.0+ + String + The Cipher to be used + + relocation 1.0.0+ diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java index 460a168..899e010 100644 --- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java @@ -19,6 +19,7 @@ import java.util.Map; import java.util.Set; +import org.codehaus.plexus.components.cipher.internal.AESGCMNoPadding; import org.codehaus.plexus.components.cipher.internal.DefaultPlexusCipher; import org.codehaus.plexus.components.secdispatcher.SecDispatcher; import org.codehaus.plexus.components.secdispatcher.SecDispatcherException; @@ -40,11 +41,11 @@ public class DefaultSecDispatcherTest { String masterPassword = "masterPw"; String password = "somePassword"; - String passwordEncrypted = "{TT2NQZ4iAdoHqsSfYUab3s6X2IHl5qaf4vx/F8DvtSI=}"; private void saveSec(String masterSource) throws Exception { SettingsSecurity sec = new SettingsSecurity(); sec.setMasterSource(masterSource); + sec.setMasterCipher(AESGCMNoPadding.CIPHER_ALG); try (FileWriter fw = new FileWriter("./target/sec.xml")) { new SecurityConfigurationStaxWriter().write(fw, sec); @@ -61,7 +62,7 @@ public void prepare() throws Exception { @Test void testEncrypt() throws Exception { DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), + new DefaultPlexusCipher(Map.of(AESGCMNoPadding.CIPHER_ALG, new AESGCMNoPadding())), Map.of("static", new StaticMasterPasswordSource(masterPassword)), Map.of(), DefaultSecDispatcher.DEFAULT_CONFIGURATION); @@ -74,11 +75,12 @@ void testEncrypt() throws Exception { @Test void testDecrypt() throws Exception { DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), + new DefaultPlexusCipher(Map.of(AESGCMNoPadding.CIPHER_ALG, new AESGCMNoPadding())), Map.of("static", new StaticMasterPasswordSource(masterPassword)), Map.of(), DefaultSecDispatcher.DEFAULT_CONFIGURATION); - String pass = sd.decrypt(passwordEncrypted); + String encrypted = sd.encrypt(password, null); + String pass = sd.decrypt(encrypted); assertNotNull(pass); assertEquals(password, pass); } @@ -88,7 +90,7 @@ void testDecryptSystemProperty() throws Exception { System.setProperty("foobar", masterPassword); saveSec("prop:foobar"); DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), + new DefaultPlexusCipher(Map.of(AESGCMNoPadding.CIPHER_ALG, new AESGCMNoPadding())), Map.of( "prop", new SystemPropertyMasterPasswordSource(), @@ -98,7 +100,8 @@ void testDecryptSystemProperty() throws Exception { new GpgAgentMasterPasswordSource()), Map.of(), DefaultSecDispatcher.DEFAULT_CONFIGURATION); - String pass = sd.decrypt(passwordEncrypted); + String encrypted = sd.encrypt(password, null); + String pass = sd.decrypt(encrypted); assertNotNull(pass); assertEquals(password, pass); } @@ -107,7 +110,7 @@ void testDecryptSystemProperty() throws Exception { void testDecryptEnv() throws Exception { saveSec("env:MASTER_PASSWORD"); DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), + new DefaultPlexusCipher(Map.of(AESGCMNoPadding.CIPHER_ALG, new AESGCMNoPadding())), Map.of( "prop", new SystemPropertyMasterPasswordSource(), @@ -117,7 +120,8 @@ void testDecryptEnv() throws Exception { new GpgAgentMasterPasswordSource()), Map.of(), DefaultSecDispatcher.DEFAULT_CONFIGURATION); - String pass = sd.decrypt(passwordEncrypted); + String encrypted = sd.encrypt(password, null); + String pass = sd.decrypt(encrypted); assertNotNull(pass); assertEquals(password, pass); } @@ -127,7 +131,7 @@ void testDecryptEnv() throws Exception { void testDecryptGpg() throws Exception { saveSec("gpg-agent:/run/user/1000/gnupg/S.gpg-agent"); DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), + new DefaultPlexusCipher(Map.of(AESGCMNoPadding.CIPHER_ALG, new AESGCMNoPadding())), Map.of( "prop", new SystemPropertyMasterPasswordSource(), @@ -137,7 +141,8 @@ void testDecryptGpg() throws Exception { new GpgAgentMasterPasswordSource()), Map.of(), DefaultSecDispatcher.DEFAULT_CONFIGURATION); - String pass = sd.decrypt(passwordEncrypted); + String encrypted = sd.encrypt(password, null); + String pass = sd.decrypt(encrypted); assertNotNull(pass); assertEquals(password, pass); } @@ -145,7 +150,7 @@ void testDecryptGpg() throws Exception { @Test void testEncryptWithDispatcher() throws Exception { DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), + new DefaultPlexusCipher(Map.of(AESGCMNoPadding.CIPHER_ALG, new AESGCMNoPadding())), Map.of("static", new StaticMasterPasswordSource(masterPassword)), Map.of("magic", new StaticDispatcher("decrypted", "encrypted")), DefaultSecDispatcher.DEFAULT_CONFIGURATION); @@ -162,7 +167,7 @@ void testEncryptWithDispatcher() throws Exception { @Test void testDecryptWithDispatcher() throws Exception { DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), + new DefaultPlexusCipher(Map.of(AESGCMNoPadding.CIPHER_ALG, new AESGCMNoPadding())), Map.of("static", new StaticMasterPasswordSource(masterPassword)), Map.of("magic", new StaticDispatcher("decrypted", "encrypted")), DefaultSecDispatcher.DEFAULT_CONFIGURATION); @@ -178,7 +183,7 @@ void testDecryptWithDispatcher() throws Exception { void testDecryptWithDispatcherConf() throws Exception { String bare = Base64.getEncoder().encodeToString("whatever".getBytes(StandardCharsets.UTF_8)); DefaultSecDispatcher sd = new DefaultSecDispatcher( - new DefaultPlexusCipher(), + new DefaultPlexusCipher(Map.of(AESGCMNoPadding.CIPHER_ALG, new AESGCMNoPadding())), Map.of("static", new StaticMasterPasswordSource(masterPassword)), Map.of("magic", new Dispatcher() { @Override diff --git a/src/test/resources/test-sec.xml b/src/test/resources/test-sec.xml deleted file mode 100644 index b452cb4..0000000 --- a/src/test/resources/test-sec.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - {CFxZA99+BAZVLyBgcmvfLZciAWn31QdSjVpSXodH13MAkHoTl8JPLGpt1rFTh07dnKoNiOUh92sash3p0PXbKi2NhY3sxvmVXnlCf+Vdz38uaZBQ7L0ebNt+YhpsyUE33iKqMwZt4oWr1acD3mpIufk2godfNP2nKGO2ufIFfIbqO4mGMWQ5VIQ=} - \ No newline at end of file From 2e351ee3171e0721224e3d0b7273efaff2965b53 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 21:51:53 +0200 Subject: [PATCH 16/17] Set encoding, add modelVersion --- src/main/mdo/settings-security.mdo | 7 +++++++ .../secdispatcher/internal/DefaultSecDispatcherTest.java | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/main/mdo/settings-security.mdo b/src/main/mdo/settings-security.mdo index 391c0c7..3702210 100644 --- a/src/main/mdo/settings-security.mdo +++ b/src/main/mdo/settings-security.mdo @@ -41,6 +41,13 @@ encrypted master password + + modelVersion + 3.0.0+ + String + The version of the model + + masterSource 3.0.0+ diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java index 899e010..937ba6b 100644 --- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java @@ -44,6 +44,8 @@ public class DefaultSecDispatcherTest { private void saveSec(String masterSource) throws Exception { SettingsSecurity sec = new SettingsSecurity(); + sec.setModelEncoding(StandardCharsets.UTF_8.name()); + sec.setModelVersion(SecDispatcher.class.getPackage().getSpecificationVersion()); sec.setMasterSource(masterSource); sec.setMasterCipher(AESGCMNoPadding.CIPHER_ALG); From 2cf9d72040e0e739e798ef139ec7f03fb7529334 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sat, 28 Sep 2024 21:57:34 +0200 Subject: [PATCH 17/17] Use stream and let stax figure out encoding --- .../internal/DefaultSecDispatcherTest.java | 9 +++++---- .../secdispatcher/internal/SecUtilTest.java | 14 +++++++------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java index 937ba6b..5ecb58d 100644 --- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java @@ -13,8 +13,10 @@ package org.codehaus.plexus.components.secdispatcher.internal; -import java.io.FileWriter; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Base64; import java.util.Map; import java.util.Set; @@ -49,9 +51,8 @@ private void saveSec(String masterSource) throws Exception { sec.setMasterSource(masterSource); sec.setMasterCipher(AESGCMNoPadding.CIPHER_ALG); - try (FileWriter fw = new FileWriter("./target/sec.xml")) { - new SecurityConfigurationStaxWriter().write(fw, sec); - fw.flush(); + try (OutputStream fos = Files.newOutputStream(Paths.get("./target/sec.xml"))) { + new SecurityConfigurationStaxWriter().write(fos, sec); } System.setProperty(DefaultSecDispatcher.SYSTEM_PROPERTY_CONFIGURATION_LOCATION, "./target/sec.xml"); } diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java index 67a4dcb..8afc4f1 100644 --- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java +++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/SecUtilTest.java @@ -13,7 +13,9 @@ package org.codehaus.plexus.components.secdispatcher.internal; -import java.io.FileWriter; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Map; import org.codehaus.plexus.components.secdispatcher.model.Config; @@ -54,9 +56,8 @@ private void saveSec(String masterSource) throws Exception { sec.addConfiguration(conf); - try (FileWriter fw = new FileWriter("./target/sec1.xml")) { - new SecurityConfigurationStaxWriter().write(fw, sec); - fw.flush(); + try (OutputStream fos = Files.newOutputStream(Paths.get("./target/sec1.xml"))) { + new SecurityConfigurationStaxWriter().write(fos, sec); } } @@ -65,9 +66,8 @@ public void prepare() throws Exception { System.setProperty(DefaultSecDispatcher.SYSTEM_PROPERTY_CONFIGURATION_LOCATION, "./target/sec.xml"); SettingsSecurity sec = new SettingsSecurity(); sec.setRelocation("./target/sec1.xml"); - try (FileWriter fw = new FileWriter("./target/sec.xml")) { - new SecurityConfigurationStaxWriter().write(fw, sec); - fw.flush(); + try (OutputStream fos = Files.newOutputStream(Paths.get("./target/sec.xml"))) { + new SecurityConfigurationStaxWriter().write(fos, sec); } saveSec("magic:mighty"); }