Skip to content

Commit 4509688

Browse files
committed
Some slight improvements
1 parent 3c5a2c2 commit 4509688

File tree

2 files changed

+80
-30
lines changed

2 files changed

+80
-30
lines changed

src/main/java/org/codehaus/plexus/components/secdispatcher/PinEntry.java

Lines changed: 77 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
import static java.util.Objects.requireNonNull;
3333

3434
/**
35-
* Inspired by <a href="https://velvetcache.org/2023/03/26/a-peek-inside-pinentry/">A peek inside pinentry</a>
35+
* Inspired by <a href="https://velvetcache.org/2023/03/26/a-peek-inside-pinentry/">A peek inside pinentry</a>.
36+
* Also look at <a href="https://gorbe.io/posts/gnupg/pinentry/documentation/">Pinentry Documentation</a>.
37+
* Finally, source mirror is at <a href="https://github.com/gpg/pinentry">gpg/pinentry</a>.
3638
*/
3739
public class PinEntry {
3840
public enum Outcome {
@@ -43,59 +45,96 @@ public enum Outcome {
4345
FAILED;
4446
}
4547

46-
public record Result<T>(Outcome outcome, T payload) {}
48+
public record Result(Outcome outcome, String payload) {}
4749

4850
private final Logger logger = LoggerFactory.getLogger(getClass());
4951
private final String cmd;
5052
private final LinkedHashMap<String, String> commands;
5153

54+
/**
55+
* Creates pin entry instance that will use the passed in cmd executable.
56+
*/
5257
public PinEntry(String cmd) {
5358
this.cmd = requireNonNull(cmd);
5459
this.commands = new LinkedHashMap<>();
5560
}
5661

62+
/**
63+
* Sets a "stable key handle" for caching purposes. Optional.
64+
*/
5765
public PinEntry setKeyInfo(String keyInfo) {
5866
requireNonNull(keyInfo);
59-
commands.put("SETKEYINFO", keyInfo);
6067
commands.put("OPTION", "allow-external-password-cache");
68+
commands.put("SETKEYINFO", keyInfo);
6169
return this;
6270
}
6371

72+
/**
73+
* Sets the OK button label, by default "Ok".
74+
*/
6475
public PinEntry setOk(String msg) {
6576
requireNonNull(msg);
6677
commands.put("SETOK", msg);
6778
return this;
6879
}
6980

81+
/**
82+
* Sets the CANCEL button label, by default "Cancel".
83+
*/
7084
public PinEntry setCancel(String msg) {
7185
requireNonNull(msg);
7286
commands.put("SETCANCEL", msg);
7387
return this;
7488
}
7589

90+
/**
91+
* Sets the window title.
92+
*/
7693
public PinEntry setTitle(String title) {
7794
requireNonNull(title);
7895
commands.put("SETTITLE", title);
7996
return this;
8097
}
8198

99+
/**
100+
* Sets additional test in window.
101+
*/
82102
public PinEntry setDescription(String desc) {
83103
requireNonNull(desc);
84104
commands.put("SETDESC", desc);
85105
return this;
86106
}
87107

108+
/**
109+
* Sets the prompt.
110+
*/
88111
public PinEntry setPrompt(String prompt) {
89112
requireNonNull(prompt);
90113
commands.put("SETPROMPT", prompt);
91114
return this;
92115
}
93116

117+
/**
118+
* If set, window will show "Error: xxx", usable for second attempt (ie "bad password").
119+
*/
120+
public PinEntry setError(String error) {
121+
requireNonNull(error);
122+
commands.put("SETERROR", error);
123+
return this;
124+
}
125+
126+
/**
127+
* Usable with {@link #getPin()}, window will contain two input fields and will force user to type in same
128+
* input in both fields, ie to "confirm" the pin.
129+
*/
94130
public PinEntry confirmPin() {
95-
commands.put("SETREPEAT", cmd);
131+
commands.put("SETREPEAT", null);
96132
return this;
97133
}
98134

135+
/**
136+
* Sets the window timeout, if no button pressed and timeout passes, Result will by {@link Outcome#TIMEOUT}.
137+
*/
99138
public PinEntry setTimeout(Duration timeout) {
100139
long seconds = timeout.toSeconds();
101140
if (seconds < 0) {
@@ -105,22 +144,31 @@ public PinEntry setTimeout(Duration timeout) {
105144
return this;
106145
}
107146

108-
public Result<String> getPin() throws IOException {
147+
/**
148+
* Initiates a "get pin" dialogue with input field(s) using previously set options.
149+
*/
150+
public Result getPin() throws IOException {
109151
commands.put("GETPIN", null);
110152
return execute();
111153
}
112154

113-
public Result<String> confirm() throws IOException {
155+
/**
156+
* Initiates a "confirmation" dialogue (no input) using previously set options.
157+
*/
158+
public Result confirm() throws IOException {
114159
commands.put("CONFIRM", null);
115160
return execute();
116161
}
117162

118-
public Result<String> message() throws IOException {
163+
/**
164+
* Initiates a "message" dialogue (no input) using previously set options.
165+
*/
166+
public Result message() throws IOException {
119167
commands.put("MESSAGE", null);
120168
return execute();
121169
}
122170

123-
private Result<String> execute() throws IOException {
171+
private Result execute() throws IOException {
124172
Process process = new ProcessBuilder(cmd).start();
125173
BufferedReader reader = process.inputReader();
126174
BufferedWriter writer = process.outputWriter();
@@ -141,20 +189,20 @@ private Result<String> execute() throws IOException {
141189
expectOK(reader);
142190
}
143191
}
144-
Result<String> result = lastExpect(reader);
192+
Result result = lastExpect(reader);
145193
writer.write("BYE");
146194
writer.newLine();
147195
writer.flush();
148196
try {
149197
process.waitFor(5, TimeUnit.SECONDS);
150198
int exitCode = process.exitValue();
151199
if (exitCode != 0) {
152-
return new Result<>(Outcome.FAILED, "Exit code: " + exitCode);
200+
return new Result(Outcome.FAILED, "Exit code: " + exitCode);
153201
} else {
154202
return result;
155203
}
156204
} catch (Exception e) {
157-
return new Result<>(Outcome.FAILED, e.getMessage());
205+
return new Result(Outcome.FAILED, e.getMessage());
158206
}
159207
}
160208

@@ -166,7 +214,7 @@ private void expectOK(BufferedReader in) throws IOException {
166214
}
167215
}
168216

169-
private Result<String> lastExpect(BufferedReader in) throws IOException {
217+
private Result lastExpect(BufferedReader in) throws IOException {
170218
while (true) {
171219
String response = in.readLine();
172220
logger.debug("< {}", response);
@@ -178,47 +226,49 @@ private Result<String> lastExpect(BufferedReader in) throws IOException {
178226
}
179227
if (response.startsWith("ERR")) {
180228
if (response.contains("83886142")) {
181-
return new Result<>(Outcome.TIMEOUT, response);
229+
return new Result(Outcome.TIMEOUT, response);
182230
}
183231
if (response.contains("83886179")) {
184-
return new Result<>(Outcome.CANCELED, response);
232+
return new Result(Outcome.CANCELED, response);
185233
}
186234
if (response.contains("83886194")) {
187-
return new Result<>(Outcome.NOT_CONFIRMED, response);
235+
return new Result(Outcome.NOT_CONFIRMED, response);
188236
}
189237
}
190238
if (response.startsWith("D")) {
191-
return new Result<>(Outcome.SUCCESS, response.substring(2));
239+
return new Result(Outcome.SUCCESS, response.substring(2));
192240
}
193241
if (response.startsWith("OK")) {
194-
return new Result<>(Outcome.SUCCESS, response);
242+
return new Result(Outcome.SUCCESS, response);
195243
}
196244
}
197245
}
198246

199247
public static void main(String[] args) throws IOException {
200-
Result<String> pinResult = new PinEntry("/usr/bin/pinentry-gnome3")
201-
.setTimeout(Duration.ofSeconds(5))
248+
// check what pinentry apps you have and replace the execName
249+
String cmd = "/usr/bin/pinentry-gnome3";
250+
Result pinResult = new PinEntry(cmd)
251+
.setTimeout(Duration.ofSeconds(15))
202252
.setKeyInfo("maven:masterPassword")
203253
.setTitle("Maven Master Password")
204254
.setDescription("Please enter the Maven master password")
205-
.setPrompt("Master password")
206-
.setOk("Her you go!")
255+
.setPrompt("Password")
256+
.setOk("Here you go!")
207257
.setCancel("Uh oh, rather not")
208258
// .confirmPin() (will not let you through if you cannot type same thing twice)
209259
.getPin();
210260
if (pinResult.outcome() == Outcome.SUCCESS) {
211-
Result<String> confirmResult = new PinEntry("/usr/bin/pinentry-gnome3")
261+
Result confirmResult = new PinEntry(cmd)
212262
.setTitle("Password confirmation")
213-
.setDescription("Please confirm that the password you entered is correct")
214-
.setPrompt("Is the password '" + pinResult.payload() + "' the one you want?")
263+
.setPrompt("Please confirm the password")
264+
.setDescription("Is the password '" + pinResult.payload() + "' the one you want?")
215265
.confirm();
216266
if (confirmResult.outcome() == Outcome.SUCCESS) {
217-
new PinEntry("/usr/bin/pinentry-gnome3")
267+
new PinEntry(cmd)
218268
.setTitle("Password confirmed")
219-
.setDescription("You confirmed your password")
220269
.setPrompt("The password '" + pinResult.payload() + "' is confirmed.")
221-
.confirm();
270+
.setDescription("You confirmed your password")
271+
.message();
222272
} else {
223273
System.out.println(confirmResult);
224274
}

src/main/java/org/codehaus/plexus/components/secdispatcher/internal/sources/PinEntryMasterSource.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import org.codehaus.plexus.components.secdispatcher.SecDispatcherException;
3232

3333
/**
34-
* Inspired by <a href="https://velvetcache.org/2023/03/26/a-peek-inside-pinentry/">A peek inside pinentry</a>
34+
* Master source using {@link PinEntry}
3535
*/
3636
@Singleton
3737
@Named(PinEntryMasterSource.NAME)
@@ -55,9 +55,9 @@ public Optional<String> configTemplate() {
5555
@Override
5656
public String doHandle(String s) throws SecDispatcherException {
5757
try {
58-
PinEntry.Result<String> result = new PinEntry(s)
58+
PinEntry.Result result = new PinEntry(s)
5959
.setTimeout(Duration.ofSeconds(30))
60-
.setKeyInfo("maven:masterPassword")
60+
.setKeyInfo("Maven: n/masterPassword")
6161
.setTitle("Maven Master Password")
6262
.setDescription("Please enter the Maven master password")
6363
.setPrompt("Maven master password")

0 commit comments

Comments
 (0)