View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.cling.invoker.mvnenc;
20  
21  import java.io.InterruptedIOException;
22  import java.util.ArrayList;
23  
24  import org.apache.maven.api.cli.InvokerRequest;
25  import org.apache.maven.api.cli.mvnenc.EncryptOptions;
26  import org.apache.maven.cling.invoker.LookupInvoker;
27  import org.apache.maven.cling.invoker.ProtoLookup;
28  import org.apache.maven.cling.utils.CLIReportingUtils;
29  import org.jline.consoleui.prompt.ConsolePrompt;
30  import org.jline.reader.LineReaderBuilder;
31  import org.jline.reader.UserInterruptException;
32  import org.jline.terminal.Terminal;
33  import org.jline.utils.AttributedStyle;
34  import org.jline.utils.Colors;
35  import org.jline.utils.OSUtils;
36  
37  /**
38   * mvnenc invoker implementation.
39   */
40  public class EncryptInvoker extends LookupInvoker<EncryptContext> {
41  
42      public EncryptInvoker(ProtoLookup protoLookup) {
43          super(protoLookup);
44      }
45  
46      @Override
47      protected int execute(EncryptContext context) throws Exception {
48          return doExecute(context);
49      }
50  
51      @Override
52      protected EncryptContext createContext(InvokerRequest invokerRequest) {
53          return new EncryptContext(invokerRequest);
54      }
55  
56      @Override
57      protected void lookup(EncryptContext context) {
58          context.goals = context.lookup.lookupMap(Goal.class);
59      }
60  
61      public static final int OK = 0; // OK
62      public static final int ERROR = 1; // "generic" error
63      public static final int BAD_OPERATION = 2; // bad user input or alike
64      public static final int CANCELED = 3; // user canceled
65  
66      protected int doExecute(EncryptContext context) throws Exception {
67          try {
68              if (!context.interactive) {
69                  context.terminal.writer().println("This tool works only in interactive mode!");
70                  context.terminal
71                          .writer()
72                          .println("Tool purpose is to configure password management on developer workstations.");
73                  context.terminal
74                          .writer()
75                          .println(
76                                  "Note: Generated configuration can be moved/copied to headless environments, if configured as such.");
77                  return BAD_OPERATION;
78              }
79  
80              context.header = new ArrayList<>();
81              context.style = new AttributedStyle();
82              context.addInHeader(
83                      context.style.italic().bold().foreground(Colors.rgbColor("green")),
84                      "Maven Encryption " + CLIReportingUtils.showVersionMinimal());
85              context.addInHeader("Tool for secure password management on workstations.");
86              context.addInHeader("This tool is part of Apache Maven 4 distribution.");
87              context.addInHeader("");
88  
89              Thread executeThread = Thread.currentThread();
90              context.terminal.handle(Terminal.Signal.INT, signal -> executeThread.interrupt());
91              ConsolePrompt.UiConfig config;
92              if (context.terminal.getType().equals(Terminal.TYPE_DUMB)
93                      || context.terminal.getType().equals(Terminal.TYPE_DUMB_COLOR)) {
94                  context.terminal.writer().println(context.terminal.getName() + ": " + context.terminal.getType());
95                  throw new IllegalStateException("Dumb terminal detected.\nThis tool requires real terminal to work!\n"
96                          + "Note: On Windows Jansi or JNA library must be included in classpath.");
97              } else if (OSUtils.IS_WINDOWS) {
98                  config = new ConsolePrompt.UiConfig(">", "( )", "(x)", "( )");
99              } else {
100                 config = new ConsolePrompt.UiConfig("❯", "◯ ", "◉ ", "◯ ");
101             }
102             config.setCancellableFirstPrompt(true);
103 
104             context.reader =
105                     LineReaderBuilder.builder().terminal(context.terminal).build();
106             context.prompt = new ConsolePrompt(context.reader, context.terminal, config);
107 
108             EncryptOptions options = (EncryptOptions) context.invokerRequest.options();
109             if (options.goals().isEmpty() || options.goals().get().size() != 1) {
110                 return badGoalsErrorMessage("No goal or multiple goals specified, specify only one goal.", context);
111             }
112 
113             String goalName = options.goals().get().get(0);
114             Goal goal = context.goals.get(goalName);
115 
116             if (goal == null) {
117                 return badGoalsErrorMessage("Unknown goal: " + goalName, context);
118             }
119 
120             return goal.execute(context);
121         } catch (InterruptedException | InterruptedIOException | UserInterruptException e) {
122             context.terminal.writer().println("Goal canceled by user.");
123             return CANCELED;
124         } catch (Exception e) {
125             if (context.invokerRequest.options().showErrors().orElse(false)) {
126                 context.terminal.writer().println(e.getMessage());
127                 e.printStackTrace(context.terminal.writer());
128             } else {
129                 context.terminal.writer().println(e.getMessage());
130             }
131             return ERROR;
132         } finally {
133             context.terminal.writer().flush();
134         }
135     }
136 
137     protected int badGoalsErrorMessage(String message, EncryptContext context) {
138         context.terminal.writer().println(message);
139         context.terminal.writer().println("Supported goals are: " + String.join(", ", context.goals.keySet()));
140         context.terminal.writer().println("Use -h to display help.");
141         return BAD_OPERATION;
142     }
143 }