Skip to content

Commit

Permalink
Don't require config for the forwarding dispatcher
Browse files Browse the repository at this point in the history
Allow to use arbitrary master sources in a mix & match fashion

Some refactoring
  • Loading branch information
kwin committed Jan 30, 2025
1 parent 5f0a83b commit 0f7a316
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 156 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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.dispatchers;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import org.codehaus.plexus.components.secdispatcher.Dispatcher;
import org.codehaus.plexus.components.secdispatcher.DispatcherMeta;
import org.codehaus.plexus.components.secdispatcher.MasterSource;
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
import org.codehaus.plexus.components.secdispatcher.SecDispatcherException;

/**
* This dispatcher does not actually do any crypto operations, but just forwards the string to be decrypted
* to a {@link MasterSource}. The given string is supposed to contain a valid source reference which is resolvable
* by one of the bound {@link MasterSource} implementations (and not actually an encrypted value).
* This dispatcher doesn't support encrypting but just returns a given master source
*/
@Singleton
@Named(MasterSourceLookupDispatcher.NAME)
public class MasterSourceLookupDispatcher implements Dispatcher, DispatcherMeta {
public static final String NAME = "masterSourceLookup";

protected final Collection<MasterSource> sources;

@Inject
public MasterSourceLookupDispatcher(Collection<MasterSource> sources) {
this.sources = sources;
}

@Override
public String name() {
return NAME;
}

@Override
public String displayName() {
return "Master Source Lookup Dispatcher";
}

@Override
public Collection<Field> fields() {
return Collections.emptyList();
}

@Override
public EncryptPayload encrypt(String str, Map<String, String> attributes, Map<String, String> config)
throws SecDispatcherException {
// just make sure the given string is a valid reference!
decrypt(str, attributes, config);
return new EncryptPayload(attributes, str);
}

@Override
public String decrypt(String str, Map<String, String> attributes, Map<String, String> config)
throws SecDispatcherException {
Optional<String> plain = sources.stream()
.map(source -> source.handle(str))
.filter(Objects::nonNull)
.findFirst();
if (plain.isPresent()) {
return plain.get();
} else {
throw new SecDispatcherException("No master source found for : " + str);
}
}

@Override
public SecDispatcher.ValidationResponse validateConfiguration(Map<String, String> config) {
// there is nothing really to validate without having a master reference at hand (which is outside the config)
HashMap<SecDispatcher.ValidationResponse.Level, List<String>> report = new HashMap<>();
ArrayList<SecDispatcher.ValidationResponse> subsystems = new ArrayList<>();
report.computeIfAbsent(SecDispatcher.ValidationResponse.Level.INFO, k -> new ArrayList<>())
.add("Configured Source configuration valid");
return new SecDispatcher.ValidationResponse(getClass().getSimpleName(), true, report, subsystems);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
import org.codehaus.plexus.components.secdispatcher.internal.cipher.AESGCMNoPadding;
import org.codehaus.plexus.components.secdispatcher.internal.dispatchers.ForwardingDispatcher;
import org.codehaus.plexus.components.secdispatcher.internal.dispatchers.LegacyDispatcher;
import org.codehaus.plexus.components.secdispatcher.internal.dispatchers.MasterDispatcher;
import org.codehaus.plexus.components.secdispatcher.internal.dispatchers.MasterSourceLookupDispatcher;
import org.codehaus.plexus.components.secdispatcher.internal.sources.EnvMasterSource;
import org.codehaus.plexus.components.secdispatcher.internal.sources.GpgAgentMasterSource;
import org.codehaus.plexus.components.secdispatcher.internal.sources.SystemPropertyMasterSource;
Expand Down Expand Up @@ -83,14 +85,14 @@ void masterWithSystemPropertyRoundTrip() throws Exception {

@Test
void forwardingWithEnvDecrypt() throws Exception {
saveSec("forwarding", Map.of("source", "env"));
decryptForwarding("{[name=forwarding,version=something]env:MASTER_PASSWORD}", "masterPw");
saveSec("masterSourceLookup", Collections.emptyMap());
decryptForwarding("{[name=masterSourceLookup,version=something]env:MASTER_PASSWORD}", "masterPw");
}

@Test
void forwardingWithSystemPropertyDecrypt() throws Exception {
saveSec("forwarding", Map.of("source", "system-property"));
decryptForwarding("{[name=forwarding,version=something]system-property:masterPassword}", "masterPw");
saveSec("masterSourceLookup", Collections.emptyMap());
decryptForwarding("{[name=masterSourceLookup,version=something]system-property:masterPassword}", "masterPw");
}

@Test
Expand Down Expand Up @@ -206,14 +208,9 @@ protected DefaultSecDispatcher construct() {
new GpgAgentMasterSource())),
"legacy",
new LegacyDispatcher(),
"forwarding",
new ForwardingDispatcher(Map.of(
EnvMasterSource.NAME,
new EnvMasterSource(),
SystemPropertyMasterSource.NAME,
new SystemPropertyMasterSource(),
GpgAgentMasterSource.NAME,
new GpgAgentMasterSource()))),
"masterSourceLookup",
new MasterSourceLookupDispatcher(List.of(
new EnvMasterSource(), new SystemPropertyMasterSource(), new GpgAgentMasterSource()))),
CONFIG_PATH);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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.dispatchers;

