# HG changeset patch # User Andrew Azores # Date 1391094642 18000 # Node ID 7f886f9e911a16438d543f7fbd5ab58ef0739587 # Parent 2e5478f9c65a2d97c4f5d2c878486c02e093e78e Backport of PolicyPanel PolicyPanel (custom policies in itweb-settings) ported to 1.4 from 1.5pre diff -r 2e5478f9c65a -r 7f886f9e911a ChangeLog --- a/ChangeLog Mon Jan 20 15:26:06 2014 +0100 +++ b/ChangeLog Thu Jan 30 10:10:42 2014 -0500 @@ -1,3 +1,14 @@ +2014-01-30 Andrew Azores + + Backport of custom policy panel. + * NEWS: added entry for policy panel + * netx/net/sourceforge/jnlp/controlpanel/ControlPanel.java: added + PolicyPanel + * netx/net/sourceforge/jnlp/resources/Messages.properties: new messages + for PolicyPanel + * netx/net/sourceforge/jnlp/controlpanel/PolicyPanel.java: new + itweb-settings control panel for custom policy information and editing + 2014-01-20 Jiri Vanek Added Christmas splashscreen extension. diff -r 2e5478f9c65a -r 7f886f9e911a NEWS --- a/NEWS Mon Jan 20 15:26:06 2014 +0100 +++ b/NEWS Thu Jan 30 10:10:42 2014 -0500 @@ -11,6 +11,7 @@ New in release 1.4.2 (2013-MM-DD): * Dialogs center on screen before becoming visible * Support for u45 new manifest attributes (Application-Name) +* Custom applet permission policies panel in itweb-settings control panel * Plugin - PR1271: icedtea-web does not handle 'javascript:'-protocol URLs - RH976833: Multiple applets on one page cause deadlock diff -r 2e5478f9c65a -r 7f886f9e911a netx/net/sourceforge/jnlp/controlpanel/ControlPanel.java --- a/netx/net/sourceforge/jnlp/controlpanel/ControlPanel.java Mon Jan 20 15:26:06 2014 +0100 +++ b/netx/net/sourceforge/jnlp/controlpanel/ControlPanel.java Thu Jan 30 10:10:42 2014 -0500 @@ -263,7 +263,8 @@ // new SettingsPanel(Translator.R("CPTabRuntimes"), createRuntimesSettingsPanel()), new SettingsPanel(Translator.R("CPTabSecurity"), createSecuritySettingsPanel()), //todo refactor to work with tmp file and apply as asu designed it - new SettingsPanel(Translator.R("APPEXTSECControlPanelExtendedAppletSecurityTitle"), new UnsignedAppletsTrustingListPanel(DeploymentConfiguration.getAppletTrustGlobalSettingsPath(),DeploymentConfiguration.getAppletTrustUserSettingsPath(), this.config) ) + new SettingsPanel(Translator.R("CPTabPolicy"), createPolicySettingsPanel()), + new SettingsPanel(Translator.R("APPEXTSECControlPanelExtendedAppletSecurityTitle"), new UnsignedAppletsTrustingListPanel(DeploymentConfiguration.getAppletTrustGlobalSettingsPath(), DeploymentConfiguration.getAppletTrustUserSettingsPath(), this.config)) }; // Add panels. @@ -356,6 +357,10 @@ return new SecuritySettingsPanel(this.config); } + private JPanel createPolicySettingsPanel() { + return new PolicyPanel(this, this.config); + } + private JPanel createJVMSettingsPanel() { return new JVMPanel(this.config); } diff -r 2e5478f9c65a -r 7f886f9e911a netx/net/sourceforge/jnlp/controlpanel/PolicyPanel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/jnlp/controlpanel/PolicyPanel.java Thu Jan 30 10:10:42 2014 -0500 @@ -0,0 +1,360 @@ +/* Copyright (C) 2014 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2. + +IcedTea 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. + */ + +package net.sourceforge.jnlp.controlpanel; + +import static net.sourceforge.jnlp.runtime.Translator.R; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; + +import net.sourceforge.jnlp.config.DeploymentConfiguration; +import net.sourceforge.jnlp.util.FileUtils; + +/** + * Implements a Policy Settings panel for the itweb-settings control panel. + * This gives the user information about custom user-level JNLP Policy files, + * as well as offering a way to launch a policy file editor with the correct + * file path to the user's personal policy file location presupplied. + */ +public class PolicyPanel extends NamedBorderPanel { + + /** + * Indicates whether a file was successfully opened. If not, provides specific reasons + * along with a general failure case + */ + private enum OpenFileResult { + /** The file was successfully opened */ + SUCCESS, + /** The file could not be opened, for non-specified reasons */ + FAILURE, + /** The file could not be opened because it did not exist and could not be created */ + CANT_CREATE, + /** The file can be opened but in read-only */ + CANT_WRITE, + /** The specified path pointed to a non-file filesystem object, ie a directory */ + NOT_FILE + } + + public PolicyPanel(final JFrame frame, final DeploymentConfiguration config) { + super(R("CPHeadPolicy"), new GridBagLayout()); + addComponents(frame, config); + } + + private void addComponents(final JFrame frame, final DeploymentConfiguration config) { + final JLabel aboutLabel = new JLabel("" + R("CPPolicyDetail") + ""); + + final String fileUrlString = config.getProperty(DeploymentConfiguration.KEY_USER_SECURITY_POLICY); + final JButton showUserPolicyButton = new JButton(R("CPButPolicy")); + showUserPolicyButton.addActionListener(new ViewPolicyButtonAction(frame, fileUrlString)); + + final String pathPart = localFilePathFromUrlString(fileUrlString); + showUserPolicyButton.setToolTipText(R("CPPolicyTooltip", FileUtils.displayablePath(pathPart, 60))); + + final JTextField locationField = new JTextField(pathPart); + locationField.setEditable(false); + + final GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + c.gridx = 1; + c.gridy = 0; + c.weightx = 1; + add(aboutLabel, c); + + c.weighty = 0; + c.weightx = 0; + c.gridy++; + add(locationField, c); + + c.fill = GridBagConstraints.NONE; + c.gridx++; + add(showUserPolicyButton, c); + + /* Keep all the elements at the top of the panel (Extra padding) + * Keep View/Edit button next to location field, with padding between + * the right edge of the frame and the button + */ + c.fill = GridBagConstraints.BOTH; + final Component filler1 = Box.createRigidArea(new Dimension(240, 1)); + final Component filler2 = Box.createRigidArea(new Dimension(1, 1)); + c.gridx++; + add(filler1, c); + c.gridx--; + c.weighty = 1; + c.gridy++; + add(filler2, c); + } + + /** + * Launch the policytool for a specified file path + * @param frame a {@link JFrame} to act as parent to warning dialogs which may appear + * @param filePath a {@link String} representing the path to the file to be opened + */ + private static void launchPolicyTool(final JFrame frame, final String filePath) { + try { + final File policyFile = new File(filePath).getCanonicalFile(); + final OpenFileResult result = canOpenPolicyFile(policyFile); + if (result == OpenFileResult.SUCCESS) { + policyToolLaunchHelper(frame, filePath); + } else if (result == OpenFileResult.CANT_WRITE) { + showReadOnlyDialog(frame); + policyToolLaunchHelper(frame, filePath); + } else { + showCouldNotOpenFileDialog(frame, policyFile.getPath(), result); + } + } catch (IOException e) { + e.printStackTrace(); + showCouldNotOpenFileDialog(frame, filePath); + } + } + + /** + * This executes a new process for policytool using ProcessBuilder, with the new process' + * working directory set to the user's home directory. policytool then attempts to + * open the provided policy file path, if policytool can be run. ProcessBuilder does + * some verification to ensure that the built command can be executed - if not, it + * throws an IOException. In this event, we try our reflective fallback launch. + * We do this in a new {@link Thread} to ensure that the fallback launch does not + * block the AWT thread, and neither does ProcessBuilder#start() in case it happens + * to be synchronous on the current system. + * @param frame a {@link JFrame} to act as parent to warning dialogs which may appear + * @param filePath a {@link String} representing the path to the file to be opened + */ + private static void policyToolLaunchHelper(final JFrame frame, final String filePath) { + new Thread(new Runnable() { + @Override + public void run() { + final ProcessBuilder pb = new ProcessBuilder("policytool", "-file", filePath) + .directory(new File(System.getProperty("user.home"))); + try { + pb.start(); + } catch (IOException ioe) { + ioe.printStackTrace(); + try { + reflectivePolicyToolLaunch(filePath); + } catch (Exception e) { + e.printStackTrace(); + showCouldNotOpenFileDialog(frame, filePath, R("CPPolicyEditorNotFound")); + } + } + } + }).start(); + } + + /** + * This is used as a fallback in case launching the policytool by executing a new process + * fails. This probably happens because we are running on a system where the policytool + * executable is not on the PATH, or because we are running on a non-POSIX compliant system. + * We do this reflectively to avoid needing to add PolicyTool as build dependency simply for + * this small edge case. + * @param filePath a {@link String} representing the path of the file to attempt to open + * @throws Exception if any sort of exception occurs during reflective launch of policytool + */ + private static void reflectivePolicyToolLaunch(final String filePath) throws Exception { + Class policyTool; + try { + // Java 7 location + policyTool = Class.forName("sun.security.tools.policytool.PolicyTool"); + } catch (ClassNotFoundException cnfe) { + // Java 6 location + policyTool = Class.forName("sun.security.tools.PolicyTool"); + } + final Class[] signature = new Class[] { String[].class }; + final Method main = policyTool.getMethod("main", signature); + final String[] args = new String[] { "-file", filePath }; + main.invoke(null, (Object) args); + } + + /** + * Verify that a given file object points to a real, accessible plain file. + * As a side effect, if the file is accessible but does not yet exist, it will be created + * as an empty plain file. + * @param policyFile the {@link File} to verify + * @return an {@link OpenFileResult} representing the accessibility level of the file + */ + private static OpenFileResult canOpenPolicyFile(final File policyFile) { + try { + FileUtils.createParentDir(policyFile); + } catch (IOException e) { + return OpenFileResult.FAILURE; + } + if (policyFile.isDirectory()) + return OpenFileResult.NOT_FILE; + try { + if (!policyFile.exists() && !policyFile.createNewFile()) { + return OpenFileResult.CANT_CREATE; + } + } catch (IOException e) { + return OpenFileResult.CANT_CREATE; + } + final boolean read = policyFile.canRead(), write = policyFile.canWrite(); + if (read && write) + return OpenFileResult.SUCCESS; + else if (read) + return OpenFileResult.CANT_WRITE; + else + return OpenFileResult.FAILURE; + } + + /** + * Show a generic error dialog indicating the policy file could not be opened + * @param frame a {@link JFrame} to act as parent to this dialog + * @param filePath a {@link String} representing the path to the file we failed to open + */ + private static void showCouldNotOpenFileDialog(final JFrame frame, final String filePath) { + showCouldNotOpenFileDialog(frame, filePath, OpenFileResult.FAILURE); + } + + /** + * Show an error dialog indicating the policy file could not be opened, with a particular reason + * @param frame a {@link JFrame} to act as parent to this dialog + * @param filePath a {@link String} representing the path to the file we failed to open + * @param reason a {@link OpenFileResult} specifying more precisely why we failed to open the file + */ + private static void showCouldNotOpenFileDialog(final JFrame frame, final String filePath, final OpenFileResult reason) { + final String message; + switch (reason) { + case CANT_CREATE: + message = R("RCantCreateFile", filePath); + break; + case CANT_WRITE: + message = R("RCantWriteFile", filePath); + break; + case NOT_FILE: + message = R("RExpectedFile", filePath); + break; + default: + message = R("RCantOpenFile", filePath); + break; + } + showCouldNotOpenFileDialog(frame, filePath, message); + } + + /** + * Show a dialog informing the user that the policy file could not be opened + * @param frame a {@link JFrame} to act as parent to this dialog + * @param filePath a {@link String} representing the path to the file we failed to open + * @param message a {@link String} giving the specific reason the file could not be opened + */ + private static void showCouldNotOpenFileDialog(final JFrame frame, final String filePath, final String message) { + System.err.println("Could not open user JNLP policy"); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + JOptionPane.showMessageDialog(frame, message, R("Error"), JOptionPane.ERROR_MESSAGE); + } + }); + } + + /** + * Show a dialog informing the user that the policy file is currently read-only + * @param frame the parent frame for this dialog + * @param frame a {@link JFrame} to act as parent to this dialog + */ + private static void showReadOnlyDialog(final JFrame frame) { + System.err.println("Opening user JNLP policy read-only"); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + JOptionPane.showMessageDialog(frame, R("RFileReadOnly"), R("Warning"), JOptionPane.WARNING_MESSAGE); + } + }); + } + + /** + * Loosely attempt to get the path part of a file URL string. If this fails, + * simply return back the input. This is only intended to be used for displaying + * GUI elements such as the CPPolicyTooltip. + * @param url the {@link String} representing the URL whose path is desired + * @return a {@link String} representing the local filepath of the given file:/ URL + */ + private static String localFilePathFromUrlString(final String url) { + try { + final URL u = new URL(url); + return u.getPath(); + } catch (MalformedURLException e) { + return url; + } + } + + /** + * Implements the action to be performed when the "View Policy" button is clicked + */ + private class ViewPolicyButtonAction implements ActionListener { + private final JFrame frame; + private final String fileUrlString; + + public ViewPolicyButtonAction(final JFrame frame, final String fileUrlString) { + this.fileUrlString = fileUrlString; + this.frame = frame; + } + + @Override + public void actionPerformed(final ActionEvent event) { + try { + final URL fileUrl = new URL(fileUrlString); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + launchPolicyTool(frame, fileUrl.getPath()); + } + }); + } catch (MalformedURLException ex) { + ex.printStackTrace(); + showCouldNotOpenFileDialog(frame, fileUrlString); + } + } + } +} diff -r 2e5478f9c65a -r 7f886f9e911a netx/net/sourceforge/jnlp/resources/Messages.properties --- a/netx/net/sourceforge/jnlp/resources/Messages.properties Mon Jan 20 15:26:06 2014 +0100 +++ b/netx/net/sourceforge/jnlp/resources/Messages.properties Thu Jan 30 10:10:42 2014 -0500 @@ -21,6 +21,7 @@ AlwaysAllowAction=Always allow this action Usage=Usage: Error=Error +Warning=Warning Continue=Do you want to continue? Field=Field @@ -151,6 +152,10 @@ RCantReplaceSM=Changing the SecurityManager is not allowed. RCantCreateFile=Cant create file {0} RCantDeleteFile=Cant delete file {0} +RCantOpenFile=Could not open file {0} +RCantWriteFile=Could not write to file {0} +RFileReadOnly=Opening file in read-only mode +RExpectedFile=Expected {0} to be a file but it was not RRemoveRPermFailed=Removing read permission on file {0} failed RRemoveWPermFailed=Removing write permissions on file {0} failed RRemoveXPermFailed=Removing execute permissions on file {0} failed @@ -337,6 +342,9 @@ CPJVMNotokMessage2=You might be seeing this message because:
* Some validity tests have not been passed
* Non-OpenJDK is detected
With invalid JDK IcedTea-Web will probably not be able to start.
You will have to modify or remove {0} property in your configuration file {1}.
You should try to search for OpenJDK in your system or be sure you know what you are doing. CPJVMconfirmInvalidJdkTitle=Confirm invalid JDK CPJVMconfirmReset=Reset to default? +CPPolicyDetail=View or edit your user-level Java Policy File. This allows you to grant or deny runtime permissions to applets regardless of the standard security sandboxing rules. +CPPolicyTooltip=Open {0} in policy editor +CPPolicyEditorNotFound=Could not find a system policy file editor. Check that policytool is on your PATH. # Control Panel - Buttons CPButAbout=About... @@ -344,6 +352,7 @@ CPButSettings=Settings... CPButView=View... CPButCertificates=Certificates... +CPButPolicy=View/Edit with Policy Tool # Control Panel - Headers CPHead=IcedTea-Web Control Panel @@ -356,6 +365,7 @@ CPHeadDesktopIntegration=\u00a0Desktop\u00a0Integrations\u00a0 CPHeadSecurity=\u00a0Security\u00a0Settings\u00a0 CPHeadJVMSettings=\u00a0JVM\u00a0Settings\u00a0 +CPHeadPolicy=\u00a0Custom\u00a0Policy\u00a0Settings\u00a0 # Control Panel - Tabs CPTabAbout=About IcedTea-Web @@ -368,6 +378,7 @@ CPTabRuntimes=Runtimes CPTabSecurity=Security CPTabJVMSettings=JVM Settings +CPTabPolicy=Policy Settings # Control Panel - AboutPanel CPAboutInfo=This is the control panel for setting deployments.properties.
Not all options will take effect until implemented.
The use of multiple JREs is currently unsupported.