Skip to content

Commit e2eb1b6

Browse files
authored
plexus-sec-dispatcher 3.0.0 (#73)
This library is now Java 17. Fixed all the "planned" but never done feats.
1 parent 3c4b0f7 commit e2eb1b6

23 files changed

+1147
-470
lines changed

.github/workflows/maven.yml

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ jobs:
2323
build:
2424
name: Build it
2525
uses: codehaus-plexus/.github/.github/workflows/maven.yml@master
26+
with:
27+
jdk-matrix: '[ "23", "21", "17" ]'
2628

2729
deploy:
2830
name: Deploy

pom.xml

+24-15
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</parent>
1111

1212
<artifactId>plexus-sec-dispatcher</artifactId>
13-
<version>2.1.0-SNAPSHOT</version>
13+
<version>3.0.0-SNAPSHOT</version>
1414

1515
<name>Plexus Security Dispatcher Component</name>
1616

@@ -33,31 +33,30 @@
3333
</distributionManagement>
3434

3535
<properties>
36+
<javaVersion>17</javaVersion>
3637
<project.build.outputTimestamp>2023-05-22T22:22:22Z</project.build.outputTimestamp>
3738
</properties>
3839

3940
<dependencies>
40-
<dependency>
41-
<groupId>org.codehaus.plexus</groupId>
42-
<artifactId>plexus-xml</artifactId>
43-
<version>3.0.1</version>
44-
</dependency>
45-
<dependency>
46-
<groupId>org.codehaus.plexus</groupId>
47-
<artifactId>plexus-utils</artifactId>
48-
<version>4.0.2</version>
49-
</dependency>
5041
<dependency>
5142
<groupId>org.codehaus.plexus</groupId>
5243
<artifactId>plexus-cipher</artifactId>
53-
<version>2.0</version>
44+
<version>3.0.0</version>
5445
</dependency>
5546

5647
<dependency>
5748
<groupId>javax.inject</groupId>
5849
<artifactId>javax.inject</artifactId>
5950
<version>1</version>
51+
<scope>provided</scope>
6052
</dependency>
53+
<dependency>
54+
<groupId>org.eclipse.sisu</groupId>
55+
<artifactId>org.eclipse.sisu.inject</artifactId>
56+
<version>${sisuMavenPluginVersion}</version>
57+
<scope>provided</scope>
58+
</dependency>
59+
6160
<dependency>
6261
<groupId>org.junit.jupiter</groupId>
6362
<artifactId>junit-jupiter</artifactId>
@@ -76,7 +75,7 @@
7675
<artifactId>modello-maven-plugin</artifactId>
7776
<version>2.4.0</version>
7877
<configuration>
79-
<version>1.0.0</version>
78+
<version>3.0.0</version>
8079
<models>
8180
<model>src/main/mdo/settings-security.mdo</model>
8281
</models>
@@ -86,12 +85,22 @@
8685
<id>standard</id>
8786
<goals>
8887
<goal>java</goal>
89-
<goal>xpp3-reader</goal>
90-
<goal>xpp3-writer</goal>
88+
<goal>xsd</goal>
89+
<goal>stax-reader</goal>
90+
<goal>stax-writer</goal>
9191
</goals>
9292
</execution>
9393
</executions>
9494
</plugin>
95+
<plugin>
96+
<groupId>org.apache.maven.plugins</groupId>
97+
<artifactId>maven-surefire-plugin</artifactId>
98+
<configuration>
99+
<environmentVariables>
100+
<MASTER_PASSWORD>masterPw</MASTER_PASSWORD>
101+
</environmentVariables>
102+
</configuration>
103+
</plugin>
95104
</plugins>
96105
</build>
97106
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright (c) 2008 Sonatype, Inc. All rights reserved.
3+
*
4+
* This program is licensed to you under the Apache License Version 2.0,
5+
* and you may not use this file except in compliance with the Apache License Version 2.0.
6+
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
*
8+
* Unless required by applicable law or agreed to in writing,
9+
* software distributed under the Apache License Version 2.0 is distributed on an
10+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
14+
package org.codehaus.plexus.components.secdispatcher;
15+
16+
import java.util.Map;
17+
import java.util.Set;
18+
19+
/**
20+
* This component decrypts a string, passed to it
21+
*
22+
* @author Oleg Gusakov
23+
*/
24+
public interface SecDispatcher {
25+
/**
26+
* The default path of configuration.
27+
* <p>
28+
* The character {@code ~} (tilde) may be present as first character ONLY and is
29+
* interpreted as "user home".
30+
*/
31+
String DEFAULT_CONFIGURATION = "~/.m2/settings-security.xml";
32+
33+
/**
34+
* Java System Property that may be set, to override configuration path.
35+
*/
36+
String SYSTEM_PROPERTY_CONFIGURATION_LOCATION = "settings.security";
37+
38+
/**
39+
* Attribute that selects a dispatcher.
40+
*
41+
* @see #availableDispatchers()
42+
*/
43+
String DISPATCHER_NAME_ATTR = "name";
44+
45+
/**
46+
* Returns the set of available dispatcher names, never {@code null}.
47+
*/
48+
Set<String> availableDispatchers();
49+
50+
/**
51+
* encrypt given plaintext string
52+
*
53+
* @param str the plaintext to encrypt
54+
* @param attr the attributes, may be {@code null}
55+
* @return encrypted string
56+
* @throws SecDispatcherException in case of problem
57+
*/
58+
String encrypt(String str, Map<String, String> attr) throws SecDispatcherException;
59+
60+
/**
61+
* decrypt given encrypted string
62+
*
63+
* @param str the encrypted string
64+
* @return plaintext string
65+
* @throws SecDispatcherException in case of problem
66+
*/
67+
String decrypt(String str) throws SecDispatcherException;
68+
}

src/main/java/org/sonatype/plexus/components/sec/dispatcher/SecDispatcherException.java src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcherException.java

+2-6
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,13 @@
1111
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
1212
*/
1313

14-
package org.sonatype.plexus.components.sec.dispatcher;
14+
package org.codehaus.plexus.components.secdispatcher;
1515

16-
public class SecDispatcherException extends Exception {
16+
public class SecDispatcherException extends RuntimeException {
1717
public SecDispatcherException(String message) {
1818
super(message);
1919
}
2020

21-
public SecDispatcherException(Throwable cause) {
22-
super(cause);
23-
}
24-
2521
public SecDispatcherException(String message, Throwable cause) {
2622
super(message, cause);
2723
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* Copyright (c) 2008 Sonatype, Inc. All rights reserved.
3+
*
4+
* This program is licensed to you under the Apache License Version 2.0,
5+
* and you may not use this file except in compliance with the Apache License Version 2.0.
6+
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
*
8+
* Unless required by applicable law or agreed to in writing,
9+
* software distributed under the Apache License Version 2.0 is distributed on an
10+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
14+
package org.codehaus.plexus.components.secdispatcher.internal;
15+
16+
import javax.inject.Inject;
17+
import javax.inject.Named;
18+
import javax.inject.Singleton;
19+
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
import java.util.Set;
23+
import java.util.StringTokenizer;
24+
import java.util.stream.Collectors;
25+
26+
import org.codehaus.plexus.components.cipher.PlexusCipher;
27+
import org.codehaus.plexus.components.cipher.PlexusCipherException;
28+
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
29+
import org.codehaus.plexus.components.secdispatcher.SecDispatcherException;
30+
import org.codehaus.plexus.components.secdispatcher.model.SettingsSecurity;
31+
32+
import static java.util.Objects.requireNonNull;
33+
34+
/**
35+
* @author Oleg Gusakov
36+
*/
37+
@Singleton
38+
@Named
39+
public class DefaultSecDispatcher implements SecDispatcher {
40+
public static final String ATTR_START = "[";
41+
public static final String ATTR_STOP = "]";
42+
43+
protected final PlexusCipher cipher;
44+
protected final Map<String, MasterPasswordSource> masterPasswordSources;
45+
protected final Map<String, Dispatcher> dispatchers;
46+
protected final String configurationFile;
47+
48+
@Inject
49+
public DefaultSecDispatcher(
50+
PlexusCipher cipher,
51+
Map<String, MasterPasswordSource> masterPasswordSources,
52+
Map<String, Dispatcher> dispatchers,
53+
@Named("${configurationFile:-" + DEFAULT_CONFIGURATION + "}") final String configurationFile) {
54+
this.cipher = requireNonNull(cipher);
55+
this.masterPasswordSources = requireNonNull(masterPasswordSources);
56+
this.dispatchers = requireNonNull(dispatchers);
57+
this.configurationFile = requireNonNull(configurationFile);
58+
}
59+
60+
@Override
61+
public Set<String> availableDispatchers() {
62+
return Set.copyOf(dispatchers.keySet());
63+
}
64+
65+
@Override
66+
public String encrypt(String str, Map<String, String> attr) throws SecDispatcherException {
67+
if (isEncryptedString(str)) return str;
68+
69+
try {
70+
String res;
71+
if (attr == null || attr.get(DISPATCHER_NAME_ATTR) == null) {
72+
SettingsSecurity sec = getConfiguration(true);
73+
String master = getMasterPassword(sec, true);
74+
res = cipher.encrypt(getMasterCipher(sec), str, master);
75+
} else {
76+
String type = attr.get(DISPATCHER_NAME_ATTR);
77+
Dispatcher dispatcher = dispatchers.get(type);
78+
if (dispatcher == null) throw new SecDispatcherException("no dispatcher for name " + type);
79+
res = ATTR_START
80+
+ attr.entrySet().stream()
81+
.map(e -> e.getKey() + "=" + e.getValue())
82+
.collect(Collectors.joining(","))
83+
+ ATTR_STOP;
84+
res += dispatcher.encrypt(str, attr, prepareDispatcherConfig(type));
85+
}
86+
return cipher.decorate(res);
87+
} catch (PlexusCipherException e) {
88+
throw new SecDispatcherException(e.getMessage(), e);
89+
}
90+
}
91+
92+
@Override
93+
public String decrypt(String str) throws SecDispatcherException {
94+
if (!isEncryptedString(str)) return str;
95+
try {
96+
String bare = cipher.unDecorate(str);
97+
Map<String, String> attr = stripAttributes(bare);
98+
if (attr == null || attr.get(DISPATCHER_NAME_ATTR) == null) {
99+
SettingsSecurity sec = getConfiguration(true);
100+
String master = getMasterPassword(sec, true);
101+
return cipher.decrypt(getMasterCipher(sec), bare, master);
102+
} else {
103+
String type = attr.get(DISPATCHER_NAME_ATTR);
104+
Dispatcher dispatcher = dispatchers.get(type);
105+
if (dispatcher == null) throw new SecDispatcherException("no dispatcher for name " + type);
106+
return dispatcher.decrypt(strip(bare), attr, prepareDispatcherConfig(type));
107+
}
108+
} catch (PlexusCipherException e) {
109+
throw new SecDispatcherException(e.getMessage(), e);
110+
}
111+
}
112+
113+
private Map<String, String> prepareDispatcherConfig(String type) {
114+
HashMap<String, String> dispatcherConf = new HashMap<>();
115+
SettingsSecurity sec = getConfiguration(false);
116+
String master = getMasterPassword(sec, false);
117+
if (master != null) {
118+
dispatcherConf.put(Dispatcher.CONF_MASTER_PASSWORD, master);
119+
}
120+
Map<String, String> conf = SecUtil.getConfig(sec, type);
121+
if (conf != null) {
122+
dispatcherConf.putAll(conf);
123+
}
124+
return dispatcherConf;
125+
}
126+
127+
private String strip(String str) {
128+
int start = str.indexOf(ATTR_START);
129+
int stop = str.indexOf(ATTR_STOP);
130+
if (start != -1 && stop != -1 && stop > start) {
131+
return str.substring(stop + 1);
132+
}
133+
return str;
134+
}
135+
136+
private Map<String, String> stripAttributes(String str) {
137+
int start = str.indexOf(ATTR_START);
138+
int stop = str.indexOf(ATTR_STOP);
139+
if (start != -1 && stop != -1 && stop > start) {
140+
if (start != 0) throw new SecDispatcherException("Attributes can be prefix only");
141+
if (stop == start + 1) return null;
142+
String attrs = str.substring(start + 1, stop).trim();
143+
if (attrs.isEmpty()) return null;
144+
Map<String, String> res = null;
145+
StringTokenizer st = new StringTokenizer(attrs, ",");
146+
while (st.hasMoreTokens()) {
147+
if (res == null) res = new HashMap<>(st.countTokens());
148+
String pair = st.nextToken();
149+
int pos = pair.indexOf('=');
150+
if (pos == -1) throw new SecDispatcherException("Attribute malformed: " + pair);
151+
String key = pair.substring(0, pos).trim();
152+
String val = pair.substring(pos + 1).trim();
153+
res.put(key, val);
154+
}
155+
return res;
156+
}
157+
return null;
158+
}
159+
160+
private boolean isEncryptedString(String str) {
161+
if (str == null) return false;
162+
return cipher.isEncryptedString(str);
163+
}
164+
165+
private SettingsSecurity getConfiguration(boolean mandatory) throws SecDispatcherException {
166+
String location = System.getProperty(SYSTEM_PROPERTY_CONFIGURATION_LOCATION, getConfigurationFile());
167+
location = location.charAt(0) == '~' ? System.getProperty("user.home") + location.substring(1) : location;
168+
SettingsSecurity sec = SecUtil.read(location, true);
169+
if (mandatory && sec == null)
170+
throw new SecDispatcherException("Please check that configuration file on path " + location + " exists");
171+
172+
return sec;
173+
}
174+
175+
private String getMasterPassword(SettingsSecurity sec, boolean mandatory) throws SecDispatcherException {
176+
if (sec == null && !mandatory) {
177+
return null;
178+
}
179+
requireNonNull(sec, "configuration is null");
180+
String masterSource = requireNonNull(sec.getMasterSource(), "masterSource is null");
181+
for (MasterPasswordSource masterPasswordSource : masterPasswordSources.values()) {
182+
String masterPassword = masterPasswordSource.handle(masterSource);
183+
if (masterPassword != null) return masterPassword;
184+
}
185+
if (mandatory) {
186+
throw new SecDispatcherException("master password could not be fetched");
187+
} else {
188+
return null;
189+
}
190+
}
191+
192+
private String getMasterCipher(SettingsSecurity sec) throws SecDispatcherException {
193+
requireNonNull(sec, "configuration is null");
194+
return requireNonNull(sec.getMasterCipher(), "masterCipher is null");
195+
}
196+
197+
public String getConfigurationFile() {
198+
return configurationFile;
199+
}
200+
}

0 commit comments

Comments
 (0)