import java.util.Collections;
import java.util.Map;

import org.codehaus.plexus.components.secdispatcher.Dispatcher.EncryptPayload;
import org.codehaus.plexus.components.secdispatcher.SecDispatcher.ValidationResponse;
import org.codehaus.plexus.components.secdispatcher.SecDispatcherException;
import org.codehaus.plexus.components.secdispatcher.internal.sources.EnvMasterSource;
import org.codehaus.plexus.components.secdispatcher.internal.sources.SystemPropertyMasterSource;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class MasterSourceLookupDispatcherTest {

@Test
void testUnknownPrefix() {
MasterSourceLookupDispatcher masterSourceLookupDispatcher =
new MasterSourceLookupDispatcher(Collections.singleton(new EnvMasterSource()));
assertThrows(
SecDispatcherException.class,
() -> masterSourceLookupDispatcher.decrypt("unknown-prefix:test", Map.of(), Map.of()));
assertThrows(
SecDispatcherException.class,
() -> masterSourceLookupDispatcher.encrypt("unknown-prefix:test", Map.of(), Map.of()));
}

@Test
void testSystemPropertyMasterSourceDecrypt() {
System.setProperty("myprop", "plaintext");
MasterSourceLookupDispatcher masterSourceLookupDispatcher =
new MasterSourceLookupDispatcher(Collections.singleton(new SystemPropertyMasterSource()));
// SecDispatcher "un decorates" the PW
String cleartext = masterSourceLookupDispatcher.decrypt("system-property:myprop", Map.of(), Map.of());
assertEquals("plaintext", cleartext);
}

@Test
void testEncrypt() {
System.setProperty("myprop", "plaintext");
MasterSourceLookupDispatcher masterSourceLookupDispatcher =
new MasterSourceLookupDispatcher(Collections.singleton(new SystemPropertyMasterSource()));
// SecDispatcher "un decorates" the PW
EncryptPayload payload = masterSourceLookupDispatcher.encrypt("system-property:myprop", Map.of(), Map.of());
assertEquals("system-property:myprop", payload.getEncrypted());
}

@Test
void testValidateConfiguration() {
MasterSourceLookupDispatcher masterSourceLookupDispatcher =
new MasterSourceLookupDispatcher(Collections.singleton(new SystemPropertyMasterSource()));
ValidationResponse response = masterSourceLookupDispatcher.validateConfiguration(Collections.emptyMap());
assertTrue(response.isValid());
}
}

0 comments on commit 0f7a316

Please sign in to comment.