mirror of https://github.com/apache/maven.git
[MNG-8285] Implement mvnenc CLI tool (#1793)
Implements the `mvnenc` tool that is on par with Maven3 master password encryption functionality wise, but is _really secure_ unlike Maven3 conterpart. On the other hand, _is backward compatible if legacy config is setup_. Implemented goals: `init`, `encrypt`, `decrypt`, `diag`. Also provides one extra "master source" based on Maven infra Prompter: console master password prompt. --- https://issues.apache.org/jira/browse/MNG-8285
This commit is contained in:
parent
7df5b16f78
commit
f5e54ca6fa
|
@ -85,6 +85,7 @@ under the License.
|
||||||
<include>mvn</include>
|
<include>mvn</include>
|
||||||
<include>mvnenc</include>
|
<include>mvnenc</include>
|
||||||
<include>mvnDebug</include>
|
<include>mvnDebug</include>
|
||||||
|
<include>mvnencDebug</include>
|
||||||
<!-- This is so that CI systems can periodically run the profiler -->
|
<!-- This is so that CI systems can periodically run the profiler -->
|
||||||
<include>mvnyjp</include>
|
<include>mvnyjp</include>
|
||||||
</includes>
|
</includes>
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Apache Maven Debug Script
|
||||||
|
#
|
||||||
|
# Environment Variable Prerequisites
|
||||||
|
#
|
||||||
|
# JAVA_HOME (Optional) Points to a Java installation.
|
||||||
|
# MAVEN_OPTS (Optional) Java runtime options used when Maven is executed.
|
||||||
|
# MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files.
|
||||||
|
# MAVEN_DEBUG_ADDRESS (Optional) Set the debug address. Default value is localhost:8000
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
MAVEN_DEBUG_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=${MAVEN_DEBUG_ADDRESS:-localhost:8000}"
|
||||||
|
|
||||||
|
echo Preparing to execute Maven in debug mode
|
||||||
|
|
||||||
|
env MAVEN_OPTS="$MAVEN_OPTS" MAVEN_DEBUG_OPTS="$MAVEN_DEBUG_OPTS" "`dirname "$0"`/mvnenc" "$@"
|
|
@ -0,0 +1,44 @@
|
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
|
@REM distributed with this work for additional information
|
||||||
|
@REM regarding copyright ownership. The ASF licenses this file
|
||||||
|
@REM to you under the Apache License, Version 2.0 (the
|
||||||
|
@REM "License"); you may not use this file except in compliance
|
||||||
|
@REM with the License. You may obtain a copy of the License at
|
||||||
|
@REM
|
||||||
|
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@REM
|
||||||
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
|
@REM software distributed under the License is distributed on an
|
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
@REM KIND, either express or implied. See the License for the
|
||||||
|
@REM specific language governing permissions and limitations
|
||||||
|
@REM under the License.
|
||||||
|
|
||||||
|
@REM -----------------------------------------------------------------------------
|
||||||
|
@REM Apache Maven Debug Script
|
||||||
|
@REM
|
||||||
|
@REM Environment Variable Prerequisites
|
||||||
|
@REM
|
||||||
|
@REM JAVA_HOME (Optional) Points to a Java installation.
|
||||||
|
@REM MAVEN_BATCH_ECHO (Optional) Set to 'on' to enable the echoing of the batch commands.
|
||||||
|
@REM MAVEN_BATCH_PAUSE (Optional) set to 'on' to wait for a key stroke before ending.
|
||||||
|
@REM MAVEN_OPTS (Optional) Java runtime options used when Maven is executed.
|
||||||
|
@REM MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files.
|
||||||
|
@REM MAVEN_DEBUG_ADDRESS (Optional) Set the debug address. Default value is localhost:8000
|
||||||
|
@REM -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||||
|
@echo off
|
||||||
|
@REM set title of command window
|
||||||
|
title %0
|
||||||
|
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||||
|
@if "%MAVEN_BATCH_ECHO%"=="on" echo %MAVEN_BATCH_ECHO%
|
||||||
|
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
if "%MAVEN_DEBUG_ADDRESS%"=="" @set MAVEN_DEBUG_ADDRESS=localhost:8000
|
||||||
|
|
||||||
|
@set MAVEN_DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=%MAVEN_DEBUG_ADDRESS%
|
||||||
|
|
||||||
|
@call "%~dp0"mvnenc.cmd %*
|
|
@ -36,28 +36,18 @@ import org.apache.maven.api.cli.Options;
|
||||||
@Experimental
|
@Experimental
|
||||||
public interface EncryptOptions extends Options {
|
public interface EncryptOptions extends Options {
|
||||||
/**
|
/**
|
||||||
* Returns the cipher that the user wants to use for non-dispatched encryption.
|
* Should the operation be forced (ie overwrite existing config, if any).
|
||||||
*
|
*
|
||||||
* @return an {@link Optional} containing the cipher string, or empty if not specified
|
* @return an {@link Optional} containing the boolean value {@code true} if specified, or empty
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
Optional<Boolean> force();
|
||||||
Optional<String> cipher();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the master source that the user wants to use for non-dispatched encryption.
|
* Should imply "yes" to all questions.
|
||||||
*
|
*
|
||||||
* @return an {@link Optional} containing the master source string, or empty if not specified
|
* @return an {@link Optional} containing the boolean value {@code true} if specified, or empty
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
Optional<Boolean> yes();
|
||||||
Optional<String> masterSource();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the dispatcher to use for dispatched encryption.
|
|
||||||
*
|
|
||||||
* @return an {@link Optional} containing the dispatcher string, or empty if not specified
|
|
||||||
*/
|
|
||||||
@Nonnull
|
|
||||||
Optional<String> dispatcher();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of encryption goals to be executed.
|
* Returns the list of encryption goals to be executed.
|
||||||
|
|
|
@ -92,6 +92,10 @@ under the License.
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.eclipse.sisu</groupId>
|
||||||
|
<artifactId>sisu-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
|
|
@ -25,7 +25,6 @@ import org.apache.maven.api.cli.InvokerException;
|
||||||
import org.apache.maven.api.cli.InvokerRequest;
|
import org.apache.maven.api.cli.InvokerRequest;
|
||||||
import org.apache.maven.api.cli.Options;
|
import org.apache.maven.api.cli.Options;
|
||||||
import org.apache.maven.api.cli.ParserException;
|
import org.apache.maven.api.cli.ParserException;
|
||||||
import org.apache.maven.jline.MessageUtils;
|
|
||||||
import org.codehaus.plexus.classworlds.ClassWorld;
|
import org.codehaus.plexus.classworlds.ClassWorld;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
@ -65,8 +64,6 @@ public abstract class ClingSupport<O extends Options, R extends InvokerRequest<O
|
||||||
* The main entry point.
|
* The main entry point.
|
||||||
*/
|
*/
|
||||||
public int run(String[] args) throws IOException {
|
public int run(String[] args) throws IOException {
|
||||||
MessageUtils.systemInstall();
|
|
||||||
MessageUtils.registerShutdownHook();
|
|
||||||
try (Invoker<R> invoker = createInvoker()) {
|
try (Invoker<R> invoker = createInvoker()) {
|
||||||
return invoker.invoke(parseArguments(args));
|
return invoker.invoke(parseArguments(args));
|
||||||
} catch (ParserException e) {
|
} catch (ParserException e) {
|
||||||
|
@ -75,12 +72,8 @@ public abstract class ClingSupport<O extends Options, R extends InvokerRequest<O
|
||||||
} catch (InvokerException e) {
|
} catch (InvokerException e) {
|
||||||
return 1;
|
return 1;
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
if (classWorldManaged) {
|
||||||
if (classWorldManaged) {
|
classWorld.close();
|
||||||
classWorld.close();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
MessageUtils.systemUninstall();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.apache.maven.cling.invoker.ProtoLookup;
|
||||||
import org.apache.maven.cling.invoker.mvn.DefaultMavenParser;
|
import org.apache.maven.cling.invoker.mvn.DefaultMavenParser;
|
||||||
import org.apache.maven.cling.invoker.mvn.local.DefaultLocalMavenInvoker;
|
import org.apache.maven.cling.invoker.mvn.local.DefaultLocalMavenInvoker;
|
||||||
import org.apache.maven.jline.JLineMessageBuilderFactory;
|
import org.apache.maven.jline.JLineMessageBuilderFactory;
|
||||||
|
import org.apache.maven.jline.MessageUtils;
|
||||||
import org.codehaus.plexus.classworlds.ClassWorld;
|
import org.codehaus.plexus.classworlds.ClassWorld;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,6 +60,17 @@ public class MavenCling extends ClingSupport<MavenOptions, MavenInvokerRequest<M
|
||||||
super(classWorld);
|
super(classWorld);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int run(String[] args) throws IOException {
|
||||||
|
MessageUtils.systemInstall();
|
||||||
|
MessageUtils.registerShutdownHook();
|
||||||
|
try {
|
||||||
|
return super.run(args);
|
||||||
|
} finally {
|
||||||
|
MessageUtils.systemUninstall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Invoker<MavenInvokerRequest<MavenOptions>> createInvoker() {
|
protected Invoker<MavenInvokerRequest<MavenOptions>> createInvoker() {
|
||||||
return new DefaultLocalMavenInvoker(
|
return new DefaultLocalMavenInvoker(
|
||||||
|
|
|
@ -30,7 +30,10 @@ import org.apache.maven.cling.invoker.ProtoLookup;
|
||||||
import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
|
import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
|
||||||
import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptParser;
|
import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptParser;
|
||||||
import org.apache.maven.jline.JLineMessageBuilderFactory;
|
import org.apache.maven.jline.JLineMessageBuilderFactory;
|
||||||
|
import org.apache.maven.jline.MessageUtils;
|
||||||
import org.codehaus.plexus.classworlds.ClassWorld;
|
import org.codehaus.plexus.classworlds.ClassWorld;
|
||||||
|
import org.jline.terminal.Terminal;
|
||||||
|
import org.jline.terminal.TerminalBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maven encrypt CLI "new-gen".
|
* Maven encrypt CLI "new-gen".
|
||||||
|
@ -52,6 +55,8 @@ public class MavenEncCling extends ClingSupport<EncryptOptions, EncryptInvokerRe
|
||||||
return new MavenEncCling(world).run(args);
|
return new MavenEncCling(world).run(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Terminal terminal;
|
||||||
|
|
||||||
public MavenEncCling() {
|
public MavenEncCling() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -60,10 +65,24 @@ public class MavenEncCling extends ClingSupport<EncryptOptions, EncryptInvokerRe
|
||||||
super(classWorld);
|
super(classWorld);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int run(String[] args) throws IOException {
|
||||||
|
terminal = TerminalBuilder.builder().build();
|
||||||
|
MessageUtils.systemInstall(terminal);
|
||||||
|
MessageUtils.registerShutdownHook();
|
||||||
|
try {
|
||||||
|
return super.run(args);
|
||||||
|
} finally {
|
||||||
|
MessageUtils.systemUninstall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Invoker<EncryptInvokerRequest> createInvoker() {
|
protected Invoker<EncryptInvokerRequest> createInvoker() {
|
||||||
return new DefaultEncryptInvoker(
|
return new DefaultEncryptInvoker(ProtoLookup.builder()
|
||||||
ProtoLookup.builder().addMapping(ClassWorld.class, classWorld).build());
|
.addMapping(ClassWorld.class, classWorld)
|
||||||
|
.addMapping(Terminal.class, terminal)
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -72,25 +72,17 @@ public class CommonsCliEncryptOptions extends CommonsCliOptions implements Encry
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> cipher() {
|
public Optional<Boolean> force() {
|
||||||
if (commandLine.hasOption(CLIManager.CIPHER)) {
|
if (commandLine.hasOption(CLIManager.FORCE)) {
|
||||||
return Optional.of(commandLine.getOptionValue(CLIManager.CIPHER));
|
return Optional.of(Boolean.TRUE);
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> masterSource() {
|
public Optional<Boolean> yes() {
|
||||||
if (commandLine.hasOption(CLIManager.MASTER_SOURCE)) {
|
if (commandLine.hasOption(CLIManager.YES)) {
|
||||||
return Optional.of(commandLine.getOptionValue(CLIManager.MASTER_SOURCE));
|
return Optional.of(Boolean.TRUE);
|
||||||
}
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<String> dispatcher() {
|
|
||||||
if (commandLine.hasOption(CLIManager.DISPATCHER)) {
|
|
||||||
return Optional.of(commandLine.getOptionValue(CLIManager.DISPATCHER));
|
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
@ -109,24 +101,19 @@ public class CommonsCliEncryptOptions extends CommonsCliOptions implements Encry
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class CLIManager extends CommonsCliOptions.CLIManager {
|
protected static class CLIManager extends CommonsCliOptions.CLIManager {
|
||||||
public static final String CIPHER = "c";
|
public static final String FORCE = "f";
|
||||||
public static final String MASTER_SOURCE = "m";
|
public static final String YES = "y";
|
||||||
public static final String DISPATCHER = "d";
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void prepareOptions(org.apache.commons.cli.Options options) {
|
protected void prepareOptions(org.apache.commons.cli.Options options) {
|
||||||
super.prepareOptions(options);
|
super.prepareOptions(options);
|
||||||
options.addOption(Option.builder(CIPHER)
|
options.addOption(Option.builder(FORCE)
|
||||||
.longOpt("cipher")
|
.longOpt("force")
|
||||||
.desc("The cipher that user wants to use for non-dispatched encryption")
|
.desc("Should overwrite without asking any configuration?")
|
||||||
.build());
|
.build());
|
||||||
options.addOption(Option.builder(MASTER_SOURCE)
|
options.addOption(Option.builder(YES)
|
||||||
.longOpt("master-source")
|
.longOpt("yes")
|
||||||
.desc("The master source that user wants to use for non-dispatched encryption")
|
.desc("Should imply user answered \"yes\" to all incoming questions?")
|
||||||
.build());
|
|
||||||
options.addOption(Option.builder(DISPATCHER)
|
|
||||||
.longOpt("dispatcher")
|
|
||||||
.desc("The dispatcher to use for dispatched encryption")
|
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.maven.cling.invoker.mvnenc;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.apache.maven.api.services.Prompter;
|
||||||
|
import org.apache.maven.api.services.PrompterException;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.MasterSource;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.MasterSourceMeta;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.SecDispatcherException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trivial master password source using Maven {@link Prompter} service.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
@Named(ConsolePasswordPrompt.NAME)
|
||||||
|
public class ConsolePasswordPrompt implements MasterSource, MasterSourceMeta {
|
||||||
|
public static final String NAME = "console-prompt";
|
||||||
|
|
||||||
|
private final Prompter prompter;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ConsolePasswordPrompt(Prompter prompter) {
|
||||||
|
this.prompter = prompter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String description() {
|
||||||
|
return "Secure console password prompt";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<String> configTemplate() {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String handle(String config) throws SecDispatcherException {
|
||||||
|
if (NAME.equals(config)) {
|
||||||
|
try {
|
||||||
|
return prompter.promptForPassword("Enter the master password: ");
|
||||||
|
} catch (PrompterException e) {
|
||||||
|
throw new SecDispatcherException("Could not collect the password", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecDispatcher.ValidationResponse validateConfiguration(String config) {
|
||||||
|
if (NAME.equals(config)) {
|
||||||
|
return new SecDispatcher.ValidationResponse(getClass().getSimpleName(), true, Map.of(), List.of());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,12 +18,27 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.maven.cling.invoker.mvnenc;
|
package org.apache.maven.cling.invoker.mvnenc;
|
||||||
|
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.maven.api.cli.mvnenc.EncryptInvoker;
|
import org.apache.maven.api.cli.mvnenc.EncryptInvoker;
|
||||||
import org.apache.maven.api.cli.mvnenc.EncryptInvokerRequest;
|
import org.apache.maven.api.cli.mvnenc.EncryptInvokerRequest;
|
||||||
import org.apache.maven.api.cli.mvnenc.EncryptOptions;
|
import org.apache.maven.api.cli.mvnenc.EncryptOptions;
|
||||||
|
import org.apache.maven.cli.CLIReportingUtils;
|
||||||
import org.apache.maven.cling.invoker.LookupInvoker;
|
import org.apache.maven.cling.invoker.LookupInvoker;
|
||||||
import org.apache.maven.cling.invoker.ProtoLookup;
|
import org.apache.maven.cling.invoker.ProtoLookup;
|
||||||
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
|
import org.jline.consoleui.prompt.ConsolePrompt;
|
||||||
|
import org.jline.reader.LineReader;
|
||||||
|
import org.jline.reader.LineReaderBuilder;
|
||||||
|
import org.jline.reader.UserInterruptException;
|
||||||
|
import org.jline.terminal.Terminal;
|
||||||
|
import org.jline.utils.AttributedString;
|
||||||
|
import org.jline.utils.AttributedStringBuilder;
|
||||||
|
import org.jline.utils.AttributedStyle;
|
||||||
|
import org.jline.utils.Colors;
|
||||||
|
import org.jline.utils.OSUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypt invoker implementation, when Encrypt CLI is being run. System uses ClassWorld launcher, and class world
|
* Encrypt invoker implementation, when Encrypt CLI is being run. System uses ClassWorld launcher, and class world
|
||||||
|
@ -33,17 +48,36 @@ public class DefaultEncryptInvoker
|
||||||
extends LookupInvoker<EncryptOptions, EncryptInvokerRequest, DefaultEncryptInvoker.LocalContext>
|
extends LookupInvoker<EncryptOptions, EncryptInvokerRequest, DefaultEncryptInvoker.LocalContext>
|
||||||
implements EncryptInvoker {
|
implements EncryptInvoker {
|
||||||
|
|
||||||
|
@SuppressWarnings("VisibilityModifier")
|
||||||
public static class LocalContext
|
public static class LocalContext
|
||||||
extends LookupInvokerContext<EncryptOptions, EncryptInvokerRequest, DefaultEncryptInvoker.LocalContext> {
|
extends LookupInvokerContext<EncryptOptions, EncryptInvokerRequest, DefaultEncryptInvoker.LocalContext> {
|
||||||
protected LocalContext(DefaultEncryptInvoker invoker, EncryptInvokerRequest invokerRequest) {
|
protected LocalContext(DefaultEncryptInvoker invoker, EncryptInvokerRequest invokerRequest) {
|
||||||
super(invoker, invokerRequest);
|
super(invoker, invokerRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SecDispatcher secDispatcher;
|
public Map<String, Goal> goals;
|
||||||
|
|
||||||
|
public List<AttributedString> header;
|
||||||
|
public AttributedStyle style;
|
||||||
|
public LineReader reader;
|
||||||
|
public ConsolePrompt prompt;
|
||||||
|
|
||||||
|
public void addInHeader(String text) {
|
||||||
|
addInHeader(AttributedStyle.DEFAULT, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addInHeader(AttributedStyle style, String text) {
|
||||||
|
AttributedStringBuilder asb = new AttributedStringBuilder();
|
||||||
|
asb.style(style).append(text);
|
||||||
|
header.add(asb.toAttributedString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Terminal terminal;
|
||||||
|
|
||||||
public DefaultEncryptInvoker(ProtoLookup protoLookup) {
|
public DefaultEncryptInvoker(ProtoLookup protoLookup) {
|
||||||
super(protoLookup);
|
super(protoLookup);
|
||||||
|
this.terminal = protoLookup.lookup(Terminal.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -58,14 +92,80 @@ public class DefaultEncryptInvoker
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void lookup(LocalContext context) {
|
protected void lookup(LocalContext context) {
|
||||||
context.secDispatcher = context.lookup.lookup(SecDispatcher.class);
|
context.goals = context.lookup.lookupMap(Goal.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int doExecute(LocalContext localContext) throws Exception {
|
public static final int OK = 0; // OK
|
||||||
localContext.logger.info("Hello, this is SecDispatcher.");
|
public static final int ERROR = 1; // "generic" error
|
||||||
localContext.logger.info("Available Ciphers: " + localContext.secDispatcher.availableCiphers());
|
public static final int BAD_OPERATION = 2; // bad user input or alike
|
||||||
localContext.logger.info("Available Dispatchers: " + localContext.secDispatcher.availableDispatchers());
|
public static final int CANCELED = 3; // user canceled
|
||||||
// TODO: implement mvnenc
|
|
||||||
return 0;
|
protected int doExecute(LocalContext context) throws Exception {
|
||||||
|
if (!context.interactive) {
|
||||||
|
System.out.println("This tool works only in interactive mode!");
|
||||||
|
System.out.println("Tool purpose is to configure password management on developer workstations.");
|
||||||
|
System.out.println(
|
||||||
|
"Note: Generated configuration can be moved/copied to headless environments, if configured as such.");
|
||||||
|
return BAD_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.header = new ArrayList<>();
|
||||||
|
context.style = new AttributedStyle();
|
||||||
|
context.addInHeader(
|
||||||
|
context.style.italic().bold().foreground(Colors.rgbColor("green")),
|
||||||
|
"Maven Encryption " + CLIReportingUtils.showVersionMinimal());
|
||||||
|
context.addInHeader("Tool for secure password management on workstations.");
|
||||||
|
context.addInHeader("This tool is part of Apache Maven 4 distribution.");
|
||||||
|
context.addInHeader("");
|
||||||
|
try {
|
||||||
|
Thread executeThread = Thread.currentThread();
|
||||||
|
terminal.handle(Terminal.Signal.INT, signal -> executeThread.interrupt());
|
||||||
|
ConsolePrompt.UiConfig config;
|
||||||
|
if (terminal.getType().equals(Terminal.TYPE_DUMB)
|
||||||
|
|| terminal.getType().equals(Terminal.TYPE_DUMB_COLOR)) {
|
||||||
|
System.out.println(terminal.getName() + ": " + terminal.getType());
|
||||||
|
throw new IllegalStateException("Dumb terminal detected.\nThis tool requires real terminal to work!\n"
|
||||||
|
+ "Note: On Windows Jansi or JNA library must be included in classpath.");
|
||||||
|
} else if (OSUtils.IS_WINDOWS) {
|
||||||
|
config = new ConsolePrompt.UiConfig(">", "( )", "(x)", "( )");
|
||||||
|
} else {
|
||||||
|
config = new ConsolePrompt.UiConfig("❯", "◯ ", "◉ ", "◯ ");
|
||||||
|
}
|
||||||
|
config.setCancellableFirstPrompt(true);
|
||||||
|
|
||||||
|
context.reader = LineReaderBuilder.builder().terminal(terminal).build();
|
||||||
|
context.prompt = new ConsolePrompt(context.reader, terminal, config);
|
||||||
|
|
||||||
|
if (context.invokerRequest.options().goals().isEmpty()
|
||||||
|
|| context.invokerRequest.options().goals().get().size() != 1) {
|
||||||
|
return badGoalsErrorMessage("No goal or multiple goals specified, specify only one goal.", context);
|
||||||
|
}
|
||||||
|
|
||||||
|
String goalName = context.invokerRequest.options().goals().get().get(0);
|
||||||
|
Goal goal = context.goals.get(goalName);
|
||||||
|
|
||||||
|
if (goal == null) {
|
||||||
|
return badGoalsErrorMessage("Unknown goal: " + goalName, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return goal.execute(context);
|
||||||
|
} catch (InterruptedException | InterruptedIOException | UserInterruptException e) {
|
||||||
|
System.out.println("Goal canceled by user.");
|
||||||
|
return CANCELED;
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (context.invokerRequest.options().showErrors().orElse(false)) {
|
||||||
|
context.logger.error(e.getMessage(), e);
|
||||||
|
} else {
|
||||||
|
context.logger.error(e.getMessage());
|
||||||
|
}
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int badGoalsErrorMessage(String message, LocalContext context) {
|
||||||
|
System.out.println(message);
|
||||||
|
System.out.println("Supported goals are: " + String.join(", ", context.goals.keySet()));
|
||||||
|
System.out.println("Use -h to display help.");
|
||||||
|
return BAD_OPERATION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.maven.cling.invoker.mvnenc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mvnenc tool goal.
|
||||||
|
*/
|
||||||
|
public interface Goal {
|
||||||
|
int execute(DefaultEncryptInvoker.LocalContext context) throws Exception;
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.maven.cling.invoker.mvnenc.goals;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.apache.maven.api.services.MessageBuilderFactory;
|
||||||
|
import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
|
||||||
|
|
||||||
|
import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.ERROR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The support class for goal implementations that requires valid/workable config.
|
||||||
|
*/
|
||||||
|
public abstract class ConfiguredGoalSupport extends GoalSupport {
|
||||||
|
protected ConfiguredGoalSupport(MessageBuilderFactory messageBuilderFactory, SecDispatcher secDispatcher) {
|
||||||
|
super(messageBuilderFactory, secDispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int execute(DefaultEncryptInvoker.LocalContext context) throws Exception {
|
||||||
|
if (!validateConfiguration()) {
|
||||||
|
logger.error(messageBuilderFactory
|
||||||
|
.builder()
|
||||||
|
.error("Maven Encryption is not configured, run `mvnenc init` first.")
|
||||||
|
.build());
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
return doExecute(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean validateConfiguration() {
|
||||||
|
SecDispatcher.ValidationResponse response = secDispatcher.validateConfiguration();
|
||||||
|
if (!response.isValid() || logger.isDebugEnabled()) {
|
||||||
|
dumpResponse("", response);
|
||||||
|
}
|
||||||
|
return response.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void dumpResponse(String indent, SecDispatcher.ValidationResponse response) {
|
||||||
|
logger.info(
|
||||||
|
response.isValid()
|
||||||
|
? messageBuilderFactory
|
||||||
|
.builder()
|
||||||
|
.success("{}Configuration validation of {}: {}")
|
||||||
|
.build()
|
||||||
|
: messageBuilderFactory
|
||||||
|
.builder()
|
||||||
|
.failure("{}Configuration validation of {}: {}")
|
||||||
|
.build(),
|
||||||
|
indent,
|
||||||
|
response.getSource(),
|
||||||
|
response.isValid() ? "VALID" : "INVALID");
|
||||||
|
for (Map.Entry<SecDispatcher.ValidationResponse.Level, List<String>> entry :
|
||||||
|
response.getReport().entrySet()) {
|
||||||
|
Consumer<String> consumer =
|
||||||
|
s -> logger.info(messageBuilderFactory.builder().info(s).build());
|
||||||
|
if (entry.getKey() == SecDispatcher.ValidationResponse.Level.ERROR) {
|
||||||
|
consumer = s ->
|
||||||
|
logger.error(messageBuilderFactory.builder().error(s).build());
|
||||||
|
} else if (entry.getKey() == SecDispatcher.ValidationResponse.Level.WARNING) {
|
||||||
|
consumer = s ->
|
||||||
|
logger.warn(messageBuilderFactory.builder().warning(s).build());
|
||||||
|
}
|
||||||
|
for (String line : entry.getValue()) {
|
||||||
|
consumer.accept(indent + " " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (SecDispatcher.ValidationResponse subsystem : response.getSubsystems()) {
|
||||||
|
dumpResponse(indent + " ", subsystem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract int doExecute(DefaultEncryptInvoker.LocalContext context) throws Exception;
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.maven.cling.invoker.mvnenc.goals;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import org.apache.maven.api.services.MessageBuilderFactory;
|
||||||
|
import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
|
||||||
|
|
||||||
|
import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.BAD_OPERATION;
|
||||||
|
import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.OK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "decrypt" goal.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
@Named("decrypt")
|
||||||
|
public class Decrypt extends ConfiguredGoalSupport {
|
||||||
|
@Inject
|
||||||
|
public Decrypt(MessageBuilderFactory messageBuilderFactory, SecDispatcher secDispatcher) {
|
||||||
|
super(messageBuilderFactory, secDispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doExecute(DefaultEncryptInvoker.LocalContext context) throws Exception {
|
||||||
|
String encrypted = context.reader.readLine("Enter the password to decrypt: ");
|
||||||
|
if (secDispatcher.isAnyEncryptedString(encrypted)) {
|
||||||
|
logger.info(secDispatcher.decrypt(encrypted));
|
||||||
|
return OK;
|
||||||
|
} else {
|
||||||
|
logger.error("Malformed encrypted string");
|
||||||
|
return BAD_OPERATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.maven.cling.invoker.mvnenc.goals;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import org.apache.maven.api.services.MessageBuilderFactory;
|
||||||
|
import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
|
||||||
|
|
||||||
|
import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.OK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "diag" goal.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
@Named("diag")
|
||||||
|
public class Diag extends ConfiguredGoalSupport {
|
||||||
|
@Inject
|
||||||
|
public Diag(MessageBuilderFactory messageBuilderFactory, SecDispatcher secDispatcher) {
|
||||||
|
super(messageBuilderFactory, secDispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doExecute(DefaultEncryptInvoker.LocalContext context) {
|
||||||
|
dumpResponse("", secDispatcher.validateConfiguration());
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.apache.maven.cling.invoker.mvnenc.goals;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import org.apache.maven.api.services.MessageBuilderFactory;
|
||||||
|
import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
|
||||||
|
|
||||||
|
import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.OK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "encrypt" goal.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
@Named("encrypt")
|
||||||
|
public class Encrypt extends ConfiguredGoalSupport {
|
||||||
|
@Inject
|
||||||
|
public Encrypt(MessageBuilderFactory messageBuilderFactory, SecDispatcher secDispatcher) {
|
||||||
|
super(messageBuilderFactory, secDispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doExecute(DefaultEncryptInvoker.LocalContext context) throws Exception {
|
||||||
|
String cleartext = context.reader.readLine("Enter the password to encrypt: ", '*');
|
||||||
|
logger.info(secDispatcher.encrypt(cleartext, null));
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.maven.cling.invoker.mvnenc.goals;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.maven.api.services.MessageBuilderFactory;
|
||||||
|
import org.apache.maven.cling.invoker.mvnenc.Goal;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The support class for goal implementations.
|
||||||
|
*/
|
||||||
|
public abstract class GoalSupport implements Goal {
|
||||||
|
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||||
|
protected final MessageBuilderFactory messageBuilderFactory;
|
||||||
|
protected final SecDispatcher secDispatcher;
|
||||||
|
|
||||||
|
protected GoalSupport(MessageBuilderFactory messageBuilderFactory, SecDispatcher secDispatcher) {
|
||||||
|
this.messageBuilderFactory = messageBuilderFactory;
|
||||||
|
this.secDispatcher = secDispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean configExists() throws IOException {
|
||||||
|
return secDispatcher.readConfiguration(false) != null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,275 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.maven.cling.invoker.mvnenc.goals;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.apache.maven.api.services.MessageBuilderFactory;
|
||||||
|
import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.DispatcherMeta;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
|
||||||
|
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.jline.consoleui.elements.ConfirmChoice;
|
||||||
|
import org.jline.consoleui.prompt.ConfirmResult;
|
||||||
|
import org.jline.consoleui.prompt.ConsolePrompt;
|
||||||
|
import org.jline.consoleui.prompt.PromptResultItemIF;
|
||||||
|
import org.jline.consoleui.prompt.builder.ListPromptBuilder;
|
||||||
|
import org.jline.consoleui.prompt.builder.PromptBuilder;
|
||||||
|
import org.jline.reader.Candidate;
|
||||||
|
import org.jline.reader.Completer;
|
||||||
|
import org.jline.reader.LineReader;
|
||||||
|
import org.jline.reader.ParsedLine;
|
||||||
|
import org.jline.utils.Colors;
|
||||||
|
|
||||||
|
import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.BAD_OPERATION;
|
||||||
|
import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.OK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "init" goal.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
@Named("init")
|
||||||
|
public class Init extends GoalSupport {
|
||||||
|
private static final String NONE = "__none__";
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public Init(MessageBuilderFactory messageBuilderFactory, SecDispatcher secDispatcher) {
|
||||||
|
super(messageBuilderFactory, secDispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int execute(DefaultEncryptInvoker.LocalContext context) throws Exception {
|
||||||
|
context.addInHeader(context.style.italic().bold().foreground(Colors.rgbColor("yellow")), "goal: init");
|
||||||
|
context.addInHeader("");
|
||||||
|
|
||||||
|
ConsolePrompt prompt = context.prompt;
|
||||||
|
boolean force = context.invokerRequest.options().force().orElse(false);
|
||||||
|
boolean yes = context.invokerRequest.options().yes().orElse(false);
|
||||||
|
|
||||||
|
if (configExists() && !force) {
|
||||||
|
System.out.println(messageBuilderFactory
|
||||||
|
.builder()
|
||||||
|
.error("Error: configuration exist. Use --force if you want to reset existing configuration."));
|
||||||
|
return BAD_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSecurity config = secDispatcher.readConfiguration(true);
|
||||||
|
|
||||||
|
// reset config
|
||||||
|
config.setDefaultDispatcher(null);
|
||||||
|
config.getConfigurations().clear();
|
||||||
|
|
||||||
|
Map<String, PromptResultItemIF> result = prompt.prompt(
|
||||||
|
context.header, dispatcherPrompt(prompt.getPromptBuilder()).build());
|
||||||
|
if (result == null) {
|
||||||
|
throw new InterruptedException();
|
||||||
|
}
|
||||||
|
if (NONE.equals(result.get("defaultDispatcher").getResult())) {
|
||||||
|
logger.warn(messageBuilderFactory
|
||||||
|
.builder()
|
||||||
|
.warning(
|
||||||
|
"Maven4 SecDispatcher disabled; Maven3 fallback may still work, use `mvnenc diag` to check")
|
||||||
|
.build());
|
||||||
|
secDispatcher.writeConfiguration(config);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
config.setDefaultDispatcher(result.get("defaultDispatcher").getResult());
|
||||||
|
|
||||||
|
DispatcherMeta meta = secDispatcher.availableDispatchers().stream()
|
||||||
|
.filter(d -> Objects.equals(config.getDefaultDispatcher(), d.name()))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow();
|
||||||
|
if (!meta.fields().isEmpty()) {
|
||||||
|
result = prompt.prompt(
|
||||||
|
context.header,
|
||||||
|
configureDispatcher(context, meta, prompt.getPromptBuilder())
|
||||||
|
.build());
|
||||||
|
if (result == null) {
|
||||||
|
throw new InterruptedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map.Entry<String, PromptResultItemIF>> editables = result.entrySet().stream()
|
||||||
|
.filter(e -> e.getValue().getResult().contains("$"))
|
||||||
|
.toList();
|
||||||
|
if (!editables.isEmpty()) {
|
||||||
|
context.addInHeader("");
|
||||||
|
context.addInHeader("Please customize the editable value:");
|
||||||
|
Map<String, PromptResultItemIF> editMap;
|
||||||
|
for (Map.Entry<String, PromptResultItemIF> editable : editables) {
|
||||||
|
String template = editable.getValue().getResult();
|
||||||
|
String prefix = template.substring(0, template.indexOf("$"));
|
||||||
|
editMap = prompt.prompt(
|
||||||
|
context.header,
|
||||||
|
prompt.getPromptBuilder()
|
||||||
|
.createInputPrompt()
|
||||||
|
.name("edit")
|
||||||
|
.message(template)
|
||||||
|
.addCompleter(new Completer() {
|
||||||
|
@Override
|
||||||
|
public void complete(
|
||||||
|
LineReader reader, ParsedLine line, List<Candidate> candidates) {
|
||||||
|
if (!line.line().startsWith(prefix)) {
|
||||||
|
candidates.add(
|
||||||
|
new Candidate(prefix, prefix, null, null, null, null, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addPrompt()
|
||||||
|
.build());
|
||||||
|
if (editMap == null) {
|
||||||
|
throw new InterruptedException();
|
||||||
|
}
|
||||||
|
result.put(editable.getKey(), editMap.get("edit"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Config dispatcherConfig = new Config();
|
||||||
|
dispatcherConfig.setName(meta.name());
|
||||||
|
for (DispatcherMeta.Field field : meta.fields()) {
|
||||||
|
ConfigProperty property = new ConfigProperty();
|
||||||
|
property.setName(field.getKey());
|
||||||
|
property.setValue(result.get(field.getKey()).getResult());
|
||||||
|
dispatcherConfig.addProperty(property);
|
||||||
|
}
|
||||||
|
if (!dispatcherConfig.getProperties().isEmpty()) {
|
||||||
|
config.addConfiguration(dispatcherConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (yes) {
|
||||||
|
secDispatcher.writeConfiguration(config);
|
||||||
|
} else {
|
||||||
|
context.addInHeader("");
|
||||||
|
context.addInHeader("Values set:");
|
||||||
|
context.addInHeader("defaultDispatcher=" + config.getDefaultDispatcher());
|
||||||
|
for (Config c : config.getConfigurations()) {
|
||||||
|
context.addInHeader(" dispatcherName=" + c.getName());
|
||||||
|
for (ConfigProperty cp : c.getProperties()) {
|
||||||
|
context.addInHeader(" " + cp.getName() + "=" + cp.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = prompt.prompt(
|
||||||
|
context.header, confirmPrompt(prompt.getPromptBuilder()).build());
|
||||||
|
ConfirmResult confirm = (ConfirmResult) result.get("confirm");
|
||||||
|
if (confirm.getConfirmed() == ConfirmChoice.ConfirmationValue.YES) {
|
||||||
|
logger.info(messageBuilderFactory
|
||||||
|
.builder()
|
||||||
|
.info("Writing out the configuration...")
|
||||||
|
.build());
|
||||||
|
secDispatcher.writeConfiguration(config);
|
||||||
|
} else {
|
||||||
|
logger.warn(messageBuilderFactory
|
||||||
|
.builder()
|
||||||
|
.warning("Values not accepted; not saving configuration.")
|
||||||
|
.build());
|
||||||
|
return BAD_OPERATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PromptBuilder confirmPrompt(PromptBuilder promptBuilder) {
|
||||||
|
promptBuilder
|
||||||
|
.createConfirmPromp()
|
||||||
|
.name("confirm")
|
||||||
|
.message("Are values above correct?")
|
||||||
|
.defaultValue(ConfirmChoice.ConfirmationValue.YES)
|
||||||
|
.addPrompt();
|
||||||
|
return promptBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PromptBuilder dispatcherPrompt(PromptBuilder promptBuilder) {
|
||||||
|
ListPromptBuilder listPromptBuilder = promptBuilder
|
||||||
|
.createListPrompt()
|
||||||
|
.name("defaultDispatcher")
|
||||||
|
.message("Which dispatcher you want to use as default?");
|
||||||
|
listPromptBuilder
|
||||||
|
.newItem()
|
||||||
|
.name(NONE)
|
||||||
|
.text("None (disable MavenSecDispatcher)")
|
||||||
|
.add();
|
||||||
|
for (DispatcherMeta meta : secDispatcher.availableDispatchers()) {
|
||||||
|
if (!meta.isHidden()) {
|
||||||
|
listPromptBuilder
|
||||||
|
.newItem()
|
||||||
|
.name(meta.name())
|
||||||
|
.text(meta.displayName())
|
||||||
|
.add();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listPromptBuilder.addPrompt();
|
||||||
|
return promptBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PromptBuilder configureDispatcher(
|
||||||
|
DefaultEncryptInvoker.LocalContext context, DispatcherMeta dispatcherMeta, PromptBuilder promptBuilder)
|
||||||
|
throws Exception {
|
||||||
|
context.addInHeader(
|
||||||
|
context.style.italic().bold().foreground(Colors.rgbColor("yellow")),
|
||||||
|
"Configure " + dispatcherMeta.displayName());
|
||||||
|
context.addInHeader("");
|
||||||
|
|
||||||
|
for (DispatcherMeta.Field field : dispatcherMeta.fields()) {
|
||||||
|
String fieldKey = field.getKey();
|
||||||
|
String fieldDescription = "Configure " + fieldKey + ": " + field.getDescription();
|
||||||
|
if (field.getOptions().isPresent()) {
|
||||||
|
// list options
|
||||||
|
ListPromptBuilder listPromptBuilder =
|
||||||
|
promptBuilder.createListPrompt().name(fieldKey).message(fieldDescription);
|
||||||
|
for (DispatcherMeta.Field option : field.getOptions().get()) {
|
||||||
|
listPromptBuilder
|
||||||
|
.newItem()
|
||||||
|
.name(
|
||||||
|
option.getDefaultValue().isPresent()
|
||||||
|
? option.getDefaultValue().get()
|
||||||
|
: option.getKey())
|
||||||
|
.text(option.getDescription())
|
||||||
|
.add();
|
||||||
|
}
|
||||||
|
listPromptBuilder.addPrompt();
|
||||||
|
} else if (field.getDefaultValue().isPresent()) {
|
||||||
|
// input w/ def value
|
||||||
|
promptBuilder
|
||||||
|
.createInputPrompt()
|
||||||
|
.name(fieldKey)
|
||||||
|
.message(fieldDescription)
|
||||||
|
.defaultValue(field.getDefaultValue().get())
|
||||||
|
.addPrompt();
|
||||||
|
} else {
|
||||||
|
// ? plain input?
|
||||||
|
promptBuilder
|
||||||
|
.createInputPrompt()
|
||||||
|
.name(fieldKey)
|
||||||
|
.message(fieldDescription)
|
||||||
|
.addPrompt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return promptBuilder;
|
||||||
|
}
|
||||||
|
}
|
|
@ -214,10 +214,15 @@ public class DefaultRepositorySystemSessionFactory implements RepositorySystemSe
|
||||||
decrypt.setProxies(request.getProxies());
|
decrypt.setProxies(request.getProxies());
|
||||||
decrypt.setServers(request.getServers());
|
decrypt.setServers(request.getServers());
|
||||||
SettingsDecryptionResult decrypted = settingsDecrypter.decrypt(decrypt);
|
SettingsDecryptionResult decrypted = settingsDecrypter.decrypt(decrypt);
|
||||||
|
for (SettingsProblem problem : decrypted.getProblems()) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (problem.getSeverity() == SettingsProblem.Severity.WARNING) {
|
||||||
for (SettingsProblem problem : decrypted.getProblems()) {
|
logger.warn(problem.getMessage());
|
||||||
logger.debug(problem.getMessage(), problem.getException());
|
} else if (problem.getSeverity() == SettingsProblem.Severity.ERROR) {
|
||||||
|
logger.error(
|
||||||
|
problem.getMessage(),
|
||||||
|
request.isShowErrors()
|
||||||
|
? problem.getException()
|
||||||
|
: problem.getException().getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,10 +88,6 @@ under the License.
|
||||||
<groupId>org.codehaus.plexus</groupId>
|
<groupId>org.codehaus.plexus</groupId>
|
||||||
<artifactId>plexus-sec-dispatcher</artifactId>
|
<artifactId>plexus-sec-dispatcher</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.codehaus.plexus</groupId>
|
|
||||||
<artifactId>plexus-cipher</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.codehaus.plexus</groupId>
|
<groupId>org.codehaus.plexus</groupId>
|
||||||
<artifactId>plexus-interpolation</artifactId>
|
<artifactId>plexus-interpolation</artifactId>
|
||||||
|
|
|
@ -42,6 +42,18 @@ under the License.
|
||||||
<groupId>org.jline</groupId>
|
<groupId>org.jline</groupId>
|
||||||
<artifactId>jline-reader</artifactId>
|
<artifactId>jline-reader</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jline</groupId>
|
||||||
|
<artifactId>jline-style</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jline</groupId>
|
||||||
|
<artifactId>jline-builtins</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jline</groupId>
|
||||||
|
<artifactId>jline-console-ui</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jline</groupId>
|
<groupId>org.jline</groupId>
|
||||||
<artifactId>jline-terminal-jni</artifactId>
|
<artifactId>jline-terminal-jni</artifactId>
|
||||||
|
|
|
@ -62,10 +62,6 @@ under the License.
|
||||||
<groupId>org.codehaus.plexus</groupId>
|
<groupId>org.codehaus.plexus</groupId>
|
||||||
<artifactId>plexus-sec-dispatcher</artifactId>
|
<artifactId>plexus-sec-dispatcher</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.codehaus.plexus</groupId>
|
|
||||||
<artifactId>plexus-cipher</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.inject</groupId>
|
<groupId>javax.inject</groupId>
|
||||||
|
|
|
@ -22,6 +22,7 @@ import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -58,28 +59,52 @@ public class DefaultSettingsDecrypter implements SettingsDecrypter {
|
||||||
for (Server server : request.getServers()) {
|
for (Server server : request.getServers()) {
|
||||||
server = server.clone();
|
server = server.clone();
|
||||||
|
|
||||||
try {
|
String password = server.getPassword();
|
||||||
server.setPassword(decrypt(server.getPassword()));
|
if (securityDispatcher.isAnyEncryptedString(password)) {
|
||||||
} catch (SecDispatcherException e) {
|
try {
|
||||||
problems.add(new DefaultSettingsProblem(
|
if (securityDispatcher.isLegacyEncryptedString(password)) {
|
||||||
"Failed to decrypt password for server " + server.getId() + ": " + e.getMessage(),
|
problems.add(new DefaultSettingsProblem(
|
||||||
Severity.ERROR,
|
"Legacy/insecurely encrypted password detected for server " + server.getId(),
|
||||||
"server: " + server.getId(),
|
Severity.WARNING,
|
||||||
-1,
|
"server: " + server.getId(),
|
||||||
-1,
|
-1,
|
||||||
e));
|
-1,
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
server.setPassword(securityDispatcher.decrypt(password));
|
||||||
|
} catch (SecDispatcherException | IOException e) {
|
||||||
|
problems.add(new DefaultSettingsProblem(
|
||||||
|
"Failed to decrypt password for server " + server.getId() + ": " + e.getMessage(),
|
||||||
|
Severity.ERROR,
|
||||||
|
"server: " + server.getId(),
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
String passphrase = server.getPassphrase();
|
||||||
server.setPassphrase(decrypt(server.getPassphrase()));
|
if (securityDispatcher.isAnyEncryptedString(passphrase)) {
|
||||||
} catch (SecDispatcherException e) {
|
try {
|
||||||
problems.add(new DefaultSettingsProblem(
|
if (securityDispatcher.isLegacyEncryptedString(passphrase)) {
|
||||||
"Failed to decrypt passphrase for server " + server.getId() + ": " + e.getMessage(),
|
problems.add(new DefaultSettingsProblem(
|
||||||
Severity.ERROR,
|
"Legacy/insecurely encrypted passphrase detected for server " + server.getId(),
|
||||||
"server: " + server.getId(),
|
Severity.WARNING,
|
||||||
-1,
|
"server: " + server.getId(),
|
||||||
-1,
|
-1,
|
||||||
e));
|
-1,
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
server.setPassphrase(securityDispatcher.decrypt(passphrase));
|
||||||
|
} catch (SecDispatcherException | IOException e) {
|
||||||
|
problems.add(new DefaultSettingsProblem(
|
||||||
|
"Failed to decrypt passphrase for server " + server.getId() + ": " + e.getMessage(),
|
||||||
|
Severity.ERROR,
|
||||||
|
"server: " + server.getId(),
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
servers.add(server);
|
servers.add(server);
|
||||||
|
@ -88,16 +113,28 @@ public class DefaultSettingsDecrypter implements SettingsDecrypter {
|
||||||
List<Proxy> proxies = new ArrayList<>();
|
List<Proxy> proxies = new ArrayList<>();
|
||||||
|
|
||||||
for (Proxy proxy : request.getProxies()) {
|
for (Proxy proxy : request.getProxies()) {
|
||||||
try {
|
String password = proxy.getPassword();
|
||||||
proxy.setPassword(decrypt(proxy.getPassword()));
|
if (securityDispatcher.isAnyEncryptedString(password)) {
|
||||||
} catch (SecDispatcherException e) {
|
try {
|
||||||
problems.add(new DefaultSettingsProblem(
|
if (securityDispatcher.isLegacyEncryptedString(password)) {
|
||||||
"Failed to decrypt password for proxy " + proxy.getId() + ": " + e.getMessage(),
|
problems.add(new DefaultSettingsProblem(
|
||||||
Severity.ERROR,
|
"Legacy/insecurely encrypted password detected for proxy " + proxy.getId(),
|
||||||
"proxy: " + proxy.getId(),
|
Severity.WARNING,
|
||||||
-1,
|
"proxy: " + proxy.getId(),
|
||||||
-1,
|
-1,
|
||||||
e));
|
-1,
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
proxy.setPassword(securityDispatcher.decrypt(password));
|
||||||
|
} catch (SecDispatcherException | IOException e) {
|
||||||
|
problems.add(new DefaultSettingsProblem(
|
||||||
|
"Failed to decrypt password for proxy " + proxy.getId() + ": " + e.getMessage(),
|
||||||
|
Severity.ERROR,
|
||||||
|
"proxy: " + proxy.getId(),
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proxies.add(proxy);
|
proxies.add(proxy);
|
||||||
|
@ -105,8 +142,4 @@ public class DefaultSettingsDecrypter implements SettingsDecrypter {
|
||||||
|
|
||||||
return new DefaultSettingsDecryptionResult(servers, proxies, problems);
|
return new DefaultSettingsDecryptionResult(servers, proxies, problems);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String decrypt(String str) throws SecDispatcherException {
|
|
||||||
return (str == null) ? null : securityDispatcher.decrypt(str);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.maven.settings.crypto;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.maven.api.Constants;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.Dispatcher;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
|
||||||
|
import org.codehaus.plexus.components.secdispatcher.internal.DefaultSecDispatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements "Maven specific" {@link SecDispatcher}.
|
||||||
|
*
|
||||||
|
* @deprecated since 4.0.0
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
@Deprecated(since = "4.0.0")
|
||||||
|
public class MavenSecDispatcher extends DefaultSecDispatcher {
|
||||||
|
private static final String FILE_NAME = "settings-security4.xml";
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public MavenSecDispatcher(Map<String, Dispatcher> dispatchers) {
|
||||||
|
super(dispatchers, configurationFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path configurationFile() {
|
||||||
|
String mavenUserConf = System.getProperty(Constants.MAVEN_USER_CONF);
|
||||||
|
if (mavenUserConf != null) {
|
||||||
|
return Paths.get(mavenUserConf, FILE_NAME);
|
||||||
|
}
|
||||||
|
// this means we are in UT or alike
|
||||||
|
return Paths.get(System.getProperty("user.home"), ".m2", FILE_NAME);
|
||||||
|
}
|
||||||
|
}
|
23
pom.xml
23
pom.xml
|
@ -167,7 +167,6 @@ under the License.
|
||||||
<assertjVersion>3.26.3</assertjVersion>
|
<assertjVersion>3.26.3</assertjVersion>
|
||||||
<asmVersion>9.7.1</asmVersion>
|
<asmVersion>9.7.1</asmVersion>
|
||||||
<byteBuddyVersion>1.15.3</byteBuddyVersion>
|
<byteBuddyVersion>1.15.3</byteBuddyVersion>
|
||||||
<cipherVersion>3.0.0</cipherVersion>
|
|
||||||
<classWorldsVersion>2.8.0</classWorldsVersion>
|
<classWorldsVersion>2.8.0</classWorldsVersion>
|
||||||
<commonsCliVersion>1.9.0</commonsCliVersion>
|
<commonsCliVersion>1.9.0</commonsCliVersion>
|
||||||
<guiceVersion>6.0.0</guiceVersion>
|
<guiceVersion>6.0.0</guiceVersion>
|
||||||
|
@ -186,7 +185,7 @@ under the License.
|
||||||
<plexusTestingVersion>1.4.0</plexusTestingVersion>
|
<plexusTestingVersion>1.4.0</plexusTestingVersion>
|
||||||
<plexusXmlVersion>4.0.4</plexusXmlVersion>
|
<plexusXmlVersion>4.0.4</plexusXmlVersion>
|
||||||
<resolverVersion>2.0.1</resolverVersion>
|
<resolverVersion>2.0.1</resolverVersion>
|
||||||
<securityDispatcherVersion>3.0.0</securityDispatcherVersion>
|
<securityDispatcherVersion>4.0.1</securityDispatcherVersion>
|
||||||
<sisuVersion>0.9.0.M3</sisuVersion>
|
<sisuVersion>0.9.0.M3</sisuVersion>
|
||||||
<slf4jVersion>2.0.16</slf4jVersion>
|
<slf4jVersion>2.0.16</slf4jVersion>
|
||||||
<stax2ApiVersion>4.2.2</stax2ApiVersion>
|
<stax2ApiVersion>4.2.2</stax2ApiVersion>
|
||||||
|
@ -475,6 +474,21 @@ under the License.
|
||||||
<artifactId>jline-reader</artifactId>
|
<artifactId>jline-reader</artifactId>
|
||||||
<version>${jlineVersion}</version>
|
<version>${jlineVersion}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jline</groupId>
|
||||||
|
<artifactId>jline-style</artifactId>
|
||||||
|
<version>${jlineVersion}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jline</groupId>
|
||||||
|
<artifactId>jline-builtins</artifactId>
|
||||||
|
<version>${jlineVersion}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jline</groupId>
|
||||||
|
<artifactId>jline-console-ui</artifactId>
|
||||||
|
<version>${jlineVersion}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jline</groupId>
|
<groupId>org.jline</groupId>
|
||||||
<artifactId>jline-terminal-ffm</artifactId>
|
<artifactId>jline-terminal-ffm</artifactId>
|
||||||
|
@ -590,11 +604,6 @@ under the License.
|
||||||
<artifactId>plexus-sec-dispatcher</artifactId>
|
<artifactId>plexus-sec-dispatcher</artifactId>
|
||||||
<version>${securityDispatcherVersion}</version>
|
<version>${securityDispatcherVersion}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.codehaus.plexus</groupId>
|
|
||||||
<artifactId>plexus-cipher</artifactId>
|
|
||||||
<version>${cipherVersion}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.woodstox</groupId>
|
<groupId>com.fasterxml.woodstox</groupId>
|
||||||
<artifactId>woodstox-core</artifactId>
|
<artifactId>woodstox-core</artifactId>
|
||||||
|
|
Loading…
Reference in New Issue