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.goals;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Objects;
28
29 import org.apache.maven.api.cli.mvnenc.EncryptOptions;
30 import org.apache.maven.api.services.MessageBuilderFactory;
31 import org.apache.maven.cling.invoker.mvnenc.EncryptContext;
32 import org.codehaus.plexus.components.secdispatcher.DispatcherMeta;
33 import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
34 import org.codehaus.plexus.components.secdispatcher.model.Config;
35 import org.codehaus.plexus.components.secdispatcher.model.ConfigProperty;
36 import org.codehaus.plexus.components.secdispatcher.model.SettingsSecurity;
37 import org.jline.consoleui.elements.ConfirmChoice;
38 import org.jline.consoleui.prompt.ConfirmResult;
39 import org.jline.consoleui.prompt.ConsolePrompt;
40 import org.jline.consoleui.prompt.PromptResultItemIF;
41 import org.jline.consoleui.prompt.builder.ListPromptBuilder;
42 import org.jline.consoleui.prompt.builder.PromptBuilder;
43 import org.jline.reader.Candidate;
44 import org.jline.reader.Completer;
45 import org.jline.reader.LineReader;
46 import org.jline.reader.ParsedLine;
47 import org.jline.utils.Colors;
48 import org.jline.utils.OSUtils;
49
50 import static org.apache.maven.cling.invoker.mvnenc.EncryptInvoker.BAD_OPERATION;
51 import static org.apache.maven.cling.invoker.mvnenc.EncryptInvoker.OK;
52
53
54
55
56 @Singleton
57 @Named("init")
58 public class Init extends InteractiveGoalSupport {
59 private static final String NONE = "__none__";
60
61 @Inject
62 public Init(MessageBuilderFactory messageBuilderFactory, SecDispatcher secDispatcher) {
63 super(messageBuilderFactory, secDispatcher);
64 }
65
66 @Override
67 public int doExecute(EncryptContext context) throws Exception {
68 EncryptOptions options = (EncryptOptions) context.invokerRequest.options();
69 boolean force = options.force().orElse(false);
70 boolean yes = options.yes().orElse(false);
71
72 if (configExists() && !force) {
73 context.logger.error(messageBuilderFactory
74 .builder()
75 .error("Error: configuration exist. Use --force if you want to reset existing configuration.")
76 .build());
77 return BAD_OPERATION;
78 }
79
80 context.addInHeader(context.style.italic().bold().foreground(Colors.rgbColor("yellow")), "goal: init");
81 context.addInHeader("");
82
83 ConsolePrompt.UiConfig promptConfig;
84 if (OSUtils.IS_WINDOWS) {
85 promptConfig = new ConsolePrompt.UiConfig(">", "( )", "(x)", "( )");
86 } else {
87 promptConfig = new ConsolePrompt.UiConfig("❯", "◯ ", "◉ ", "◯ ");
88 }
89 promptConfig.setCancellableFirstPrompt(true);
90 ConsolePrompt prompt = new ConsolePrompt(context.reader, context.terminal, promptConfig);
91
92 SettingsSecurity config = secDispatcher.readConfiguration(true);
93
94
95 config.setDefaultDispatcher(null);
96 config.getConfigurations().clear();
97
98 Map<String, PromptResultItemIF> result = prompt.prompt(
99 context.header, dispatcherPrompt(prompt.getPromptBuilder()).build());
100 if (result == null) {
101 throw new InterruptedException();
102 }
103 if (NONE.equals(result.get("defaultDispatcher").getResult())) {
104 context.terminal
105 .writer()
106 .println(messageBuilderFactory
107 .builder()
108 .warning(
109 "Maven4 SecDispatcher disabled; Maven3 fallback may still work, use `mvnenc diag` to check")
110 .build());
111 secDispatcher.writeConfiguration(config);
112 return OK;
113 }
114 config.setDefaultDispatcher(result.get("defaultDispatcher").getResult());
115
116 DispatcherMeta meta = secDispatcher.availableDispatchers().stream()
117 .filter(d -> Objects.equals(config.getDefaultDispatcher(), d.name()))
118 .findFirst()
119 .orElseThrow();
120 if (!meta.fields().isEmpty()) {
121 result = prompt.prompt(
122 context.header,
123 configureDispatcher(context, meta, prompt.getPromptBuilder())
124 .build());
125 if (result == null) {
126 throw new InterruptedException();
127 }
128
129 List<Map.Entry<String, PromptResultItemIF>> editables = result.entrySet().stream()
130 .filter(e -> e.getValue().getResult().contains("$"))
131 .toList();
132 if (!editables.isEmpty()) {
133 context.addInHeader("");
134 context.addInHeader("Please customize the editable value:");
135 Map<String, PromptResultItemIF> editMap;
136 for (Map.Entry<String, PromptResultItemIF> editable : editables) {
137 String template = editable.getValue().getResult();
138 String prefix = template.substring(0, template.indexOf("$"));
139 editMap = prompt.prompt(
140 context.header,
141 prompt.getPromptBuilder()
142 .createInputPrompt()
143 .name("edit")
144 .message(template)
145 .addCompleter(new Completer() {
146 @Override
147 public void complete(
148 LineReader reader, ParsedLine line, List<Candidate> candidates) {
149 if (!line.line().startsWith(prefix)) {
150 candidates.add(
151 new Candidate(prefix, prefix, null, null, null, null, false));
152 }
153 }
154 })
155 .addPrompt()
156 .build());
157 if (editMap == null) {
158 throw new InterruptedException();
159 }
160 result.put(editable.getKey(), editMap.get("edit"));
161 }
162 }
163
164 Config dispatcherConfig = new Config();
165 dispatcherConfig.setName(meta.name());
166 for (DispatcherMeta.Field field : meta.fields()) {
167 ConfigProperty property = new ConfigProperty();
168 property.setName(field.getKey());
169 property.setValue(result.get(field.getKey()).getResult());
170 dispatcherConfig.addProperty(property);
171 }
172 if (!dispatcherConfig.getProperties().isEmpty()) {
173 config.addConfiguration(dispatcherConfig);
174 }
175 }
176
177 if (yes) {
178 secDispatcher.writeConfiguration(config);
179 } else {
180 context.addInHeader("");
181 context.addInHeader("Values set:");
182 context.addInHeader("defaultDispatcher=" + config.getDefaultDispatcher());
183 for (Config c : config.getConfigurations()) {
184 context.addInHeader(" dispatcherName=" + c.getName());
185 for (ConfigProperty cp : c.getProperties()) {
186 context.addInHeader(" " + cp.getName() + "=" + cp.getValue());
187 }
188 }
189
190 result = prompt.prompt(
191 context.header, confirmPrompt(prompt.getPromptBuilder()).build());
192 ConfirmResult confirm = (ConfirmResult) result.get("confirm");
193 if (confirm.getConfirmed() == ConfirmChoice.ConfirmationValue.YES) {
194 context.terminal
195 .writer()
196 .println(messageBuilderFactory
197 .builder()
198 .info("Writing out the configuration...")
199 .build());
200 secDispatcher.writeConfiguration(config);
201 } else {
202 context.terminal
203 .writer()
204 .println(messageBuilderFactory
205 .builder()
206 .warning("Values not accepted; not saving configuration.")
207 .build());
208 return BAD_OPERATION;
209 }
210 }
211
212 return OK;
213 }
214
215 protected PromptBuilder confirmPrompt(PromptBuilder promptBuilder) {
216 promptBuilder
217 .createConfirmPromp()
218 .name("confirm")
219 .message("Are values above correct?")
220 .defaultValue(ConfirmChoice.ConfirmationValue.YES)
221 .addPrompt();
222 return promptBuilder;
223 }
224
225 protected PromptBuilder dispatcherPrompt(PromptBuilder promptBuilder) {
226 ListPromptBuilder listPromptBuilder = promptBuilder
227 .createListPrompt()
228 .name("defaultDispatcher")
229 .message("Which dispatcher you want to use as default?");
230 listPromptBuilder
231 .newItem()
232 .name(NONE)
233 .text("None (disable MavenSecDispatcher)")
234 .add();
235 for (DispatcherMeta meta : secDispatcher.availableDispatchers()) {
236 if (!meta.isHidden()) {
237 listPromptBuilder
238 .newItem()
239 .name(meta.name())
240 .text(meta.displayName())
241 .add();
242 }
243 }
244 listPromptBuilder.addPrompt();
245 return promptBuilder;
246 }
247
248 private PromptBuilder configureDispatcher(
249 EncryptContext context, DispatcherMeta dispatcherMeta, PromptBuilder promptBuilder) throws Exception {
250 context.addInHeader(
251 context.style.italic().bold().foreground(Colors.rgbColor("yellow")),
252 "Configure " + dispatcherMeta.displayName());
253 context.addInHeader("");
254
255 for (DispatcherMeta.Field field : dispatcherMeta.fields()) {
256 String fieldKey = field.getKey();
257 String fieldDescription = "Configure " + fieldKey + ": " + field.getDescription();
258 if (field.getOptions().isPresent()) {
259
260 ListPromptBuilder listPromptBuilder =
261 promptBuilder.createListPrompt().name(fieldKey).message(fieldDescription);
262 for (DispatcherMeta.Field option : field.getOptions().get()) {
263 listPromptBuilder
264 .newItem()
265 .name(
266 option.getDefaultValue().isPresent()
267 ? option.getDefaultValue().get()
268 : option.getKey())
269 .text(option.getDescription())
270 .add();
271 }
272 listPromptBuilder.addPrompt();
273 } else if (field.getDefaultValue().isPresent()) {
274
275 promptBuilder
276 .createInputPrompt()
277 .name(fieldKey)
278 .message(fieldDescription)
279 .defaultValue(field.getDefaultValue().get())
280 .addPrompt();
281 } else {
282
283 promptBuilder
284 .createInputPrompt()
285 .name(fieldKey)
286 .message(fieldDescription)
287 .addPrompt();
288 }
289 }
290 return promptBuilder;
291 }
292 }