1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.cling.invoker.mvnenc;
20
21 import java.io.InterruptedIOException;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.apache.maven.api.cli.mvnenc.EncryptInvoker;
27 import org.apache.maven.api.cli.mvnenc.EncryptInvokerRequest;
28 import org.apache.maven.api.cli.mvnenc.EncryptOptions;
29 import org.apache.maven.cli.CLIReportingUtils;
30 import org.apache.maven.cling.invoker.LookupInvoker;
31 import org.apache.maven.cling.invoker.ProtoLookup;
32 import org.jline.consoleui.prompt.ConsolePrompt;
33 import org.jline.reader.LineReader;
34 import org.jline.reader.LineReaderBuilder;
35 import org.jline.reader.UserInterruptException;
36 import org.jline.terminal.Terminal;
37 import org.jline.utils.AttributedString;
38 import org.jline.utils.AttributedStringBuilder;
39 import org.jline.utils.AttributedStyle;
40 import org.jline.utils.Colors;
41 import org.jline.utils.OSUtils;
42
43
44
45
46
47 public class DefaultEncryptInvoker
48 extends LookupInvoker<EncryptOptions, EncryptInvokerRequest, DefaultEncryptInvoker.LocalContext>
49 implements EncryptInvoker {
50
51 @SuppressWarnings("VisibilityModifier")
52 public static class LocalContext
53 extends LookupInvokerContext<EncryptOptions, EncryptInvokerRequest, DefaultEncryptInvoker.LocalContext> {
54 protected LocalContext(DefaultEncryptInvoker invoker, EncryptInvokerRequest invokerRequest) {
55 super(invoker, invokerRequest);
56 }
57
58 public Map<String, Goal> goals;
59
60 public List<AttributedString> header;
61 public AttributedStyle style;
62 public LineReader reader;
63 public ConsolePrompt prompt;
64
65 public void addInHeader(String text) {
66 addInHeader(AttributedStyle.DEFAULT, text);
67 }
68
69 public void addInHeader(AttributedStyle style, String text) {
70 AttributedStringBuilder asb = new AttributedStringBuilder();
71 asb.style(style).append(text);
72 header.add(asb.toAttributedString());
73 }
74 }
75
76 public DefaultEncryptInvoker(ProtoLookup protoLookup) {
77 super(protoLookup);
78 }
79
80 @Override
81 protected int execute(LocalContext context) throws Exception {
82 return doExecute(context);
83 }
84
85 @Override
86 protected LocalContext createContext(EncryptInvokerRequest invokerRequest) {
87 return new LocalContext(this, invokerRequest);
88 }
89
90 @Override
91 protected void lookup(LocalContext context) {
92 context.goals = context.lookup.lookupMap(Goal.class);
93 }
94
95 public static final int OK = 0;
96 public static final int ERROR = 1;
97 public static final int BAD_OPERATION = 2;
98 public static final int CANCELED = 3;
99
100 protected int doExecute(LocalContext context) throws Exception {
101 try {
102 if (!context.interactive) {
103 context.terminal.writer().println("This tool works only in interactive mode!");
104 context.terminal
105 .writer()
106 .println("Tool purpose is to configure password management on developer workstations.");
107 context.terminal
108 .writer()
109 .println(
110 "Note: Generated configuration can be moved/copied to headless environments, if configured as such.");
111 return BAD_OPERATION;
112 }
113
114 context.header = new ArrayList<>();
115 context.style = new AttributedStyle();
116 context.addInHeader(
117 context.style.italic().bold().foreground(Colors.rgbColor("green")),
118 "Maven Encryption " + CLIReportingUtils.showVersionMinimal());
119 context.addInHeader("Tool for secure password management on workstations.");
120 context.addInHeader("This tool is part of Apache Maven 4 distribution.");
121 context.addInHeader("");
122
123 Thread executeThread = Thread.currentThread();
124 context.terminal.handle(Terminal.Signal.INT, signal -> executeThread.interrupt());
125 ConsolePrompt.UiConfig config;
126 if (context.terminal.getType().equals(Terminal.TYPE_DUMB)
127 || context.terminal.getType().equals(Terminal.TYPE_DUMB_COLOR)) {
128 context.terminal.writer().println(context.terminal.getName() + ": " + context.terminal.getType());
129 throw new IllegalStateException("Dumb terminal detected.\nThis tool requires real terminal to work!\n"
130 + "Note: On Windows Jansi or JNA library must be included in classpath.");
131 } else if (OSUtils.IS_WINDOWS) {
132 config = new ConsolePrompt.UiConfig(">", "( )", "(x)", "( )");
133 } else {
134 config = new ConsolePrompt.UiConfig("❯", "◯ ", "◉ ", "◯ ");
135 }
136 config.setCancellableFirstPrompt(true);
137
138 context.reader =
139 LineReaderBuilder.builder().terminal(context.terminal).build();
140 context.prompt = new ConsolePrompt(context.reader, context.terminal, config);
141
142 if (context.invokerRequest.options().goals().isEmpty()
143 || context.invokerRequest.options().goals().get().size() != 1) {
144 return badGoalsErrorMessage("No goal or multiple goals specified, specify only one goal.", context);
145 }
146
147 String goalName = context.invokerRequest.options().goals().get().get(0);
148 Goal goal = context.goals.get(goalName);
149
150 if (goal == null) {
151 return badGoalsErrorMessage("Unknown goal: " + goalName, context);
152 }
153
154 return goal.execute(context);
155 } catch (InterruptedException | InterruptedIOException | UserInterruptException e) {
156 context.terminal.writer().println("Goal canceled by user.");
157 return CANCELED;
158 } catch (Exception e) {
159 if (context.invokerRequest.options().showErrors().orElse(false)) {
160 context.terminal.writer().println(e.getMessage());
161 e.printStackTrace(context.terminal.writer());
162 } else {
163 context.terminal.writer().println(e.getMessage());
164 }
165 return ERROR;
166 } finally {
167 context.terminal.writer().flush();
168 }
169 }
170
171 protected int badGoalsErrorMessage(String message, LocalContext context) {
172 context.terminal.writer().println(message);
173 context.terminal.writer().println("Supported goals are: " + String.join(", ", context.goals.keySet()));
174 context.terminal.writer().println("Use -h to display help.");
175 return BAD_OPERATION;
176 }
177 }