authentication for extensions as suggested by LittleJ

This commit is contained in:
sirjonasxx 2018-10-16 17:47:00 +02:00
parent df87eb339d
commit aaf3878878
7 changed files with 142 additions and 10 deletions

View File

@ -30,6 +30,7 @@ public abstract class Extension {
private boolean isCorrupted = false; private boolean isCorrupted = false;
private static final String[] PORT_FLAG = {"--port", "-p"}; private static final String[] PORT_FLAG = {"--port", "-p"};
private static final String[] FILE_FLAG = {"--filename", "-f"}; private static final String[] FILE_FLAG = {"--filename", "-f"};
private static final String[] COOKIE_FLAG = {"--auth-token", "-c"}; // don't add a cookie or filename when debugging
private OutputStream out = null; private OutputStream out = null;
private final Map<Integer, List<MessageListener>> incomingMessageListeners = new HashMap<>(); private final Map<Integer, List<MessageListener>> incomingMessageListeners = new HashMap<>();
@ -81,6 +82,7 @@ public abstract class Extension {
int port = Integer.parseInt(getArgument(args, PORT_FLAG)); int port = Integer.parseInt(getArgument(args, PORT_FLAG));
String file = getArgument(args, FILE_FLAG); String file = getArgument(args, FILE_FLAG);
String cookie = getArgument(args, COOKIE_FLAG);
Socket gEarthExtensionServer = null; Socket gEarthExtensionServer = null;
try { try {
@ -123,6 +125,7 @@ public abstract class Extension {
.appendBoolean(isOnClickMethodUsed()) .appendBoolean(isOnClickMethodUsed())
.appendBoolean(file != null) .appendBoolean(file != null)
.appendString(file == null ? "": file) .appendString(file == null ? "": file)
.appendString(cookie == null ? "" : cookie)
.appendBoolean(CANLEAVE) .appendBoolean(CANLEAVE)
.appendBoolean(CANDELETE); .appendBoolean(CANDELETE);
writeToStream(response.toBytes()); writeToStream(response.toBytes());

View File

@ -7,14 +7,17 @@ import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox; import javafx.scene.control.CheckBox;
import javafx.scene.control.DialogPane; import javafx.scene.control.DialogPane;
import java.util.HashSet;
import java.util.Set;
/** /**
* Created by Jonas on 27/09/18. * Created by Jonas on 27/09/18.
*/ */
public class ConfirmationDialog { public class ConfirmationDialog {
public static boolean showDialog = true; private static Set<String> ignoreDialogs = new HashSet<>();
public static Alert createAlertWithOptOut(Alert.AlertType type, String title, String headerText, public static Alert createAlertWithOptOut(Alert.AlertType type, String dialogKey, String title, String headerText,
String message, String optOutMessage, /*Callback<Boolean, Void> optOutAction,*/ String message, String optOutMessage, /*Callback<Boolean, Void> optOutAction,*/
ButtonType... buttonTypes) { ButtonType... buttonTypes) {
Alert alert = new Alert(type); Alert alert = new Alert(type);
@ -29,7 +32,11 @@ public class ConfirmationDialog {
protected Node createDetailsButton() { protected Node createDetailsButton() {
CheckBox optOut = new CheckBox(); CheckBox optOut = new CheckBox();
optOut.setText(optOutMessage); optOut.setText(optOutMessage);
optOut.setOnAction(event -> showDialog = !optOut.isSelected()); optOut.setOnAction(event -> {
if (optOut.isSelected()) {
ignoreDialogs.add(dialogKey);
}
});
return optOut; return optOut;
} }
}); });
@ -46,5 +53,8 @@ public class ConfirmationDialog {
return alert; return alert;
} }
public static boolean showDialog(String dialogKey) {
return !ignoreDialogs.contains(dialogKey);
}
} }

View File

@ -101,12 +101,14 @@ public class ExtensionItemContainer extends GridPane {
Tooltip.install(deleteButton, uninstall); Tooltip.install(deleteButton, uninstall);
deleteButton.show(); deleteButton.show();
GridPane this2 = this; GridPane this2 = this;
final String uninstallKey = "uninstallExtension";
deleteButton.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> { deleteButton.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
boolean delet_dis = true; boolean delet_dis = true;
if (ConfirmationDialog.showDialog) { if (ConfirmationDialog.showDialog(uninstallKey)) {
Alert alert = ConfirmationDialog.createAlertWithOptOut(Alert.AlertType.CONFIRMATION, Alert alert = ConfirmationDialog.createAlertWithOptOut(Alert.AlertType.CONFIRMATION, uninstallKey
"Confirmation Dialog", null, ,"Confirmation Dialog", null,
"Are you sure want to uninstall this extension?", "Do not ask again", "Are you sure want to uninstall this extension?", "Do not ask again",
ButtonType.YES, ButtonType.NO ButtonType.YES, ButtonType.NO
); );

View File

@ -6,6 +6,7 @@ import gearth.protocol.HPacket;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import gearth.ui.extensions.authentication.Authenticator;
import java.net.Socket; import java.net.Socket;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -26,6 +27,7 @@ public class GEarthExtension {
private boolean isInstalledExtension; // <- extension is in the extensions directory private boolean isInstalledExtension; // <- extension is in the extensions directory
private String fileName; private String fileName;
private String cookie;
private Socket connection; private Socket connection;
@ -60,7 +62,14 @@ public class GEarthExtension {
connection, connection,
onDisconnectedCallback onDisconnectedCallback
); );
if (Authenticator.evaluate(gEarthExtension)) {
callback.act(gEarthExtension); callback.act(gEarthExtension);
}
else {
gEarthExtension.closeConnection(); //you shall not pass...
}
break; break;
} }
} }
@ -79,6 +88,7 @@ public class GEarthExtension {
this.isInstalledExtension = extensionInfo.readBoolean(); this.isInstalledExtension = extensionInfo.readBoolean();
this.fileName = extensionInfo.readString(); this.fileName = extensionInfo.readString();
this.cookie = extensionInfo.readString();
this.leaveButtonVisible = extensionInfo.readBoolean(); this.leaveButtonVisible = extensionInfo.readBoolean();
this.deleteButtonVisible = extensionInfo.readBoolean(); this.deleteButtonVisible = extensionInfo.readBoolean();
@ -151,6 +161,9 @@ public class GEarthExtension {
public String getFileName() { public String getFileName() {
return fileName; return fileName;
} }
public String getCookie() {
return cookie;
}
public boolean isDeleteButtonVisible() { public boolean isDeleteButtonVisible() {
return deleteButtonVisible; return deleteButtonVisible;
} }

View File

@ -0,0 +1,101 @@
package gearth.ui.extensions.authentication;
import gearth.extensions.Extension;
import gearth.misc.ConfirmationDialog;
import gearth.ui.extensions.GEarthExtension;
import gearth.ui.extensions.executer.ExtensionRunner;
import gearth.ui.extensions.executer.ExtensionRunnerFactory;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
* Created by Jonas on 16/10/18.
*/
public class Authenticator {
private static Map<String, String> cookies = new HashMap<>();
public static String generateCookieForExtension(String filename) {
String cookie = getRandomCookie();
cookies.put(filename, cookie);
return cookie;
}
public static boolean evaluate(GEarthExtension extension) {
if (extension.isInstalledExtension()) {
return claimSession(extension.getFileName(), extension.getCookie());
}
else {
return askForPermission(extension);
}
}
/**
* authenticator: authenticate an extension and remove the cookie
* @param filename
* @param cookie
* @return if the extension is authenticated
*/
private static boolean claimSession(String filename, String cookie) {
if (cookies.containsKey(filename) && cookies.get(filename).equals(cookie)) {
cookies.remove(filename);
return true;
}
return false;
}
private static volatile boolean rememberOption = false;
//for not-installed extensions, popup a dialog
private static boolean askForPermission(GEarthExtension extension) {
boolean[] allowConnection = {true};
final String connectExtensionKey = "allow_extension_connection";
if (ConfirmationDialog.showDialog(connectExtensionKey)) {
boolean[] done = {false};
Platform.runLater(() -> {
Alert alert = ConfirmationDialog.createAlertWithOptOut(Alert.AlertType.WARNING, connectExtensionKey
,"Confirmation Dialog", null,
"Extension \""+extension.getTitle()+"\" tries to connect but isn't known to G-Earth, accept this connection?", "Remember my choice",
ButtonType.YES, ButtonType.NO
);
if (!(alert.showAndWait().filter(t -> t == ButtonType.YES).isPresent())) {
allowConnection[0] = false;
}
done[0] = true;
if (!ConfirmationDialog.showDialog(connectExtensionKey)) {
rememberOption = allowConnection[0];
}
});
while (!done[0]) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return allowConnection[0];
}
return rememberOption;
}
private static String getRandomCookie() {
StringBuilder builder = new StringBuilder();
Random r = new Random();
for (int i = 0; i < 40; i++) {
builder.append(r.nextInt(40));
}
return builder.toString();
}
}

View File

@ -25,7 +25,7 @@ public class ExecutionInfo {
for(String type : extensionTypeToExecutionCommand.keySet()) { for(String type : extensionTypeToExecutionCommand.keySet()) {
extensionTypeToExecutionCommand.put( extensionTypeToExecutionCommand.put(
type, type,
extensionTypeToExecutionCommand.get(type) + " -p {port} -f {filename}" extensionTypeToExecutionCommand.get(type) + " -p {port} -f {filename} -c {cookie}"
); );
} }

View File

@ -1,6 +1,7 @@
package gearth.ui.extensions.executer; package gearth.ui.extensions.executer;
import gearth.Main; import gearth.Main;
import gearth.ui.extensions.authentication.Authenticator;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -70,11 +71,13 @@ public class NormalExtensionRunner implements ExtensionRunner {
public void tryRunExtension(String path, int port) { public void tryRunExtension(String path, int port) {
try { try {
String filename = Paths.get(path).getFileName().toString();
Runtime.getRuntime().exec( Runtime.getRuntime().exec(
ExecutionInfo.getExecutionCommand(getFileExtension(path)) ExecutionInfo.getExecutionCommand(getFileExtension(path))
.replace("{path}", path) .replace("{path}", path)
.replace("{port}", port+"") .replace("{port}", port+"")
.replace("{filename}", Paths.get(path).getFileName().toString()) .replace("{filename}", filename)
.replace("{cookie}", Authenticator.generateCookieForExtension(filename))
); );
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@ -119,7 +122,7 @@ public class NormalExtensionRunner implements ExtensionRunner {
private String getRandomString() { private String getRandomString() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
Random r = new Random(); Random r = new Random();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 12; i++) {
builder.append(r.nextInt(10)); builder.append(r.nextInt(10));
} }