Merge branch 'improve_updatecenter_wip'
Review on private@netbeans.apache.org - References:
https://lists.apache.org/thread.html/3313fe7ecab7d861356f2a2d218041ee97aafec588120ade4e7480d3%40%3Cprivate.netbeans.apache.org%3E
https://lists.apache.org/thread.html/3313fe7ecab7d861356f2a2d218041ee97aafec588120ade4e7480d3%40%3Cprivate.netbeans.apache.org%3E
https://lists.apache.org/thread.html/4b628153ef6ce965d757608926e2a6dae1ebee3a6ba9ee630c4d7c6d%40%3Cprivate.netbeans.apache.org%3E
diff --git a/nb/updatecenters/src/org/netbeans/modules/updatecenters/resources/mf-layer.xml b/nb/updatecenters/src/org/netbeans/modules/updatecenters/resources/mf-layer.xml
index de190a3..b57d7a8 100644
--- a/nb/updatecenters/src/org/netbeans/modules/updatecenters/resources/mf-layer.xml
+++ b/nb/updatecenters/src/org/netbeans/modules/updatecenters/resources/mf-layer.xml
@@ -31,6 +31,7 @@
<attr name="url" bundlevalue="org.netbeans.modules.updatecenters.resources.Bundle#URL_Distribution"/>
<attr name="category" stringvalue="STANDARD"/>
<attr name="enabled" boolvalue="true"/>
+ <attr name="trusted" boolvalue="true"/>
<attr name="instanceOf" stringvalue="org.netbeans.spi.autoupdate.UpdateProvider" />
<attr name="instanceCreate" methodvalue="org.netbeans.modules.autoupdate.updateprovider.AutoupdateCatalogFactory.createUpdateProvider"/>
</file>
diff --git a/nbbuild/antsrc/org/netbeans/nbbuild/MakeUpdateDesc.java b/nbbuild/antsrc/org/netbeans/nbbuild/MakeUpdateDesc.java
index 825ffba..90a30e0 100644
--- a/nbbuild/antsrc/org/netbeans/nbbuild/MakeUpdateDesc.java
+++ b/nbbuild/antsrc/org/netbeans/nbbuild/MakeUpdateDesc.java
@@ -28,6 +28,7 @@
import org.apache.tools.ant.DirectoryScanner;
import java.io.File;
import java.io.FileFilter;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -36,17 +37,22 @@
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.file.Files;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.text.Collator;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map.Entry;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.TimeZone;
@@ -55,8 +61,11 @@
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.Path;
@@ -105,6 +114,19 @@
private List<Group> groups = new ArrayList<>();
private List<FileSet> filesets = new ArrayList<>();
+ private List<String> includeMessageDigests = Arrays.asList("SHA-512");
+
+ public String getIncludeMessageDigests() {
+ return includeMessageDigests.stream().collect(Collectors.joining(" "));
+ }
+
+ public void setIncludeMessageDigests(String includeMessageDigests) {
+ if(includeMessageDigests == null) {
+ includeMessageDigests = "";
+ }
+ this.includeMessageDigests = Arrays.asList(includeMessageDigests.split(" "));
+ }
+
private File desc;
/** Description file to create. */
@@ -301,7 +323,9 @@
}
File desc_ent = new File(ent_name);
desc_ent.delete();
- if (isPreferredUpdateDefined || (contentDescription != null && ! contentDescription.isEmpty())) {
+ if (includeMessageDigests != null && (! includeMessageDigests.isEmpty())) {
+ pw.println("<!DOCTYPE module_updates PUBLIC \"-//NetBeans//DTD Autoupdate Catalog 2.8//EN\" \"http://www.netbeans.org/dtds/autoupdate-catalog-2_8.dtd\" [");
+ } else if (isPreferredUpdateDefined || (contentDescription != null && ! contentDescription.isEmpty())) {
pw.println("<!DOCTYPE module_updates PUBLIC \"-//NetBeans//DTD Autoupdate Catalog 2.7//EN\" \"http://www.netbeans.org/dtds/autoupdate-catalog-2_7.dtd\" [");
} else if (useLicenseUrl) {
pw.println("<!DOCTYPE module_updates PUBLIC \"-//NetBeans//DTD Autoupdate Catalog 2.6//EN\" \"http://www.netbeans.org/dtds/autoupdate-catalog-2_6.dtd\" [");
@@ -339,7 +363,9 @@
pw.println ();
} else {
- if (isPreferredUpdateDefined || (contentDescription != null && ! contentDescription.isEmpty())) {
+ if (includeMessageDigests != null && (! includeMessageDigests.isEmpty())) {
+ pw.println("<!DOCTYPE module_updates PUBLIC \"-//NetBeans//DTD Autoupdate Catalog 2.8//EN\" \"http://www.netbeans.org/dtds/autoupdate-catalog-2_8.dtd\">");
+ } else if (isPreferredUpdateDefined || (contentDescription != null && ! contentDescription.isEmpty())) {
pw.println("<!DOCTYPE module_updates PUBLIC \"-//NetBeans//DTD Autoupdate Catalog 2.7//EN\" \"http://www.netbeans.org/dtds/autoupdate-catalog-2_7.dtd\">");
} else if (useLicenseUrl) {
pw.println("<!DOCTYPE module_updates PUBLIC \"-//NetBeans//DTD Autoupdate Catalog 2.6//EN\" \"http://www.netbeans.org/dtds/autoupdate-catalog-2_6.dtd\">");
@@ -642,6 +668,15 @@
}
}
}
+
+ Map<String,String> messageDigests = createMessageDigests(n_file);
+ for(Entry<String,String> messageDigest: messageDigests.entrySet()) {
+ Element messageDigestElement = m.xml.getOwnerDocument().createElement("message_digest");
+ messageDigestElement.setAttribute("algorithm", messageDigest.getKey());
+ messageDigestElement.setAttribute("value", messageDigest.getValue());
+ m.xml.appendChild(messageDigestElement);
+ }
+
moduleCollection.add(m);
}
} catch (BuildException x) {
@@ -882,4 +917,57 @@
return sb;
}
+ private Map<String,String> createMessageDigests(File targetFile) {
+ if(includeMessageDigests.isEmpty()) {
+ return Collections.<String,String>emptyMap();
+ }
+
+ Map<String,MessageDigest> digests = new HashMap<>(includeMessageDigests.size());
+
+ for(String messageDigest: includeMessageDigests) {
+ try {
+ digests.put(messageDigest, MessageDigest.getInstance(messageDigest));
+ } catch (NoSuchAlgorithmException ex) {
+ throw new BuildException("Failed to load requested messageDigest: " + messageDigest, ex);
+ }
+ }
+
+ try(FileInputStream fis = new FileInputStream(targetFile)) {
+ byte[] buffer = new byte[102400];
+ int read;
+ while((read = fis.read(buffer)) >= 0) {
+ for(MessageDigest md: digests.values()) {
+ md.update(buffer, 0, read);
+ }
+ }
+ } catch (IOException ex) {
+ Logger.getLogger(MakeUpdateDesc.class.getName()).log(Level.SEVERE, null, ex);
+ }
+
+ Map<String,String> result = new HashMap<>();
+ for(Entry<String,MessageDigest> md: digests.entrySet()) {
+ result.put(md.getKey(), hexEncode(md.getValue().digest()));
+ }
+ return result;
+ }
+
+ private static String hexEncode(byte[] input) {
+ StringBuilder sb = new StringBuilder(input.length * 2);
+ for(byte b: input) {
+ sb.append(Character.forDigit((b & 0xF0) >> 4, 16));
+ sb.append(Character.forDigit((b & 0x0F), 16));
+ }
+ return sb.toString();
+ }
+
+ private static byte[] hexDecode(String input) {
+ int length = input.length() / 2;
+ byte[] result = new byte[length];
+ for(int i = 0; i < length; i++) {
+ int b = Character.digit(input.charAt(i * 2), 16) << 4;
+ b |= Character.digit(input.charAt(i * 2 + 1), 16);
+ result[i] = (byte) (b & 0xFF);
+ }
+ return result;
+ }
}
diff --git a/platform/autoupdate.services/apichanges.xml b/platform/autoupdate.services/apichanges.xml
index 9cee0f7..a51099c 100644
--- a/platform/autoupdate.services/apichanges.xml
+++ b/platform/autoupdate.services/apichanges.xml
@@ -33,6 +33,47 @@
<!-- ACTUAL CHANGES BEGIN HERE: -->
<changes>
+ <change id="enable-trusted-update-centers">
+ <api name="general"/>
+ <summary>KeyStoreProviders can now report which trustlevel they intent to supply</summary>
+ <version major="1" minor="62"/>
+ <date day="4" month="11" year="2019"/>
+ <author login="matthiasblaesing"/>
+ <compatibility addition="yes" binary="compatible" deletion="no" deprecation="no" semantic="compatible" source="compatible"/>
+ <description>
+ <p>
+ The UpdateUnitProvider can now be declared trusted. If
+ UpdateItems are provided by a trusted UpdateUnitProvider
+ and can be linked to it (for example because the provider provides
+ checksums in the catalog), they are considered trusted by the Autoupdate
+ mechanism and will not trigger certificate warnings.
+ </p>
+ </description>
+ <class package="org.netbeans.api.autoupdate" name="OperationContainer"/>
+ </change>
+ <change id="keystores-for-validation">
+ <api name="general"/>
+ <summary>KeyStoreProviders can now report which trustlevel they intent to supply</summary>
+ <version major="1" minor="61"/>
+ <date day="4" month="11" year="2019"/>
+ <author login="matthiasblaesing"/>
+ <compatibility addition="yes" binary="compatible" deletion="no" deprecation="no" semantic="compatible" source="compatible"/>
+ <description>
+ <p>
+ The validation of signatures of NBMs was done by checking if at least a partial certificate chain was present
+ and if that chain was valid (i.e. not expired, not revoked). Instead of relying on this partial check this
+ version bases the verification on a list of trusted certificates, similar to the existing KeyStoreProvider.
+ </p>
+ <p>
+ The existing KeyStoreProviders provide Certificates, that are fully trusted. The new getTrustLevel method
+ allows a KeyStoreProvider to provide certificate for the new, lower trusted level. In addition to the to
+ levels TRUST and VALIDATE, two variants: TRUST_CA and VALIDATE_CA are introduced. Certificates provided with
+ that level are expected to be CA certificates and they are only trusted if a `CertPathValidator` validates
+ the chain.
+ </p>
+ </description>
+ <class package="org.netbeans.api.autoupdate" name="OperationContainer"/>
+ </change>
<change id="missing-elements">
<api name="general"/>
<summary>Report parts of a feature which is not installed yet</summary>
diff --git a/platform/autoupdate.services/build.xml b/platform/autoupdate.services/build.xml
index 8e0f1ed..33f8799 100644
--- a/platform/autoupdate.services/build.xml
+++ b/platform/autoupdate.services/build.xml
@@ -52,7 +52,22 @@
<target name="netbeans-extra" depends="jar-updater"/>
<target name="do-unit-test-build" depends="projectized.do-unit-test-build">
+ <property name="validation-store" value="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/test-validate-keystore.jks" />
+ <delete file="${validation-store}" />
+ <genkey
+ keystore="${validation-store}"
+ alias="demo-key" storepass="password"
+ dname="CN=Demo Key, OU=NetBeans, O=Apache.org, C=US"
+ />
+ <property name="unvalidation-store" value="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/test-unvalidate-keystore.jks" />
+ <delete file="${unvalidation-store}" />
+ <genkey
+ keystore="${unvalidation-store}"
+ alias="demo-key-unvalidated" storepass="password"
+ dname="CN=Demo Key (unvalidated), OU=NetBeans, O=Apache.org, C=US"
+ />
<touch file="${build.dir}/Dummy.class" />
+ <touch file="${build.dir}/Dummy2.class" />
<jar destfile="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/dummy-signed.jar">
<zipfileset prefix="dummy/" file="${build.dir}/Dummy.class"/>
</jar>
@@ -71,6 +86,31 @@
keystore="${test.unit.src.dir}/org/netbeans/api/autoupdate/data/test-keystore.jks"
storepass="password"
alias="test-two" />
+ <jar destfile="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/dummy-partial-signed.jar">
+ <zipfileset prefix="dummy/" file="${build.dir}/Dummy.class"/>
+ </jar>
+ <signjar jar="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/dummy-partial-signed.jar"
+ keystore="${test.unit.src.dir}/org/netbeans/api/autoupdate/data/test-keystore.jks"
+ storepass="password"
+ alias="test-one" />
+ <zip destfile="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/dummy-partial-signed.jar"
+ update="true">
+ <zipfileset prefix="dummy/" file="${build.dir}/Dummy2.class" />
+ </zip>
+ <jar destfile="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/dummy-validated.jar">
+ <zipfileset prefix="dummy/" file="${build.dir}/Dummy.class"/>
+ </jar>
+ <signjar jar="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/dummy-validated.jar"
+ keystore="${validation-store}"
+ storepass="password"
+ alias="demo-key" />
+ <jar destfile="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/dummy-unvalidated.jar">
+ <zipfileset prefix="dummy/" file="${build.dir}/Dummy.class"/>
+ </jar>
+ <signjar jar="${build.test.unit.classes.dir}/org/netbeans/api/autoupdate/data/dummy-unvalidated.jar"
+ keystore="${unvalidation-store}"
+ storepass="password"
+ alias="demo-key-unvalidated" />
</target>
</project>
diff --git a/platform/autoupdate.services/libsrc/org/netbeans/updater/XMLUtil.java b/platform/autoupdate.services/libsrc/org/netbeans/updater/XMLUtil.java
index 7ce7088..b0e7560 100644
--- a/platform/autoupdate.services/libsrc/org/netbeans/updater/XMLUtil.java
+++ b/platform/autoupdate.services/libsrc/org/netbeans/updater/XMLUtil.java
@@ -183,6 +183,8 @@
return new InputSource(XMLUtil.class.getResource("resources/autoupdate-catalog-2_7.dtd").toString()); // NOI18N
} else if ("-//NetBeans//DTD Autoupdate Module Info 2.7//EN".equals(publicID)) { // NOI18N
return new InputSource(XMLUtil.class.getResource("resources/autoupdate-info-2_7.dtd").toString()); // NOI18N
+ } else if ("-//NetBeans//DTD Autoupdate Catalog 2.8//EN".equals(publicID)) { // NOI18N
+ return new InputSource(XMLUtil.class.getResource("resources/autoupdate-catalog-2_8.dtd").toString()); // NOI18N
} else {
if (systemID.endsWith(".dtd")) { // NOI18N
return new InputSource(new ByteArrayInputStream(new byte[0]));
diff --git a/platform/autoupdate.services/libsrc/org/netbeans/updater/resources/autoupdate-catalog-2_8.dtd b/platform/autoupdate.services/libsrc/org/netbeans/updater/resources/autoupdate-catalog-2_8.dtd
new file mode 100644
index 0000000..074e636
--- /dev/null
+++ b/platform/autoupdate.services/libsrc/org/netbeans/updater/resources/autoupdate-catalog-2_8.dtd
@@ -0,0 +1,96 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+<!-- -//NetBeans//DTD Autoupdate Catalog 2.8//EN -->
+<!-- XML representation of Autoupdate Modules/Updates Catalog -->
+
+<!ELEMENT module_updates ((notification?, content_description?, (module_group|module)*, license*)|error)>
+<!ATTLIST module_updates timestamp CDATA #REQUIRED>
+
+<!ELEMENT module_group ((module_group|module)*)>
+<!ATTLIST module_group name CDATA #REQUIRED>
+
+<!ELEMENT notification (#PCDATA)>
+<!ATTLIST notification url CDATA #IMPLIED>
+
+<!ELEMENT content_description (#PCDATA)>
+<!ATTLIST content_description url CDATA #IMPLIED>
+
+<!ELEMENT module (description?, module_notification?, external_package*, (manifest | l10n), message_digest* )>
+<!ATTLIST module codenamebase CDATA #REQUIRED
+ homepage CDATA #IMPLIED
+ distribution CDATA #REQUIRED
+ license CDATA #IMPLIED
+ downloadsize CDATA #REQUIRED
+ needsrestart (true|false) #IMPLIED
+ moduleauthor CDATA #IMPLIED
+ releasedate CDATA #IMPLIED
+ global (true|false) #IMPLIED
+ targetcluster CDATA #IMPLIED
+ preferredupdate (true|false) #IMPLIED
+ eager (true|false) #IMPLIED
+ autoload (true|false) #IMPLIED>
+
+<!ELEMENT description (#PCDATA)>
+
+<!ELEMENT module_notification (#PCDATA)>
+
+<!ELEMENT external_package EMPTY>
+<!ATTLIST external_package
+ name CDATA #REQUIRED
+ target_name CDATA #REQUIRED
+ start_url CDATA #REQUIRED
+ description CDATA #IMPLIED>
+
+<!ELEMENT manifest EMPTY>
+<!ATTLIST manifest OpenIDE-Module CDATA #REQUIRED
+ OpenIDE-Module-Name CDATA #REQUIRED
+ OpenIDE-Module-Specification-Version CDATA #REQUIRED
+ OpenIDE-Module-Implementation-Version CDATA #IMPLIED
+ OpenIDE-Module-Module-Dependencies CDATA #IMPLIED
+ OpenIDE-Module-Package-Dependencies CDATA #IMPLIED
+ OpenIDE-Module-Java-Dependencies CDATA #IMPLIED
+ OpenIDE-Module-IDE-Dependencies CDATA #IMPLIED
+ OpenIDE-Module-Short-Description CDATA #IMPLIED
+ OpenIDE-Module-Long-Description CDATA #IMPLIED
+ OpenIDE-Module-Display-Category CDATA #IMPLIED
+ OpenIDE-Module-Provides CDATA #IMPLIED
+ OpenIDE-Module-Requires CDATA #IMPLIED
+ OpenIDE-Module-Recommends CDATA #IMPLIED
+ OpenIDE-Module-Needs CDATA #IMPLIED
+ AutoUpdate-Show-In-Client (true|false) #IMPLIED
+ AutoUpdate-Essential-Module (true|false) #IMPLIED
+ OpenIDE-Module-Fragment-Host CDATA #IMPLIED>
+
+<!ELEMENT l10n EMPTY>
+<!ATTLIST l10n langcode CDATA #IMPLIED
+ brandingcode CDATA #IMPLIED
+ module_spec_version CDATA #IMPLIED
+ module_major_version CDATA #IMPLIED
+ OpenIDE-Module-Name CDATA #IMPLIED
+ OpenIDE-Module-Long-Description CDATA #IMPLIED>
+
+<!ELEMENT license (#PCDATA)>
+<!ATTLIST license name CDATA #REQUIRED
+ url CDATA #IMPLIED>
+
+<!ELEMENT message_digest EMPTY>
+<!ATTLIST message_digest algorithm CDATA #REQUIRED
+ value CDATA #REQUIRED>
\ No newline at end of file
diff --git a/platform/autoupdate.services/manifest.mf b/platform/autoupdate.services/manifest.mf
index 0948f17..c9df2d4 100644
--- a/platform/autoupdate.services/manifest.mf
+++ b/platform/autoupdate.services/manifest.mf
@@ -1,7 +1,7 @@
Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.modules.autoupdate.services
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/autoupdate/services/resources/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.60
+OpenIDE-Module-Specification-Version: 1.62
OpenIDE-Module-Layer: org/netbeans/modules/autoupdate/services/resources/layer.xml
AutoUpdate-Show-In-Client: false
AutoUpdate-Essential-Module: true
diff --git a/platform/autoupdate.services/src/org/netbeans/api/autoupdate/UpdateUnitProvider.java b/platform/autoupdate.services/src/org/netbeans/api/autoupdate/UpdateUnitProvider.java
index 80d17d2..92f2e8c 100644
--- a/platform/autoupdate.services/src/org/netbeans/api/autoupdate/UpdateUnitProvider.java
+++ b/platform/autoupdate.services/src/org/netbeans/api/autoupdate/UpdateUnitProvider.java
@@ -193,4 +193,27 @@
public String toString() {
return super.toString() + "[" + impl + "]";
}
+
+ /** Sets the trusted flag.
+ *
+ * @see #isTrusted
+ * @param trusted
+ * @since 1.62
+ */
+ public void setTrusted (boolean trusted) {
+ impl.setTrusted(trusted);
+ }
+
+ /** Returns <code>true</code> if the provider is considered to be trusted.
+ * If {@link UpdateItem} are provided by a trusted {@code UpdateUnitProvider}
+ * and can be linked to it (for example because the provider provides
+ * checksums in the catalog), they are considered trusted by the Autoupdate
+ * mechanism and will not trigger certificate warnings.
+ *
+ * @return trusted flag
+ * @since 1.62
+ */
+ public boolean isTrusted () {
+ return impl.isTrusted();
+ }
}
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java
index 5098c28..61f7ba9 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java
@@ -28,13 +28,14 @@
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
+import java.security.CodeSigner;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.Certificate;
+import java.security.cert.TrustAnchor;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
-import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
@@ -48,10 +49,12 @@
import org.netbeans.api.autoupdate.OperationSupport.Restarter;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.modules.autoupdate.updateprovider.AutoupdateInfoParser;
+import org.netbeans.modules.autoupdate.updateprovider.MessageDigestValue;
import org.netbeans.modules.autoupdate.updateprovider.ModuleItem;
import org.netbeans.modules.autoupdate.updateprovider.NetworkAccess;
import org.netbeans.modules.autoupdate.updateprovider.NetworkAccess.Task;
import org.netbeans.modules.autoupdate.updateprovider.UpdateItemImpl;
+import org.netbeans.spi.autoupdate.KeyStoreProvider;
import org.netbeans.spi.autoupdate.UpdateItem;
import org.netbeans.updater.ModuleDeactivator;
import org.netbeans.updater.ModuleUpdater;
@@ -66,6 +69,8 @@
import org.openide.util.NbCollections;
import org.xml.sax.SAXException;
+import static org.netbeans.modules.autoupdate.services.Utilities.VERIFICATION_RESULT_COMPARATOR;
+
/**
*
* @author Jiri Rechtacek
@@ -924,9 +929,10 @@
String label) throws MalformedURLException, IOException {
OpenConnectionListener listener = new OpenConnectionListener(source);
- final Task task = NetworkAccess.createNetworkAcessTask(source,
+ final Task task = NetworkAccess.createNetworkAccessTask(source,
AutoupdateSettings.getOpenConnectionTimeout(),
- listener);
+ listener,
+ false);
new Thread(new Runnable() {
@SuppressWarnings("SleepWhileInLoop")
@Override
@@ -1010,7 +1016,7 @@
// + ") of is equal to estimatedSize (" + estimatedSize + ").";
if (estimatedSize != increment) {
LOG.log (Level.FINEST, "Increment (" + increment + ") of is not equal to estimatedSize (" + estimatedSize + ").");
- }
+ }
} catch (IOException ioe) {
LOG.log (Level.INFO, "Writing content of URL " + source + " failed.", ioe);
} finally {
@@ -1022,6 +1028,7 @@
LOG.log (Level.INFO, ioe.getMessage (), ioe);
}
}
+
if (contentLength != -1 && increment != contentLength) {
if(canceled) {
LOG.log(Level.INFO, "Download of " + source + " was cancelled");
@@ -1049,29 +1056,98 @@
}
private int verifyNbm (UpdateElement el, File nbmFile, ProgressHandle progress, int verified) throws OperationException {
- String res;
+ UpdateElementImpl impl = Trampoline.API.impl(el);
+
+ modified.remove(impl);
+ trusted.remove(impl);
+ signedVerified.remove(impl);
+ signedUnverified.remove(impl);
+
+ String res = null;
try {
// get trusted certificates
- List<Certificate> trustedCerts = new ArrayList<Certificate> ();
- for (KeyStore ks : Utilities.getKeyStore ()) {
- trustedCerts.addAll (Utilities.getCertificates (ks));
+ Set<Certificate> trustedCerts = new HashSet<> ();
+ Set<Certificate> validationCerts = new HashSet<>();
+ Set<TrustAnchor> trustedCACerts = new HashSet<>();
+ Set<TrustAnchor> validationCACerts = new HashSet<>();
+ for (KeyStore ks : Utilities.getKeyStore (KeyStoreProvider.TrustLevel.TRUST)) {
+ trustedCerts.addAll(Utilities.getCertificates(ks));
+ }
+ for (KeyStore ks : Utilities.getKeyStore (KeyStoreProvider.TrustLevel.VALIDATE)) {
+ validationCerts.addAll(Utilities.getCertificates(ks));
+ }
+ for (KeyStore ks : Utilities.getKeyStore (KeyStoreProvider.TrustLevel.TRUST_CA)) {
+ trustedCACerts.addAll(Utilities.getTrustAnchor(ks));
+ }
+ for (KeyStore ks : Utilities.getKeyStore (KeyStoreProvider.TrustLevel.VALIDATE_CA)) {
+ validationCACerts.addAll(Utilities.getTrustAnchor(ks));
}
// load user certificates
KeyStore ks = Utilities.loadKeyStore ();
if (ks != null) {
- trustedCerts.addAll (Utilities.getCertificates (ks));
+ trustedCerts.addAll(Utilities.getCertificates(ks));
}
-
+
verified += el.getDownloadSize ();
if (progress != null) {
progress.progress (el.getDisplayName (), verified < wasDownloaded ? verified : wasDownloaded);
}
- Collection<Certificate> nbmCerts = Utilities.getNbmCertificates (nbmFile);
- if (nbmCerts != null && nbmCerts.size () > 0) {
- certs.put (el, nbmCerts);
+
+ {
+ MessageDigestChecker mdChecker = new MessageDigestChecker(impl.getMessageDigests());
+ byte[] buffer = new byte[102400];
+ int read;
+ try(FileInputStream fis = new FileInputStream(nbmFile)) {
+ while((read = fis.read(buffer)) > 0) {
+ mdChecker.update(buffer, 0, read);
+ }
+ }
+ if(!mdChecker.validate()) {
+ for (String algorithm : mdChecker.getFailingHashes()) {
+ LOG.log(Level.INFO,
+ "Failed to validate message digest for ''{0}'' expected ''{1}'' got ''{2}''",
+ new Object[]{
+ nbmFile.getAbsolutePath(),
+ mdChecker.getExpectedHashAsString(algorithm),
+ mdChecker.getCalculatedHashAsString(algorithm)
+ });
+ }
+ res = Utilities.MODIFIED;
+ } else if (mdChecker.isDigestAvailable() && impl.isCatalogTrusted()) {
+ res = Utilities.TRUSTED;
+ }
}
- res = Utilities.verifyCertificates(nbmCerts, trustedCerts);
- UpdateElementImpl impl = Trampoline.API.impl(el);
+
+ if(res == null) {
+ try {
+ Collection<CodeSigner> nbmCerts = Utilities.getNbmCertificates(nbmFile);
+ if (nbmCerts == null) {
+ res = Utilities.N_A;
+ } else if (nbmCerts.isEmpty()) {
+ res = Utilities.UNSIGNED;
+ } else {
+ // Iterate all certpaths that can be considered for the NBM
+ // choose the certpath, that has the highest trust level
+ // TRUSTED -> SIGNATURE_VERIFIED -> SIGNATURE_UNVERIFIED
+ // or comes first
+ for (CodeSigner cs : nbmCerts) {
+ String localRes = Utilities.verifyCertificates(cs, trustedCerts, trustedCACerts, validationCerts, validationCACerts);
+ // If there is no previous result or if the local
+ // verification yielded a better result than the
+ // previous result, replace it
+ if (res == null
+ || VERIFICATION_RESULT_COMPARATOR.compare(res, localRes) > 0) {
+ res = localRes;
+ certs.put(el, (List<Certificate>) cs.getSignerCertPath().getCertificates());
+ }
+ }
+ }
+ } catch (SecurityException ex) {
+ LOG.log(Level.INFO, "The content of the jar/nbm has been modified or certificate paths were inconsistent - " + ex.getMessage(), ex);
+ res = Utilities.MODIFIED;
+ }
+ }
+
if (res != null) {
switch (res) {
case Utilities.MODIFIED:
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/MessageDigestChecker.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/MessageDigestChecker.java
new file mode 100644
index 0000000..53d3eaf
--- /dev/null
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/MessageDigestChecker.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.autoupdate.services;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.modules.autoupdate.updateprovider.MessageDigestValue;
+
+public class MessageDigestChecker {
+
+ private static final Logger LOG = Logger.getLogger(MessageDigestChecker.class.getName());
+
+ private final Map<String,MessageDigest> messageDigest = new HashMap<>();
+ private final Map<String,byte[]> exptectedResult = new HashMap<>();
+ private final Map<String,byte[]> calculatedResult = new HashMap<>();
+ private Boolean overallValid = null;
+
+ public MessageDigestChecker(Collection<MessageDigestValue> referenceHashes) {
+ if (referenceHashes != null) {
+ for (MessageDigestValue h : referenceHashes) {
+ try {
+ MessageDigest md = MessageDigest.getInstance(h.getAlgorithm());
+ messageDigest.put(h.getAlgorithm(), md);
+ exptectedResult.put(h.getAlgorithm(), hexDecode(h.getValue()));
+ } catch (NoSuchAlgorithmException ex) {
+ LOG.log(Level.FINE, "Unsupported Hash Algorithm: {0}", h.getAlgorithm());
+ }
+ }
+ }
+ }
+
+ public void update(byte[] data) {
+ ensureValidateNotCalled();
+ for(MessageDigest md: messageDigest.values()) {
+ md.update(data);
+ }
+ }
+
+ public void update(byte[] data, int offset, int len) {
+ ensureValidateNotCalled();
+ for(MessageDigest md: messageDigest.values()) {
+ md.update(data, offset, len);
+ }
+ }
+
+ public void update(byte data) {
+ ensureValidateNotCalled();
+ for(MessageDigest md: messageDigest.values()) {
+ md.update(data);
+ }
+ }
+
+ private void ensureValidateNotCalled() throws IllegalStateException {
+ if(overallValid != null) {
+ throw new IllegalStateException("update must not be called after validate is invoked");
+ }
+ }
+
+ private void ensureValidateCalled() throws IllegalStateException {
+ if(overallValid == null) {
+ throw new IllegalStateException("This method must not be called before validate is invoked");
+ }
+ }
+
+ public boolean isDigestAvailable() {
+ return ! messageDigest.isEmpty();
+ }
+
+ public boolean validate() throws IOException {
+ if (overallValid == null) {
+ overallValid = true;
+ for (Entry<String, MessageDigest> e : messageDigest.entrySet()) {
+ String algorithm = e.getKey();
+ calculatedResult.put(algorithm, e.getValue().digest());
+ boolean localValid = Arrays.equals(
+ exptectedResult.get(algorithm),
+ calculatedResult.get(algorithm));
+ overallValid &= localValid;
+ }
+ }
+ return overallValid;
+ }
+
+ public List<String> getFailingHashes() {
+ ensureValidateCalled();
+ List<String> result = new ArrayList<>();
+ for(String algorithm: messageDigest.keySet()) {
+ if(! Arrays.equals(
+ exptectedResult.get(algorithm),
+ calculatedResult.get(algorithm))) {
+ result.add(algorithm);
+ }
+ }
+ return result;
+ }
+
+ public String getExpectedHashAsString(String algorithm) {
+ ensureValidateCalled();
+ return hexEncode(exptectedResult.get(algorithm));
+ }
+
+ public String getCalculatedHashAsString(String algorithm) {
+ ensureValidateCalled();
+ return hexEncode(calculatedResult.get(algorithm));
+ }
+
+ public static String hexEncode(byte[] input) {
+ StringBuilder sb = new StringBuilder(input.length * 2);
+ for(byte b: input) {
+ sb.append(Character.forDigit((b & 0xF0) >> 4, 16));
+ sb.append(Character.forDigit((b & 0x0F), 16));
+ }
+ return sb.toString();
+ }
+
+ public static byte[] hexDecode(String input) {
+ int length = input.length() / 2;
+ byte[] result = new byte[length];
+ for(int i = 0; i < length; i++) {
+ int b = Character.digit(input.charAt(i * 2), 16) << 4;
+ b |= Character.digit(input.charAt(i * 2 + 1), 16);
+ result[i] = (byte) (b & 0xFF);
+ }
+ return result;
+ }
+}
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateElementImpl.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateElementImpl.java
index 7cc580c..31a2688 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateElementImpl.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateElementImpl.java
@@ -19,10 +19,12 @@
package org.netbeans.modules.autoupdate.services;
+import java.util.ArrayList;
import java.util.List;
import org.netbeans.api.autoupdate.UpdateElement;
import org.netbeans.api.autoupdate.UpdateManager;
import org.netbeans.api.autoupdate.UpdateUnit;
+import org.netbeans.modules.autoupdate.updateprovider.MessageDigestValue;
import org.netbeans.modules.autoupdate.updateprovider.InstallInfo;
import org.netbeans.modules.autoupdate.updateprovider.UpdateItemImpl;
import org.openide.modules.ModuleInfo;
@@ -35,8 +37,15 @@
public abstract class UpdateElementImpl extends Object {
private UpdateUnit unit;
private UpdateElement element;
-
- public UpdateElementImpl (UpdateItemImpl item, String providerName) {}
+ private List<MessageDigestValue> messageDigests = new ArrayList<>();
+ private boolean catalogTrusted = false;
+
+ public UpdateElementImpl (UpdateItemImpl item, String providerName) {
+ if(item.getMessageDigests() != null) {
+ messageDigests.addAll(item.getMessageDigests());
+ }
+ this.catalogTrusted = item.isCatalogTrusted();
+ }
public UpdateUnit getUpdateUnit () {
return unit;
@@ -99,4 +108,20 @@
// XXX: try to rid of this
public abstract InstallInfo getInstallInfo ();
+ public List<MessageDigestValue> getMessageDigests() {
+ return messageDigests;
+ }
+
+ public void setMessageDigests(List<MessageDigestValue> messageDigests) {
+ this.messageDigests = messageDigests;
+ }
+
+ public boolean isCatalogTrusted() {
+ return catalogTrusted;
+ }
+
+ public void setCatalogTrusted(boolean catalogTrusted) {
+ this.catalogTrusted = catalogTrusted;
+ }
+
}
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateUnitFactory.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateUnitFactory.java
index 646df0a..529ada0 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateUnitFactory.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateUnitFactory.java
@@ -113,7 +113,7 @@
for (UpdateUnitProvider up : updates) {
UpdateUnitProviderImpl impl = Trampoline.API.impl (up);
-
+
// append units from provider
mappedImpl = appendUpdateItems (mappedImpl, impl.getUpdateProvider ());
reportRunTime ("AppendUpdateItems for " + impl.getUpdateProvider ().getDisplayName ());
@@ -126,7 +126,7 @@
//TODO: this call should be forced not to be called from AWT
//assert !SwingUtilities.isEventDispatchThread();
resetRunTime ("Measuring UpdateUnitFactory.getUpdateUnits (" + provider.getDisplayName () + ")"); // NOI18N
-
+
// append units from provider
Map<String, UpdateUnit> temp = appendUpdateItems (new HashMap<String, UpdateUnit> (), provider);
reportRunTime ("Get appendUpdateItems for " + provider.getDisplayName ());
@@ -143,6 +143,8 @@
Map<String, UpdateUnit> appendUpdateItems (Map<String, UpdateUnit> originalUnits, UpdateProvider provider) {
assert originalUnits != null : "Map of original UnitImpl cannot be null";
+ boolean trusted = UpdateUnitProviderImpl.loadTrusted(provider);
+
Map<String, UpdateItem> items;
try {
items = provider.getUpdateItems ();
@@ -155,7 +157,7 @@
// append updates
for (String simpleItemId : items.keySet ()) {
-
+
UpdateElement updateEl = null;
try {
@@ -204,6 +206,7 @@
// add element to map
if (updateEl != null) {
+ Trampoline.API.impl(updateEl).setCatalogTrusted(trusted);
addElement (originalUnits, updateEl, provider);
}
}
@@ -282,7 +285,7 @@
// set UpdateUnit into element
elImpl.setUpdateUnit (unit);
-
+
}
private UpdateUnit mergeInstalledUpdateUnit (UpdateUnit uu) {
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateUnitProviderImpl.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateUnitProviderImpl.java
index 102ee99..adc8f20 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateUnitProviderImpl.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateUnitProviderImpl.java
@@ -31,6 +31,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.BackingStoreException;
@@ -68,6 +69,7 @@
private static final String URL = "url";
private static final String DISPLAY_NAME = "displayName";
private static final String ENABLED = "enabled";
+ private static final String TRUSTED = "trusted";
private static final String CATEGORY_NAME = "categoryName";
private static final LookupListenerImpl UPDATE_PROVIDERS = new LookupListenerImpl();
@@ -133,7 +135,15 @@
public void setProviderURL (URL url) {
storeUrl (getUpdateProvider (), url);
}
-
+
+ public boolean isTrusted() {
+ return loadTrusted(getUpdateProvider());
+ }
+
+ public void setTrusted(Boolean trusted) {
+ storeTrusted(getUpdateProvider(), trusted);
+ }
+
public List<UpdateUnit> getUpdateUnits (UpdateManager.TYPE... types) {
return UpdateManagerImpl.getUpdateUnits (getUpdateProvider (), types);
}
@@ -343,6 +353,7 @@
String toUrl = providerPreferences.get (URL, providerPreferences.get (AutoupdateCatalogFactory.ORIGINAL_URL, null));
String displayName = providerPreferences.get (DISPLAY_NAME, providerPreferences.get (AutoupdateCatalogFactory.ORIGINAL_DISPLAY_NAME, codeName));
String categoryName = providerPreferences.get (CATEGORY_NAME, providerPreferences.get (AutoupdateCatalogFactory.ORIGINAL_CATEGORY_NAME, CATEGORY.COMMUNITY.name()));
+ Boolean trusted = providerPreferences.getBoolean(TRUSTED, providerPreferences.getBoolean(AutoupdateCatalogFactory.ORIGINAL_TRUSTED, false));
CATEGORY c;
try {
c = CATEGORY.valueOf(categoryName);
@@ -368,7 +379,9 @@
} catch (MalformedURLException mue) {
assert false : mue;
}
- return new AutoupdateCatalogProvider (codeName, displayName, url, pc);
+ AutoupdateCatalogProvider acp = new AutoupdateCatalogProvider (codeName, displayName, url, pc);
+ acp.setTrusted(trusted);
+ return acp;
}
private static boolean loadState (String codename) {
@@ -461,6 +474,32 @@
}
}
+ static boolean loadTrusted (UpdateProvider p) {
+ Preferences providerPreferences = getPreferences ().node (p.getName ());
+ assert providerPreferences != null : "Preferences node " + p.getName () + " found.";
+
+ Boolean trusted = null;
+ if (p instanceof AutoupdateCatalogProvider) {
+ trusted = ((AutoupdateCatalogProvider) p).isTrusted();
+ }
+ if(trusted == null) {
+ trusted = false;
+ }
+ return providerPreferences.getBoolean(TRUSTED, trusted);
+ }
+
+ private static void storeTrusted (UpdateProvider p, Boolean trusted) {
+ Preferences providerPreferences = getPreferences ().node (p.getName ());
+ assert providerPreferences != null : "Preferences node " + p.getName () + " found.";
+
+ // store only if differs
+ if (trusted == null) {
+ providerPreferences.remove (TRUSTED);
+ } else {
+ providerPreferences.putBoolean(TRUSTED, trusted);
+ }
+ }
+
private static class LookupListenerImpl implements LookupListener {
final Lookup.Result<UpdateProvider> result = Lookup.getDefault ().lookupResult(UpdateProvider.class);
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/Utilities.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/Utilities.java
index f5d886b..fdef5f2 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/Utilities.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/Utilities.java
@@ -22,20 +22,18 @@
import java.io.*;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
+import java.security.CodeSigner;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
-import java.security.Principal;
-import java.security.Security;
-import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.PKIXCertPathValidatorResult;
import java.security.cert.PKIXParameters;
+import java.security.cert.PKIXRevocationChecker;
+import java.security.cert.PKIXRevocationChecker.Option;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.text.ParseException;
@@ -46,6 +44,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
+import java.util.regex.Pattern;
import org.netbeans.Module;
import org.netbeans.ModuleManager;
import org.netbeans.api.autoupdate.UpdateElement;
@@ -58,6 +57,7 @@
import org.netbeans.modules.autoupdate.updateprovider.InstalledModuleProvider;
import org.netbeans.modules.autoupdate.updateprovider.UpdateItemImpl;
import org.netbeans.spi.autoupdate.KeyStoreProvider;
+import org.netbeans.spi.autoupdate.KeyStoreProvider.TrustLevel;
import org.netbeans.spi.autoupdate.UpdateItem;
import org.netbeans.updater.ModuleDeactivator;
import org.netbeans.updater.ModuleUpdater;
@@ -102,135 +102,160 @@
private static final String USER_KS_KEY = "userKS";
private static final String USER_KS_FILE_NAME = "user.ks";
private static final String KS_USER_PASSWORD = "open4user";
- private static Lookup.Result<KeyStoreProvider> result;
+ private static Lookup.Result<KeyStoreProvider> keyStoreLookupResult;
+ private static Map<TrustLevel,List<KeyStore>> keystoreCache = Collections.synchronizedMap(new HashMap<>());
private static final Logger err = Logger.getLogger(Utilities.class.getName ());
-
-
- public static Collection<KeyStore> getKeyStore () {
- if (result == null) {
- result = Lookup.getDefault ().lookupResult (KeyStoreProvider.class);
- result.addLookupListener (new KeyStoreProviderListener ());
- }
- Collection<? extends KeyStoreProvider> c = result.allInstances ();
- if (c == null || c.isEmpty ()) {
- return Collections.emptyList ();
- }
- List<KeyStore> kss = new ArrayList<>();
-
- for (KeyStoreProvider provider : c) {
- KeyStore ks = provider.getKeyStore ();
- if (ks != null) {
- kss.add (ks);
- }
- }
-
- return kss;
- }
-
- public static String verifyCertificates(Collection<Certificate> archiveCertificates, Collection<Certificate> trustedCertificates) {
- if (archiveCertificates == null) {
- return N_A;
- }
- if (!archiveCertificates.isEmpty()) {
- Collection<Certificate> c = new HashSet<>(trustedCertificates);
- c.retainAll(archiveCertificates);
- if (c.isEmpty()) {
- Map<Principal, X509Certificate> certSubjectsMap = new HashMap<>();
- Set<Principal> certIssuersSet = new HashSet<>();
- for (Certificate cert : archiveCertificates) {
- if (cert != null) {
- X509Certificate x509Cert = (X509Certificate) cert;
- certSubjectsMap.put(x509Cert.getSubjectDN(), x509Cert);
- if (x509Cert.getIssuerDN() != null) {
- certIssuersSet.add(x509Cert.getIssuerDN());
- }
- }
- }
-
- Map<X509Certificate, X509Certificate> candidates = new HashMap<>();
-
- for (Principal p : certSubjectsMap.keySet()) {
- // cert chain may not be ordered - trust anchor could before certificate itself
- if (certIssuersSet.contains(p)) {
- continue;
- }
-
- X509Certificate cert = certSubjectsMap.get(p);
-
- Principal tap = cert.getIssuerDN();
- if (tap != null) {
- X509Certificate tempTrustAnchor = certSubjectsMap.get(tap);
- if (tempTrustAnchor != null) {
- candidates.put(cert, tempTrustAnchor);
- }
- }
- }
- // TRUSTED = 2
- // SIGNATURE_VERIFIED = 1
- // SIGNATURE_UNVERIFIED = 0
- int res = 0;
- for (X509Certificate cert : candidates.keySet()) {
- X509Certificate trustCert = candidates.get(cert);
- PKIXCertPathValidatorResult validResult = null;
- try {
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- List certList = new ArrayList();
- certList.add(cert);
- CertPath cp = cf.generateCertPath(certList);
- TrustAnchor trustAnchor = new TrustAnchor(trustCert, null);
- PKIXParameters params = new PKIXParameters(Collections.singleton(trustAnchor));
- params.setRevocationEnabled(true);
- Security.setProperty("ocsp.enable", "true");
- System.setProperty("com.sun.security.enableCRLDP", "true"); // CRL fallback
- CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
- validResult = (PKIXCertPathValidatorResult) cpv.validate(cp, params);
- } catch (CertificateException | InvalidAlgorithmParameterException | NoSuchAlgorithmException ex) {
- // CertificateException - Should not get here - "X.509" is proper certificate type
- // InvalidAlgorithmParameterException - Should not get here - trustAnchor cannot be null -> collection cannot be empty
- // NoSuchAlgorithmException - Should not get here - "PKIX" is proper algorythm
- err.log(Level.SEVERE, "Certificate verification failed - " + ex.getMessage(), ex);
- //SIGNATURE_UNVERIFIED - result = 0;
- } catch (CertPathValidatorException ex) {
- // CertPath cannot be validated
- err.log(Level.INFO, "Cannot validate certificate path - " + ex.getMessage(), ex);
- //SIGNATURE_UNVERIFIED - result = 0;
- } catch (SecurityException ex) {
- // When jar/nbm correctly signed, but content modified
- err.log(Level.INFO, "The content of the jar/nbm has been modified - " + ex.getMessage(), ex);
- return MODIFIED;
- }
-
- if (validResult != null) {
- String certDNName = cert.getSubjectDN().getName();
- if (certDNName.contains("CN=\"Oracle America, Inc.\"")
- && (certDNName.contains("OU=Software Engineering") || certDNName.contains("OU=Code Signing Bureau"))) {
- res = 2;
- break;
- } else {
- res = 1;
- }
- }
- }
-
- switch (res) {
- case 2:
- return TRUSTED;
- case 1:
- return SIGNATURE_VERIFIED;
- default:
- return SIGNATURE_UNVERIFIED;
- }
- } else {
- // signed by trusted certificate stored in user's keystore od ide.ks
- return TRUSTED;
- }
+ public static Collection<KeyStore> getKeyStore (TrustLevel trustLevel) {
+ if (keyStoreLookupResult == null) {
+ keyStoreLookupResult = Lookup.getDefault().lookupResult(KeyStoreProvider.class);
+ keyStoreLookupResult.addLookupListener(new KeyStoreProviderListener());
}
- return UNSIGNED;
+
+ List<KeyStore> result = keystoreCache.get(trustLevel);
+
+ if (result == null) {
+ Collection<? extends KeyStoreProvider> c = keyStoreLookupResult.allInstances();
+ if (c == null || c.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<KeyStore> kss = new ArrayList<>();
+
+ for (KeyStoreProvider provider : c) {
+ KeyStore ks = provider.getKeyStore();
+ if (ks != null) {
+ kss.add(ks);
+ }
+ }
+
+ result = Collections.unmodifiableList(kss);
+
+ keystoreCache.put(trustLevel, result);
+ }
+
+ return result;
}
-
+
+ /**
+ *
+ * @param archiveCertPath
+ * @param trustedCertificates
+ * @return
+ */
+ public static String verifyCertificates(CodeSigner archiveCertPath,
+ Collection<Certificate> trustedCertificates,
+ Set<TrustAnchor> trustedCACertificates,
+ Collection<Certificate> validateCertificates,
+ Set<TrustAnchor> validationCACertificates) {
+ assert archiveCertPath != null;
+
+ List<? extends Certificate> archiveCertificates = archiveCertPath.getSignerCertPath().getCertificates();
+
+ if(archiveCertificates.isEmpty()) {
+ return UNSIGNED;
+ }
+
+ // Case 1: We have direct trust into one of the certificates of the
+ // certificate chain
+ if(isChainTrusted(archiveCertificates, trustedCertificates)) {
+ return TRUSTED;
+ }
+
+ // Case 2: We trust the CA, that issued a certificate - do the
+ // normal CertPathValidation
+ if(verifyCACertificatePath(trustedCACertificates, archiveCertPath)) {
+ return TRUSTED;
+ }
+
+ // Case 3: We have a list of certificates, that we directly mark as
+ // valid
+ if(isChainTrusted(archiveCertificates, validateCertificates)) {
+ return SIGNATURE_VERIFIED;
+ }
+
+ // Case 4: We trust the CA to do validation
+ if (verifyCACertificatePath(validationCACertificates, archiveCertPath)) {
+ return SIGNATURE_VERIFIED;
+ }
+
+ // Case 5: File is not signed
+ return SIGNATURE_UNVERIFIED;
+ }
+
+ private static boolean verifyCACertificatePath(Set<TrustAnchor> trustedCACertificates, CodeSigner archiveCertPath) {
+ if(trustedCACertificates.isEmpty()) {
+ return false;
+ }
+ try {
+ CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
+ PKIXParameters verificationParameters = new PKIXParameters(trustedCACertificates);
+ PKIXRevocationChecker rc = (PKIXRevocationChecker) cpv.getRevocationChecker();
+ rc.setOptions(EnumSet.of(Option.SOFT_FAIL));
+ verificationParameters.addCertPathChecker(rc);
+ if (archiveCertPath.getTimestamp() != null) {
+ cpv.validate(archiveCertPath.getSignerCertPath(), verificationParameters);
+ verificationParameters.setDate(archiveCertPath.getTimestamp().getTimestamp());
+ }
+ // validate raises a CertPathValidatorException if validation failed
+ cpv.validate(archiveCertPath.getSignerCertPath(), verificationParameters);
+ return true;
+ } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException ex) {
+ // InvalidAlgorithmParameterException - Should not get here - trustAnchor cannot be null -> collection cannot be empty
+ // NoSuchAlgorithmException - Should not get here - "PKIX" is proper algorythm
+ err.log(Level.SEVERE, "Certificate verification failed - " + ex.getMessage(), ex);
+ //SIGNATURE_UNVERIFIED - result = 0;
+ } catch (CertPathValidatorException ex) {
+ // CertPath cannot be validated
+ err.log(Level.INFO, "Cannot validate certificate path - " + ex.getMessage(), ex);
+ //SIGNATURE_UNVERIFIED - result = 0;
+ }
+ return false;
+ }
+
+ private static boolean isChainTrusted(Collection<? extends Certificate> archiveCertificates, Collection<? extends Certificate> trustedCertificates) {
+ Collection<Certificate> c = new HashSet(trustedCertificates);
+ c.retainAll(archiveCertificates);
+ return ! c.isEmpty();
+ }
+
+ /**
+ * Establishes an order for verificication result strings. Only the values
+ * <ul>
+ * <li>{@code null}</li>
+ * <li>{@link #UNSIGNED}</li>
+ * <li>{@link #SIGNATURE_UNVERIFIED}</li>
+ * <li>{@link #SIGNATURE_VERIFIED}</li>
+ * <li>{@link #TRUSTED}</li>
+ * </ul>
+ *
+ * @param verificationResult1
+ * @param verificationResult2
+ * @return
+ */
+ public static final Comparator<String> VERIFICATION_RESULT_COMPARATOR = new Comparator<String>() {
+ @Override
+ public int compare(String o1, String o2) {
+ int i1 = mapVerificationResultToInt(o1);
+ int i2 = mapVerificationResultToInt(o2);
+ return i1 - i2;
+ }
+ };
+
+ private static int mapVerificationResultToInt(String input) {
+ if(input == null) {
+ return 0;
+ }
+ switch(input) {
+ case UNSIGNED: return 1;
+ case SIGNATURE_UNVERIFIED: return 2;
+ case SIGNATURE_VERIFIED: return 3;
+ case TRUSTED: return 4;
+ default: throw new IllegalArgumentException("Unmappable value: " + input);
+ }
+ }
+
public static Collection<Certificate> getCertificates (KeyStore keyStore) throws KeyStoreException {
- Set<Certificate> certs = new HashSet<Certificate> ();
+ Set<Certificate> certs = new HashSet<> ();
for (String alias: Collections.list (keyStore.aliases ())) {
Certificate[] certificateChain = keyStore.getCertificateChain(alias);
if (certificateChain != null) {
@@ -240,33 +265,91 @@
}
return certs;
}
-
- public static Collection<Certificate> getNbmCertificates (File nbmFile) throws IOException {
- Set<Certificate> certs = new HashSet<Certificate>();
- JarFile jf = new JarFile(nbmFile);
- boolean empty = true;
- try {
- for (JarEntry entry : Collections.list(jf.entries())) {
- verifyEntry(jf, entry);
- if (!entry.getName().startsWith("META-INF/")) {
- empty = false;
- if (entry.getCertificates() != null) {
- certs.addAll(Arrays.asList(entry.getCertificates()));
+
+ public static Collection<TrustAnchor> getTrustAnchor (KeyStore keyStore) throws KeyStoreException {
+ Set<TrustAnchor> certs = new HashSet<> ();
+ for (String alias: Collections.list (keyStore.aliases ())) {
+ Certificate[] certificateChain = keyStore.getCertificateChain(alias);
+ if (certificateChain != null) {
+ for(Certificate cert: certificateChain) {
+ if(cert instanceof X509Certificate) {
+ certs.add(new TrustAnchor((X509Certificate) cert, null));
}
}
}
- } finally {
- jf.close();
+ Certificate aliasCert = keyStore.getCertificate(alias);
+ if(aliasCert instanceof X509Certificate) {
+ certs.add(new TrustAnchor((X509Certificate) aliasCert, null));
+ }
+ }
+ return certs;
+ }
+
+ /**
+ * Get the certpaths that were used to sign the NBM content.
+ *
+ * @param nbmFile
+ * @return collection of CodeSigners, that were used to sign the non-signature
+ * entries of the NBM
+ * @throws IOException
+ * @throws SecurityException if JAR was tampered with or if the certificate
+ * chains are not consistent
+ */
+ public static Collection<CodeSigner> getNbmCertificates (File nbmFile) throws IOException, SecurityException {
+ Set<CodeSigner> certs = null;
+
+ // Empty means only the MANIFEST.MF is present - special cased to be in
+ // line with established behaviour
+ boolean empty = true;
+
+ // The idea:
+ // - iterate over all JAR entries
+ // - read each entry (as required by the JarFile specificiation for verification)
+ // - extract the certificate paths, that were used to sign the
+ // entry
+ // - compare it with the previously read entries. If they are not
+ // identical, raise a SecurityException.
+ //
+ // Excluded from the above algorithm are:
+ // - directory entries
+ // - files that are part of the signature (each entry in META-INF, that
+ // ends with .SF, .DSA, .RSA or .EC
+ try (JarFile jf = new JarFile(nbmFile)) {
+ for (JarEntry entry : Collections.list(jf.entries())) {
+ verifyEntry(jf, entry);
+ if ((! entry.isDirectory()) && (! isSignatureEntry(entry))) {
+ if(! entry.getName().equals("META-INF/MANIFEST.MF")) {
+ empty = false;
+ }
+ Set<CodeSigner> entryCerts = new HashSet<>();
+ CodeSigner[] codeSigners = entry.getCodeSigners();
+ if (codeSigners != null) {
+ for (CodeSigner cs : entry.getCodeSigners()) {
+ entryCerts.add(cs);
+ }
+ }
+ if(certs == null) {
+ certs = entryCerts;
+ } else if (! certs.equals(entryCerts)) {
+ throw new SecurityException("Inconsistent certificate paths used for signing");
+ }
+ }
+ }
}
return empty ? null : certs;
}
-
+
+ private static final Pattern SIGNATURE_PATTERN = Pattern.compile("META-INF/([^/]+)\\.(SF|DSA|RSA|EC)");
+ private static boolean isSignatureEntry(JarEntry je) {
+ return SIGNATURE_PATTERN.matcher(je.getName()).matches();
+ }
+
/**
* @throws SecurityException
*/
@SuppressWarnings("empty-statement")
- private static void verifyEntry (JarFile jf, JarEntry je) throws IOException {
+ private static void verifyEntry (JarFile jf, JarEntry je) throws IOException, SecurityException {
InputStream is = null;
try {
is = jf.getInputStream (je);
@@ -283,7 +366,7 @@
@Override
public void resultChanged (LookupEvent ev) {
- result = null;
+ keystoreCache.clear();
}
}
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogCache.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogCache.java
index ab94735..ba81ccd 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogCache.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogCache.java
@@ -216,7 +216,7 @@
DownloadListener nwl = new DownloadListener(sourceUrl, temp, allowZeroSize);
- NetworkAccess.Task task = NetworkAccess.createNetworkAcessTask (sourceUrl, AutoupdateSettings.getOpenConnectionTimeout (), nwl);
+ NetworkAccess.Task task = NetworkAccess.createNetworkAccessTask (sourceUrl, AutoupdateSettings.getOpenConnectionTimeout (), nwl, true);
task.waitFinished ();
nwl.notifyException ();
synchronized(getLock(cache)) {
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogFactory.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogFactory.java
index ae134a1..1f0ec51 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogFactory.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogFactory.java
@@ -62,6 +62,7 @@
public static final String ORIGINAL_ENABLED = "originalEnabled"; // NOI18N
public static final String ORIGINAL_CATEGORY_NAME = "originalCategoryName"; // NOI18N
public static final String ORIGINAL_CATEGORY_ICON_BASE = "originalCategoryIconBase"; // NOI18N
+ public static final String ORIGINAL_TRUSTED = "originalTrusted"; // NOI18N
public static UpdateProvider createUpdateProvider (FileObject fo) {
String sKey = (String) fo.getAttribute ("url_key"); // NOI18N
@@ -143,6 +144,12 @@
providerPreferences.putBoolean (ORIGINAL_ENABLED, en);
}
+ Boolean trusted = (Boolean) fo.getAttribute("trusted"); // NOI18N
+ if(trusted != null) {
+ au_catalog.setTrusted(trusted);
+ providerPreferences.putBoolean(ORIGINAL_TRUSTED, trusted);
+ }
+
return au_catalog;
}
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogParser.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogParser.java
index d95e1b4..a6d737f 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogParser.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogParser.java
@@ -34,7 +34,6 @@
import java.util.zip.GZIPInputStream;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
-import org.netbeans.Module;
import org.netbeans.modules.autoupdate.services.Trampoline;
import org.netbeans.modules.autoupdate.services.UpdateLicenseImpl;
import org.netbeans.modules.autoupdate.services.Utilities;
@@ -52,6 +51,7 @@
private final AutoupdateCatalogProvider provider;
private final EntityResolver entityResolver;
private final URI baseUri;
+ private final List<MessageDigestValue> messageDigestsBuffer = new ArrayList<>();
private AutoupdateCatalogParser (Map<String, UpdateItem> items, AutoupdateCatalogProvider provider, URI base) {
this.items = items;
@@ -111,7 +111,7 @@
private static enum ELEMENTS {
module_updates, module_group, notification, content_description, module, description,
- module_notification, external_package, manifest, l10n, license
+ module_notification, external_package, manifest, l10n, license, message_digest
}
private static final String MODULE_UPDATES_ATTR_TIMESTAMP = "timestamp"; // NOI18N
@@ -149,7 +149,10 @@
private static final String L10N_ATTR_MODULE_MAJOR_VERSION = "module_major_version"; // NOI18N
private static final String L10N_ATTR_LOCALIZED_MODULE_NAME = "OpenIDE-Module-Name"; // NOI18N
private static final String L10N_ATTR_LOCALIZED_MODULE_DESCRIPTION = "OpenIDE-Module-Long-Description"; // NOI18N
-
+
+ private static final String MESSAGE_DIGEST_ATTR_ALGORITHM = "algorithm"; // NOI18N
+ private static final String MESSAGE_DIGEST_ATTR_VALUE = "value"; // NOI18N
+
private static String GZIP_EXTENSION = ".gz"; // NOI18N
private static String XML_EXTENSION = ".xml"; // NOI18N
private static String GZIP_MIME_TYPE = "application/x-gzip"; // NOI18N
@@ -359,6 +362,8 @@
currentLicense.pop ();
break;
+ case message_digest:
+ break;
default:
ERR.warning ("Unknown element " + qName);
}
@@ -454,6 +459,16 @@
map.put(attributes.getValue (LICENSE_ATTR_NAME), attributes.getValue (LICENSE_ATTR_URL));
currentLicense.push (map);
break;
+ case message_digest:
+ ModuleDescriptor desc2 = currentModule.peek ();
+ // At this point the manifest element must have been seen
+ UpdateItem ui = items.get (desc2.getId ());
+ UpdateItemImpl uiImpl = Trampoline.SPI.impl (ui);
+ uiImpl.getMessageDigests().add(new MessageDigestValue(
+ attributes.getValue(MESSAGE_DIGEST_ATTR_ALGORITHM),
+ attributes.getValue(MESSAGE_DIGEST_ATTR_VALUE)
+ ));
+ break;
default:
ERR.warning ("Unknown element " + qName);
}
@@ -511,6 +526,8 @@
private String catalogDate;
private String fragmentHost;
+
+ private List<MessageDigestValue> hashes;
private static ModuleDescriptor md = null;
@@ -608,7 +625,7 @@
return res;
}
- private void cleanUp (){
+ public void cleanUp (){
this.specVersion = null;
this.mf = null;
this.notification = null;
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogProvider.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogProvider.java
index a9ea300..326fd11 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogProvider.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/AutoupdateCatalogProvider.java
@@ -26,6 +26,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.autoupdate.UpdateUnitProvider.CATEGORY;
+import org.netbeans.modules.autoupdate.services.Trampoline;
import org.netbeans.spi.autoupdate.UpdateItem;
import org.netbeans.spi.autoupdate.UpdateProvider;
import org.openide.util.Parameters;
@@ -45,6 +46,7 @@
private ProviderCategory category;
private String contentDescription;
private boolean contentDescriptionInitialized;
+ private boolean trusted;
public AutoupdateCatalogProvider (String name, String displayName, URL updateCenter) {
this(name, displayName, updateCenter, ProviderCategory.forValue(CATEGORY.COMMUNITY));
@@ -117,6 +119,9 @@
synchronized(cache.getLock(toParse)) {
map = AutoupdateCatalogParser.getUpdateItems (toParse, this);
}
+ for(UpdateItem ui: map.values()) {
+ UpdateItemImpl impl = Trampoline.SPI.impl(ui);
+ }
descriptionInitialized = true;
return map;
}
@@ -158,4 +163,12 @@
public ProviderCategory getProviderCategory() {
return category;
}
+
+ public boolean isTrusted() {
+ return trusted;
+ }
+
+ public void setTrusted(boolean trusted) {
+ this.trusted = trusted;
+ }
}
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/MessageDigestValue.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/MessageDigestValue.java
new file mode 100644
index 0000000..1351ef9
--- /dev/null
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/MessageDigestValue.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.autoupdate.updateprovider;
+
+import java.util.Objects;
+
+public class MessageDigestValue {
+ private String algorithm;
+ private String value;
+
+ public MessageDigestValue() {
+ }
+
+ public MessageDigestValue(String algorithm, String value) {
+ this.algorithm = algorithm;
+ this.value = value;
+ }
+
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ public void setAlgorithm(String algorithm) {
+ this.algorithm = algorithm;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 3;
+ hash = 53 * hash + Objects.hashCode(this.algorithm);
+ hash = 53 * hash + Objects.hashCode(this.value);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final MessageDigestValue other = (MessageDigestValue) obj;
+ if (!Objects.equals(this.algorithm, other.algorithm)) {
+ return false;
+ }
+ if (!Objects.equals(this.value, other.value)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/NetworkAccess.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/NetworkAccess.java
index c79154a..1560eda 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/NetworkAccess.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/NetworkAccess.java
@@ -24,10 +24,6 @@
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
-import java.security.KeyManagementException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
@@ -40,12 +36,6 @@
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
import org.openide.util.Cancellable;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
@@ -63,19 +53,20 @@
private NetworkAccess () {}
- public static Task createNetworkAcessTask (URL url, int timeout, NetworkListener networkAcesssListener) {
- return new Task (url, timeout, networkAcesssListener);
+ public static Task createNetworkAccessTask (URL url, int timeout, NetworkListener networkAccessListener, boolean disableInsecureRedirects) {
+ return new Task (url, timeout, networkAccessListener, disableInsecureRedirects);
}
public static class Task implements Cancellable {
+ private final ExecutorService es = Executors.newSingleThreadExecutor();
+ private final boolean disableInsecureRedirects;
private URL url;
private int timeout;
private NetworkListener listener;
- private final ExecutorService es = Executors.newSingleThreadExecutor ();
private Future<InputStream> connect = null;
private RequestProcessor.Task rpTask = null;
- private Task (URL url, int timeout, NetworkListener listener) {
+ private Task (URL url, int timeout, NetworkListener listener, boolean disableInsecureRedirects) {
if (url == null) {
throw new IllegalArgumentException ("URL cannot be null.");
}
@@ -85,6 +76,7 @@
this.url = url;
this.timeout = timeout;
this.listener = listener;
+ this.disableInsecureRedirects = disableInsecureRedirects;
postTask ();
}
@@ -146,11 +138,7 @@
@Override
public InputStream call () throws Exception {
URLConnection conn = url.openConnection ();
- conn.setConnectTimeout (timeout);
- conn.setReadTimeout(timeout);
- if(conn instanceof HttpsURLConnection){
- NetworkAccess.initSSL((HttpsURLConnection) conn);
- }
+ configureConnection(conn, timeout);
// handle redirection here
int redirCount = 0;
@@ -181,13 +169,52 @@
}
};
}
-
+
@Override
public boolean cancel () {
- return connect.cancel (true);
+ return connect.cancel(true);
}
-
+
+ private URLConnection checkRedirect(URLConnection conn, int timeout) throws IOException {
+ if (conn instanceof HttpURLConnection) {
+ conn.connect();
+ int code = ((HttpURLConnection) conn).getResponseCode();
+ boolean isInsecure = "http".equalsIgnoreCase(conn.getURL().getProtocol());
+ if (code == HttpURLConnection.HTTP_MOVED_TEMP
+ || code == HttpURLConnection.HTTP_MOVED_PERM) {
+ // in case of redirection, try to obtain new URL
+ String redirUrl = conn.getHeaderField("Location"); //NOI18N
+ if (null != redirUrl && !redirUrl.isEmpty()) {
+ //create connection to redirected url and substitute original connection
+ URL redirectedUrl = new URL(redirUrl);
+ if (disableInsecureRedirects && (!isInsecure) && (!redirectedUrl.getProtocol().equalsIgnoreCase(conn.getURL().getProtocol()))) {
+ throw new IOException(String.format(
+ "Redirect from secure URL '%s' to '%s' blocked.",
+ conn.getURL().toExternalForm(),
+ redirectedUrl.toExternalForm()
+ ));
+ }
+ URLConnection connRedir = redirectedUrl.openConnection();
+ connRedir.setRequestProperty("User-Agent", "NetBeans"); // NOI18N
+ connRedir.setConnectTimeout(timeout);
+ connRedir.setReadTimeout(timeout);
+ return connRedir;
+ }
+ }
+ }
+ return conn;
+ }
+
+ private static void configureConnection(URLConnection conn, int timeout) {
+ if (conn instanceof HttpURLConnection) {
+ ((HttpURLConnection) conn).setInstanceFollowRedirects(false);
+ }
+ conn.setRequestProperty("User-Agent", "NetBeans");
+ conn.setConnectTimeout(timeout);
+ conn.setReadTimeout(timeout);
+ }
}
+
private interface SizedConnection extends Callable<InputStream> {
public int getContentLength();
}
@@ -197,67 +224,4 @@
public void accessTimeOut ();
public void notifyException (Exception x);
}
-
- public static void initSSL(HttpURLConnection httpCon) throws IOException {
- if (httpCon instanceof HttpsURLConnection) {
- HttpsURLConnection https = (HttpsURLConnection) httpCon;
-
- try {
- TrustManager[] trustAllCerts = new TrustManager[]{
- new X509TrustManager() {
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0];
- }
-
- @Override
- public void checkClientTrusted(X509Certificate[] certs, String authType) {
- }
-
- @Override
- public void checkServerTrusted(X509Certificate[] certs, String authType) {
- }
- }};
- SSLContext sslContext = SSLContext.getInstance("SSL"); // NOI18N
- sslContext.init(null, trustAllCerts, new SecureRandom());
- https.setHostnameVerifier(new HostnameVerifier() {
- @Override
- public boolean verify(String hostname, SSLSession session) {
- return true;
- }
- });
- https.setSSLSocketFactory(sslContext.getSocketFactory());
- } catch (KeyManagementException ex) {
- throw new IOException(ex);
- } catch (NoSuchAlgorithmException ex) {
- throw new IOException(ex);
- }
- }
- }
-
- private static URLConnection checkRedirect(URLConnection conn, int timeout) throws IOException {
- if (conn instanceof HttpURLConnection) {
- conn.connect();
- int code = ((HttpURLConnection) conn).getResponseCode();
- if (code == HttpURLConnection.HTTP_MOVED_TEMP
- || code == HttpURLConnection.HTTP_MOVED_PERM) {
- // in case of redirection, try to obtain new URL
- String redirUrl = conn.getHeaderField("Location"); //NOI18N
- if (null != redirUrl && !redirUrl.isEmpty()) {
- //create connection to redirected url and substitute original conn
- URL redirectedUrl = new URL(redirUrl);
- URLConnection connRedir = redirectedUrl.openConnection();
- // XXX is this neede
- connRedir.setRequestProperty("User-Agent", "NetBeans"); // NOI18N
- connRedir.setConnectTimeout(timeout);
- connRedir.setReadTimeout(timeout);
- if (connRedir instanceof HttpsURLConnection) {
- NetworkAccess.initSSL((HttpsURLConnection) connRedir);
- }
- return connRedir;
- }
- }
- }
- return conn;
- }
}
diff --git a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/UpdateItemImpl.java b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/UpdateItemImpl.java
index 0f639dd..30d410c 100644
--- a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/UpdateItemImpl.java
+++ b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/updateprovider/UpdateItemImpl.java
@@ -19,6 +19,8 @@
package org.netbeans.modules.autoupdate.updateprovider;
+import java.util.ArrayList;
+import java.util.List;
import org.netbeans.modules.autoupdate.services.UpdateLicenseImpl;
import org.netbeans.spi.autoupdate.UpdateItem;
@@ -28,6 +30,8 @@
*/
public abstract class UpdateItemImpl {
private UpdateItem originalUpdateItem;
+ private List<MessageDigestValue> messageDigests = new ArrayList<>();
+ private boolean catalogTrusted = false;
/** Creates a new instance of UpdateItemImpl */
UpdateItemImpl () {
@@ -64,4 +68,20 @@
public String getFragmentHost() {
return null;
}
+
+ public List<MessageDigestValue> getMessageDigests() {
+ return messageDigests;
+ }
+
+ public void setMessageDigests(List<MessageDigestValue> messageDigests) {
+ this.messageDigests = new ArrayList<>(messageDigests);
+ }
+
+ public boolean isCatalogTrusted() {
+ return catalogTrusted;
+ }
+
+ public void setCatalogTrusted(boolean catalogTrusted) {
+ this.catalogTrusted = catalogTrusted;
+ }
}
diff --git a/platform/autoupdate.services/src/org/netbeans/spi/autoupdate/KeyStoreProvider.java b/platform/autoupdate.services/src/org/netbeans/spi/autoupdate/KeyStoreProvider.java
index 5f2b3ad..e94fb1d 100644
--- a/platform/autoupdate.services/src/org/netbeans/spi/autoupdate/KeyStoreProvider.java
+++ b/platform/autoupdate.services/src/org/netbeans/spi/autoupdate/KeyStoreProvider.java
@@ -29,9 +29,52 @@
* @author Jiri Rechtacek
*/
public interface KeyStoreProvider {
-
+ /**
+ * TrustLevel describes the level of trust, that a {@link KeyStoreProvider}
+ * assigns to the provided keystore.
+ *
+ * @since 1.61
+ */
+ public enum TrustLevel {
+ /**
+ * Unlimited trust - modules signed with certificates in this store
+ * will be installed without further user requests. This level is by
+ * default used for the update centers of the IDE itself.
+ */
+ TRUST,
+ /**
+ * Unlimited trust - modules signed with certificates in this store
+ * will be installed without further user requests. This level is by
+ * default used for the update centers of the IDE itself. It differes
+ * from {@link TRUST} in that, these certificates are subject to a
+ * {@code CertPathValidator}
+ */
+ TRUST_CA,
+ /**
+ * Plugins signed with certificates from this store will show up as
+ * "Signed and valid".
+ */
+ VALIDATE,
+ /**
+ * Plugins signed with certificates created by these certificates
+ * will show up as "Signed and valid". While certificates provided by
+ * {@link VALIDATE} not subject to PKIX checking, these certificates
+ * are run through a {@code CertPathValidator}.
+ */
+ VALIDATE_CA
+ }
+
/**
* @return KeyStore
*/
public KeyStore getKeyStore ();
+
+ /**
+ * @return TrustLevel that is provided by the keystore this
+ * {@link KeyStoreProvider} provides
+ * @since 1.61
+ */
+ default TrustLevel getTrustLevel() {
+ return TrustLevel.TRUST;
+ }
}
diff --git a/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/MessageDigestCheckerTest.java b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/MessageDigestCheckerTest.java
new file mode 100644
index 0000000..79af4ab
--- /dev/null
+++ b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/MessageDigestCheckerTest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.autoupdate.services;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class MessageDigestCheckerTest {
+
+ public MessageDigestCheckerTest() {
+ }
+
+ @Test
+ public void testHexEncode() throws NoSuchAlgorithmException {
+ MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
+ byte[] hash = sha512.digest(new byte[] {});
+ assertEquals(
+ "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
+ MessageDigestChecker.hexEncode(hash));
+ }
+
+ @Test
+ public void testHexDecode() throws NoSuchAlgorithmException {
+ MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
+ byte[] hash = sha512.digest(new byte[] {});
+ assertArrayEquals(
+ hash,
+ MessageDigestChecker.hexDecode("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"));
+ }
+}
diff --git a/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/VerifyFileTest.java b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/VerifyFileTest.java
index 9b6da19..7813bc5 100644
--- a/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/VerifyFileTest.java
+++ b/platform/autoupdate.services/test/unit/src/org/netbeans/modules/autoupdate/services/VerifyFileTest.java
@@ -25,20 +25,28 @@
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
+import java.security.CodeSigner;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.Certificate;
+import java.security.cert.TrustAnchor;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.autoupdate.TestUtils;
import org.netbeans.junit.NbTestCase;
+import static org.netbeans.modules.autoupdate.services.Utilities.VERIFICATION_RESULT_COMPARATOR;
+
public class VerifyFileTest extends NbTestCase {
private static final Logger LOG = Logger.getLogger(VerifyFileTest.class.getName());
private KeyStore ks;
+ private KeyStore validateKs;
public VerifyFileTest(String testName) {
super(testName);
@@ -48,9 +56,14 @@
protected void setUp() throws Exception {
URL urlToKS = TestUtils.class.getResource("data/test-keystore.jks");
assertNotNull(urlToKS);
+ URL urlToValidateKS = TestUtils.class.getResource("data/test-validate-keystore.jks");
+ assertNotNull(urlToValidateKS);
File ksFile = org.openide.util.Utilities.toFile(urlToKS.toURI());
assertTrue(ksFile.exists());
+ File validateKsFile = org.openide.util.Utilities.toFile(urlToValidateKS.toURI());
+ assertTrue(validateKsFile.exists());
ks = getKeyStore(ksFile, "password");
+ validateKs = getKeyStore(validateKsFile, "password");
}
private String doVerification(String path) throws URISyntaxException, IOException, KeyStoreException {
@@ -58,9 +71,36 @@
assertNotNull(urlToFile);
File jar = org.openide.util.Utilities.toFile(urlToFile.toURI());
assertTrue(jar.exists());
- Collection<Certificate> nbmCertificates = Utilities.getNbmCertificates(jar);
- Collection<Certificate> trustedCertificates = Utilities.getCertificates(ks);
- return Utilities.verifyCertificates(nbmCertificates, trustedCertificates);
+ String res = null;
+ try {
+ Collection<CodeSigner> nbmCerts = Utilities.getNbmCertificates(jar);
+ Collection<Certificate> trustedCerts = Utilities.getCertificates(ks);
+ Set<TrustAnchor> trustedCACerts = Collections.EMPTY_SET;
+ Collection<Certificate> validationAnchors = new HashSet<>(Utilities.getCertificates(validateKs));
+ Set<TrustAnchor> validationCACerts = Collections.EMPTY_SET;
+ if (nbmCerts == null) {
+ res = Utilities.N_A;
+ } else if (nbmCerts.isEmpty()) {
+ res = Utilities.UNSIGNED;
+ } else {
+ // Iterate all certpaths that can be considered for the NBM
+ // choose the certpath, that has the highest trust level
+ // TRUSTED -> SIGNATURE_VERIFIED -> SIGNATURE_UNVERIFIED -> UNSIGNED
+ // or comes first
+ for (CodeSigner cp : nbmCerts) {
+ String localRes = Utilities.verifyCertificates(cp, trustedCerts, trustedCACerts, validationAnchors, validationCACerts);
+ if (res == null
+ || VERIFICATION_RESULT_COMPARATOR.compare(res, localRes) > 0) {
+ res = localRes;
+ }
+ }
+ }
+ } catch (SecurityException ex) {
+ LOG.log(Level.INFO, "The content of the jar/nbm has been modified or certificate paths were inconsistent - " + ex.getMessage(), ex);
+ res = Utilities.MODIFIED;
+ }
+
+ return res;
}
@SuppressWarnings("unchecked")
@@ -86,6 +126,18 @@
assertEquals(Utilities.TRUSTED, doVerification("data/dummy-signed-twice.jar"));
}
+ public void testValidatedSigned() throws MalformedURLException, URISyntaxException, IOException, KeyStoreException {
+ assertEquals(Utilities.SIGNATURE_VERIFIED, doVerification("data/dummy-validated.jar"));
+ }
+
+ public void testUnvalidatedSigned() throws MalformedURLException, URISyntaxException, IOException, KeyStoreException {
+ assertEquals(Utilities.SIGNATURE_UNVERIFIED, doVerification("data/dummy-unvalidated.jar"));
+ }
+
+ public void testUnsignedPartiallySigned() throws MalformedURLException, URISyntaxException, IOException, KeyStoreException {
+ assertEquals(Utilities.MODIFIED, doVerification("data/dummy-partial-signed.jar"));
+ }
+
private static KeyStore getKeyStore(File file, String password) {
if (file == null) {
return null;
diff --git a/platform/autoupdate.ui/manifest.mf b/platform/autoupdate.ui/manifest.mf
index 40be84a..d55d1d8 100644
--- a/platform/autoupdate.ui/manifest.mf
+++ b/platform/autoupdate.ui/manifest.mf
@@ -2,6 +2,6 @@
OpenIDE-Module: org.netbeans.modules.autoupdate.ui
OpenIDE-Module-Install: org/netbeans/modules/autoupdate/ui/actions/Installer.class
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/autoupdate/ui/resources/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.52
+OpenIDE-Module-Specification-Version: 1.53
AutoUpdate-Show-In-Client: false
AutoUpdate-Essential-Module: true
diff --git a/platform/autoupdate.ui/nbproject/project.xml b/platform/autoupdate.ui/nbproject/project.xml
index 8615fb5..d09a3ce 100644
--- a/platform/autoupdate.ui/nbproject/project.xml
+++ b/platform/autoupdate.ui/nbproject/project.xml
@@ -56,7 +56,7 @@
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>1.57</specification-version>
+ <specification-version>1.62</specification-version>
</run-dependency>
</dependency>
<dependency>
@@ -101,14 +101,6 @@
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.openide.util.ui</code-name-base>
- <build-prerequisite/>
- <compile-dependency/>
- <run-dependency>
- <specification-version>9.3</specification-version>
- </run-dependency>
- </dependency>
- <dependency>
<code-name-base>org.openide.util</code-name-base>
<build-prerequisite/>
<compile-dependency/>
@@ -125,6 +117,14 @@
</run-dependency>
</dependency>
<dependency>
+ <code-name-base>org.openide.util.ui</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <specification-version>9.3</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
<code-name-base>org.openide.windows</code-name-base>
<build-prerequisite/>
<compile-dependency/>
diff --git a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/Bundle.properties b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/Bundle.properties
index af27211..e718266 100644
--- a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/Bundle.properties
+++ b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/Bundle.properties
@@ -211,3 +211,4 @@
Unit_InternalUpdates_Title=Internal Updates
ProblemPanel.cbShowAgain.text=&Don't show this message again
SettingsTab.lLocation.text=Plugin &Install Location:
+UpdateUnitProviderPanel.cbTrusted.text=&Trust update center fully and allow automatic installations
diff --git a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/ProblemPanel.form b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/ProblemPanel.form
index dce5dfb..bc654d7 100644
--- a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/ProblemPanel.form
+++ b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/ProblemPanel.form
@@ -22,6 +22,11 @@
-->
<Form version="1.4" maxVersion="1.4" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+ <Properties>
+ <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+ <Dimension value="[700, 400]"/>
+ </Property>
+ </Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/netbeans/modules/autoupdate/ui/wizards/Bundle.properties" key="NetworkProblemPanel_ACD" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
@@ -42,16 +47,16 @@
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="cbShowAgain" alignment="1" max="32767" attributes="0"/>
- <Component id="jScrollPane1" alignment="0" pref="425" max="32767" attributes="0"/>
+ <Component id="jScrollPane1" alignment="0" pref="682" max="32767" attributes="0"/>
<Component id="jScrollPane2" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
- <Component id="jScrollPane2" pref="106" max="32767" attributes="0"/>
+ <Component id="jScrollPane2" pref="140" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
- <Component id="jScrollPane1" pref="169" max="32767" attributes="0"/>
+ <Component id="jScrollPane1" pref="201" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="cbShowAgain" min="-2" max="-2" attributes="0"/>
</Group>
@@ -79,11 +84,7 @@
<Component class="javax.swing.JTextPane" name="tpMessage">
<Properties>
<Property name="editable" type="boolean" value="false"/>
- <Property name="contentType" type="java.lang.String" value="text/html" noResource="true"/>
<Property name="opaque" type="boolean" value="false"/>
- <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
- <Dimension value="[400, 100]"/>
- </Property>
</Properties>
</Component>
</SubComponents>
@@ -98,11 +99,7 @@
<Component class="javax.swing.JTextPane" name="taTitle">
<Properties>
<Property name="editable" type="boolean" value="false"/>
- <Property name="contentType" type="java.lang.String" value="text/html" noResource="true"/>
<Property name="opaque" type="boolean" value="false"/>
- <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
- <Dimension value="[200, 100]"/>
- </Property>
</Properties>
</Component>
</SubComponents>
diff --git a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/ProblemPanel.java b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/ProblemPanel.java
index 6876782..cc4ab13 100644
--- a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/ProblemPanel.java
+++ b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/ProblemPanel.java
@@ -176,6 +176,8 @@
jScrollPane2 = new javax.swing.JScrollPane();
taTitle = new javax.swing.JTextPane();
+ setPreferredSize(new java.awt.Dimension(700, 400));
+
org.openide.awt.Mnemonics.setLocalizedText(cbShowAgain, org.openide.util.NbBundle.getMessage(ProblemPanel.class, "ProblemPanel.cbShowAgain.text")); // NOI18N
cbShowAgain.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
@@ -184,15 +186,11 @@
});
tpMessage.setEditable(false);
- tpMessage.setContentType("text/html"); // NOI18N
tpMessage.setOpaque(false);
- tpMessage.setPreferredSize(new java.awt.Dimension(400, 100));
jScrollPane1.setViewportView(tpMessage);
taTitle.setEditable(false);
- taTitle.setContentType("text/html"); // NOI18N
taTitle.setOpaque(false);
- taTitle.setPreferredSize(new java.awt.Dimension(200, 100));
jScrollPane2.setViewportView(taTitle);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
@@ -200,15 +198,15 @@
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(cbShowAgain, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
- .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 425, Short.MAX_VALUE)
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 682, Short.MAX_VALUE)
.addComponent(jScrollPane2)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
- .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 106, Short.MAX_VALUE)
+ .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 140, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 169, Short.MAX_VALUE)
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 201, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cbShowAgain))
);
diff --git a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/SettingsTab.java b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/SettingsTab.java
index 40e33dd..49024ee 100644
--- a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/SettingsTab.java
+++ b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/SettingsTab.java
@@ -512,6 +512,7 @@
}
provider.setEnable (panel.isActive ());
+ provider.setTrusted(panel.isTrusted());
if (panel.isActive ()) {
// was not enabled and will be -> add it to model and read its content
refreshProvider (provider, forceRead);
@@ -566,7 +567,8 @@
final UpdateUnitProviderPanel panel = new UpdateUnitProviderPanel(provider.isEnabled(),
provider.getDisplayName(), // display name
provider.getProviderURL().toExternalForm(), // URL
- true); // editing
+ true, // editing
+ provider.isTrusted());
DialogDescriptor descriptor = getCustomizerDescriptor(panel);
panel.getOKButton().addActionListener(new ActionListener(){
@Override
@@ -628,6 +630,7 @@
final UpdateUnitProviderPanel panel = new UpdateUnitProviderPanel(true,
NbBundle.getMessage(SettingsTab.class, "SettingsTab_NewProviderName"), // NOI18N
NbBundle.getMessage(SettingsTab.class, "SettingsTab_NewProviderURL"), // NOI18N
+ false,
false);
DialogDescriptor descriptor = getCustomizerDescriptor(panel);
panel.getOKButton().addActionListener(new ActionListener(){
diff --git a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UpdateUnitProviderPanel.form b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UpdateUnitProviderPanel.form
index bf2d7c3..495a23c 100644
--- a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UpdateUnitProviderPanel.form
+++ b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UpdateUnitProviderPanel.form
@@ -49,7 +49,13 @@
<Group type="103" groupAlignment="0" attributes="0">
<Component id="tfURL" alignment="0" pref="474" max="32767" attributes="0"/>
<Component id="tfName" pref="474" max="32767" attributes="0"/>
- <Component id="cbActive" alignment="0" min="-2" max="-2" attributes="0"/>
+ <Group type="102" attributes="0">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="cbTrusted" min="-2" max="-2" attributes="0"/>
+ <Component id="cbActive" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
+ </Group>
</Group>
</Group>
</Group>
@@ -72,7 +78,9 @@
<Component id="lURL" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="tfURL" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
- <EmptySpace pref="20" max="32767" attributes="0"/>
+ <EmptySpace type="separate" max="-2" attributes="0"/>
+ <Component id="cbTrusted" min="-2" max="-2" attributes="0"/>
+ <EmptySpace max="32767" attributes="0"/>
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
@@ -142,5 +150,20 @@
</Component>
<Component class="javax.swing.JLabel" name="errorLabel">
</Component>
+ <Component class="javax.swing.JCheckBox" name="cbTrusted">
+ <Properties>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/autoupdate/ui/Bundle.properties" key="UpdateUnitProviderPanel.cbTrusted.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+ <Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
+ <EmptyBorder bottom="0" left="0" right="0" top="0"/>
+ </Border>
+ </Property>
+ </Properties>
+ <AuxValues>
+ <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+ </AuxValues>
+ </Component>
</SubComponents>
</Form>
diff --git a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UpdateUnitProviderPanel.java b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UpdateUnitProviderPanel.java
index 98e2dc1..c419202 100644
--- a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UpdateUnitProviderPanel.java
+++ b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UpdateUnitProviderPanel.java
@@ -50,7 +50,7 @@
private final Icon errorIcon = ImageUtilities.loadImageIcon("org/netbeans/modules/autoupdate/ui/resources/error.png", false);
/** Creates new form UpdateUnitProviderPanel */
- public UpdateUnitProviderPanel(boolean isActive, String name, String url, boolean editing) {
+ public UpdateUnitProviderPanel(boolean isActive, String name, String url, boolean editing, boolean isTrusted) {
isEdit = editing;
originalName = name;
initComponents();
@@ -58,6 +58,7 @@
tfURL.setText(url);
tfName.setText(name);
cbActive.setSelected(isActive);
+ cbTrusted.setSelected(isTrusted);
getAccessibleContext().setAccessibleName("ACN_UpdateCenterCustomizer");
getAccessibleContext().setAccessibleDescription("ACD_UpdateCenterCustomizer");
}
@@ -239,7 +240,11 @@
public String getProviderURL() {
return tfURL.getText().trim();
}
-
+
+ public boolean isTrusted() {
+ return cbTrusted.isSelected();
+ }
+
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
@@ -254,6 +259,7 @@
tfURL = new javax.swing.JTextField();
cbActive = new javax.swing.JCheckBox();
errorLabel = new javax.swing.JLabel();
+ cbTrusted = new javax.swing.JCheckBox();
lName.setLabelFor(tfName);
org.openide.awt.Mnemonics.setLocalizedText(lName, org.openide.util.NbBundle.getMessage(UpdateUnitProviderPanel.class, "UpdateUnitProviderPanel.lName.text")); // NOI18N
@@ -264,6 +270,9 @@
org.openide.awt.Mnemonics.setLocalizedText(cbActive, org.openide.util.NbBundle.getMessage(UpdateUnitProviderPanel.class, "UpdateUnitProviderPanel.cbActive.text")); // NOI18N
cbActive.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ org.openide.awt.Mnemonics.setLocalizedText(cbTrusted, org.openide.util.NbBundle.getMessage(UpdateUnitProviderPanel.class, "UpdateUnitProviderPanel.cbTrusted.text")); // NOI18N
+ cbTrusted.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
@@ -280,7 +289,11 @@
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(tfURL, javax.swing.GroupLayout.DEFAULT_SIZE, 474, Short.MAX_VALUE)
.addComponent(tfName, javax.swing.GroupLayout.DEFAULT_SIZE, 474, Short.MAX_VALUE)
- .addComponent(cbActive))))
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(cbTrusted)
+ .addComponent(cbActive))
+ .addGap(0, 0, Short.MAX_VALUE)))))
.addContainerGap())
);
layout.setVerticalGroup(
@@ -296,7 +309,9 @@
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lURL)
.addComponent(tfURL, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 20, Short.MAX_VALUE)
+ .addGap(18, 18, 18)
+ .addComponent(cbTrusted)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(errorLabel)
.addContainerGap())
);
@@ -309,6 +324,7 @@
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox cbActive;
+ private javax.swing.JCheckBox cbTrusted;
private javax.swing.JLabel errorLabel;
private javax.swing.JLabel lName;
private javax.swing.JLabel lURL;
diff --git a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/Bundle.properties b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/Bundle.properties
index e0f4999..7086f1f 100644
--- a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/Bundle.properties
+++ b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/Bundle.properties
@@ -164,4 +164,5 @@
InstallStep_DownloadProblem_SomePlugins=selected plugins
LicenseApprovalPanel.lbPlugins.text=Plugins:
OperationDescriptionStep_NoteCachesNotBuilt=<br><hr><br>\
- <b>Note:</b> Missing modules may be published on Update Centers. Please run "<b>Check for Newest</b>" from the "<b>Available Plugins</b>" tab.
\ No newline at end of file
+ <b>Note:</b> Missing modules may be published on Update Centers. Please run "<b>Check for Newest</b>" from the "<b>Available Plugins</b>" tab.
+ValidationWarningPanel.taError.text=Error: There were plugins, that were modified or damaged, Installation can't continue.
diff --git a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/ValidationWarningPanel.form b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/ValidationWarningPanel.form
index 70d3f6c..09ee81d 100644
--- a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/ValidationWarningPanel.form
+++ b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/ValidationWarningPanel.form
@@ -22,6 +22,20 @@
-->
<Form version="1.4" maxVersion="1.4" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+ <Properties>
+ <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+ <Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
+ <EmptyBorder bottom="6" left="6" right="6" top="6"/>
+ </Border>
+ </Property>
+ <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+ <Dimension value="[480, 400]"/>
+ </Property>
+ <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+ <Dimension value="[480, 400]"/>
+ </Property>
+ <Property name="requestFocusEnabled" type="boolean" value="false"/>
+ </Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/netbeans/modules/autoupdate/ui/wizards/Bundle.properties" key="ValidationWarningPanel_ACD" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
@@ -36,36 +50,10 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+ <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,4,29,0,0,1,-30"/>
</AuxValues>
- <Layout>
- <DimensionLayout dim="0">
- <Group type="103" groupAlignment="0" attributes="0">
- <Group type="102" attributes="0">
- <EmptySpace min="-2" max="-2" attributes="0"/>
- <Group type="103" groupAlignment="0" attributes="0">
- <Component id="spPlugins" alignment="0" max="32767" attributes="0"/>
- <Component id="taHead" alignment="0" max="32767" attributes="2"/>
- <Component id="taWarning" alignment="1" max="32767" attributes="2"/>
- </Group>
- <EmptySpace min="-2" max="-2" attributes="0"/>
- </Group>
- </Group>
- </DimensionLayout>
- <DimensionLayout dim="1">
- <Group type="103" groupAlignment="0" attributes="0">
- <Group type="102" alignment="0" attributes="0">
- <EmptySpace min="-2" max="-2" attributes="0"/>
- <Component id="taHead" min="-2" pref="57" max="-2" attributes="0"/>
- <EmptySpace min="-2" max="-2" attributes="0"/>
- <Component id="spPlugins" pref="196" max="32767" attributes="0"/>
- <EmptySpace min="-2" max="-2" attributes="0"/>
- <Component id="taWarning" pref="58" max="32767" attributes="0"/>
- <EmptySpace min="-2" max="-2" attributes="0"/>
- </Group>
- </Group>
- </DimensionLayout>
- </Layout>
+ <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JTextArea" name="taHead">
<Properties>
@@ -82,8 +70,37 @@
<ResourceString bundle="org/netbeans/modules/autoupdate/ui/wizards/Bundle.properties" key="ValidationWarningPanel_taHead_ACN" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
</Property>
</AccessibilityProperties>
+ <Constraints>
+ <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+ <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="3" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
+ </Constraint>
+ </Constraints>
+ </Component>
+ <Component class="javax.swing.JTextArea" name="taError">
+ <Properties>
+ <Property name="editable" type="boolean" value="false"/>
+ <Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+ <Color blue="12" green="0" red="cc" type="rgb"/>
+ </Property>
+ <Property name="lineWrap" type="boolean" value="true"/>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/autoupdate/ui/wizards/Bundle.properties" key="ValidationWarningPanel.taError.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ <Property name="wrapStyleWord" type="boolean" value="true"/>
+ <Property name="opaque" type="boolean" value="false"/>
+ </Properties>
+ <Constraints>
+ <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+ <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="3" insetsRight="0" anchor="512" weightX="0.0" weightY="0.0"/>
+ </Constraint>
+ </Constraints>
</Component>
<Container class="javax.swing.JScrollPane" name="spPlugins">
+ <Constraints>
+ <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+ <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="3" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
+ </Constraint>
+ </Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
@@ -114,6 +131,11 @@
<ResourceString bundle="org/netbeans/modules/autoupdate/ui/wizards/Bundle.properties" key="ValidationWarningPanel_taWarning_Text_ACD" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
</Property>
</AccessibilityProperties>
+ <Constraints>
+ <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+ <GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="3" insetsRight="0" anchor="512" weightX="0.0" weightY="0.0"/>
+ </Constraint>
+ </Constraints>
</Component>
</SubComponents>
</Form>
diff --git a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/ValidationWarningPanel.java b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/ValidationWarningPanel.java
index a60b35d..e413163 100644
--- a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/ValidationWarningPanel.java
+++ b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/ValidationWarningPanel.java
@@ -44,6 +44,8 @@
taHead.setBorder(BorderFactory.createEmptyBorder());
taWarning.setBackground( new Color(0,0,0,0) );
taWarning.setBorder(BorderFactory.createEmptyBorder());
+ taError.setVisible(! modified.isEmpty());
+ taHead.setVisible(modified.isEmpty());
postInitComponents (signedVerified, signedUnverified, unsigned, modified, total);
}
@@ -51,6 +53,7 @@
DefaultMutableTreeNode signedAndValidNode = new DefaultMutableTreeNode("Signed and Valid (" + signedVerified.size() + ")");
DefaultMutableTreeNode selfSignedNode = new DefaultMutableTreeNode("Self signed (" + signedUnverified.size() + ")");
DefaultMutableTreeNode unsignedNode = new DefaultMutableTreeNode("Unsigned (" + unsigned.size() + ")");
+ DefaultMutableTreeNode modifiedNode = new DefaultMutableTreeNode("Modified/Damaged (" + modified.size() + ")");
for (UpdateElement el : signedVerified) {
signedAndValidNode.add(new DefaultMutableTreeNode(el.getDisplayName()));
@@ -66,7 +69,12 @@
unsignedNode.add(new DefaultMutableTreeNode(el.getDisplayName()));
}
pluginModelRoot.add(unsignedNode);
-
+
+ for (UpdateElement el : modified) {
+ modifiedNode.add(new DefaultMutableTreeNode(el.getDisplayName()));
+ }
+ pluginModelRoot.add(modifiedNode);
+
pluginsModel.reload();
int requiresAttention = signedVerified.size() + signedUnverified.size() + unsigned.size();
@@ -78,6 +86,10 @@
taHead.setText(org.openide.util.NbBundle.getMessage(ValidationWarningPanel.class, "ValidationWarningPanel_taHead_NotTrustedTextSg",
requiresAttention, total));
}
+
+ if(modified.size() > 0) {
+
+ }
}
public String getSelectedNode() {
@@ -96,52 +108,73 @@
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
+ java.awt.GridBagConstraints gridBagConstraints;
taHead = new javax.swing.JTextArea();
+ taError = new javax.swing.JTextArea();
spPlugins = new javax.swing.JScrollPane();
tPlugins = new javax.swing.JTree();
taWarning = new javax.swing.JTextArea();
+ setBorder(javax.swing.BorderFactory.createEmptyBorder(6, 6, 6, 6));
+ setMinimumSize(new java.awt.Dimension(480, 400));
+ setPreferredSize(new java.awt.Dimension(480, 400));
+ setRequestFocusEnabled(false);
+ setLayout(new java.awt.GridBagLayout());
+
taHead.setEditable(false);
taHead.setLineWrap(true);
taHead.setWrapStyleWord(true);
taHead.setOpaque(false);
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 0;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+ gridBagConstraints.insets = new java.awt.Insets(0, 0, 3, 0);
+ add(taHead, gridBagConstraints);
+ taHead.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(ValidationWarningPanel.class, "ValidationWarningPanel_taHead_ACN")); // NOI18N
+ taHead.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(ValidationWarningPanel.class, "ValidationWarningPanel_taHead_ACN")); // NOI18N
+
+ taError.setEditable(false);
+ taError.setForeground(new java.awt.Color(204, 0, 18));
+ taError.setLineWrap(true);
+ taError.setText(org.openide.util.NbBundle.getMessage(ValidationWarningPanel.class, "ValidationWarningPanel.taError.text")); // NOI18N
+ taError.setWrapStyleWord(true);
+ taError.setOpaque(false);
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 1;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.BASELINE_LEADING;
+ gridBagConstraints.insets = new java.awt.Insets(0, 0, 3, 0);
+ add(taError, gridBagConstraints);
tPlugins.setModel(pluginsModel);
spPlugins.setViewportView(tPlugins);
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 2;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+ gridBagConstraints.weightx = 1.0;
+ gridBagConstraints.weighty = 1.0;
+ gridBagConstraints.insets = new java.awt.Insets(3, 0, 3, 0);
+ add(spPlugins, gridBagConstraints);
+
taWarning.setEditable(false);
taWarning.setLineWrap(true);
taWarning.setText(org.openide.util.NbBundle.getMessage(ValidationWarningPanel.class, "ValidationWarningPanel_taWarning_Text")); // NOI18N
taWarning.setWrapStyleWord(true);
taWarning.setOpaque(false);
-
- javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
- this.setLayout(layout);
- layout.setHorizontalGroup(
- layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(layout.createSequentialGroup()
- .addContainerGap()
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(spPlugins, javax.swing.GroupLayout.DEFAULT_SIZE, 398, Short.MAX_VALUE)
- .addComponent(taHead)
- .addComponent(taWarning, javax.swing.GroupLayout.Alignment.TRAILING))
- .addContainerGap())
- );
- layout.setVerticalGroup(
- layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(layout.createSequentialGroup()
- .addContainerGap()
- .addComponent(taHead, javax.swing.GroupLayout.PREFERRED_SIZE, 57, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(spPlugins, javax.swing.GroupLayout.DEFAULT_SIZE, 196, Short.MAX_VALUE)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(taWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 58, Short.MAX_VALUE)
- .addContainerGap())
- );
-
- taHead.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(ValidationWarningPanel.class, "ValidationWarningPanel_taHead_ACN")); // NOI18N
- taHead.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(ValidationWarningPanel.class, "ValidationWarningPanel_taHead_ACN")); // NOI18N
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 3;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.BASELINE_LEADING;
+ gridBagConstraints.insets = new java.awt.Insets(3, 0, 3, 0);
+ add(taWarning, gridBagConstraints);
taWarning.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(ValidationWarningPanel.class, "ValidationWarningPanel_taWarning_Text_ACN")); // NOI18N
taWarning.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(ValidationWarningPanel.class, "ValidationWarningPanel_taWarning_Text_ACD")); // NOI18N
@@ -152,6 +185,7 @@
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane spPlugins;
private javax.swing.JTree tPlugins;
+ private javax.swing.JTextArea taError;
private javax.swing.JTextArea taHead;
private javax.swing.JTextArea taWarning;
// End of variables declaration//GEN-END:variables