result = new CompletableFuture<>();
- CachedDaemonThreadPoolProvider.getThreadPool().execute(() -> {
+ return CompletableFuture.supplyAsync(() -> {
try {
- result.complete(tryDownloading(url));
- } catch (Exception e) {
- result.completeExceptionally(e);
+ return tryDownloading(url);
+ } catch (IOException e) {
+ throw new RuntimeException("Error while downloading " + url, e);
}
- });
- return result;
+ }, CachedDaemonThreadPoolProvider.getThreadPool());
}
private Resource tryDownloading(final URL downloadFrom) throws IOException {
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/resources/initializer/BaseResourceInitializer.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/resources/initializer/BaseResourceInitializer.java
index 3184b7e1f..f2ac24143 100644
--- a/core/src/main/java/net/adoptopenjdk/icedteaweb/resources/initializer/BaseResourceInitializer.java
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/resources/initializer/BaseResourceInitializer.java
@@ -1,7 +1,7 @@
package net.adoptopenjdk.icedteaweb.resources.initializer;
import net.adoptopenjdk.icedteaweb.client.parts.dialogs.security.InetSecurity511Panel;
-import net.adoptopenjdk.icedteaweb.client.parts.dialogs.security.SecurityDialogs;
+import net.adoptopenjdk.icedteaweb.client.parts.dialogs.Dialogs;
import net.adoptopenjdk.icedteaweb.http.HttpMethod;
import net.adoptopenjdk.icedteaweb.jnlp.version.VersionId;
import net.adoptopenjdk.icedteaweb.logging.Logger;
@@ -102,7 +102,7 @@ private UrlRequestResult testUrl(URL url) throws IOException {
final UrlRequestResult response = UrlProber.getUrlResponseCodeWithRedirectionResult(url, requestProperties, requestMethod);
if (response.getResponseCode() == NETWORK_AUTHENTICATION_REQUIRED && !InetSecurity511Panel.isSkip()) {
- boolean result511 = SecurityDialogs.show511Dialogue(resource);
+ boolean result511 = Dialogs.show511Dialogue(resource);
if (!result511) {
throw new RuntimeException("Terminated on users request after encountering 'http 511 authentication'.");
}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/PermissionsManager.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/PermissionsManager.java
new file mode 100644
index 000000000..c908257d8
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/PermissionsManager.java
@@ -0,0 +1,394 @@
+package net.adoptopenjdk.icedteaweb.security;
+
+import net.adoptopenjdk.icedteaweb.jnlp.element.resource.JARDesc;
+import net.adoptopenjdk.icedteaweb.jnlp.element.security.ApplicationEnvironment;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.config.ConfigurationConstants;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.util.UrlUtils;
+
+import java.awt.AWTPermission;
+import java.io.FilePermission;
+import java.lang.reflect.Constructor;
+import java.net.SocketPermission;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.AllPermission;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.URIParameter;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.PropertyPermission;
+
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.ARRAY_LEGACY_MERGE_SORT;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.AWT_AA_FONT_SETTINGS;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.AWT_DISABLE_MIXING;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.AWT_ERASE_BACKGROUND_ON_RESIZE;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.AWT_NO_ERASE_BACKGROUND;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.AWT_SYNC_LWREQUESTS;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.AWT_WINDOW_LOCATION_BY_PLATFORM;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.BROWSER;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.BROWSER_STAR;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.EXIT_VM;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.FILE_SEPARATOR;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.HTTP_AGENT;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.HTTP_KEEP_ALIVE;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JAVA2D_D3D;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JAVA2D_DPI_AWARE;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JAVA2D_NO_DDRAW;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JAVA2D_OPENGL;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JAVAWS;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JAVA_CLASS_VERSION;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JAVA_PLUGIN;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JAVA_SPEC_NAME;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JAVA_SPEC_VENDOR;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JAVA_SPEC_VERSION;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JAVA_VENDOR;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JAVA_VENDOR_URL;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JAVA_VERSION;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.JNLP;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.LINE_SEPARATOR;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.OS_ARCH;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.OS_NAME;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.OS_VERSION;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.PATH_SEPARATOR;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.STOP_THREAD;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.SWING_BOLD_METAL;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.SWING_DEFAULT_LF;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.SWING_METAL_THEME;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.SWING_NO_XP;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.SWING_USE_SYSTEM_FONT;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.VM_NAME;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.VM_SPEC_NAME;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.VM_SPEC_VENDOR;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.VM_VENDOR;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.VM_VERSION;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.WEBSTART_JAUTHENTICATOR;
+import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.WEBSTART_VERSION;
+import static sun.security.util.SecurityConstants.PROPERTY_READ_ACTION;
+import static sun.security.util.SecurityConstants.PROPERTY_RW_ACTION;
+
+public class PermissionsManager {
+ private static final Logger LOG = LoggerFactory.getLogger(PermissionsManager.class);
+
+ /**
+ * URLPermission is new in Java 8, so we use reflection to check for it to keep compatibility
+ * with Java 6/7. If we can't find the class or fail to construct it then we continue as usual
+ * without.
+ *
+ * These are saved as fields so that the reflective lookup only needs to be performed once
+ * when the SecurityDesc is constructed, rather than every time a call is made to
+ * {@link PermissionsManager#getSandBoxPermissions(JNLPFile)}, which is called frequently.
+ */
+ private static Class urlPermissionClass;
+ private static Constructor urlPermissionConstructor;
+
+ private static PermissionCollection j2EEPermissions;
+ private static PermissionCollection jnlpRIAPermissions;
+ private static PermissionCollection sandboxPermissions;
+ private static PermissionCollection urlPermissions;
+
+ static {
+ try {
+ urlPermissionClass = (Class) Class.forName("java.net.URLPermission");
+ urlPermissionConstructor = urlPermissionClass.getDeclaredConstructor(String.class);
+ } catch (final ReflectiveOperationException | SecurityException e) {
+ LOG.error("Exception while reflectively finding URLPermission - host is probably not running Java 8+", e);
+ urlPermissionClass = null;
+ urlPermissionConstructor = null;
+ }
+ }
+
+ // We go by the rules here:
+ // http://java.sun.com/docs/books/tutorial/deployment/doingMoreWithRIA/properties.html
+
+ // Since this is security sensitive, take a conservative approach:
+ // Allow only what is specifically allowed, and deny everything else
+
+ /**
+ * @return a read-only PermissionCollection containing the sandbox permissions
+ */
+ public static PermissionCollection getSandBoxPermissions(final JNLPFile file) {
+ if (sandboxPermissions == null) {
+ sandboxPermissions = new Permissions();
+
+ sandboxPermissions.add(new SocketPermission("localhost:1024-", "listen"));
+ // sandboxPermissions.add(new SocketPermission("", "connect, accept")); // added by code
+ sandboxPermissions.add(new PropertyPermission(ARRAY_LEGACY_MERGE_SORT, PROPERTY_RW_ACTION));
+ sandboxPermissions.add(new PropertyPermission(JAVA_VERSION, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(JAVA_VENDOR, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(JAVA_VENDOR_URL, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(JAVA_CLASS_VERSION, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(OS_NAME, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(OS_VERSION, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(OS_ARCH, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(FILE_SEPARATOR, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(PATH_SEPARATOR, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(LINE_SEPARATOR, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(JAVA_SPEC_VERSION, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(JAVA_SPEC_VENDOR, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(JAVA_SPEC_NAME, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(VM_SPEC_VENDOR, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(VM_SPEC_NAME, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(VM_VERSION, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(VM_VENDOR, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(VM_NAME, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(WEBSTART_VERSION, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(JAVA_PLUGIN, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(JNLP, PROPERTY_RW_ACTION));
+ sandboxPermissions.add(new PropertyPermission(JAVAWS, PROPERTY_RW_ACTION));
+ sandboxPermissions.add(new PropertyPermission(BROWSER, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new PropertyPermission(BROWSER_STAR, PROPERTY_READ_ACTION));
+ sandboxPermissions.add(new RuntimePermission(EXIT_VM));
+ sandboxPermissions.add(new RuntimePermission(STOP_THREAD));
+ // disabled because we can't at this time prevent an
+ // application from accessing other applications' event
+ // queues, or even prevent access to security dialog queues.
+ //
+ // sandboxPermissions.add(new AWTPermission("accessEventQueue"));
+
+ if (shouldAddShowWindowWithoutWarningBannerAwtPermission()) {
+ sandboxPermissions.add(new AWTPermission("showWindowWithoutWarningBanner"));
+ }
+
+ if (file.isApplication()) {
+ Collections.list(getJnlpRiaPermissions().elements()).forEach(sandboxPermissions::add);
+ }
+ URL downloadHost = UrlUtils.guessCodeBase(file);
+ if (downloadHost != null && downloadHost.getHost().length() > 0) {
+ sandboxPermissions.add(new SocketPermission(UrlUtils.getHostAndPort(downloadHost), "connect, accept"));
+ }
+
+ Collections.list(getUrlPermissions(file).elements()).forEach(sandboxPermissions::add);
+
+ sandboxPermissions.setReadOnly(); // set permission collection to readonly
+ }
+ return sandboxPermissions;
+ }
+
+ /**
+ * @return a read-only PermissionCollection containing the J2EE permissions
+ */
+ public static PermissionCollection getJ2EEPermissions() {
+ if (j2EEPermissions == null) {
+ j2EEPermissions = new Permissions();
+
+ j2EEPermissions.add(new AWTPermission("accessClipboard"));
+ // disabled because we can't at this time prevent an
+ // application from accessing other applications' event
+ // queues, or even prevent access to security dialog queues.
+ //
+ // j2EEPermissions.add(new AWTPermission("accessEventQueue"));
+ j2EEPermissions.add(new RuntimePermission("exitVM"));
+ j2EEPermissions.add(new RuntimePermission("loadLibrary"));
+ j2EEPermissions.add(new RuntimePermission("queuePrintJob"));
+ j2EEPermissions.add(new SocketPermission("*", "connect"));
+ j2EEPermissions.add(new SocketPermission("localhost:1024-", "accept, listen"));
+ j2EEPermissions.add(new FilePermission("*", "read, write"));
+ j2EEPermissions.add(new PropertyPermission("*", PROPERTY_READ_ACTION));
+
+ j2EEPermissions.setReadOnly(); // set permission collection to readonly
+ }
+ return j2EEPermissions;
+ }
+
+ /**
+ * @return a read-only PermissionCollection containing the JNLP RIA permissions
+ * @see Secure System Properties
+ */
+ public static PermissionCollection getJnlpRiaPermissions() {
+ if (jnlpRIAPermissions == null) {
+ jnlpRIAPermissions = new Permissions();
+
+ jnlpRIAPermissions.add(new PropertyPermission(AWT_AA_FONT_SETTINGS, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(HTTP_AGENT, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(HTTP_KEEP_ALIVE, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(AWT_SYNC_LWREQUESTS, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(AWT_WINDOW_LOCATION_BY_PLATFORM, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(AWT_DISABLE_MIXING, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(WEBSTART_JAUTHENTICATOR, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(SWING_DEFAULT_LF, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(AWT_NO_ERASE_BACKGROUND, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(AWT_ERASE_BACKGROUND_ON_RESIZE, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(JAVA2D_D3D, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(JAVA2D_DPI_AWARE, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(JAVA2D_NO_DDRAW, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(JAVA2D_OPENGL, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(SWING_BOLD_METAL, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(SWING_METAL_THEME, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(SWING_NO_XP, PROPERTY_RW_ACTION));
+ jnlpRIAPermissions.add(new PropertyPermission(SWING_USE_SYSTEM_FONT, PROPERTY_RW_ACTION));
+
+ jnlpRIAPermissions.setReadOnly(); // set permission collection to readonly
+ }
+ return jnlpRIAPermissions;
+ }
+
+ /**
+ * @param file
+ * @return a read-only PermissionCollection containing the URL permissions
+ */
+ private static PermissionCollection getUrlPermissions(final JNLPFile file) {
+ if (urlPermissions == null) {
+ urlPermissions = new Permissions();
+
+ if (urlPermissionClass != null && urlPermissionConstructor != null) {
+ final PermissionCollection permissions = new Permissions();
+ for (final JARDesc jar : file.getResources().getJARs()) {
+ try {
+ // Allow applets all HTTP methods (ex POST, GET) with any request headers
+ // on resources anywhere recursively in or below the applet codebase, only on
+ // default ports and ports explicitly specified in resource locations
+ final URI resourceLocation = jar.getLocation().toURI().normalize();
+ final URI host = getHost(resourceLocation);
+ final String hostUriString = host.toString();
+ final String urlPermissionUrlString = appendRecursiveSubdirToCodebaseHostString(hostUriString);
+ final Permission p = urlPermissionConstructor.newInstance(urlPermissionUrlString);
+ permissions.add(p);
+ } catch (final ReflectiveOperationException e) {
+ LOG.error("Exception while attempting to reflectively generate a URLPermission, probably not running on Java 8+?", e);
+ } catch (final URISyntaxException e) {
+ LOG.error("Could not determine codebase host for resource at " + jar.getLocation() + " while generating URLPermissions", e);
+ }
+ }
+ try {
+ final URI codebase = file.getNotNullProbableCodeBase().toURI().normalize();
+ final URI host = getHost(codebase);
+ final String codebaseHostUriString = host.toString();
+ final String urlPermissionUrlString = appendRecursiveSubdirToCodebaseHostString(codebaseHostUriString);
+ final Permission p = urlPermissionConstructor.newInstance(urlPermissionUrlString);
+ permissions.add(p);
+ } catch (final ReflectiveOperationException e) {
+ LOG.error("Exception while attempting to reflectively generate a URLPermission, probably not running on Java 8+?", e);
+ } catch (final URISyntaxException e) {
+ LOG.error("Could not determine codebase host for codebase " + file.getCodeBase() + " while generating URLPermissions", e);
+ }
+ }
+
+ urlPermissions.setReadOnly();
+ }
+
+ return urlPermissions;
+ }
+
+ /**
+ * @param cs the CodeSource to get permissions for
+ * @return a read-only PermissionCollection containing the basic permissions granted depending on
+ * the {@link ApplicationEnvironment}
+ */
+ public static PermissionCollection getPermissions(final JNLPFile file, final CodeSource cs, final ApplicationEnvironment applicationEnvironment) {
+
+ if (applicationEnvironment == ApplicationEnvironment.ALL) {
+ final Policy customTrustedPolicy = getCustomTrustedPolicy();
+ if (customTrustedPolicy != null) {
+ final PermissionCollection customPolicyPermissions = customTrustedPolicy.getPermissions(cs);
+ customPolicyPermissions.setReadOnly();
+ return customPolicyPermissions;
+ }
+
+ final PermissionCollection allPermissions = new Permissions();
+ allPermissions.add(new AllPermission());
+ allPermissions.setReadOnly();
+ return allPermissions;
+ }
+
+ if (applicationEnvironment == ApplicationEnvironment.J2EE) {
+ PermissionCollection j2eePermissions = new Permissions();
+ Collections.list(getSandBoxPermissions(file).elements()).forEach(j2eePermissions::add);
+ Collections.list(getJ2EEPermissions().elements()).forEach(j2eePermissions::add);
+ j2eePermissions.setReadOnly();
+ return j2eePermissions;
+ }
+
+ return getSandBoxPermissions(file);
+ }
+
+ /**
+ * Check whether {@link AWTPermission} should be added, as a property is defined in the {@link JNLPRuntime}
+ * {@link net.sourceforge.jnlp.config.DeploymentConfiguration}.
+ *
+ * @return true, if permission should be added
+ * @see
+ */
+ private static boolean shouldAddShowWindowWithoutWarningBannerAwtPermission() {
+ return Boolean.parseBoolean(JNLPRuntime.getConfiguration().getProperty(ConfigurationConstants.KEY_SECURITY_ALLOW_HIDE_WINDOW_WARNING));
+ }
+
+ /**
+ * Returns a Policy object that represents a custom policy to use instead
+ * of granting {@link AllPermission} to a {@link CodeSource}
+ *
+ * @return a {@link Policy} object to delegate to. May be null, which
+ * indicates that no policy exists and AllPermissions should be granted
+ * instead.
+ */
+ private static Policy getCustomTrustedPolicy() {
+ final String key = ConfigurationConstants.KEY_SECURITY_TRUSTED_POLICY;
+ final String policyLocation = JNLPRuntime.getConfiguration().getProperty(key);
+
+ Policy policy = null;
+
+ if (policyLocation != null) {
+ try {
+ final URI policyUri = new URI("file://" + policyLocation);
+ policy = Policy.getInstance("JavaPolicy", new URIParameter(policyUri));
+ } catch (Exception e) {
+ LOG.error("Unable to create trusted policy for policy file at " + policyLocation, e);
+ }
+ }
+
+ return policy;
+ }
+
+ /**
+ * Gets the host domain part of an applet's codebase. Removes path, query, and fragment, but preserves scheme,
+ * user info, and host. The port used is overridden with the specified port.
+ *
+ * @param codebase the applet codebase URL
+ * @param port the port
+ * @return the host domain of the codebase
+ */
+ static URI getHostWithSpecifiedPort(final URI codebase, final int port) throws URISyntaxException {
+ Objects.requireNonNull(codebase);
+ return new URI(codebase.getScheme(), codebase.getUserInfo(), codebase.getHost(), port, null, null, null);
+ }
+
+ /**
+ * Gets the host domain part of an applet's codebase. Removes path, query, and fragment, but preserves scheme,
+ * user info, host, and port.
+ *
+ * @param codebase the applet codebase URL
+ * @return the host domain of the codebase
+ */
+ static URI getHost(final URI codebase) throws URISyntaxException {
+ Objects.requireNonNull(codebase);
+ return getHostWithSpecifiedPort(codebase, codebase.getPort());
+ }
+
+ /**
+ * Appends a recursive access marker to a codebase host, for granting Java 8 URLPermissions which are no
+ * more restrictive than the existing SocketPermissions
+ * See http://docs.oracle.com/javase/8/docs/api/java/net/URLPermission.html
+ *
+ * @param codebaseHost the applet's codebase's host domain URL as a String. Expected to be formatted as eg
+ * "http://example.com:8080" or "http://example.com/"
+ * @return the resulting String eg "http://example.com:8080/-
+ */
+ static String appendRecursiveSubdirToCodebaseHostString(final String codebaseHost) {
+ Objects.requireNonNull(codebaseHost);
+ String result = codebaseHost;
+ while (result.endsWith("/")) {
+ result = result.substring(0, result.length() - 1);
+ }
+ // See http://docs.oracle.com/javase/8/docs/api/java/net/URLPermission.html
+ result = result + "/-"; // allow access to any resources recursively on the host domain
+ return result;
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/RememberingSecurityUserInteractions.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/RememberingSecurityUserInteractions.java
new file mode 100644
index 000000000..642e55471
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/RememberingSecurityUserInteractions.java
@@ -0,0 +1,135 @@
+package net.adoptopenjdk.icedteaweb.security;
+
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.lockingfile.StorageIoException;
+import net.adoptopenjdk.icedteaweb.security.dialog.DialogProvider;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AccessWarningResult;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDeny;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDenySandbox;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.RememberableResult;
+import net.adoptopenjdk.icedteaweb.userdecision.UserDecisions;
+import net.adoptopenjdk.icedteaweb.userdecision.UserDecisionsFileStore;
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.config.ConfigurationConstants;
+import net.sourceforge.jnlp.config.DeploymentConfiguration;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.security.AccessType;
+import net.sourceforge.jnlp.security.CertificateUtils;
+import net.sourceforge.jnlp.tools.CertInformation;
+
+import java.security.cert.CertPath;
+import java.security.cert.X509Certificate;
+import java.util.Optional;
+
+import static net.adoptopenjdk.icedteaweb.security.dialog.result.AccessWarningResult.ALWAYS;
+import static net.adoptopenjdk.icedteaweb.userdecision.UserDecision.Key.RUN_UNSIGNED_APPLICATION;
+import static net.adoptopenjdk.icedteaweb.userdecision.UserDecision.of;
+
+/**
+ * Interactions with user for concerning security and permission related decisions.
+ */
+public class RememberingSecurityUserInteractions implements SecurityUserInteractions {
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private final UserDecisions userDecisions;
+
+ public RememberingSecurityUserInteractions() {
+ this(new UserDecisionsFileStore());
+ }
+
+ RememberingSecurityUserInteractions(UserDecisions userDecisions) {
+ this.userDecisions = userDecisions;
+ }
+
+ /**
+ * Ask the user for permission by showing an {@link net.adoptopenjdk.icedteaweb.security.dialog.UnsignedWarningDialog}
+ * when an application is unsigned. This is used with 'high-security' setting.
+ *
+ * @param file
+ * @return
+ * @see Java 8 Properties
+ */
+ public AllowDeny askUserForPermissionToRunUnsignedApplication(final JNLPFile file) {
+ DeploymentConfiguration conf = JNLPRuntime.getConfiguration();
+ if (conf == null) {
+ throw new StorageIoException("JNLPRuntime configuration is null. Try to reinstall IcedTea-Web");
+ }
+ SecurityLevel securityLevel = SecurityLevel.valueOf(conf.getProperty(ConfigurationConstants.KEY_SECURITY_LEVEL));
+
+ if (securityLevel == SecurityLevel.VERY_HIGH) {
+ return AllowDeny.DENY;
+ }
+
+ final Optional remembered = this.userDecisions.getUserDecisions(RUN_UNSIGNED_APPLICATION, file, AllowDeny.class);
+
+ return remembered.orElseGet(() -> {
+ final RememberableResult dialogResult = DialogProvider.showUnsignedWarningDialog(file);
+ userDecisions.save(dialogResult.getRemember(), file, of(RUN_UNSIGNED_APPLICATION, dialogResult.getResult()));
+ return dialogResult.getResult();
+ });
+ }
+
+ @Override
+ public AllowDenySandbox askUserHowToRunApplicationWithCertIssues(final JNLPFile file, final CertPath certPath, final CertInformation certInfo) {
+ final boolean rootInCaCerts = certInfo.isRootInCacerts();
+
+ AccessType accessType;
+ if (rootInCaCerts && !certInfo.hasSigningIssues()) {
+ accessType = AccessType.VERIFIED;
+ } else if (certInfo.isRootInCacerts()) {
+ accessType = AccessType.SIGNING_ERROR;
+ } else {
+ accessType = AccessType.UNVERIFIED;
+ }
+
+ final String message = getMessageFor(accessType);
+ final boolean alwaysTrustSelected = (accessType == AccessType.VERIFIED);
+ final String moreInformationText = getMoreInformationText(accessType, rootInCaCerts);
+
+ final AccessWarningResult result = DialogProvider.showJarCertWarningDialog(file, certPath.getCertificates(), certInfo.getDetailsAsStrings(), message, alwaysTrustSelected, moreInformationText);
+
+ if (result == ALWAYS) {
+ certPath.getCertificates().stream()
+ .findFirst()
+ .filter(cert -> cert instanceof X509Certificate)
+ .map(cert -> (X509Certificate) cert)
+ .ifPresent(CertificateUtils::saveCertificate);
+ }
+
+ switch (result) {
+ case YES:
+ case ALWAYS:
+ return AllowDenySandbox.ALLOW;
+ case SANDBOX:
+ return AllowDenySandbox.SANDBOX;
+ default:
+ return AllowDenySandbox.DENY;
+ }
+ }
+
+ private static String getMoreInformationText(final AccessType accessType, final boolean rootInCaCerts) {
+ String moreInformationText = rootInCaCerts ?
+ TRANSLATOR.translate("STrustedSource") : TRANSLATOR.translate("SUntrustedSource");
+
+ switch (accessType) {
+ case UNVERIFIED:
+ case SIGNING_ERROR:
+ return moreInformationText + " " + TRANSLATOR.translate("SWarnFullPermissionsIgnorePolicy");
+ default:
+ return moreInformationText;
+ }
+ }
+
+ private static String getMessageFor(final AccessType accessType) {
+ switch (accessType) {
+ case VERIFIED:
+ return TRANSLATOR.translate("SSigVerified");
+ case UNVERIFIED:
+ return TRANSLATOR.translate("SSigUnverified");
+ case SIGNING_ERROR:
+ return TRANSLATOR.translate("SSignatureError");
+ default:
+ return "";
+ }
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/SecurityLevel.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/SecurityLevel.java
new file mode 100644
index 000000000..864156a92
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/SecurityLevel.java
@@ -0,0 +1,6 @@
+package net.adoptopenjdk.icedteaweb.security;
+
+public enum SecurityLevel {
+ HIGH,
+ VERY_HIGH
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/SecurityUserInteractions.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/SecurityUserInteractions.java
new file mode 100644
index 000000000..5db11904f
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/SecurityUserInteractions.java
@@ -0,0 +1,18 @@
+package net.adoptopenjdk.icedteaweb.security;
+
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDeny;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDenySandbox;
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.tools.CertInformation;
+
+import java.security.cert.CertPath;
+
+/**
+ * Interactions with user for concerning security and permission related decisions.
+ */
+public interface SecurityUserInteractions {
+
+ AllowDeny askUserForPermissionToRunUnsignedApplication(final JNLPFile file);
+
+ AllowDenySandbox askUserHowToRunApplicationWithCertIssues(final JNLPFile file, final CertPath certPath, final CertInformation certInformation);
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/AccessWarningDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/AccessWarningDialog.java
new file mode 100644
index 000000000..87c6a4488
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/AccessWarningDialog.java
@@ -0,0 +1,66 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.GridBagPanelBuilder;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.LayoutPartsBuilder;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.RememberUserDecisionPanel;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDeny;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.RememberableResult;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogButton;
+import net.sourceforge.jnlp.JNLPFile;
+
+import javax.swing.JComponent;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ *
+ */
+public class AccessWarningDialog extends BasicSecurityDialog> {
+ private static final Logger LOG = LoggerFactory.getLogger(AccessWarningDialog.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private final JNLPFile file;
+ private final DialogButton> allowButton;
+ private final DialogButton> denyButton;
+ private RememberUserDecisionPanel rememberUserDecisionPanel;
+
+ private AccessWarningDialog(final JNLPFile file, final String message) {
+ super(message);
+ this.file = file;
+ allowButton = ButtonFactory.createAllowButton(() -> new RememberableResult<>(AllowDeny.ALLOW, rememberUserDecisionPanel.getResult()));
+ denyButton = ButtonFactory.createDenyButton(() -> new RememberableResult<>(AllowDeny.DENY, rememberUserDecisionPanel.getResult()));
+ }
+
+ public static AccessWarningDialog create(final JNLPFile jnlpFile, final String message) {
+ return new AccessWarningDialog(jnlpFile, message);
+ }
+
+ @Override
+ public String createTitle() {
+ return "Security Warning";
+ }
+
+ @Override
+ protected JComponent createDetailPaneContent() {
+ final GridBagPanelBuilder gridBuilder = new GridBagPanelBuilder();
+ try {
+ gridBuilder.addRows(LayoutPartsBuilder.getApplicationDetails(file));
+ gridBuilder.addHorizontalSpacer();
+
+ rememberUserDecisionPanel = new RememberUserDecisionPanel();
+ gridBuilder.addComponentRow(rememberUserDecisionPanel);
+
+ } catch (final Exception e) {
+ LOG.error("Error while trying to read properties for Access warning dialog!", e);
+ }
+ return gridBuilder.createGrid();
+ }
+
+ @Override
+ protected List>> createButtons() {
+ return Arrays.asList(allowButton, denyButton);
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/AuthenticationDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/AuthenticationDialog.java
new file mode 100644
index 000000000..3b10aa619
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/AuthenticationDialog.java
@@ -0,0 +1,86 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.GridBagPanelBuilder;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.image.ImageGallery;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogButton;
+import net.adoptopenjdk.icedteaweb.ui.swing.dialogresults.NamePassword;
+
+import javax.swing.ImageIcon;
+import javax.swing.JComponent;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+import java.awt.Dimension;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ *
+ */
+public class AuthenticationDialog extends BasicSecurityDialog> {
+ private static final Logger LOG = LoggerFactory.getLogger(AuthenticationDialog.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private final DialogButton> loginButton;
+ private final DialogButton> cancelButton;
+ private JTextField usernameTextField;
+ private JPasswordField passwordTextField;
+
+ private AuthenticationDialog(final String message) {
+ super(message);
+ loginButton = ButtonFactory.createLoginButton(() -> {
+ final NamePassword value = new NamePassword(usernameTextField.getText(), passwordTextField.getPassword());
+ return Optional.of(value);
+ });
+ cancelButton = ButtonFactory.createCancelButton(Optional::empty);
+ }
+
+ /**
+ * @param host hostname of the site or proxy requesting authentication
+ * @param port port number for the requested connection
+ * @param prompt the prompt string given by the requestor
+ * @param type type that defines that requestor is a Proxy or a Server
+ * @return
+ */
+ public static AuthenticationDialog create(final String host, final int port, final String prompt, final String type) {
+ String message = TRANSLATOR.translate("SAuthenticationPrompt", type, host, prompt);
+ return new AuthenticationDialog(message);
+ }
+
+ public Dimension getPreferredSize() {
+ return new Dimension(500, super.getPreferredSize().height);
+ }
+
+ @Override
+ protected ImageIcon createIcon() {
+ return ImageGallery.LOGIN.asImageIcon();
+ }
+
+ @Override
+ protected String createTitle() {
+ return TRANSLATOR.translate("CVPasswordTitle");
+ }
+
+ @Override
+ protected JComponent createDetailPaneContent() {
+ final GridBagPanelBuilder gridBuilder = new GridBagPanelBuilder();
+ try {
+ usernameTextField = new JTextField("", 20);
+ gridBuilder.addKeyComponentRow(TRANSLATOR.translate("Username"), usernameTextField);
+ passwordTextField = new JPasswordField("", 20);
+ gridBuilder.addKeyComponentRow(TRANSLATOR.translate("Password"), passwordTextField);
+
+ } catch (final Exception e) {
+ LOG.error("Error while trying to read properties for AuthenticationDialog!", e);
+ }
+ return gridBuilder.createGrid();
+ }
+
+ @Override
+ protected List>> createButtons() {
+ return Arrays.asList(loginButton, cancelButton);
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/BasicSecurityDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/BasicSecurityDialog.java
new file mode 100644
index 000000000..439f3c980
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/BasicSecurityDialog.java
@@ -0,0 +1,141 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.image.ImageGallery;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogButton;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogWithResult;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+import javax.swing.UIManager;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.FlowLayout;
+import java.util.Collections;
+import java.util.List;
+
+import static net.adoptopenjdk.icedteaweb.ui.swing.SwingUtils.htmlWrap;
+
+public abstract class BasicSecurityDialog extends DialogWithResult {
+ private String message;
+
+ public BasicSecurityDialog(String message) {
+ super();
+ this.message = message;
+ }
+
+ protected ImageIcon createIcon() {
+ return ImageGallery.QUESTION.asImageIcon();
+ }
+
+ protected abstract List> createButtons();
+
+ protected abstract JComponent createDetailPaneContent();
+
+ @Override
+ protected JPanel createContentPane() {
+ final JPanel contentPanel = new JPanel(new BorderLayout());
+ contentPanel.add(createBanner(), BorderLayout.NORTH);
+ contentPanel.add(createDetails(), BorderLayout.CENTER);
+ contentPanel.add(createActionButtons(), BorderLayout.SOUTH);
+ return contentPanel;
+ }
+
+ private JPanel createBanner() {
+ final JPanel bannerPanel = new JPanel(new BorderLayout(15, 0));
+ bannerPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
+ bannerPanel.setBackground(Color.WHITE);
+ bannerPanel.add(createBannerImage(), BorderLayout.WEST);
+ bannerPanel.add(createBannerMessage(), BorderLayout.CENTER);
+ return bannerPanel;
+ }
+
+ private JPanel createBannerImage() {
+ JPanel alignHelperPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
+ alignHelperPanel.setBackground(null);
+ final JLabel iconLabel = new JLabel(createIcon());
+ alignHelperPanel.add(iconLabel);
+ return alignHelperPanel;
+ }
+
+ private JLabel createBannerMessage() {
+ final JLabel bannerText = new JLabel(htmlWrap(message), SwingConstants.CENTER);
+ bannerText.setIconTextGap(10);
+ bannerText.setBackground(null);
+ bannerText.setFont(bannerText.getFont().deriveFont(16f));
+ return bannerText;
+ }
+
+ private JPanel createDetails() {
+ final JPanel detailPanel = new JPanel();
+ detailPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
+ detailPanel.add(createDetailPaneContent());
+ return detailPanel;
+ }
+
+ private JPanel createActionButtons() {
+ final JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
+ buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ buttonPanel.add(Box.createHorizontalGlue());
+
+ final List> buttons = createButtons();
+ buttons.forEach(b -> {
+ final JButton button = b.createButton(this::close);
+ buttonPanel.add(button);
+ });
+ return buttonPanel;
+ }
+
+ public static void main(String[] args) throws Exception {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ final String msg1 = "This is a long text that should be displayed in more than 1 line. " +
+ "This is a long text that should be displayed in more than 1 line. " +
+ "This is a long text that should be displayed in more than 1 line.";
+ final String msg2 = "This is a small text line." +
+ "\n\nDo you want to continue with no proxy or exit the application?";
+
+ final DialogButton exitButton = new DialogButton<>("BasicSecurityDialog 1 Title", () -> 0);
+
+ new BasicSecurityDialog(msg1) {
+ @Override
+ public String createTitle() {
+ return "Security Warning 1";
+ }
+
+ @Override
+ protected List> createButtons() {
+ return Collections.singletonList(exitButton);
+ }
+
+ @Override
+ protected JComponent createDetailPaneContent() {
+ return new JLabel("Detail pane content");
+ }
+ }.showAndWait();
+
+ new BasicSecurityDialog(msg2) {
+ @Override
+ public String createTitle() {
+ return "Security Warning 2";
+ }
+
+ @Override
+ protected List> createButtons() {
+ return Collections.singletonList(exitButton);
+ }
+
+ @Override
+ protected JComponent createDetailPaneContent() {
+ return new JLabel("Detail pane content");
+ }
+
+ }.showAndWait();
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/ButtonFactory.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/ButtonFactory.java
new file mode 100644
index 000000000..385b90abc
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/ButtonFactory.java
@@ -0,0 +1,54 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogButton;
+
+import java.util.function.Supplier;
+
+public class ButtonFactory {
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ public static DialogButton createAllowButton(final Supplier onAction) {
+ return new DialogButton<>(TRANSLATOR.translate("ButAllow"), onAction);
+ }
+
+ public static DialogButton createCreateButton(final Supplier onAction) {
+ return new DialogButton<>(TRANSLATOR.translate("ButCreate"), onAction);
+ }
+
+ public static DialogButton createDenyButton(final Supplier onAction) {
+ return new DialogButton<>(TRANSLATOR.translate("ButDeny"), onAction);
+ }
+
+ public static DialogButton createCancelButton(final Supplier onAction) {
+ return new DialogButton<>(TRANSLATOR.translate("ButCancel"), onAction);
+ }
+
+ public static DialogButton createCloseButton(final Supplier onAction) {
+ return new DialogButton<>(TRANSLATOR.translate("ButClose"), onAction);
+ }
+
+ public static DialogButton createCancelButton(String toolTipText, final Supplier onAction) {
+ return new DialogButton<>(TRANSLATOR.translate("ButCancel"), onAction, toolTipText);
+ }
+
+ public static DialogButton createRunButton(final Supplier onAction) {
+ return new DialogButton<>(TRANSLATOR.translate("ButRun"), onAction, TRANSLATOR.translate("CertWarnRunTip"));
+ }
+
+ public static DialogButton createLoginButton(final Supplier onAction) {
+ return new DialogButton<>(TRANSLATOR.translate("Login"), onAction, TRANSLATOR.translate("LoginButtonTooltip"));
+ }
+
+ public static DialogButton createSandboxButton(final Supplier onAction) {
+ return new DialogButton<>(TRANSLATOR.translate("ButSandbox"), onAction, TRANSLATOR.translate("CertWarnSandboxTip"));
+ }
+
+ public static DialogButton createYesButton(final Supplier onAction) {
+ return new DialogButton<>(TRANSLATOR.translate("ButYes"), onAction, TRANSLATOR.translate("CertWarnHTTPSAcceptTip"));
+ }
+
+ public static DialogButton createNoButton(final Supplier onAction) {
+ return new DialogButton<>(TRANSLATOR.translate("ButNo"), onAction, TRANSLATOR.translate("CertWarnHTTPSRejectTip"));
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/CertInfoDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/CertInfoDialog.java
new file mode 100644
index 000000000..173253375
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/CertInfoDialog.java
@@ -0,0 +1,101 @@
+// Copyright (C) 2019 Karakun AG
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.CertificateDetailsPanel;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogButton;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogWithResult;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import java.awt.BorderLayout;
+import java.awt.Dialog;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.List;
+
+import static net.adoptopenjdk.icedteaweb.i18n.Translator.R;
+
+/**
+ *
+ */
+public class CertInfoDialog extends DialogWithResult {
+ private static final Logger LOG = LoggerFactory.getLogger(CertInfoDialog.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private CertificateDetailsPanel certificateDetailsPanel;
+ private final DialogButton closeButton;
+
+ private List extends Certificate> certificates;
+
+ private CertInfoDialog(final Dialog owner, final List extends Certificate> certificates) {
+ super(owner);
+ this.certificates = certificates;
+ this.closeButton = ButtonFactory.createCloseButton(() -> null);
+ }
+
+ public static CertInfoDialog create(final Dialog owner, final List extends Certificate> certificates) {
+ return new CertInfoDialog(owner, certificates);
+ }
+
+ @Override
+ protected String createTitle() {
+ return TRANSLATOR.translate("SCertificateDetails");
+ }
+
+ private List> createButtons() {
+ return Collections.singletonList(closeButton);
+ }
+
+ private JButton createCopyToClipboardButton() {
+ JButton copyToClipboard = new JButton(R("ButCopy"));
+ copyToClipboard.addActionListener(e -> {
+ certificateDetailsPanel.copyToClipboard();
+ });
+ return copyToClipboard;
+ }
+
+ @Override
+ protected JPanel createContentPane() {
+ final JPanel contentPanel = new JPanel(new BorderLayout(10, 10));
+ contentPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
+ contentPanel.add(new CertificateDetailsPanel(certificates), BorderLayout.CENTER);
+ contentPanel.add(createActionButtons(), BorderLayout.SOUTH);
+ return contentPanel;
+ }
+
+ private JPanel createActionButtons() {
+ final JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
+ buttonPanel.add(Box.createHorizontalGlue());
+
+ buttonPanel.add(createCopyToClipboardButton());
+
+ final List> buttons = createButtons();
+ buttons.forEach(b -> {
+ final JButton button = b.createButton(this::close);
+ buttonPanel.add(button);
+ });
+
+ return buttonPanel;
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/CertWarningDetailsDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/CertWarningDetailsDialog.java
new file mode 100644
index 000000000..071ef4478
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/CertWarningDetailsDialog.java
@@ -0,0 +1,164 @@
+// Copyright (C) 2019 Karakun AG
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.image.ImageGallery;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.CertificateDetailsPanel;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogButton;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogWithResult;
+import net.sourceforge.jnlp.JNLPFile;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+import java.awt.BorderLayout;
+import java.awt.Dialog;
+import java.awt.GridLayout;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static net.adoptopenjdk.icedteaweb.i18n.Translator.R;
+import static net.adoptopenjdk.icedteaweb.ui.swing.SwingUtils.htmlWrap;
+
+/**
+ *
+ */
+public class CertWarningDetailsDialog extends DialogWithResult {
+ private static final Logger LOG = LoggerFactory.getLogger(CertWarningDetailsDialog.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private CertificateDetailsPanel certificateDetailsPanel;
+ private final DialogButton closeButton;
+
+ private final List details;
+ private List extends Certificate> certificates;
+
+ private CertWarningDetailsDialog(final Dialog owner, final JNLPFile file, final List extends Certificate> certificates, final List certIssues) {
+ super(owner);
+ this.certificates = certificates;
+ details = new ArrayList<>(certIssues);
+
+ // Show signed JNLP warning if the signed main jar does not have a
+ // signed JNLP file and the launching JNLP file contains special properties
+ if (file != null && file.requiresSignedJNLPWarning()) {
+ details.add(TRANSLATOR.translate("SJNLPFileIsNotSigned"));
+ }
+
+ this.closeButton = ButtonFactory.createCloseButton(() -> null);
+ }
+
+ public static CertWarningDetailsDialog create(final Dialog owner, final JNLPFile file, final List extends Certificate> certificates, final List certIssues) {
+ return new CertWarningDetailsDialog(owner, file, certificates, certIssues);
+ }
+
+ @Override
+ protected String createTitle() {
+ return TRANSLATOR.translate("MoreInformation");
+ }
+
+ private List> createButtons() {
+ return Arrays.asList(closeButton);
+ }
+
+ private JButton createShowCertificateDetailsButton() {
+ final JButton button = new JButton(TRANSLATOR.translate("SCertificateDetails"));
+ return button;
+ }
+
+ private JButton createCopyToClipboardButton() {
+ JButton copyToClipboard = new JButton(R("ButCopy"));
+ copyToClipboard.addActionListener(e -> {
+ certificateDetailsPanel.copyToClipboard();
+ });
+ return copyToClipboard;
+ }
+
+ private JComponent createDetailPaneContent() {
+ int numLabels = details.size();
+ JPanel errorPanel = new JPanel(new GridLayout(numLabels, 1, 0, 10));
+ errorPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
+ for (String detail : details) {
+ ImageIcon icon = null;
+ if (detail.equals(TRANSLATOR.translate("STrustedCertificate"))) {
+ icon = ImageGallery.INFO_SMALL.asImageIcon();
+ } else {
+ icon = ImageGallery.WARNING_SMALL.asImageIcon();
+ }
+
+ final JLabel imageLabel = new JLabel(htmlWrap(detail), icon, SwingConstants.LEFT);
+ imageLabel.setIconTextGap(10);
+ errorPanel.add(imageLabel);
+ }
+ return errorPanel;
+ }
+
+ @Override
+ protected JPanel createContentPane() {
+ final JPanel contentPanel = new JPanel(new BorderLayout(10, 10));
+ contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ contentPanel.add(createDetailPaneContent(), BorderLayout.NORTH);
+ contentPanel.add(createCertificateDetailsCollapsiblePanel(), BorderLayout.CENTER);
+ contentPanel.add(createActionButtons(), BorderLayout.SOUTH);
+ return contentPanel;
+ }
+
+ private JPanel createCertificateDetailsCollapsiblePanel() {
+ final JPanel collapsiblePanel = new JPanel(new BorderLayout());
+ collapsiblePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
+ final JButton showCertificateDetailsButton = createShowCertificateDetailsButton();
+ collapsiblePanel.add(showCertificateDetailsButton, BorderLayout.NORTH);
+
+ certificateDetailsPanel = new CertificateDetailsPanel(certificates);
+ certificateDetailsPanel.setVisible(false);
+
+ showCertificateDetailsButton.addActionListener(e -> {
+ certificateDetailsPanel.setVisible(!certificateDetailsPanel.isVisible());
+ pack();
+ });
+
+ collapsiblePanel.add(certificateDetailsPanel, BorderLayout.CENTER);
+ return collapsiblePanel;
+ }
+
+ private JPanel createActionButtons() {
+ final JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
+ buttonPanel.add(Box.createHorizontalGlue());
+
+ buttonPanel.add(createCopyToClipboardButton());
+
+ final List> buttons = createButtons();
+ buttons.forEach(b -> {
+ final JButton button = b.createButton(this::close);
+ buttonPanel.add(button);
+ });
+
+ return buttonPanel;
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/CertWarningDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/CertWarningDialog.java
new file mode 100644
index 000000000..8d6cc6f8f
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/CertWarningDialog.java
@@ -0,0 +1,73 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AccessWarningResult;
+import net.sourceforge.jnlp.JNLPFile;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.security.cert.Certificate;
+import java.util.List;
+
+import static net.adoptopenjdk.icedteaweb.i18n.Translator.R;
+
+abstract class CertWarningDialog extends BasicSecurityDialog {
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private final JNLPFile file;
+ private final List extends Certificate> certificates;
+ private final List certIssues;
+ protected final boolean initiallyAlwaysTrustedSelected;
+ private final String moreInformationText;
+
+ protected CertWarningDialog(final String message, final JNLPFile file, final List extends Certificate> certificates, final List certIssues, boolean initiallyAlwaysTrustedSelected, final String moreInformationText) {
+ super(message);
+ this.file = file;
+ this.certificates = certificates;
+ this.certIssues = certIssues;
+ this.initiallyAlwaysTrustedSelected = initiallyAlwaysTrustedSelected;
+ this.moreInformationText = moreInformationText;
+ }
+
+ @Override
+ public String createTitle() {
+ return TRANSLATOR.translate(initiallyAlwaysTrustedSelected ? "SSecurityApprovalRequired" : "SSecurityWarning");
+ }
+
+ protected JCheckBox createAlwaysTrustCheckbox() {
+ JCheckBox alwaysTrustCheckBox = new JCheckBox(R("SAlwaysTrustPublisher"));
+ alwaysTrustCheckBox.setEnabled(true);
+ alwaysTrustCheckBox.setSelected(initiallyAlwaysTrustedSelected);
+ return alwaysTrustCheckBox;
+ }
+
+ protected JPanel createMoreInformationPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ final JTextArea moreInfoTextArea = new JTextArea(moreInformationText);
+ moreInfoTextArea.setBackground(getBackground());
+ moreInfoTextArea.setWrapStyleWord(true);
+ moreInfoTextArea.setLineWrap(true);
+ moreInfoTextArea.setEditable(false);
+
+ final JScrollPane scrollPane = new JScrollPane(moreInfoTextArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ scrollPane.setBorder(BorderFactory.createEmptyBorder());
+ scrollPane.setHorizontalScrollBar(null);
+
+ panel.add(scrollPane, BorderLayout.CENTER);
+ JButton moreInfoButton = new JButton(TRANSLATOR.translate("ButMoreInformation"));
+
+ moreInfoButton.addActionListener((e) -> DialogProvider.showMoreInfoDialog(certificates, certIssues, file));
+ JPanel alignHelperPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+ alignHelperPanel.add(moreInfoButton);
+ panel.add(alignHelperPanel, BorderLayout.SOUTH);
+ panel.setPreferredSize(new Dimension(720, 110));
+ return panel;
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/CreateShortcutDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/CreateShortcutDialog.java
new file mode 100644
index 000000000..4ef05d62c
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/CreateShortcutDialog.java
@@ -0,0 +1,122 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.GridBagPanelBuilder;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.jnlp.element.information.InformationDesc;
+import net.adoptopenjdk.icedteaweb.jnlp.element.information.MenuDesc;
+import net.adoptopenjdk.icedteaweb.jnlp.element.information.ShortcutDesc;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.RememberUserDecisionPanel;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.LayoutPartsBuilder;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.CreateShortcutResult;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.RememberableResult;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogButton;
+import net.sourceforge.jnlp.JNLPFile;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+import static net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDeny.valueOf;
+
+/**
+ *
+ */
+public class CreateShortcutDialog extends BasicSecurityDialog>> {
+ private static final Logger LOG = LoggerFactory.getLogger(CreateShortcutDialog.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private final JNLPFile file;
+ private final DialogButton>> createButton;
+ private final DialogButton>> cancelButton;
+ private JCheckBox desktopCheckBox;
+ private JCheckBox menuCheckBox;
+ private RememberUserDecisionPanel rememberUserDecisionPanel;
+
+ private CreateShortcutDialog(final JNLPFile file, final String message) {
+ super(message);
+ this.file = file;
+ createButton = ButtonFactory.createCreateButton(() -> {
+ final CreateShortcutResult shortcutResult = new CreateShortcutResult(valueOf(desktopCheckBox), valueOf(menuCheckBox));
+ return Optional.of(new RememberableResult<>(shortcutResult, rememberUserDecisionPanel.getResult()));
+ });
+ cancelButton = ButtonFactory.createCancelButton(Optional::empty);
+ }
+
+ public static CreateShortcutDialog create(final JNLPFile jnlpFile) {
+ return new CreateShortcutDialog(jnlpFile, TRANSLATOR.translate("SDesktopShortcut"));
+ }
+
+ private JCheckBox createDesktopCheckBox() {
+ final Boolean applicationRequested = Optional.ofNullable(file.getInformation())
+ .map(InformationDesc::getShortcut)
+ .map(ShortcutDesc::onDesktop)
+ .orElse(false);
+
+ final String textKey = applicationRequested ? "EXAWdesktopWants" : "EXAWdesktopDontWants";
+
+ return new JCheckBox(TRANSLATOR.translate(textKey), applicationRequested);
+ }
+
+ private JCheckBox createMenuCheckBox() {
+
+ final Boolean includeInMenuRequested = Optional.ofNullable(file.getInformation())
+ .map(InformationDesc::getShortcut)
+ .map(ShortcutDesc::toMenu)
+ .orElse(false);
+
+ final Optional subMenu = Optional.ofNullable(file.getInformation())
+ .map(InformationDesc::getShortcut)
+ .map(ShortcutDesc::getMenu)
+ .map(MenuDesc::getSubMenu);
+
+ if (includeInMenuRequested) {
+ final String text;
+ if (subMenu.isPresent()) {
+ text = TRANSLATOR.translate("EXAWsubmenu", subMenu.get());
+ } else {
+ text = TRANSLATOR.translate("EXAWmenuWants");
+ }
+ return new JCheckBox(text, true);
+
+ } else {
+ return new JCheckBox(TRANSLATOR.translate("EXAWmenuDontWants"), false);
+ }
+ }
+
+ @Override
+ public String createTitle() {
+ return "Security Warning";
+ }
+
+ @Override
+ protected JComponent createDetailPaneContent() {
+ final GridBagPanelBuilder gridBuilder = new GridBagPanelBuilder();
+ try {
+ gridBuilder.addRows(LayoutPartsBuilder.getApplicationDetails(file));
+ gridBuilder.addHorizontalSpacer();
+
+ desktopCheckBox = createDesktopCheckBox();
+ gridBuilder.addComponentRow(desktopCheckBox);
+ menuCheckBox = createMenuCheckBox();
+ gridBuilder.addComponentRow(menuCheckBox);
+
+ gridBuilder.addHorizontalSpacer();
+
+ rememberUserDecisionPanel = new RememberUserDecisionPanel();
+ gridBuilder.addComponentRow(rememberUserDecisionPanel);
+
+ } catch (final Exception e) {
+ LOG.error("Error while trying to read properties for create shortcut dialog!", e);
+ }
+ return gridBuilder.createGrid();
+ }
+
+ @Override
+ protected List>>> createButtons() {
+ return Arrays.asList(createButton, cancelButton);
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/DialogProvider.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/DialogProvider.java
new file mode 100644
index 000000000..74ac22cfd
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/DialogProvider.java
@@ -0,0 +1,142 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AccessWarningResult;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDeny;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDenySandbox;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.CreateShortcutResult;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.RememberableResult;
+import net.adoptopenjdk.icedteaweb.ui.swing.dialogresults.NamePassword;
+import net.sourceforge.jnlp.JNLPFile;
+
+import java.awt.Dialog;
+import java.net.URL;
+import java.security.cert.Certificate;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import static java.util.Optional.ofNullable;
+
+/**
+ * This is a dialog provider that is able to show various application dialogs and wait for the user input to
+ * deliver it as specific result object. There are no side-effects during user interaction by the dialog code.
+ * All user decisions are provided in the result.
+ */
+public class DialogProvider {
+ private static final Logger LOG = LoggerFactory.getLogger(DialogProvider.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ public static RememberableResult showReadWriteFileAccessWarningDialog(final JNLPFile file, final String filePath) {
+ return showFileAccessWarningDialog(file, "SFileReadWriteAccess", filePath);
+ }
+
+ public static RememberableResult showReadFileAccessWarningDialog(final JNLPFile file, final String filePath) {
+ return showFileAccessWarningDialog(file, "SFileReadAccess", filePath);
+ }
+
+ public static RememberableResult showWriteFileAccessWarningDialog(final JNLPFile file, final String filePath) {
+ return showFileAccessWarningDialog(file, "SFileWriteAccess", filePath);
+ }
+
+ private static RememberableResult showFileAccessWarningDialog(final JNLPFile file, String messageKey, final String filePath) {
+ String message = ofNullable(filePath)
+ // .map(FileUtils::displayablePath) // TODO: we should discuss if this is really wanted
+ .map(p -> TRANSLATOR.translate(messageKey, p))
+ .orElse(TRANSLATOR.translate(messageKey, "AFileOnTheMachine"));
+
+ return showAccessWarningDialog(file, message);
+ }
+
+ public static RememberableResult showReadClipboardAccessWarningDialog(final JNLPFile file) {
+ return showClipboardAccessWarningDialog(file, "SClipboardReadAccess");
+ }
+
+ public static RememberableResult showWriteClipboardAccessWarningDialog(final JNLPFile file) {
+ return showClipboardAccessWarningDialog(file, "SClipboardWriteAccess");
+ }
+
+ private static RememberableResult showClipboardAccessWarningDialog(final JNLPFile file, String messageKey) {
+ String message = TRANSLATOR.translate(messageKey);
+ return showAccessWarningDialog(file, message);
+ }
+
+ public static RememberableResult showPrinterAccessWarningDialog(final JNLPFile file) {
+ String message = TRANSLATOR.translate("SPrinterAccess");
+ return showAccessWarningDialog(file, message);
+ }
+
+ public static RememberableResult showNetworkAccessWarningDialog(final JNLPFile file, final Object address) {
+ // TODO: Network access seems not to be used up to now, therefore it is unclear what type address really will be
+ String message = ofNullable(address)
+ .map(a -> TRANSLATOR.translate("SNetworkAccess", a))
+ .orElse(TRANSLATOR.translate("SNetworkAccess", ""));
+ return showAccessWarningDialog(file, message);
+ }
+
+ public static RememberableResult showAccessWarningDialog(final JNLPFile file, final String message) {
+ final AccessWarningDialog dialogWithResult = AccessWarningDialog.create(file, message);
+ return dialogWithResult.showAndWait();
+ }
+
+ public static Optional> showCreateShortcutDialog(final JNLPFile file) {
+ final CreateShortcutDialog createShortcutDialog = CreateShortcutDialog.create(file);
+ return createShortcutDialog.showAndWait();
+ }
+
+ public static void showCertWarningDetailsDialog(final Dialog owner, final JNLPFile file, final List extends Certificate> certificates, final List certIssues) {
+ CertWarningDetailsDialog dialog = CertWarningDetailsDialog.create(owner, file, certificates, certIssues);
+ dialog.showAndWait();
+ }
+
+ public static RememberableResult showUnsignedWarningDialog(final JNLPFile file) {
+ final UnsignedWarningDialog unsignedWarningDialog = UnsignedWarningDialog.create(file);
+ return unsignedWarningDialog.showAndWait();
+ }
+
+ public static AccessWarningResult showHttpsCertTrustDialog(final JNLPFile file, final Certificate certificate, final boolean rootInCaCerts, final List extends Certificate> certificates, final List certIssues) {
+ final HttpsCertTrustDialog dialog = HttpsCertTrustDialog.create(file, certificate, rootInCaCerts, certificates, certIssues);
+ return dialog.showAndWait();
+ }
+
+ public static AccessWarningResult showJarCertWarningDialog(final JNLPFile file, final List extends Certificate> certificates, final List certIssues, final String message, final boolean alwaysTrustSelected, final String moreInformationText) {
+ final JarCertWarningDialog dialog = JarCertWarningDialog.create(message, file, certificates, certIssues, moreInformationText, alwaysTrustSelected);
+ return dialog.showAndWait();
+ }
+
+ public static RememberableResult showPartiallySignedWarningDialog(final JNLPFile file) {
+ final PartiallySignedWarningDialog dialog = PartiallySignedWarningDialog.create(file);
+ return dialog.showAndWait();
+ }
+
+ public static RememberableResult showMissingALACAttributeDialog(final JNLPFile file, final Set locations) {
+ final MissingALACAttributeDialog dialog = MissingALACAttributeDialog.create(file, locations);
+ return dialog.showAndWait();
+ }
+
+ public static RememberableResult showMatchingALACAttributeDialog(final JNLPFile file, final Set locations) {
+ final MatchingALACAttributeDialog dialog = MatchingALACAttributeDialog.create(file, locations);
+ return dialog.showAndWait();
+ }
+
+ public static RememberableResult showMissingPermissionsAttributeDialog(final JNLPFile file) {
+ final MissingPermissionsAttributeDialog dialog = MissingPermissionsAttributeDialog.create(file);
+ return dialog.showAndWait();
+ }
+
+ public static void showCertInfoDialog(final Dialog owner, final List extends Certificate> certificates) {
+ CertInfoDialog dialog = CertInfoDialog.create(owner, certificates);
+ dialog.showAndWait();
+ }
+
+ public static NamePassword showAuthenticationDialog(final String host, final int port, final String prompt, final String type) {
+ AuthenticationDialog dialog = AuthenticationDialog.create(host, port, prompt, type);
+ return dialog.showAndWait().orElse(null);
+ }
+
+ public static void showMoreInfoDialog(final List extends Certificate> certificates, final List certIssues, final JNLPFile file) {
+ DialogProvider.showCertWarningDetailsDialog(null, file, certificates, certIssues);
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/HttpsCertTrustDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/HttpsCertTrustDialog.java
new file mode 100644
index 000000000..6b19e725e
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/HttpsCertTrustDialog.java
@@ -0,0 +1,99 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.GridBagPanelBuilder;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.image.ImageGallery;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AccessWarningResult;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogButton;
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.security.SecurityUtil;
+
+import javax.security.auth.x500.X500Principal;
+import javax.swing.ImageIcon;
+import javax.swing.JComponent;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ *
+ */
+public class HttpsCertTrustDialog extends CertWarningDialog {
+ private static final Logger LOG = LoggerFactory.getLogger(HttpsCertTrustDialog.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private final DialogButton yesButton;
+ private final DialogButton noButton;
+ private final JNLPFile file;
+ private final Certificate certificate;
+ private final boolean rootInCaCerts;
+
+
+ private HttpsCertTrustDialog(final String message, final JNLPFile file, final Certificate certificate, final boolean rootInCaCerts, final List extends Certificate> certificates, final List certIssues, final String moreInformationText) {
+ super(message, file, certificates, certIssues, false, moreInformationText);
+ this.file = file;
+ this.certificate = certificate;
+ this.rootInCaCerts = rootInCaCerts;
+
+ this.yesButton = ButtonFactory.createYesButton(() -> AccessWarningResult.YES);
+ this.noButton = ButtonFactory.createNoButton(() -> AccessWarningResult.NO);
+ }
+
+ public static HttpsCertTrustDialog create(final JNLPFile jnlpFile, final Certificate certificate, final boolean rootInCaCerts, final List extends Certificate> certificates, final List certIssues) {
+ final String message = TRANSLATOR.translate("SHttpsUnverified") + " " + TRANSLATOR.translate("Continue");
+ final String moreInformationText = getMoreInformationText(rootInCaCerts);
+ return new HttpsCertTrustDialog(message, jnlpFile, certificate, rootInCaCerts, certificates, certIssues, moreInformationText);
+ }
+
+ @Override
+ protected JComponent createDetailPaneContent() {
+ final GridBagPanelBuilder gridBuilder = new GridBagPanelBuilder();
+ try {
+ String name;
+ String publisher = "";
+ if (certificate instanceof X509Certificate) {
+ name = SecurityUtil.getCN(Optional.ofNullable(certificate)
+ .map(cert -> (X509Certificate) certificate)
+ .map(X509Certificate::getSubjectX500Principal)
+ .map(X500Principal::getName)
+ .orElse(""));
+ publisher = name;
+ } else {
+ name = file.getInformation().getTitle();
+ }
+ gridBuilder.addKeyValueRow(TRANSLATOR.translate("Name"), name);
+ gridBuilder.addKeyValueRow(TRANSLATOR.translate("Publisher"), publisher);
+
+ gridBuilder.addHorizontalSpacer();
+
+ gridBuilder.addComponentRow(createMoreInformationPanel());
+
+ gridBuilder.addHorizontalSpacer();
+
+
+ gridBuilder.addComponentRow(createAlwaysTrustCheckbox());
+
+ } catch (final Exception e) {
+ LOG.error("Error while trying to read properties for HttpsCertWarningDialog!", e);
+ }
+ return gridBuilder.createGrid();
+ }
+
+ @Override
+ protected List> createButtons() {
+ return Arrays.asList(yesButton, noButton);
+ }
+
+ @Override
+ protected ImageIcon createIcon() {
+ return ImageGallery.WARNING.asImageIcon();
+ }
+
+ private static String getMoreInformationText(final boolean rootInCaCerts) {
+ return rootInCaCerts ? TRANSLATOR.translate("STrustedSource") : TRANSLATOR.translate("SUntrustedSource");
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/JarCertWarningDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/JarCertWarningDialog.java
new file mode 100644
index 000000000..3fec9e56c
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/JarCertWarningDialog.java
@@ -0,0 +1,84 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.GridBagPanelBuilder;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.image.ImageGallery;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.LayoutPartsBuilder;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AccessWarningResult;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogButton;
+import net.sourceforge.jnlp.JNLPFile;
+
+import javax.swing.ImageIcon;
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import java.security.cert.Certificate;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * TODO: check if advancedOptions (temporary permissions) should still be supported
+ *
+ *
+ */
+public class JarCertWarningDialog extends CertWarningDialog {
+ private static final Logger LOG = LoggerFactory.getLogger(JarCertWarningDialog.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private final DialogButton runButton;
+ private final DialogButton sandboxButton;
+ private final DialogButton cancelButton;
+
+ private final JNLPFile file;
+ private final JCheckBox alwaysTrustCheckbox;
+
+ protected JarCertWarningDialog(final String message, final JNLPFile file, final List extends Certificate> certificates, final List certIssues, final String moreInformationText, final boolean alwaysTrustSelected) {
+ super(message, file, certificates, certIssues, alwaysTrustSelected, moreInformationText);
+ this.file = file;
+ runButton = ButtonFactory.createRunButton(() -> alwaysTrustSelected ? AccessWarningResult.ALWAYS : AccessWarningResult.YES);
+ sandboxButton = ButtonFactory.createSandboxButton(() -> AccessWarningResult.SANDBOX);
+ sandboxButton.setEnabled(!alwaysTrustSelected);
+ cancelButton = ButtonFactory.createCancelButton(TRANSLATOR.translate("CertWarnCancelTip"), () -> AccessWarningResult.NO);
+ alwaysTrustCheckbox = createAlwaysTrustCheckbox(sandboxButton);
+ }
+
+ public static JarCertWarningDialog create(final String message, final JNLPFile jnlpFile, final List extends Certificate> certificates, final List certIssues, final String moreInformationText, final boolean alwaysTrustSelected) {
+ return new JarCertWarningDialog(message, jnlpFile, certificates, certIssues, moreInformationText, alwaysTrustSelected);
+ }
+
+ @Override
+ protected ImageIcon createIcon() {
+ return initiallyAlwaysTrustedSelected ? ImageGallery.QUESTION.asImageIcon() : ImageGallery.WARNING.asImageIcon();
+ }
+
+ @Override
+ protected JComponent createDetailPaneContent() {
+ final GridBagPanelBuilder gridBuilder = new GridBagPanelBuilder();
+ try {
+ gridBuilder.addRows(LayoutPartsBuilder.getApplicationDetails(file));
+ gridBuilder.addHorizontalSpacer();
+
+ gridBuilder.addComponentRow(createMoreInformationPanel());
+
+ gridBuilder.addHorizontalSpacer();
+
+ gridBuilder.addComponentRow(alwaysTrustCheckbox);
+
+ } catch (final Exception e) {
+ LOG.error("Error while trying to read properties for CertWarningDialog!", e);
+ }
+ return gridBuilder.createGrid();
+ }
+
+ @Override
+ protected List> createButtons() {
+ return Arrays.asList(runButton, sandboxButton, cancelButton);
+ }
+
+ protected JCheckBox createAlwaysTrustCheckbox(final DialogButton sandboxButton) {
+ JCheckBox alwaysTrustCheckBox = super.createAlwaysTrustCheckbox();
+ alwaysTrustCheckBox.addActionListener(e -> sandboxButton.setEnabled(!alwaysTrustCheckBox.isSelected()));
+ return alwaysTrustCheckBox;
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/MatchingALACAttributeDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/MatchingALACAttributeDialog.java
new file mode 100644
index 000000000..a6564d439
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/MatchingALACAttributeDialog.java
@@ -0,0 +1,66 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.GridBagPanelBuilder;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.image.ImageGallery;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.ReferencesPanel;
+import net.sourceforge.jnlp.JNLPFile;
+
+import javax.swing.ImageIcon;
+import java.net.URL;
+import java.util.Set;
+
+/**
+ * The ALAC attribute identifies the locations where your signed application is expected to
+ * be found.
+ *
+ *
+ */
+public class MatchingALACAttributeDialog extends MissingAttributeDialog {
+ private static final Logger LOG = LoggerFactory.getLogger(MatchingALACAttributeDialog.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private static Set locations;
+
+ public MatchingALACAttributeDialog(final String message, final JNLPFile file) {
+ super(message, file);
+ }
+
+ public static MatchingALACAttributeDialog create(final JNLPFile file, final Set locations) {
+ MatchingALACAttributeDialog.locations = locations;
+ String message = createMessage(file.getTitle(), file.getNotNullProbableCodeBase());
+ return new MatchingALACAttributeDialog(message, file);
+ }
+
+ @Override
+ protected ImageIcon createIcon() {
+ return ImageGallery.QUESTION.asImageIcon();
+ }
+
+ @Override
+ protected String createTitle() {
+ return TRANSLATOR.translate("MatchingALACAttribute");
+ }
+
+ @Override
+ protected void getAdditionalApplicationDetails(final GridBagPanelBuilder gridBuilder) {
+ gridBuilder.addKeyValueRow(TRANSLATOR.translate("Codebase"), file.getNotNullProbableCodeBase().toString());
+ gridBuilder.addHorizontalSpacer();
+ gridBuilder.addComponentRow(createLocationList(locations));
+ }
+
+ private static ReferencesPanel createLocationList(final Set locations) {
+ return new ReferencesPanel(TRANSLATOR.translate("MatchingALACAttributeLocationListTitle"), locations);
+ }
+
+ @Override
+ protected ReferencesPanel createMoreInformationPanel() {
+ return new ReferencesPanel(TRANSLATOR.translate("MatchingALACAttributeMoreInfo"));
+ }
+
+ private static String createMessage(final String applicationName, final URL codebase) {
+ return TRANSLATOR.translate("MatchingALACAttributeMessage", applicationName, codebase);
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/MissingALACAttributeDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/MissingALACAttributeDialog.java
new file mode 100644
index 000000000..8294e8d2e
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/MissingALACAttributeDialog.java
@@ -0,0 +1,64 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.GridBagPanelBuilder;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.ReferencesPanel;
+import net.sourceforge.jnlp.JNLPFile;
+
+import java.net.URL;
+import java.util.Set;
+
+/**
+ * Missing Application-Library-Allowable-Codebase (ALAC) Attribute Dialog. This dialog is shown if
+ * the ALAC attribute is not provided in the manifest. The dialog lists the multiple hosts that
+ * correspond to the locations of the JAR file and the JNLP file. The user can decide to run the
+ * application and remember the decision to show this dialog again for the application or domain.
+ *
+ * The ALAC attribute identifies the locations where your signed application is expected to be
+ * found.
+ *
+ *
+ */
+public class MissingALACAttributeDialog extends MissingAttributeDialog {
+ private static final Logger LOG = LoggerFactory.getLogger(MissingALACAttributeDialog.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private static Set locations;
+
+ public MissingALACAttributeDialog(final String message, final JNLPFile file) {
+ super(message, file);
+ }
+
+ public static MissingALACAttributeDialog create(final JNLPFile file, final Set locations) {
+ MissingALACAttributeDialog.locations = locations;
+ String message = createMessage(file.getTitle(), file.getNotNullProbableCodeBase());
+ return new MissingALACAttributeDialog(message, file);
+ }
+
+ @Override
+ protected String createTitle() {
+ return TRANSLATOR.translate("MissingALACAttribute");
+ }
+
+ @Override
+ protected void getAdditionalApplicationDetails(final GridBagPanelBuilder gridBuilder) {
+ gridBuilder.addKeyValueRow(TRANSLATOR.translate("Codebase"), file.getNotNullProbableCodeBase().toString());
+ gridBuilder.addHorizontalSpacer();
+ gridBuilder.addComponentRow(createLocationList(locations));
+ }
+
+ private static ReferencesPanel createLocationList(final Set locations) {
+ return new ReferencesPanel(TRANSLATOR.translate("MissingALACAttributeLocationListTitle"), locations);
+ }
+
+ @Override
+ protected ReferencesPanel createMoreInformationPanel() {
+ return new ReferencesPanel(TRANSLATOR.translate("MissingALACAttributeMoreInfo"));
+ }
+
+ private static String createMessage(final String applicationName, final URL codebase) {
+ return TRANSLATOR.translate("MissingALACAttributeMessage", applicationName, codebase);
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/MissingAttributeDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/MissingAttributeDialog.java
new file mode 100644
index 000000000..eb61fac50
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/MissingAttributeDialog.java
@@ -0,0 +1,73 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.GridBagPanelBuilder;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.image.ImageGallery;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.ReferencesPanel;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.RememberUserDecisionPanel;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.LayoutPartsBuilder;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDeny;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.RememberableResult;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogButton;
+import net.sourceforge.jnlp.JNLPFile;
+
+import javax.swing.ImageIcon;
+import javax.swing.JComponent;
+import java.util.Arrays;
+import java.util.List;
+
+public abstract class MissingAttributeDialog extends BasicSecurityDialog> {
+ private static final Logger LOG = LoggerFactory.getLogger(MissingAttributeDialog.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private final DialogButton> cancelButton;
+ private final DialogButton> runButton;
+ protected RememberUserDecisionPanel rememberUserDecisionPanel;
+
+ protected JNLPFile file;
+
+ protected MissingAttributeDialog(final String message, final JNLPFile file) {
+ super(message);
+ this.file = file;
+ runButton = ButtonFactory.createRunButton(() -> new RememberableResult<>(AllowDeny.ALLOW, rememberUserDecisionPanel.getResult()));
+ cancelButton = ButtonFactory.createCancelButton(() -> new RememberableResult<>(AllowDeny.DENY, rememberUserDecisionPanel.getResult()));
+ }
+
+ @Override
+ protected ImageIcon createIcon() {
+ return ImageGallery.WARNING.asImageIcon();
+ }
+
+ @Override
+ protected JComponent createDetailPaneContent() {
+ final GridBagPanelBuilder gridBuilder = new GridBagPanelBuilder();
+ try {
+ gridBuilder.addRows(LayoutPartsBuilder.getApplicationDetails(file));
+ getAdditionalApplicationDetails(gridBuilder);
+
+ gridBuilder.addHorizontalSpacer();
+
+ gridBuilder.addComponentRow(createMoreInformationPanel());
+
+ gridBuilder.addHorizontalSpacer();
+
+ rememberUserDecisionPanel = new RememberUserDecisionPanel();
+ gridBuilder.addComponentRow(rememberUserDecisionPanel);
+
+ } catch (final Exception e) {
+ LOG.error("Error while trying to read properties for MissingAttributeDialog!", e);
+ }
+ return gridBuilder.createGrid();
+ }
+
+ protected abstract void getAdditionalApplicationDetails(final GridBagPanelBuilder gridBuilder);
+
+ protected abstract ReferencesPanel createMoreInformationPanel();
+
+ @Override
+ protected List>> createButtons() {
+ return Arrays.asList(runButton, cancelButton);
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/MissingPermissionsAttributeDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/MissingPermissionsAttributeDialog.java
new file mode 100644
index 000000000..30821a44f
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/MissingPermissionsAttributeDialog.java
@@ -0,0 +1,52 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.GridBagPanelBuilder;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.ReferencesPanel;
+import net.sourceforge.jnlp.JNLPFile;
+
+import java.net.URL;
+
+/**
+ * This security dialog is shown if the permissions attribute is not provided in the JNLP. The user can decide
+ * to run the application and remember the decision to show this dialog again for the application or domain.
+ *
+ * The Permissions attribute is used to verify that the permissions level requested by the application when
+ * it runs matches the permissions level that was set when the JAR file was created.
+ *
+ *
+ */
+public class MissingPermissionsAttributeDialog extends MissingAttributeDialog {
+ private static final Logger LOG = LoggerFactory.getLogger(MissingPermissionsAttributeDialog.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private MissingPermissionsAttributeDialog(final String message, final JNLPFile file) {
+ super(message, file);
+ }
+
+ @Override
+ protected void getAdditionalApplicationDetails(final GridBagPanelBuilder gridBuilder) {
+ gridBuilder.addKeyValueRow(TRANSLATOR.translate("Codebase"), file.getNotNullProbableCodeBase().toString());
+ }
+
+ public static MissingPermissionsAttributeDialog create(final JNLPFile file) {
+ String message = createMessage(file.getTitle(), file.getNotNullProbableCodeBase());
+ return new MissingPermissionsAttributeDialog(message, file);
+ }
+
+ @Override
+ protected String createTitle() {
+ return TRANSLATOR.translate("MissingPermissionsAttribute");
+ }
+
+ @Override
+ protected ReferencesPanel createMoreInformationPanel() {
+ return new ReferencesPanel(TRANSLATOR.translate("MissingPermissionsAttributeMoreInfo"));
+ }
+
+ private static String createMessage(final String applicationName, final URL codebase) {
+ return TRANSLATOR.translate("MissingPermissionsAttributeMessage", applicationName, codebase);
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/NewDialogFactory.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/NewDialogFactory.java
new file mode 100644
index 000000000..eab1a7438
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/NewDialogFactory.java
@@ -0,0 +1,331 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.client.parts.dialogs.DialogFactory;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.resources.Resource;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AccessWarningResult;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDeny;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDenySandbox;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.CreateShortcutResult;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.RememberableResult;
+import net.adoptopenjdk.icedteaweb.ui.swing.dialogresults.AccessWarningPaneComplexReturn;
+import net.adoptopenjdk.icedteaweb.ui.swing.dialogresults.NamePassword;
+import net.adoptopenjdk.icedteaweb.ui.swing.dialogresults.Primitive;
+import net.adoptopenjdk.icedteaweb.ui.swing.dialogresults.ShortcutResult;
+import net.adoptopenjdk.icedteaweb.ui.swing.dialogresults.YesNoSandbox;
+import net.adoptopenjdk.icedteaweb.userdecision.UserDecision;
+import net.adoptopenjdk.icedteaweb.userdecision.UserDecisions;
+import net.adoptopenjdk.icedteaweb.userdecision.UserDecisionsFileStore;
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.runtime.SecurityDelegate;
+import net.sourceforge.jnlp.security.AccessType;
+import net.sourceforge.jnlp.security.CertVerifier;
+import net.sourceforge.jnlp.security.HttpsCertVerifier;
+
+import java.awt.Component;
+import java.awt.Window;
+import java.net.URL;
+import java.security.cert.CertPath;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import static net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDeny.ALLOW;
+import static net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDeny.DENY;
+import static net.adoptopenjdk.icedteaweb.userdecision.UserDecision.Key.CREATE_DESKTOP_SHORTCUT;
+import static net.adoptopenjdk.icedteaweb.userdecision.UserDecision.Key.CREATE_MENU_SHORTCUT;
+import static net.adoptopenjdk.icedteaweb.userdecision.UserDecision.Key.RUN_MATCHING_ALAC_APPLICATION;
+import static net.adoptopenjdk.icedteaweb.userdecision.UserDecision.Key.RUN_MISSING_ALAC_APPLICATION;
+import static net.adoptopenjdk.icedteaweb.userdecision.UserDecision.Key.RUN_MISSING_PERMISSIONS_APPLICATION;
+import static net.adoptopenjdk.icedteaweb.userdecision.UserDecision.Key.RUN_PARTIALLY_APPLICATION;
+import static net.adoptopenjdk.icedteaweb.userdecision.UserDecision.of;
+import static net.sourceforge.jnlp.security.AccessType.PARTIALLY_SIGNED;
+import static net.sourceforge.jnlp.security.AccessType.SIGNING_ERROR;
+import static net.sourceforge.jnlp.security.AccessType.UNSIGNED;
+import static net.sourceforge.jnlp.security.AccessType.UNVERIFIED;
+import static net.sourceforge.jnlp.security.AccessType.VERIFIED;
+
+public class NewDialogFactory implements DialogFactory {
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private final UserDecisions userDecisions;
+
+ NewDialogFactory() {
+ this(new UserDecisionsFileStore());
+ }
+
+ NewDialogFactory(final UserDecisions userDecisions) {
+ this.userDecisions = userDecisions;
+ }
+
+ @Override
+ public AccessWarningPaneComplexReturn showAccessWarningDialog(final AccessType accessType, final JNLPFile file, final Object[] extras) {
+ if (Arrays.asList(VERIFIED, UNVERIFIED, PARTIALLY_SIGNED, UNSIGNED, SIGNING_ERROR).contains(accessType)) {
+ throw new RuntimeException(accessType + " cannot be displayed in AccessWarningDialog");
+ }
+
+ if (accessType == AccessType.CREATE_DESKTOP_SHORTCUT) {
+ return askForPermissionToCreateShortcuts(file);
+ } else {
+ return askForAccessPermission(accessType, file, extras);
+ }
+ }
+
+ @Override
+ public YesNoSandbox showCertWarningDialog(final AccessType accessType, final JNLPFile file, final CertVerifier certVerifier, final SecurityDelegate securityDelegate) {
+ final AccessWarningResult certWarningResult;
+
+ final Optional certVerifierOptional = Optional.ofNullable(certVerifier);
+
+ final List extends Certificate> certificates = certVerifierOptional
+ .map(cp -> certVerifier.getCertPath())
+ .map(CertPath::getCertificates)
+ .orElse(Collections.emptyList());
+
+ final List certIssues = certVerifierOptional
+ .map(cv -> cv.getDetails(null))
+ .orElse(Collections.emptyList());
+
+ final boolean rootInCaCerts = certVerifierOptional.map(CertVerifier::getRootInCaCerts).orElse(Boolean.FALSE);
+
+ if (certVerifier instanceof HttpsCertVerifier) {
+ certWarningResult = DialogProvider.showHttpsCertTrustDialog(file, certVerifier.getPublisher(null), rootInCaCerts, certificates, certIssues);
+ } else {
+ final String message = getMessageFor(accessType);
+ final String moreInformationText = getMoreInformationText(accessType, rootInCaCerts);
+ final boolean alwaysTrustSelected = (accessType == AccessType.VERIFIED);
+
+ certWarningResult = DialogProvider.showJarCertWarningDialog(file, certificates, certIssues, message, alwaysTrustSelected, moreInformationText);
+ }
+ switch (certWarningResult) {
+ case YES:
+ return YesNoSandbox.yes();
+ case SANDBOX:
+ return YesNoSandbox.sandbox();
+ default:
+ return YesNoSandbox.no();
+ }
+ }
+
+ private static String getMoreInformationText(final AccessType accessType, final boolean rootInCaCerts) {
+ String moreInformationText = rootInCaCerts ?
+ TRANSLATOR.translate("STrustedSource") : TRANSLATOR.translate("SUntrustedSource");
+
+ switch (accessType) {
+ case UNVERIFIED:
+ case SIGNING_ERROR:
+ return moreInformationText + " " + TRANSLATOR.translate("SWarnFullPermissionsIgnorePolicy");
+ default:
+ return moreInformationText;
+ }
+ }
+
+ private static String getMessageFor(final AccessType accessType) {
+ switch (accessType) {
+ case VERIFIED:
+ return TRANSLATOR.translate("SSigVerified");
+ case UNVERIFIED:
+ return TRANSLATOR.translate("SSigUnverified");
+ case SIGNING_ERROR:
+ return TRANSLATOR.translate("SSignatureError");
+ default:
+ return "";
+ }
+ }
+
+ @Override
+ public YesNoSandbox showPartiallySignedWarningDialog(final JNLPFile file, final CertVerifier certVerifier, final SecurityDelegate securityDelegate) {
+ final Optional remembered = this.userDecisions.getUserDecisions(RUN_PARTIALLY_APPLICATION, file, AllowDenySandbox.class);
+
+ final AllowDenySandbox result = remembered.orElseGet(() -> {
+ final RememberableResult dialogResult = DialogProvider.showPartiallySignedWarningDialog(file);
+ userDecisions.save(dialogResult.getRemember(), file, of(RUN_PARTIALLY_APPLICATION, dialogResult.getResult()));
+ return dialogResult.getResult();
+ });
+
+ return result == AllowDenySandbox.ALLOW ? YesNoSandbox.yes() :
+ result == AllowDenySandbox.SANDBOX ? YesNoSandbox.sandbox() : YesNoSandbox.no();
+ }
+
+ @Override
+ public NamePassword showAuthenticationPrompt(final String host, final int port, final String prompt, final String type) {
+ return DialogProvider.showAuthenticationDialog(host, port, prompt, type);
+ }
+
+ @Override
+ public boolean showMissingALACAttributePanel(final JNLPFile file, final URL codeBase, final Set locations) {
+ // On the only place where "codeBase" is handed in, it is done as file.getCodebase().
+ // In the dialog this is checked by file.getNotNullProbableCodeBase() first, which makes
+ // the handover of codebase obsolete here
+ final Optional remembered = this.userDecisions.getUserDecisions(RUN_MISSING_ALAC_APPLICATION, file, AllowDeny.class);
+
+ final AllowDeny result = remembered.orElseGet(() -> {
+ final RememberableResult dialogResult = DialogProvider.showMissingALACAttributeDialog(file, locations);
+ userDecisions.save(dialogResult.getRemember(), file, of(RUN_MISSING_ALAC_APPLICATION, dialogResult.getResult()));
+ return dialogResult.getResult();
+ });
+
+ return result == ALLOW;
+ }
+
+ @Override
+ public boolean showMatchingALACAttributePanel(final JNLPFile file, final URL documentBase, final Set locations) {
+ // On the only place where "documentBase" is handed in, it is done as file.getCodebase().
+ // In the dialog this is checked by file.getNotNullProbableCodeBase() first, which makes
+ // the handover of codebase obsolete here
+ final Optional remembered = this.userDecisions.getUserDecisions(RUN_MATCHING_ALAC_APPLICATION, file, AllowDeny.class);
+
+ final AllowDeny result = remembered.orElseGet(() -> {
+ final RememberableResult dialogResult = DialogProvider.showMatchingALACAttributeDialog(file, locations);
+ userDecisions.save(dialogResult.getRemember(), file, of(RUN_MATCHING_ALAC_APPLICATION, dialogResult.getResult()));
+ return dialogResult.getResult();
+ });
+
+ return result == ALLOW;
+ }
+
+ @Override
+ public boolean showMissingPermissionsAttributeDialogue(final JNLPFile file) {
+ final Optional remembered = this.userDecisions.getUserDecisions(RUN_MISSING_PERMISSIONS_APPLICATION, file, AllowDeny.class);
+
+ final AllowDeny result = remembered.orElseGet(() -> {
+ final RememberableResult dialogResult = DialogProvider.showMissingPermissionsAttributeDialog(file);
+ userDecisions.save(dialogResult.getRemember(), file, of(RUN_MISSING_PERMISSIONS_APPLICATION, dialogResult.getResult()));
+ return dialogResult.getResult();
+ });
+
+ return result == ALLOW;
+ }
+
+ @Override
+ public boolean show511Dialogue(final Resource r) {
+ return false;
+ }
+
+ @Override
+ public void showMoreInfoDialog(final CertVerifier certVerifier, final JNLPFile file) {
+ final Optional certVerifierOptional = Optional.ofNullable(certVerifier);
+
+ final List extends Certificate> certificates = certVerifierOptional
+ .map(cp -> certVerifier.getCertPath())
+ .map(CertPath::getCertificates)
+ .orElse(Collections.emptyList());
+
+ final List certIssues = certVerifierOptional
+ .map(cv -> cv.getDetails(null))
+ .orElse(Collections.emptyList());
+
+ DialogProvider.showCertWarningDetailsDialog(null, file, certificates, certIssues);
+ }
+
+ @Override
+ public void showCertInfoDialog(final CertVerifier certVerifier, final Component parent) {
+ final List extends Certificate> certificates = Optional.ofNullable(certVerifier)
+ .map(cp -> certVerifier.getCertPath())
+ .map(CertPath::getCertificates)
+ .orElse(Collections.emptyList());
+ DialogProvider.showCertInfoDialog(null, certificates);
+ }
+
+ @Override
+ public void showSingleCertInfoDialog(final X509Certificate c, final Window parent) {
+ DialogProvider.showCertInfoDialog(null, Collections.singletonList(c));
+ }
+
+ private AccessWarningPaneComplexReturn askForPermissionToCreateShortcuts(JNLPFile file) {
+ final Optional> rememberedDecision = getRememberedUserDecision(file).map(Optional::of);
+
+ final Optional result = rememberedDecision.orElseGet(() -> showCreateShortcutDialog(file));
+
+ if (!result.isPresent()) {
+ return new AccessWarningPaneComplexReturn(Primitive.CANCEL);
+ } else {
+ final AccessWarningPaneComplexReturn ar;
+ ar = new AccessWarningPaneComplexReturn(Primitive.YES);
+ ar.setDesktop(new ShortcutResult(result.get().getCreateDesktopShortcut() == AllowDeny.ALLOW));
+ ar.setMenu(new ShortcutResult(result.get().getCreateMenuShortcut() == AllowDeny.ALLOW));
+ return ar;
+ }
+ }
+
+ private Optional showCreateShortcutDialog(JNLPFile file) {
+ final Optional> result = DialogProvider.showCreateShortcutDialog(file);
+ return result.map(r -> {
+ rememberUserDecision(file, r);
+ return r.getResult();
+ });
+ }
+
+ private Optional getRememberedUserDecision(JNLPFile file) {
+ final Optional createDesktop = userDecisions.getUserDecisions(CREATE_DESKTOP_SHORTCUT, file, AllowDeny.class);
+ final Optional createMenu = userDecisions.getUserDecisions(CREATE_MENU_SHORTCUT, file, AllowDeny.class);
+
+ if (createDesktop.isPresent() || createMenu.isPresent()) {
+ return Optional.of(new CreateShortcutResult(createDesktop.orElse(DENY), createMenu.orElse(DENY)));
+ }
+ return Optional.empty();
+ }
+
+ private void rememberUserDecision(final JNLPFile file, final RememberableResult result) {
+ userDecisions.save(result.getRemember(), file, of(CREATE_DESKTOP_SHORTCUT, result.getResult().getCreateDesktopShortcut()));
+ userDecisions.save(result.getRemember(), file, of(CREATE_MENU_SHORTCUT, result.getResult().getCreateMenuShortcut()));
+ }
+
+ private AccessWarningPaneComplexReturn askForAccessPermission(AccessType accessType, JNLPFile file, Object[] extras) {
+ final Optional rememberedDecision = getRememberedUserDecision(accessType, file);
+
+ final AllowDeny result = rememberedDecision.orElseGet(() -> showAccessPermissionDialog(accessType, file, extras));
+
+ return new AccessWarningPaneComplexReturn(result == AllowDeny.ALLOW);
+ }
+
+ private AllowDeny showAccessPermissionDialog(AccessType accessType, JNLPFile file, Object[] extras) {
+ final RememberableResult dialogResult;
+
+ final Object extra = Optional.ofNullable(extras)
+ .filter(nonNullExtras -> nonNullExtras.length > 0)
+ .map(nonEmptyExtras -> nonEmptyExtras[0])
+ .orElse("");
+
+ switch (accessType) {
+ case READ_WRITE_FILE:
+ dialogResult = DialogProvider.showReadWriteFileAccessWarningDialog(file, extra.toString());
+ break;
+ case READ_FILE:
+ dialogResult = DialogProvider.showReadFileAccessWarningDialog(file, extra.toString());
+ break;
+ case WRITE_FILE:
+ dialogResult = DialogProvider.showWriteFileAccessWarningDialog(file, extra.toString());
+ break;
+ case CLIPBOARD_READ:
+ dialogResult = DialogProvider.showReadClipboardAccessWarningDialog(file);
+ break;
+ case CLIPBOARD_WRITE:
+ dialogResult = DialogProvider.showWriteClipboardAccessWarningDialog(file);
+ break;
+ case PRINTER:
+ dialogResult = DialogProvider.showPrinterAccessWarningDialog(file);
+ break;
+ case NETWORK:
+ dialogResult = DialogProvider.showNetworkAccessWarningDialog(file, extra);
+ break;
+ default:
+ dialogResult = DialogProvider.showAccessWarningDialog(file, "");
+ }
+
+ rememberUserDecision(file, accessType, dialogResult);
+ return dialogResult.getResult();
+ }
+
+ private Optional getRememberedUserDecision(AccessType accessType, JNLPFile file) {
+ return userDecisions.getUserDecisions(UserDecision.Key.valueOf(accessType), file, AllowDeny.class);
+ }
+
+ private void rememberUserDecision(final JNLPFile file, final AccessType accessType, final RememberableResult result) {
+ userDecisions.save(result.getRemember(), file, of(accessType, result.getResult()));
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/PartiallySignedWarningDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/PartiallySignedWarningDialog.java
new file mode 100644
index 000000000..64cef45ef
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/PartiallySignedWarningDialog.java
@@ -0,0 +1,94 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.GridBagPanelBuilder;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.LayoutPartsBuilder;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.RememberUserDecisionPanel;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDenySandbox;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.RememberableResult;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogButton;
+import net.sourceforge.jnlp.JNLPFile;
+
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ *
+ */
+public class PartiallySignedWarningDialog extends BasicSecurityDialog> {
+ private static final Logger LOG = LoggerFactory.getLogger(PartiallySignedWarningDialog.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private final JNLPFile file;
+ private final DialogButton> allowButton;
+ private final DialogButton> sandboxButton;
+ private final DialogButton> denyButton;
+ private RememberUserDecisionPanel rememberUserDecisionPanel;
+
+ PartiallySignedWarningDialog(final JNLPFile file) {
+ super(TRANSLATOR.translate("SUnsignedSummary"));
+
+ this.file = file;
+ allowButton = ButtonFactory.createAllowButton(() -> new RememberableResult<>(AllowDenySandbox.ALLOW, rememberUserDecisionPanel.getResult()));
+ sandboxButton = ButtonFactory.createSandboxButton(() -> new RememberableResult<>(AllowDenySandbox.SANDBOX, rememberUserDecisionPanel.getResult()));
+ denyButton = ButtonFactory.createDenyButton(() -> new RememberableResult<>(AllowDenySandbox.DENY, rememberUserDecisionPanel.getResult()));
+ }
+
+ public static PartiallySignedWarningDialog create(final JNLPFile file) {
+ return new PartiallySignedWarningDialog(file);
+ }
+
+ @Override
+ protected String createTitle() {
+ return TRANSLATOR.translate("SPartiallySignedApplication");
+ }
+
+ @Override
+ protected JComponent createDetailPaneContent() {
+ final GridBagPanelBuilder gridBuilder = new GridBagPanelBuilder();
+ try {
+ gridBuilder.addRows(LayoutPartsBuilder.getApplicationDetails(file));
+ gridBuilder.addHorizontalSpacer();
+
+ gridBuilder.addComponentRow(createMoreInfoPanel());
+
+ rememberUserDecisionPanel = new RememberUserDecisionPanel();
+ gridBuilder.addComponentRow(rememberUserDecisionPanel);
+
+ } catch (final Exception e) {
+ LOG.error("Error while trying to read properties for partially signed warning dialog!", e);
+ }
+ return gridBuilder.createGrid();
+ }
+
+ private JPanel createMoreInfoPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ final JTextArea moreInfoTextArea = new JTextArea(TRANSLATOR.translate("SPartiallySignedDetail"));
+ moreInfoTextArea.setBackground(getBackground());
+ moreInfoTextArea.setWrapStyleWord(true);
+ moreInfoTextArea.setLineWrap(true);
+ moreInfoTextArea.setEditable(false);
+
+ final JScrollPane scrollPane = new JScrollPane(moreInfoTextArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ scrollPane.setBorder(BorderFactory.createEmptyBorder());
+ scrollPane.setHorizontalScrollBar(null);
+ panel.add(scrollPane, BorderLayout.CENTER);
+ panel.setPreferredSize(new Dimension(720, 70));
+
+ return panel;
+ }
+
+ @Override
+ protected List>> createButtons() {
+ return Arrays.asList(allowButton, sandboxButton, denyButton);
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/UnsignedWarningDialog.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/UnsignedWarningDialog.java
new file mode 100644
index 000000000..26787e441
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/UnsignedWarningDialog.java
@@ -0,0 +1,93 @@
+package net.adoptopenjdk.icedteaweb.security.dialog;
+
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.GridBagPanelBuilder;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.jnlp.element.information.InformationDesc;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.adoptopenjdk.icedteaweb.security.dialog.panel.RememberUserDecisionPanel;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.AllowDeny;
+import net.adoptopenjdk.icedteaweb.security.dialog.result.RememberableResult;
+import net.adoptopenjdk.icedteaweb.ui.dialogs.DialogButton;
+import net.sourceforge.jnlp.JNLPFile;
+
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import java.util.Arrays;
+import java.util.List;
+
+import static java.util.Optional.ofNullable;
+import static net.adoptopenjdk.icedteaweb.ui.swing.SwingUtils.htmlWrap;
+
+/**
+ *
+ */
+public class UnsignedWarningDialog extends BasicSecurityDialog> {
+ private static final Logger LOG = LoggerFactory.getLogger(UnsignedWarningDialog.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private final JNLPFile file;
+ private final DialogButton> allowButton;
+ private final DialogButton> denyButton;
+ private RememberUserDecisionPanel rememberUserDecisionPanel;
+
+ private UnsignedWarningDialog(final JNLPFile file) {
+ super(TRANSLATOR.translate("SUnsignedSummary"));
+ this.file = file;
+ allowButton = ButtonFactory.createAllowButton(() -> new RememberableResult<>(AllowDeny.ALLOW, rememberUserDecisionPanel.getResult()));
+ denyButton = ButtonFactory.createDenyButton(() -> new RememberableResult<>(AllowDeny.DENY, rememberUserDecisionPanel.getResult()));
+ }
+
+ public static UnsignedWarningDialog create(final JNLPFile file) {
+ return new UnsignedWarningDialog(file);
+ }
+
+ @Override
+ protected String createTitle() {
+ return TRANSLATOR.translate("SUnsignedApplication");
+ }
+
+ @Override
+ protected JComponent createDetailPaneContent() {
+ final GridBagPanelBuilder gridBuilder = new GridBagPanelBuilder();
+ try {
+ final String name = ofNullable(file)
+ .map(JNLPFile::getInformation)
+ .map(InformationDesc::getTitle)
+ .map(s -> s + " " + TRANSLATOR.translate("SUnverified"))
+ .orElse("");
+ gridBuilder.addKeyValueRow(TRANSLATOR.translate("Name"), name);
+
+ final String codebase = ofNullable(file)
+ .map(JNLPFile::getCodeBase)
+ .map(url -> url.toString())
+ .orElse("");
+ gridBuilder.addKeyValueRow(TRANSLATOR.translate("Codebase"), codebase);
+
+ final String sourceLocation = ofNullable(file)
+ .map(JNLPFile::getSourceLocation)
+ .map(url -> url.toString())
+ .orElse("");
+
+ gridBuilder.addKeyValueRow(TRANSLATOR.translate("SourceLocation"), sourceLocation);
+
+ gridBuilder.addHorizontalSpacer();
+
+ gridBuilder.addComponentRow(new JLabel(htmlWrap(TRANSLATOR.translate("SUntrustedRecommendation"))));
+
+ gridBuilder.addHorizontalSpacer();
+
+ rememberUserDecisionPanel = new RememberUserDecisionPanel();
+ gridBuilder.addComponentRow(rememberUserDecisionPanel);
+
+ } catch (final Exception e) {
+ LOG.error("Error while trying to read properties for unsigned warning dialog!", e);
+ }
+ return gridBuilder.createGrid();
+ }
+
+ @Override
+ protected List>> createButtons() {
+ return Arrays.asList(allowButton, denyButton);
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/AccessWarningDialog.png b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/AccessWarningDialog.png
new file mode 100644
index 000000000..4b2c5e9b9
Binary files /dev/null and b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/AccessWarningDialog.png differ
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/AuthenticationDialog.png b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/AuthenticationDialog.png
new file mode 100644
index 000000000..5862ad4cd
Binary files /dev/null and b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/AuthenticationDialog.png differ
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/CertInfoDialog.png b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/CertInfoDialog.png
new file mode 100644
index 000000000..d38067010
Binary files /dev/null and b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/CertInfoDialog.png differ
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/CertWarningDetailsDialog.png b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/CertWarningDetailsDialog.png
new file mode 100644
index 000000000..13ab793c3
Binary files /dev/null and b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/CertWarningDetailsDialog.png differ
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/CreateShortcutDialog.png b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/CreateShortcutDialog.png
new file mode 100644
index 000000000..2c02aed0b
Binary files /dev/null and b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/CreateShortcutDialog.png differ
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/HttpsCertTrustDialog.png b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/HttpsCertTrustDialog.png
new file mode 100644
index 000000000..dec05a119
Binary files /dev/null and b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/HttpsCertTrustDialog.png differ
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/JarCertWarningDialog.png b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/JarCertWarningDialog.png
new file mode 100644
index 000000000..c82f02d54
Binary files /dev/null and b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/JarCertWarningDialog.png differ
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/MatchingALACAttributeDialog.png b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/MatchingALACAttributeDialog.png
new file mode 100644
index 000000000..b46777dd1
Binary files /dev/null and b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/MatchingALACAttributeDialog.png differ
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/MissingALACAttributeDialog.png b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/MissingALACAttributeDialog.png
new file mode 100644
index 000000000..cd8be3273
Binary files /dev/null and b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/MissingALACAttributeDialog.png differ
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/MissingPermissionsAttributeDialog.png b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/MissingPermissionsAttributeDialog.png
new file mode 100644
index 000000000..bfc0f2827
Binary files /dev/null and b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/MissingPermissionsAttributeDialog.png differ
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/PartiallySignedWarningDialog.png b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/PartiallySignedWarningDialog.png
new file mode 100644
index 000000000..43f0ef20d
Binary files /dev/null and b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/PartiallySignedWarningDialog.png differ
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/UnsignedWarningDialog.png b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/UnsignedWarningDialog.png
new file mode 100644
index 000000000..b4107ef31
Binary files /dev/null and b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/doc-files/UnsignedWarningDialog.png differ
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/panel/CertificateDetailsPanel.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/panel/CertificateDetailsPanel.java
new file mode 100644
index 000000000..743b987eb
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/panel/CertificateDetailsPanel.java
@@ -0,0 +1,292 @@
+// Copyright (C) 2019 Karakun AG
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+package net.adoptopenjdk.icedteaweb.security.dialog.panel;
+
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+import net.sourceforge.jnlp.security.SecurityUtil;
+import sun.security.x509.CertificateValidity;
+
+import javax.security.auth.x500.X500Principal;
+import javax.swing.BorderFactory;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.JTree;
+import javax.swing.ListSelectionModel;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeSelectionModel;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.lang.reflect.Method;
+import java.security.MessageDigest;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * This panel displays data from X509Certificate(s) used in jar signing.
+ */
+public class CertificateDetailsPanel extends JPanel {
+ private final static Logger LOG = LoggerFactory.getLogger(CertificateDetailsPanel.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ private JTree tree;
+
+ private static String[] columnNames = {TRANSLATOR.translate("Field"), TRANSLATOR.translate("Value")};
+
+ private List extends Certificate> certs;
+ private List certsData;
+
+
+ public CertificateDetailsPanel(final List extends Certificate> certificates) {
+ certs = certificates != null ? certificates : Collections.emptyList();
+ certsData = new ArrayList<>();
+
+ for (Certificate cert : certs) {
+ certsData.add(parseCert((X509Certificate) cert));
+ }
+
+ createContent();
+ }
+
+ private void createContent() {
+ this.setLayout(new BorderLayout());
+
+ final JTextArea value = createValueTextArea();
+ final JTable table = createCertDetailsTable(value);
+ tree = createCertPathTree(table);
+
+ final JScrollPane treePane = new JScrollPane(tree);
+ final JScrollPane tablePane = new JScrollPane(table);
+ final JScrollPane valuePane = new JScrollPane(value);
+
+ tree.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ table.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ value.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ treePane.setBorder(BorderFactory.createLineBorder(Color.WHITE));
+ tablePane.setBorder(BorderFactory.createLineBorder(Color.WHITE));
+ valuePane.setBorder(BorderFactory.createLineBorder(Color.WHITE));
+
+
+ JSplitPane tableToValueSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, tablePane, valuePane);
+ tableToValueSplitPane.setDividerLocation(0.70);
+ tableToValueSplitPane.setResizeWeight(0.70);
+
+ JSplitPane treeToDetailsSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, treePane, tableToValueSplitPane);
+ treeToDetailsSplitPane.setPreferredSize(new Dimension(800, 300));
+ treeToDetailsSplitPane.setDividerLocation(0.30);
+ treeToDetailsSplitPane.setResizeWeight(0.30);
+
+ tableToValueSplitPane.setBorder(BorderFactory.createLineBorder(this.getBackground()));
+ treeToDetailsSplitPane.setBorder(BorderFactory.createLineBorder(this.getBackground()));
+
+ add(treeToDetailsSplitPane, BorderLayout.CENTER);
+ }
+
+ public void copyToClipboard() {
+ copyToClipboard(tree);
+ }
+
+ private JTextArea createValueTextArea() {
+ final JTextArea valueTextArea = new JTextArea();
+ valueTextArea.setEditable(false);
+
+ return valueTextArea;
+ }
+
+ private JTree createCertPathTree(final JTable table) {
+ JTree tree = new JTree();
+ tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+
+ if (!certs.isEmpty()) {
+ X509Certificate firstCert = ((X509Certificate) certs.get(0));
+ String subjectString = SecurityUtil.getCN(Optional.ofNullable(firstCert).map(c -> c.getSubjectX500Principal()).map(X500Principal::getName).orElse(""));
+ String issuerString = SecurityUtil.getCN(Optional.ofNullable(firstCert).map(c -> c.getIssuerX500Principal()).map(X500Principal::getName).orElse(""));
+
+ DefaultMutableTreeNode top = new DefaultMutableTreeNode(subjectString + " (" + issuerString + ")");
+
+ //not self signed
+ if (!Objects.equals(firstCert.getSubjectDN(), firstCert.getIssuerDN()) && (certs.size() > 1)) {
+ X509Certificate secondCert = ((X509Certificate) certs.get(1));
+ subjectString = SecurityUtil.getCN(Optional.ofNullable(secondCert).map(c -> c.getSubjectX500Principal()).map(X500Principal::getName).orElse(""));
+ issuerString = SecurityUtil.getCN(Optional.ofNullable(secondCert).map(c -> c.getIssuerX500Principal()).map(X500Principal::getName).orElse(""));
+ top.add(new DefaultMutableTreeNode(subjectString + " (" + issuerString + ")"));
+ }
+ tree.setModel(new DefaultTreeModel(top, false));
+
+ tree.addTreeSelectionListener(e -> {
+ final DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
+
+ if (certsData.isEmpty() || node == null) {
+ return;
+ }
+ final int certIndex = node.isLeaf() ? 1 : 0;
+
+ if (certsData.size() > certIndex) {
+ table.setModel(new DefaultTableModel(certsData.get(certIndex), columnNames));
+ }
+ });
+ }
+ return tree;
+ }
+
+ private void copyToClipboard(final JTree tree) {
+ final DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
+
+ if (certsData.isEmpty() || node == null) {
+ return;
+ }
+
+ final int certIndex = node.isLeaf() ? 1 : 0;
+
+ if (certsData.size() > certIndex) {
+ final String[][] cert = certsData.get(certIndex);
+ final int rows = cert.length;
+ final int cols = cert[0].length;
+
+ String certString = "";
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cols; j++) {
+ certString += cert[i][j];
+ certString += " ";
+ }
+ certString += "\n";
+ }
+
+ final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ clipboard.setContents(new StringSelection(certString.toString()), null);
+ }
+ }
+
+ private JTable createCertDetailsTable(final JTextArea valueTextArea) {
+ final Object[][] data = certsData.isEmpty() ? new Object[0][0] : certsData.get(0);
+ DefaultTableModel tableModel = new DefaultTableModel(data, columnNames);
+
+ JTable table = new JTable(tableModel);
+ table.getTableHeader().setReorderingAllowed(false);
+
+ final ListSelectionModel tableSelectionModel = table.getSelectionModel();
+ tableSelectionModel.addListSelectionListener(e -> {
+ ListSelectionModel lsm = (ListSelectionModel) e.getSource();
+ int minIndex = lsm.getMinSelectionIndex();
+ int maxIndex = lsm.getMaxSelectionIndex();
+
+ for (int i = minIndex; i <= maxIndex; i++) {
+ if (lsm.isSelectedIndex(i)) {
+ valueTextArea.setText((String) table.getValueAt(i, 1));
+ }
+ }
+ });
+ table.setFillsViewportHeight(true);
+
+ return table;
+ }
+
+
+ private static String[][] parseCert(X509Certificate c) {
+ String version = "" + c.getVersion();
+ String serialNumber = String.valueOf(c.getSerialNumber());
+ String signatureAlg = c.getSigAlgName();
+ String issuer = String.valueOf(c.getIssuerX500Principal());
+ String validity = String.valueOf(new CertificateValidity(c.getNotBefore(), c.getNotAfter()));
+ String subject = String.valueOf(c.getSubjectX500Principal());
+
+ String signature = c.getSignature() != null ? jdkIndependentHexEncoder(c.getSignature()) : null;
+
+ String md5Hash = "";
+ String sha1Hash = "";
+ try {
+ MessageDigest digest = MessageDigest.getInstance("MD5");
+ digest.update(c.getEncoded());
+ md5Hash = makeFingerprint(digest.digest());
+
+ digest = MessageDigest.getInstance("SHA-1");
+ digest.update(c.getEncoded());
+ sha1Hash = makeFingerprint(digest.digest());
+ } catch (Exception e) {
+ //fail quietly
+ }
+
+ return new String[][]{{TRANSLATOR.translate("Version"), version},
+ {TRANSLATOR.translate("SSerial"), serialNumber},
+ {TRANSLATOR.translate("SSignatureAlgorithm"), signatureAlg},
+ {TRANSLATOR.translate("SIssuer"), issuer},
+ {TRANSLATOR.translate("SValidity"), validity},
+ {TRANSLATOR.translate("SSubject"), subject},
+ {TRANSLATOR.translate("SSignature"), signature},
+ {TRANSLATOR.translate("SMD5Fingerprint"), md5Hash},
+ {TRANSLATOR.translate("SSHA1Fingerprint"), sha1Hash}
+ };
+ }
+
+ private static String jdkIndependentHexEncoder(byte[] signature) {
+ try {
+ return jdkIndependentHexEncoderImpl(signature);
+ } catch (Exception ex) {
+ String s = "Failed to encode signature: " + ex.toString();
+ LOG.error("Failed to encode signature", ex);
+ return s;
+ }
+ }
+
+ private static String jdkIndependentHexEncoderImpl(byte[] signature) throws Exception {
+ try {
+ LOG.debug("trying jdk9's HexDumpEncoder");
+ Class clazz = Class.forName("sun.security.util.HexDumpEncoder");
+ Object encoder = clazz.newInstance();
+ Method m = clazz.getDeclaredMethod("encodeBuffer", byte[].class);
+ //convert our signature into a nice human-readable form.
+ return (String) m.invoke(encoder, signature);
+ } catch (Exception ex) {
+ LOG.debug("trying jdk8's HexDumpEncoder");
+ Class clazz = Class.forName("sun.misc.HexDumpEncoder");
+ Object encoder = clazz.newInstance();
+ Method m = clazz.getMethod("encode", byte[].class);
+ //convert our signature into a nice human-readable form.
+ return (String) m.invoke(encoder, signature);
+ }
+ }
+
+ /**
+ * Makes a human readable hash fingerprint.
+ * For example: 11:22:33:44:AA:BB:CC:DD:EE:FF.
+ */
+ private static String makeFingerprint(byte[] hash) {
+ String fingerprint = "";
+ for (final byte b : hash) {
+ if (!fingerprint.equals("")) {
+ fingerprint += ":";
+ }
+ fingerprint += Integer.toHexString(
+ ((b & 0xFF) | 0x100)).substring(1, 3);
+ }
+ return fingerprint.toUpperCase();
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/panel/LayoutPartsBuilder.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/panel/LayoutPartsBuilder.java
new file mode 100644
index 000000000..d500e50ef
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/panel/LayoutPartsBuilder.java
@@ -0,0 +1,70 @@
+package net.adoptopenjdk.icedteaweb.security.dialog.panel;
+
+import net.adoptopenjdk.icedteaweb.StringUtils;
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.ComponentRow;
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.GridBagRow;
+import net.adoptopenjdk.icedteaweb.client.util.gridbag.KeyValueRow;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.jnlp.element.information.InformationDesc;
+import net.sourceforge.jnlp.JNLPFile;
+
+import javax.swing.JLabel;
+import javax.swing.border.EmptyBorder;
+import java.awt.Font;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.util.Optional.ofNullable;
+import static net.adoptopenjdk.icedteaweb.ui.ApplicationStyleConstants.PRIMARY_WARNING_COLOR;
+
+public class LayoutPartsBuilder {
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ public static List getApplicationDetails(JNLPFile file) {
+ final List rows = new ArrayList<>();
+
+ final JLabel applicationSectionTitle = new JLabel(TRANSLATOR.translate("ApplicationDetails"));
+ applicationSectionTitle.setFont(applicationSectionTitle.getFont().deriveFont(Font.BOLD));
+ applicationSectionTitle.setBorder(new EmptyBorder(0, 0, 5, 0));
+
+ rows.add(new ComponentRow(applicationSectionTitle));
+
+ if (file.isUnsigend()) {
+ final JLabel unsignedWarningLabel = new JLabel(TRANSLATOR.translate("SUnverifiedJnlp"));
+ unsignedWarningLabel.setBorder(new EmptyBorder(0, 0, 5, 0));
+ unsignedWarningLabel.setForeground(PRIMARY_WARNING_COLOR);
+
+ rows.add(new ComponentRow(unsignedWarningLabel));
+ }
+
+ final String name = ofNullable(file)
+ .map(JNLPFile::getInformation)
+ .map(InformationDesc::getTitle)
+ .orElse(TRANSLATOR.translate("SNoAssociatedCertificate"));
+ rows.add(new KeyValueRow(TRANSLATOR.translate("Name"), name));
+
+ final String publisher = ofNullable(file)
+ .map(JNLPFile::getInformation)
+ .map(InformationDesc::getVendor)
+ .map(v -> v + " " + TRANSLATOR.translate("SUnverified"))
+ .orElse(TRANSLATOR.translate("SNoAssociatedCertificate"));
+ rows.add(new KeyValueRow(TRANSLATOR.translate("Publisher"), publisher));
+
+
+ final String fromFallback = ofNullable(file)
+ .map(JNLPFile::getSourceLocation)
+ .map(URL::getAuthority)
+ .orElse("");
+
+ final String from = ofNullable(file)
+ .map(JNLPFile::getInformation)
+ .map(InformationDesc::getHomepage)
+ .map(URL::toString)
+ .map(i -> !StringUtils.isBlank(i) ? i : null)
+ .orElse(fromFallback);
+ rows.add(new KeyValueRow(TRANSLATOR.translate("From"), from));
+ return rows;
+ }
+
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/panel/ReferencesPanel.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/panel/ReferencesPanel.java
new file mode 100644
index 000000000..bd8e2d469
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/panel/ReferencesPanel.java
@@ -0,0 +1,57 @@
+package net.adoptopenjdk.icedteaweb.security.dialog.panel;
+
+import net.adoptopenjdk.icedteaweb.StringUtils;
+import net.adoptopenjdk.icedteaweb.client.util.html.HtmlUtil;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+import net.adoptopenjdk.icedteaweb.logging.Logger;
+import net.adoptopenjdk.icedteaweb.logging.LoggerFactory;
+
+import javax.swing.JEditorPane;
+import javax.swing.JPanel;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+import java.awt.BorderLayout;
+import java.awt.Desktop;
+import java.awt.Font;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Set;
+
+public class ReferencesPanel extends JPanel {
+ private static final Logger LOG = LoggerFactory.getLogger(ReferencesPanel.class);
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ public ReferencesPanel(final String htmlList) {
+ super(new BorderLayout());
+ createContent(null, htmlList);
+ }
+
+ public ReferencesPanel(final String title, final Set urls) {
+ super(new BorderLayout());
+ createContent(title, HtmlUtil.unorderedListOf(urls, 4));
+ }
+
+ private void createContent(final String listTitle, final String htmlList) {
+ final String html = StringUtils.isBlank(listTitle) ? htmlList : listTitle + "
" + htmlList;
+
+ final JEditorPane editorPane = new JEditorPane("text/html", html);
+ editorPane.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE);
+ editorPane.setEditable(false);
+ editorPane.setFont(new Font(Font.SANS_SERIF, getFont().getStyle(), getFont().getSize()));
+ editorPane.setBackground(null);
+ editorPane.addHyperlinkListener(new HyperlinkListener() {
+ @Override
+ public void hyperlinkUpdate(HyperlinkEvent e) {
+ try {
+ if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+ Desktop.getDesktop().browse(e.getURL().toURI());
+ }
+ } catch (IOException | URISyntaxException ex) {
+ LOG.error("Error while trying to open hyperlink from dialog.", e);
+ }
+ }
+ });
+ add(editorPane, BorderLayout.CENTER);
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/panel/RememberUserDecisionPanel.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/panel/RememberUserDecisionPanel.java
new file mode 100644
index 000000000..f3d64b2f8
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/panel/RememberUserDecisionPanel.java
@@ -0,0 +1,48 @@
+package net.adoptopenjdk.icedteaweb.security.dialog.panel;
+
+import net.adoptopenjdk.icedteaweb.client.parts.dialogs.security.remember.Remember;
+import net.adoptopenjdk.icedteaweb.i18n.Translator;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.border.EmptyBorder;
+import java.awt.FlowLayout;
+
+public class RememberUserDecisionPanel extends JPanel {
+ private static final Translator TRANSLATOR = Translator.getInstance();
+
+ final JRadioButton forApplicationRadioButton = new JRadioButton(TRANSLATOR.translate("EXAWrememberByApp"));
+ final JRadioButton forDomainRadioButton = new JRadioButton(TRANSLATOR.translate("EXAWrememberByPage"));
+ final JRadioButton doNotRememberRadioButton = new JRadioButton(TRANSLATOR.translate("EXAWdontRemember"), true);
+
+ public RememberUserDecisionPanel() {
+ super(new FlowLayout(FlowLayout.CENTER));
+ this.setBorder(new EmptyBorder(0, 0, 0, 0));
+
+ this.add(forApplicationRadioButton);
+ this.add(forDomainRadioButton);
+ this.add(doNotRememberRadioButton);
+
+ forApplicationRadioButton.setToolTipText(TRANSLATOR.translate("EXAWrememberByAppTooltip"));
+ forDomainRadioButton.setToolTipText(TRANSLATOR.translate("EXAWrememberByPageTooltip"));
+ doNotRememberRadioButton.setToolTipText(TRANSLATOR.translate("EXAWdontRememberTooltip"));
+
+ final ButtonGroup bg = new ButtonGroup();
+ bg.add(forApplicationRadioButton);
+ bg.add(forDomainRadioButton);
+ bg.add(doNotRememberRadioButton);
+
+ this.validate();
+ }
+
+ public Remember getResult() {
+ if (forApplicationRadioButton.isSelected()) {
+ return Remember.REMEMBER_BY_APPLICATION;
+ }
+ if (forDomainRadioButton.isSelected()) {
+ return Remember.REMEMBER_BY_DOMAIN;
+ }
+ return Remember.DO_NOT_REMEMBER;
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/AccessWarningResult.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/AccessWarningResult.java
new file mode 100644
index 000000000..aa7397746
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/AccessWarningResult.java
@@ -0,0 +1,5 @@
+package net.adoptopenjdk.icedteaweb.security.dialog.result;
+
+public enum AccessWarningResult {
+ SANDBOX, YES, NO, ALWAYS;
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/AllowDeny.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/AllowDeny.java
new file mode 100644
index 000000000..ed25ef098
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/AllowDeny.java
@@ -0,0 +1,11 @@
+package net.adoptopenjdk.icedteaweb.security.dialog.result;
+
+import javax.swing.JCheckBox;
+
+public enum AllowDeny {
+ ALLOW, DENY;
+
+ public static AllowDeny valueOf(final JCheckBox checkbox) {
+ return checkbox.isSelected() ? ALLOW : DENY;
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/AllowDenySandbox.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/AllowDenySandbox.java
new file mode 100644
index 000000000..e3d5f9e35
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/AllowDenySandbox.java
@@ -0,0 +1,5 @@
+package net.adoptopenjdk.icedteaweb.security.dialog.result;
+
+public enum AllowDenySandbox {
+ ALLOW, DENY, SANDBOX;
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/CreateShortcutResult.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/CreateShortcutResult.java
new file mode 100644
index 000000000..0933240d3
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/CreateShortcutResult.java
@@ -0,0 +1,20 @@
+package net.adoptopenjdk.icedteaweb.security.dialog.result;
+
+public class CreateShortcutResult {
+ private final AllowDeny createDesktopShortcut;
+ private final AllowDeny createMenuShortcut;
+
+
+ public CreateShortcutResult(final AllowDeny createDesktopShortcut, final AllowDeny createMenuShortcut) {
+ this.createDesktopShortcut = createDesktopShortcut;
+ this.createMenuShortcut = createMenuShortcut;
+ }
+
+ public AllowDeny getCreateDesktopShortcut() {
+ return createDesktopShortcut;
+ }
+
+ public AllowDeny getCreateMenuShortcut() {
+ return createMenuShortcut;
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/RememberableResult.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/RememberableResult.java
new file mode 100644
index 000000000..e4d352a1d
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/security/dialog/result/RememberableResult.java
@@ -0,0 +1,21 @@
+package net.adoptopenjdk.icedteaweb.security.dialog.result;
+
+import net.adoptopenjdk.icedteaweb.client.parts.dialogs.security.remember.Remember;
+
+public class RememberableResult {
+ private final T result;
+ private final Remember remember;
+
+ public RememberableResult(final T result, final Remember remember) {
+ this.result = result;
+ this.remember = remember;
+ }
+
+ public T getResult() {
+ return result;
+ }
+
+ public Remember getRemember() {
+ return remember;
+ }
+}
diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/userdecision/FileStoreEntries.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/userdecision/FileStoreEntries.java
new file mode 100644
index 000000000..177399d5e
--- /dev/null
+++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/userdecision/FileStoreEntries.java
@@ -0,0 +1,45 @@
+package net.adoptopenjdk.icedteaweb.userdecision;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+class FileStoreEntries {
+
+ @SuppressWarnings("unused") // this is used for identifying the serialization version in the JSON file.
+ private final String version = "1";
+ private final List userDecisions;
+
+ FileStoreEntries() {
+ this.userDecisions = new ArrayList<>();
+ }
+
+ > Optional getDecision(final URL domain, final Set jarNames, final UserDecision.Key key, final Class