diff options
Diffstat (limited to 'bundles')
174 files changed, 4991 insertions, 1462 deletions
diff --git a/bundles/org.eclipse.equinox.frameworkadmin.equinox/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.frameworkadmin.equinox/META-INF/MANIFEST.MF index 655b1355f..5ad6e58c5 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin.equinox/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.frameworkadmin.equinox/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.frameworkadmin.equinox;singleton:=true -Bundle-Version: 1.2.100.qualifier +Bundle-Version: 1.2.200.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Import-Package: org.eclipse.equinox.frameworkadmin;version="[2.0.0,3.0.0)", @@ -20,7 +20,7 @@ Import-Package: org.eclipse.equinox.frameworkadmin;version="[2.0.0,3.0.0)", org.osgi.util.tracker;version="1.3.0" Export-Package: org.eclipse.equinox.internal.frameworkadmin.equinox;x-friends:="org.eclipse.equinox.p2.publisher,org.eclipse.equinox.p2.publisher.eclipse,org.eclipse.equinox.simpleconfigurator.manipulator", org.eclipse.equinox.internal.frameworkadmin.equinox.utils;x-friends:="org.eclipse.equinox.p2.publisher.eclipse" -Require-Bundle: org.eclipse.equinox.common +Require-Bundle: org.eclipse.equinox.common;bundle-version="3.16.000" Bundle-RequiredExecutionEnvironment: JavaSE-11 Service-Component: OSGI-INF/fwadmin.xml Bundle-ActivationPolicy: lazy diff --git a/bundles/org.eclipse.equinox.frameworkadmin.equinox/pom.xml b/bundles/org.eclipse.equinox.frameworkadmin.equinox/pom.xml index 33bf8e699..c3ecc7ec9 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin.equinox/pom.xml +++ b/bundles/org.eclipse.equinox.frameworkadmin.equinox/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.frameworkadmin.equinox</artifactId> - <version>1.2.100-SNAPSHOT</version> + <version>1.2.200-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/EquinoxManipulatorImpl.java b/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/EquinoxManipulatorImpl.java index 5b73611ae..8e16a04c8 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/EquinoxManipulatorImpl.java +++ b/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/EquinoxManipulatorImpl.java @@ -328,20 +328,19 @@ public class EquinoxManipulatorImpl implements Manipulator { BundleInfo[] bInfos = new BundleInfo[bundles.length]; for (int i = 0; i < bundles.length; i++) { // System.out.println("bundles[" + i + "]=" + bundles[i]); - try { + Optional<File> bundleFile = FileLocator.getBundleFileLocation(bundles[i]); + if (bundleFile.isPresent()) { if (bundles[i].getBundleId() == 0) // SystemBundle bInfos[i] = new BundleInfo(bundles[i].getSymbolicName(), bundles[i].getHeaders("").get(Constants.BUNDLE_VERSION), //$NON-NLS-1$ - FileLocator.getBundleFile(bundles[i]).getAbsoluteFile().toURI(), -1, true); + bundleFile.get().getAbsoluteFile().toURI(), -1, true); else { bInfos[i] = new BundleInfo(bundles[i].getSymbolicName(), bundles[i].getHeaders("").get(Constants.BUNDLE_VERSION), //$NON-NLS-1$ - FileLocator.getBundleFile(bundles[i]).getAbsoluteFile().toURI(), + bundleFile.get().getAbsoluteFile().toURI(), bundles[i].adapt(BundleStartLevel.class).getStartLevel(), bundles[i].adapt(BundleStartLevel.class).isPersistentlyStarted()); } - } catch (IOException e) { - e.printStackTrace(); } } configData.setBundles(bInfos); diff --git a/bundles/org.eclipse.equinox.frameworkadmin.test/pom.xml b/bundles/org.eclipse.equinox.frameworkadmin.test/pom.xml index a717a575d..d48775cd2 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin.test/pom.xml +++ b/bundles/org.eclipse.equinox.frameworkadmin.test/pom.xml @@ -4,7 +4,7 @@ <parent> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> <groupId>org.eclipse</groupId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.frameworkadmin/pom.xml b/bundles/org.eclipse.equinox.frameworkadmin/pom.xml index aedce9f7a..433dcf50f 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin/pom.xml +++ b/bundles/org.eclipse.equinox.frameworkadmin/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/pom.xml b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/pom.xml index 8e091d594..72d6db126 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/pom.xml +++ b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.artifact.optimizers/pom.xml b/bundles/org.eclipse.equinox.p2.artifact.optimizers/pom.xml index ca540db6b..8d09d7881 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.optimizers/pom.xml +++ b/bundles/org.eclipse.equinox.p2.artifact.optimizers/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.artifact.processors/pom.xml b/bundles/org.eclipse.equinox.p2.artifact.processors/pom.xml index 1d3ab96a1..08e7da3c8 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.processors/pom.xml +++ b/bundles/org.eclipse.equinox.p2.artifact.processors/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF index 49e639b77..05dc70a3c 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF @@ -10,7 +10,7 @@ Export-Package: org.eclipse.equinox.internal.p2.artifact.processing;x-friends:=" org.eclipse.equinox.internal.p2.artifact.processors.checksum;x-friends:="org.eclipse.equinox.p2.publisher", org.eclipse.equinox.internal.p2.artifact.processors.md5;x-internal:=true, org.eclipse.equinox.internal.p2.artifact.processors.pack200;x-friends:="org.eclipse.equinox.p2.artifact.processors,org.eclipse.equinox.p2.artifact.optimizers", - org.eclipse.equinox.internal.p2.artifact.processors.pgp;x-friends:="org.eclipse.equinox.p2.engine,org.eclipse.equinox.p2.ui.sdk", + org.eclipse.equinox.internal.p2.artifact.processors.pgp;x-friends:="org.eclipse.equinox.p2.engine,org.eclipse.equinox.p2.ui.sdk,org.eclipse.equinox.p2.ui", org.eclipse.equinox.internal.p2.artifact.repository; x-friends:="org.eclipse.equinox.p2.publisher, org.eclipse.equinox.p2.reconciler.dropins, diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/pom.xml b/bundles/org.eclipse.equinox.p2.artifact.repository/pom.xml index 896b53571..bc016ef5a 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/pom.xml +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/Messages.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/Messages.java index 4ae7cc402..4f522f0a0 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/Messages.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/Messages.java @@ -15,11 +15,15 @@ import org.eclipse.osgi.util.NLS; public class Messages extends NLS { private static final String BUNDLE_NAME = "org.eclipse.equinox.internal.p2.artifact.processors.pgp.messages"; //$NON-NLS-1$ + public static String Error_SignatureAfterKeyExpiration; + + public static String Error_SignatureAfterKeyRevocation; + public static String Error_SignatureAndFileDontMatch; public static String Error_CouldNotLoadSignature; - public static String Error_publicKeyNotFound; + public static String Warning_publicKeyNotFound; static { // initialize resource bundle diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPPublicKeyStore.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPPublicKeyStore.java index 688765ff7..8f3d15083 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPPublicKeyStore.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPPublicKeyStore.java @@ -14,6 +14,7 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.openpgp.*; import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory; @@ -21,20 +22,21 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.equinox.internal.p2.artifact.repository.Activator; import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; public class PGPPublicKeyStore { - private Map<Long, PGPPublicKey> keys = new HashMap<>(); + private Map<String, PGPPublicKey> keys = new LinkedHashMap<>(); public PGPPublicKey addKey(PGPPublicKey key) { if (key == null) { return null; } - PGPPublicKey alreadyStoredKey = keys.putIfAbsent(key.getKeyID(), key); + PGPPublicKey alreadyStoredKey = keys.putIfAbsent(PGPPublicKeyService.toHexFingerprint(key), key); return alreadyStoredKey == null ? key : alreadyStoredKey; } - public PGPPublicKey getKey(long id) { - return keys.get(id); + public Collection<PGPPublicKey> getKeys(long id) { + return keys.values().stream().filter(key -> key.getKeyID() == id).collect(Collectors.toList()); } public void addKeys(String... armoredPublicKeys) { @@ -63,9 +65,7 @@ public class PGPPublicKeyStore { public String toArmoredString() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); ArmoredOutputStream armoredOut = new ArmoredOutputStream(out); - // PGPPublicKeyRing ring = new PGPPublicKeyRing(new ArrayList<>(keys.values())); - // ring.encode(armoredOut); - for (PGPPublicKey key : keys.values()) { + for (PGPPublicKey key : all()) { key.encode(armoredOut); } armoredOut.close(); @@ -74,14 +74,14 @@ public class PGPPublicKeyStore { } public void remove(PGPPublicKey selectedKey) { - keys.remove(selectedKey.getKeyID()); + keys.remove(PGPPublicKeyService.toHexFingerprint(selectedKey)); } public void add(File file) { try (InputStream stream = new FileInputStream(file)) { readPublicKeys(stream).forEach(this::addKey); } catch (IOException e) { - // TODO: how to log? + LogHelper.log(new Status(IStatus.ERROR, Activator.ID, "Could not read PGP key from " + file, e)); //$NON-NLS-1$ } } @@ -96,7 +96,8 @@ public class PGPPublicKeyStore { } Set<PGPPublicKey> res = new HashSet<>(); try (InputStream stream = PGPUtil.getDecoderStream(new ByteArrayInputStream( - PGPSignatureVerifier.unnormalizedPGPProperty(armoredPublicKeyring).getBytes()))) { + PGPSignatureVerifier.unnormalizedPGPProperty(armoredPublicKeyring) + .getBytes(StandardCharsets.US_ASCII)))) { new JcaPGPObjectFactory(stream).forEach(o -> { if (o instanceof PGPPublicKeyRingCollection) { collectKeys((PGPPublicKeyRingCollection) o, res::add); @@ -108,7 +109,7 @@ public class PGPPublicKeyStore { res.add((PGPPublicKey) o); } }); - } catch (IOException e) { + } catch (IOException | PGPRuntimeOperationException e) { LogHelper.log(new Status(IStatus.ERROR, Activator.ID, e.getMessage(), e)); } return res; diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java index d752d1b61..b994b7740 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java @@ -11,22 +11,30 @@ package org.eclipse.equinox.internal.p2.artifact.processors.pgp; import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.Map.Entry; import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.openpgp.*; import org.bouncycastle.openpgp.bc.BcPGPObjectFactory; +import org.bouncycastle.openpgp.operator.PGPContentVerifier; +import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilder; import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; import org.eclipse.core.runtime.*; import org.eclipse.equinox.internal.p2.artifact.repository.Activator; +import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; import org.eclipse.equinox.internal.provisional.p2.artifact.repository.processing.ProcessingStep; import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.repository.artifact.*; +import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; import org.eclipse.osgi.util.NLS; /** - * This processing step verifies PGP signatures are correct (ie artifact was not - * tampered during fetch). Note that is does <b>not</b> deal with trust. Dealing - * with trusted signers is done as part of CheckTrust touchpoint and phase. + * This processing step verifies PGP signatures are correct (i.e., the artifact + * was not tampered during fetch). Note that is does <b>not</b> deal with trust. + * Dealing with trusted signers is done as part of CheckTrust and touchpoint + * phase. */ public final class PGPSignatureVerifier extends ProcessingStep { @@ -37,11 +45,19 @@ public final class PGPSignatureVerifier extends ProcessingStep { */ public static final String ID = "org.eclipse.equinox.p2.processing.PGPSignatureCheck"; //$NON-NLS-1$ - public static final PGPPublicKeyStore KNOWN_KEYS = new PGPPublicKeyStore(); - public static final String PGP_SIGNER_KEYS_PROPERTY_NAME = "pgp.publicKeys"; //$NON-NLS-1$ + public static final String PGP_SIGNATURES_PROPERTY_NAME = "pgp.signatures"; //$NON-NLS-1$ - private Collection<PGPSignature> signaturesToVerify; + + private PGPPublicKeyService keyService; + + private IArtifactDescriptor sourceDescriptor; + + private Map<PGPSignature, List<PGPContentVerifier>> signaturesToVerify = new LinkedHashMap<>(); + + private Map<PGPContentVerifier, PGPPublicKey> verifierKeys = new LinkedHashMap<>(); + + private List<OutputStream> signatureVerifiers = new ArrayList<>(); public PGPSignatureVerifier() { super(); @@ -55,7 +71,8 @@ public final class PGPSignatureVerifier extends ProcessingStep { return Collections.emptyList(); } List<PGPSignature> res = new ArrayList<>(); - try (InputStream in = new ArmoredInputStream(new ByteArrayInputStream(signatureText.getBytes()))) { + try (InputStream in = new ArmoredInputStream( + new ByteArrayInputStream(signatureText.getBytes(StandardCharsets.US_ASCII)))) { PGPObjectFactory pgpFactory = new BcPGPObjectFactory(in); Object o = pgpFactory.nextObject(); PGPSignatureList signatureList = new PGPSignatureList(new PGPSignature[0]); @@ -71,43 +88,74 @@ public final class PGPSignatureVerifier extends ProcessingStep { return res; } + public static PGPPublicKeyStore getKeys(IArtifactDescriptor artifact) { + PGPPublicKeyStore keyStore = new PGPPublicKeyStore(); + String keyText = artifact.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME); + PGPPublicKeyStore.readPublicKeys(keyText).stream().forEach(keyStore::addKey); + return keyStore; + } + @Override public void initialize(IProvisioningAgent agent, IProcessingStepDescriptor descriptor, IArtifactDescriptor context) { super.initialize(agent, descriptor, context); + + sourceDescriptor = context; + keyService = agent.getService(PGPPublicKeyService.class); + // 1. verify declared public keys have signature from a trusted key, if so, add to KeyStore -// 2. verify artifact signature matches signture of given keys, and at least 1 of this key is trusted +// 2. verify artifact signature matches signature of given keys, and at least 1 of this key is trusted String signatureText = unnormalizedPGPProperty(context.getProperty(PGP_SIGNATURES_PROPERTY_NAME)); if (signatureText == null) { setStatus(Status.OK_STATUS); return; } + + Collection<PGPSignature> signatures; try { - signaturesToVerify = getSignatures(context); + signatures = getSignatures(context); } catch (Exception ex) { setStatus(new Status(IStatus.ERROR, Activator.ID, Messages.Error_CouldNotLoadSignature, ex)); return; } - if (signaturesToVerify.isEmpty()) { + + if (signatures.isEmpty()) { setStatus(Status.OK_STATUS); return; } IArtifactRepository repository = context.getRepository(); - KNOWN_KEYS.addKeys(context.getProperty(PGP_SIGNER_KEYS_PROPERTY_NAME), - repository != null ? repository.getProperty(PGP_SIGNER_KEYS_PROPERTY_NAME) : null); - for (PGPSignature signature : signaturesToVerify) { - PGPPublicKey publicKey = KNOWN_KEYS.getKey(signature.getKeyID()); - if (publicKey == null) { - setStatus(new Status(IStatus.ERROR, Activator.ID, - NLS.bind(Messages.Error_publicKeyNotFound, Long.toHexString(signature.getKeyID())))); - return; - } - try { - signature.init(new BcPGPContentVerifierBuilderProvider(), publicKey); - } catch (PGPException ex) { - setStatus(new Status(IStatus.ERROR, Activator.ID, ex.getMessage(), ex)); - return; + + PGPPublicKeyStore.readPublicKeys(context.getProperty(PGP_SIGNER_KEYS_PROPERTY_NAME)) + .forEach(keyService::addKey); + if (repository != null) { + PGPPublicKeyStore.readPublicKeys(repository.getProperty(PGP_SIGNER_KEYS_PROPERTY_NAME)) + .forEach(keyService::addKey); + } + + for (PGPSignature signature : signatures) { + long keyID = signature.getKeyID(); + Collection<PGPPublicKey> keys = keyService.getKeys(keyID); + if (keys.isEmpty()) { + LogHelper.log(new Status(IStatus.WARNING, Activator.ID, + NLS.bind(Messages.Warning_publicKeyNotFound, PGPPublicKeyService.toHex(keyID), + context.getArtifactKey().getId()))); + } else { + try { + PGPContentVerifierBuilder verifierBuilder = new BcPGPContentVerifierBuilderProvider() + .get(signature.getKeyAlgorithm(), signature.getHashAlgorithm()); + List<PGPContentVerifier> verifiers = new ArrayList<>(); + signaturesToVerify.put(signature, verifiers); + for (PGPPublicKey key : keys) { + PGPContentVerifier verifier = verifierBuilder.build(key); + verifierKeys.put(verifier, key); + verifiers.add(verifier); + signatureVerifiers.add(verifier.getOutputStream()); + } + } catch (PGPException ex) { + setStatus(new Status(IStatus.ERROR, Activator.ID, ex.getMessage(), ex)); + return; + } } } } @@ -134,51 +182,94 @@ public final class PGPSignatureVerifier extends ProcessingStep { } @Override - public void write(int b) { - if (signaturesToVerify != null) { - signaturesToVerify.iterator().forEachRemaining(signature -> signature.update((byte) b)); - } - - } - - @Override - public void write(byte[] b) throws IOException { + public void write(int b) throws IOException { getDestination().write(b); - if (signaturesToVerify != null) { - signaturesToVerify.iterator().forEachRemaining(signature -> signature.update(b)); + for (OutputStream verifier : signatureVerifiers) { + verifier.write(b); } } @Override public void write(byte[] b, int off, int len) throws IOException { getDestination().write(b, off, len); - if (signaturesToVerify != null) { - signaturesToVerify.iterator().forEachRemaining(signature -> signature.update(b, off, len)); + for (OutputStream verifier : signatureVerifiers) { + verifier.write(b, off, len); } } @Override - public void close() { - if (!getStatus().isOK()) { - return; - } - if (signaturesToVerify == null || signaturesToVerify.isEmpty()) { - return; - } - Iterator<PGPSignature> iterator = signaturesToVerify.iterator(); - while (iterator.hasNext()) { - PGPSignature signature = iterator.next(); - try { - if (!signature.verify()) { + public void close() throws IOException { + try { + if (!getStatus().isOK()) { + return; + } + + if (signaturesToVerify.isEmpty()) { + return; + } + + PGPPublicKeyStore keyStore = new PGPPublicKeyStore(); + for (Entry<PGPSignature, List<PGPContentVerifier>> entry : signaturesToVerify.entrySet()) { + PGPSignature signature = entry.getKey(); + List<PGPContentVerifier> verifiers = entry.getValue(); + boolean verified = false; + for (PGPContentVerifier verifier : verifiers) { + try { + verifier.getOutputStream().write(signature.getSignatureTrailer()); + if (verifier.verify(signature.getSignature())) { + PGPPublicKey verifyingKey = verifierKeys.get(verifier); + if (!Boolean.FALSE.toString() + .equalsIgnoreCase(System.getProperty("p2.pgp.verifyExpiration"))) { //$NON-NLS-1$ + if (PGPPublicKeyService.compareSignatureTimeToKeyValidityTime(signature, + verifyingKey) != 0) { + LogHelper.log(new Status(IStatus.WARNING, Activator.ID, + NLS.bind(Messages.Error_SignatureAfterKeyExpiration, PGPPublicKeyService + .toHexFingerprint(verifyingKey)))); + } + } + + if (!Boolean.FALSE.toString() + .equalsIgnoreCase(System.getProperty("p2.pgp.verifyRevocation"))) { //$NON-NLS-1$ + if (!keyService.isCreatedBeforeRevocation(signature, verifyingKey)) { + setStatus(new Status(IStatus.ERROR, Activator.ID, + NLS.bind(Messages.Error_SignatureAfterKeyRevocation, PGPPublicKeyService + .toHexFingerprint(verifyingKey)))); + return; + } + } + + keyStore.addKey(verifyingKey); + verified = true; + break; + } + } catch (PGPException ex) { + LogHelper.log(new Status(IStatus.ERROR, Activator.ID, ex.getMessage(), ex)); + } + } + + if (!verified) { setStatus(new Status(IStatus.ERROR, Activator.ID, Messages.Error_SignatureAndFileDontMatch)); return; } - } catch (PGPException ex) { - setStatus(new Status(IStatus.ERROR, Activator.ID, ex.getMessage(), ex)); - return; } + + // Update the destination artifact descriptor with the signatures that have been + // verified and the keys used for that verification. + OutputStream destination = getDestination(); + if (destination instanceof IAdaptable) { + ArtifactDescriptor destinationDescriptor = ((IAdaptable) destination) + .getAdapter(ArtifactDescriptor.class); + destinationDescriptor.setProperty(PGP_SIGNATURES_PROPERTY_NAME, + sourceDescriptor.getProperty(PGP_SIGNATURES_PROPERTY_NAME)); + destinationDescriptor.setProperty(PGP_SIGNER_KEYS_PROPERTY_NAME, keyStore.toArmoredString()); + } + + setStatus(Status.OK_STATUS); + } finally + + { + super.close(); } - setStatus(Status.OK_STATUS); } } diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/messages.properties b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/messages.properties index 90d95f756..09a1e7292 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/messages.properties +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/messages.properties @@ -9,6 +9,8 @@ # SPDX-License-Identifier: EPL-2.0 ############################################################################### -Error_SignatureAndFileDontMatch=Signature is invalid for current content. -Error_CouldNotLoadSignature=Could not load signature -Error_publicKeyNotFound=Public key not found for {0}. +Error_SignatureAfterKeyRevocation=The signature is created after the revocation time of the signature's key {0} +Error_SignatureAfterKeyExpiration=The signature is created after the expiration time of the signature's key {0} +Error_SignatureAndFileDontMatch=The signature is invalid for current content +Error_CouldNotLoadSignature=The signatures could not be loaded +Warning_publicKeyNotFound=A public key with ID {0} needed to verify the signatures of {1} could not be found diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/MirrorRequest.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/MirrorRequest.java index d9f4dfe22..ccd932110 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/MirrorRequest.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/MirrorRequest.java @@ -23,7 +23,6 @@ import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; import org.eclipse.core.runtime.*; -import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerifier; import org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactDescriptor; import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; import org.eclipse.equinox.internal.p2.repository.Transport; @@ -201,14 +200,6 @@ public class MirrorRequest extends ArtifactRequest { ((ArtifactDescriptor) destinationDescriptor).addProperties(targetDescriptorProperties); if (targetRepositoryProperties != null && destinationDescriptor instanceof SimpleArtifactDescriptor) ((SimpleArtifactDescriptor) destinationDescriptor).addRepositoryProperties(targetRepositoryProperties); - if (isCanonical && destinationDescriptor instanceof ArtifactDescriptor) { - // keep some safety properties - if (sourceDescriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME) != null) { - ((ArtifactDescriptor) destinationDescriptor) - .addProperties(Map.of(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME, - sourceDescriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME))); - } - } return destinationDescriptor; } diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepository.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepository.java index d627642d0..b13c03692 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepository.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepository.java @@ -121,7 +121,7 @@ public class SimpleArtifactRepository extends AbstractArtifactRepository impleme private long cacheTimestamp = 0l; - public class ArtifactOutputStream extends OutputStream implements IStateful { + public class ArtifactOutputStream extends OutputStream implements IStateful, IAdaptable { private boolean closed; private long count = 0; private IArtifactDescriptor descriptor; @@ -205,6 +205,14 @@ public class SimpleArtifactRepository extends AbstractArtifactRepository impleme public void setFirstLink(OutputStream value) { firstLink = value; } + + @Override + public <T> T getAdapter(Class<T> adapter) { + if (adapter.isInstance(descriptor)) { + return adapter.cast(descriptor); + } + return null; + } } // TODO: optimize @@ -506,7 +514,7 @@ public class SimpleArtifactRepository extends AbstractArtifactRepository impleme Set<String> skipChecksums = DOWNLOAD_MD5_CHECKSUM_ENABLED ? Collections.emptySet() : Collections.singleton(ChecksumHelper.MD5); ArrayList<ProcessingStep> downloadChecksumSteps = new ArrayList<>(); addChecksumVerifiers(descriptor, downloadChecksumSteps, skipChecksums, IArtifactDescriptor.DOWNLOAD_CHECKSUM); - if (downloadChecksumSteps.isEmpty()) { + if (downloadChecksumSteps.isEmpty() && !isLocal()) { LogHelper.log(new Status(IStatus.WARNING, Activator.ID, NLS.bind(Messages.noDigestAlgorithmToVerifyDownload, descriptor.getArtifactKey()))); } diff --git a/bundles/org.eclipse.equinox.p2.console/pom.xml b/bundles/org.eclipse.equinox.p2.console/pom.xml index b069fcb80..6be37ac24 100644 --- a/bundles/org.eclipse.equinox.p2.console/pom.xml +++ b/bundles/org.eclipse.equinox.p2.console/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.core/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.core/META-INF/MANIFEST.MF index fdd4ec76f..73462bd3e 100644 --- a/bundles/org.eclipse.equinox.p2.core/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.core/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.core;singleton:=true -Bundle-Version: 2.8.100.qualifier +Bundle-Version: 2.9.100.qualifier Bundle-ClassPath: . Bundle-Activator: org.eclipse.equinox.internal.p2.core.Activator Bundle-Vendor: %providerName @@ -64,14 +64,16 @@ Export-Package: org.eclipse.equinox.internal.p2.core;x-friends:="org.eclipse.equ org.eclipse.equinox.p2.updatesite, org.eclipse.equinox.p2.director.app, org.eclipse.equinox.p2.transport.ecf", - org.eclipse.equinox.p2.core;version="2.7.0", - org.eclipse.equinox.p2.core.spi;version="2.1.0" -Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.5.0,4.0.0)" + org.eclipse.equinox.p2.core;version="2.8.0";uses:="org.eclipse.core.runtime", + org.eclipse.equinox.p2.core.spi;version="2.1.0";uses:="org.eclipse.equinox.p2.core" Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Service-Component: OSGI-INF/eventBus.xml, OSGI-INF/agentProvider.xml Import-Package: org.bouncycastle.bcpg;version="1.65.0", org.bouncycastle.openpgp;version="1.65.0", + org.eclipse.core.runtime;common=split;version="[3.5.0,4.0.0)", + org.eclipse.equinox.p2.core;version="[2.8.0,2.9.0)";resolution:=optional, + org.eclipse.equinox.p2.core.spi;version="[2.1.0,2.2.0)";resolution:=optional, org.eclipse.osgi.framework.eventmgr;version="1.2.0", org.eclipse.osgi.framework.log;version="1.0.0", org.eclipse.osgi.service.debug;version="1.0.0", diff --git a/bundles/org.eclipse.equinox.p2.core/META-INF/p2.inf b/bundles/org.eclipse.equinox.p2.core/META-INF/p2.inf new file mode 100644 index 000000000..0983c41b7 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.core/META-INF/p2.inf @@ -0,0 +1,5 @@ +# Workaround https://bugs.eclipse.org/bugs/show_bug.cgi?id=525368 +# we hint about which bundle to download at install that is capable of +# resolving packages +requires.0.namespace = osgi.bundle +requires.0.name = org.eclipse.equinox.common
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.core/pom.xml b/bundles/org.eclipse.equinox.p2.core/pom.xml index 4126209ae..3913f722d 100644 --- a/bundles/org.eclipse.equinox.p2.core/pom.xml +++ b/bundles/org.eclipse.equinox.p2.core/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.core</artifactId> - <version>2.8.100-SNAPSHOT</version> + <version>2.9.100-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/internal/p2/core/helpers/SecureXMLUtil.java b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/internal/p2/core/helpers/SecureXMLUtil.java index 202921c17..0afeb2936 100644 --- a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/internal/p2/core/helpers/SecureXMLUtil.java +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/internal/p2/core/helpers/SecureXMLUtil.java @@ -16,7 +16,6 @@ package org.eclipse.equinox.internal.p2.core.helpers; import javax.xml.XMLConstants; import javax.xml.parsers.*; import org.xml.sax.*; -import org.xml.sax.helpers.XMLReaderFactory; /** * A utility class for creating an XML-related factories suitable for @@ -52,10 +51,11 @@ public class SecureXMLUtil { * Create a new {@link XMLReader}. * * @throws SAXException + * @throws ParserConfigurationException */ - public static XMLReader newSecureXMLReader() throws SAXException { - XMLReader reader = XMLReaderFactory.createXMLReader(); - reader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - return reader; + public static XMLReader newSecureXMLReader() throws SAXException, ParserConfigurationException { + SAXParserFactory factory = newSecureSAXParserFactory(); + factory.setNamespaceAware(true); + return factory.newSAXParser().getXMLReader(); } } diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/UIServices.java b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/UIServices.java index 7a29b1d9b..5be838d4b 100644 --- a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/UIServices.java +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/UIServices.java @@ -73,13 +73,14 @@ public abstract class UIServices { } /** - * Trust information returned from a trust request. * + * Trust information returned from a trust request. */ public static class TrustInfo { private final Certificate[] trustedCertificates; private final Collection<PGPPublicKey> trustedPGPKeys; private final boolean saveTrustedCertificates; private final boolean trustUnsigned; + private final boolean trustAlways; /** * @@ -96,6 +97,7 @@ public abstract class UIServices { this.trustedPGPKeys = Collections.emptyList(); this.saveTrustedCertificates = save; this.trustUnsigned = trustUnsigned; + trustAlways = false; } /** @@ -112,6 +114,28 @@ public abstract class UIServices { public TrustInfo(Collection<Certificate> trustedCertificates, Collection<PGPPublicKey> trustedPGPKeys, boolean save, boolean trustUnsigned) { + this(trustedCertificates, trustedPGPKeys, save, trustUnsigned, false); + } + + /** + * @param trustedCertificates Trusted certificates + * @param trustedPGPKeys Trusted PGP public keys + * @param save Whether to store trusted certificates and keys or + * not. + * @param trustUnsigned Whether to trust unsigned content. + * <code>true</code> if installation should continue + * despite unsigned content; <code>false</code> + * otherwise. + * @param trustAlways Whether to always trust all content regardless of + * whether it's signed, regardless of how it's + * signed, and regardless of the certificate or key + * with which it's signed. + * + * @since 2.9 + */ + public TrustInfo(Collection<Certificate> trustedCertificates, Collection<PGPPublicKey> trustedPGPKeys, + boolean save, boolean trustUnsigned, boolean trustAlways) { + this.trustAlways = trustAlways; this.trustedCertificates = trustedCertificates.toArray(Certificate[]::new); this.trustedPGPKeys = trustedPGPKeys; this.saveTrustedCertificates = save; @@ -131,7 +155,10 @@ public abstract class UIServices { /** * - * @return the trusted PGP keys + * Return a collection of the keys that should be trusted for the requested + * operation. + * + * @return the trusted PGP keys. * @since 2.8 */ public Collection<PGPPublicKey> getTrustedPGPKeys() { @@ -159,6 +186,20 @@ public abstract class UIServices { public boolean trustUnsignedContent() { return trustUnsigned; } + + /** + * Return a boolean indicating whether to always trust all content regardless of + * whether it's signed, regardless of how it's signed, and regardless of the + * certificate or key with which it's signed, both during this operation and for + * all future operations. + * + * @return <code>true</code> if all content should always be trusted, and + * <code>false</code> otherwise. + * @since 2.9 + */ + public boolean trustAlways() { + return trustAlways; + } } /** diff --git a/bundles/org.eclipse.equinox.p2.director.app/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.director.app/META-INF/MANIFEST.MF index 04b4a7068..7cbc86f19 100644 --- a/bundles/org.eclipse.equinox.p2.director.app/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.director.app/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.director.app;singleton:=true -Bundle-Version: 1.2.0.qualifier +Bundle-Version: 1.2.100.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.director.app.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin @@ -10,14 +10,15 @@ Export-Package: org.eclipse.equinox.internal.p2.director.app;x-internal:=true Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.5.0,4.0.0)" Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy -Import-Package: org.eclipse.equinox.app, +Import-Package: org.bouncycastle.openpgp, + org.eclipse.equinox.app, org.eclipse.equinox.internal.p2.core.helpers, org.eclipse.equinox.internal.p2.director, org.eclipse.equinox.internal.p2.engine, org.eclipse.equinox.internal.provisional.p2.core.eventbus, org.eclipse.equinox.internal.provisional.p2.director, org.eclipse.equinox.internal.provisional.p2.repository, - org.eclipse.equinox.p2.core;version="[2.0.0,3.0.0)", + org.eclipse.equinox.p2.core;version="[2.8.0,3.0.0)", org.eclipse.equinox.p2.core.spi;version="[2.1.0,3.0.0)", org.eclipse.equinox.p2.engine;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.engine.query;version="[2.0.0,3.0.0)", diff --git a/bundles/org.eclipse.equinox.p2.director.app/pom.xml b/bundles/org.eclipse.equinox.p2.director.app/pom.xml index 0ee657185..44f4bde1c 100644 --- a/bundles/org.eclipse.equinox.p2.director.app/pom.xml +++ b/bundles/org.eclipse.equinox.p2.director.app/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.director.app</artifactId> - <version>1.2.0-SNAPSHOT</version> + <version>1.2.100-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/DirectorApplication.java b/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/DirectorApplication.java index d163af047..4710d4cc1 100644 --- a/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/DirectorApplication.java +++ b/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/DirectorApplication.java @@ -32,6 +32,7 @@ import java.util.*; import java.util.Map.Entry; import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.bouncycastle.openpgp.PGPPublicKey; import org.eclipse.core.runtime.*; import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; @@ -85,6 +86,21 @@ public class DirectorApplication implements IApplication, ProvisioningListener { } return new TrustInfo(trusted, false, true); } + + @Override + public TrustInfo getTrustInfo(Certificate[][] untrustedChains, Collection<PGPPublicKey> untrustedPGPKeys, + String[] unsignedDetail) { + final Collection<Certificate> trusted; + if (untrustedChains == null) { + trusted = List.of(); + } else { + trusted = new ArrayList<>(untrustedChains.length); + for (Certificate[] untrustedChain : untrustedChains) { + trusted.add(untrustedChain[0]); + } + } + return new TrustInfo(trusted, untrustedPGPKeys, false, true); + } } class LocationQueryable implements IQueryable<IInstallableUnit> { diff --git a/bundles/org.eclipse.equinox.p2.director/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.director/META-INF/MANIFEST.MF index 79fb04c24..8b2a73e25 100644 --- a/bundles/org.eclipse.equinox.p2.director/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.director/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.director;singleton:=true -Bundle-Version: 2.5.100.qualifier +Bundle-Version: 2.5.200.qualifier Bundle-ClassPath: . Bundle-Activator: org.eclipse.equinox.internal.p2.director.DirectorActivator Bundle-Vendor: %providerName diff --git a/bundles/org.eclipse.equinox.p2.director/pom.xml b/bundles/org.eclipse.equinox.p2.director/pom.xml index 935a2cc28..0a32c8db0 100644 --- a/bundles/org.eclipse.equinox.p2.director/pom.xml +++ b/bundles/org.eclipse.equinox.p2.director/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.director</artifactId> - <version>2.5.100-SNAPSHOT</version> + <version>2.5.200-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/DirectorActivator.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/DirectorActivator.java index 7d331936f..a2960e384 100644 --- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/DirectorActivator.java +++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/DirectorActivator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2017 IBM Corporation and others. + * Copyright (c) 2007, 2022 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -10,24 +10,26 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Christoph Läubrich - access activator static singelton in a safe way *******************************************************************************/ package org.eclipse.equinox.internal.p2.director; +import java.util.Optional; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; public class DirectorActivator implements BundleActivator { public static final String PI_DIRECTOR = "org.eclipse.equinox.p2.director"; //$NON-NLS-1$ - public static BundleContext context; + public static volatile Optional<BundleContext> context = Optional.empty(); @Override public void start(BundleContext ctx) throws Exception { - context = ctx; + context = Optional.of(ctx); } @Override public void stop(BundleContext ctx) throws Exception { - DirectorActivator.context = null; + DirectorActivator.context = Optional.empty(); } } diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java index ae30164ad..458a8fb86 100644 --- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java +++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2020 IBM Corporation and others. + * Copyright (c) 2007, 2022 IBM Corporation and others. * * This * program and the accompanying materials are made available under the terms of @@ -17,6 +17,7 @@ * Sonatype, Inc. - ongoing development * Rapicorp, Inc. - split the optimization function * Red Hat, Inc. - support for remediation page + * Christoph Läubrich - access activator static singelton in a safe way ******************************************************************************/ package org.eclipse.equinox.internal.p2.director; @@ -207,7 +208,8 @@ public class Projector { // allow the user to specify a longer timeout. // only set the value if it is a positive integer larger than the default. // see https://bugs.eclipse.org/336967 - timeoutString = DirectorActivator.context.getProperty(PROP_PROJECTOR_TIMEOUT); + timeoutString = DirectorActivator.context.map(ctx -> ctx.getProperty(PROP_PROJECTOR_TIMEOUT)) + .orElse(null); if (timeoutString != null) timeout = Math.max(timeout, Integer.parseInt(timeoutString)); } catch (Exception e) { diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/provisional/p2/director/PlanExecutionHelper.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/provisional/p2/director/PlanExecutionHelper.java index 4c51ca1a9..83ac500b7 100644 --- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/provisional/p2/director/PlanExecutionHelper.java +++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/provisional/p2/director/PlanExecutionHelper.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2015 IBM Corporation and others. + * Copyright (c) 2009, 2022 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -11,6 +11,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Red Hat Inc. - Bug 460967 + * Christoph Läubrich - access activator static singelton in a safe way *******************************************************************************/ package org.eclipse.equinox.internal.provisional.p2.director; @@ -36,7 +37,8 @@ public class PlanExecutionHelper { .getService(IEngine.class).perform(result.getInstallerPlan(), phaseSet, progress); if (!installerPlanStatus.isOK()) return installerPlanStatus; - Configurator configChanger = ServiceHelper.getService(DirectorActivator.context, Configurator.class); + Configurator configChanger = DirectorActivator.context + .map(ctx -> ServiceHelper.getService(ctx, Configurator.class)).orElse(null); try { configChanger.applyConfiguration(); } catch (IOException e) { diff --git a/bundles/org.eclipse.equinox.p2.directorywatcher/pom.xml b/bundles/org.eclipse.equinox.p2.directorywatcher/pom.xml index 5505474fc..ece589593 100644 --- a/bundles/org.eclipse.equinox.p2.directorywatcher/pom.xml +++ b/bundles/org.eclipse.equinox.p2.directorywatcher/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.discovery.compatibility/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.discovery.compatibility/META-INF/MANIFEST.MF index 277ae2f5f..fbfa7e023 100644 --- a/bundles/org.eclipse.equinox.p2.discovery.compatibility/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.discovery.compatibility/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.equinox.p2.discovery.compatibility;singleton:=true -Bundle-Version: 1.2.100.qualifier +Bundle-Version: 1.2.200.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-RequiredExecutionEnvironment: JavaSE-11 Require-Bundle: org.eclipse.core.runtime;bundle-version="3.3.0", diff --git a/bundles/org.eclipse.equinox.p2.discovery.compatibility/pom.xml b/bundles/org.eclipse.equinox.p2.discovery.compatibility/pom.xml index 23c6cc4d1..b926dd577 100644 --- a/bundles/org.eclipse.equinox.p2.discovery.compatibility/pom.xml +++ b/bundles/org.eclipse.equinox.p2.discovery.compatibility/pom.xml @@ -14,11 +14,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.discovery.compatibility</artifactId> - <version>1.2.100-SNAPSHOT</version> + <version>1.2.200-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.discovery.compatibility/src/org/eclipse/equinox/internal/p2/discovery/compatibility/DirectoryParser.java b/bundles/org.eclipse.equinox.p2.discovery.compatibility/src/org/eclipse/equinox/internal/p2/discovery/compatibility/DirectoryParser.java index 97a21448c..9cbb8d0a3 100644 --- a/bundles/org.eclipse.equinox.p2.discovery.compatibility/src/org/eclipse/equinox/internal/p2/discovery/compatibility/DirectoryParser.java +++ b/bundles/org.eclipse.equinox.p2.discovery.compatibility/src/org/eclipse/equinox/internal/p2/discovery/compatibility/DirectoryParser.java @@ -16,6 +16,7 @@ package org.eclipse.equinox.internal.p2.discovery.compatibility; import java.io.IOException; import java.io.Reader; +import javax.xml.parsers.ParserConfigurationException; import org.eclipse.equinox.internal.p2.core.helpers.SecureXMLUtil; import org.eclipse.equinox.internal.p2.discovery.compatibility.Directory.Entry; import org.eclipse.equinox.internal.p2.discovery.compatibility.util.DefaultSaxErrorHandler; @@ -42,7 +43,7 @@ public class DirectoryParser { XMLReader xmlReader; try { xmlReader = SecureXMLUtil.newSecureXMLReader(); - } catch (SAXException e) { + } catch (SAXException | ParserConfigurationException e) { throw new IOWithCauseException(e.getMessage(), e); } xmlReader.setErrorHandler(new DefaultSaxErrorHandler()); diff --git a/bundles/org.eclipse.equinox.p2.discovery/pom.xml b/bundles/org.eclipse.equinox.p2.discovery/pom.xml index 6930630fe..08c1a9a53 100644 --- a/bundles/org.eclipse.equinox.p2.discovery/pom.xml +++ b/bundles/org.eclipse.equinox.p2.discovery/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.engine/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.engine/META-INF/MANIFEST.MF index d1e7137aa..561283093 100644 --- a/bundles/org.eclipse.equinox.p2.engine/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.engine/META-INF/MANIFEST.MF @@ -46,9 +46,10 @@ Import-Package: javax.xml.parsers, org.eclipse.equinox.p2.query;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository.artifact;version="[2.0.0,3.0.0)", + org.eclipse.equinox.p2.repository.artifact.spi;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository.metadata;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository.metadata.spi;version="[2.0.0,3.0.0)", - org.eclipse.equinox.p2.repository.spi;version="2.0.0", + org.eclipse.equinox.p2.repository.spi;version="[2.0.0,3.0.0)", org.eclipse.osgi.service.datalocation;version="1.0.0", org.eclipse.osgi.service.debug;version="1.1.0", org.eclipse.osgi.service.security;version="1.0.0", diff --git a/bundles/org.eclipse.equinox.p2.engine/plugin.xml b/bundles/org.eclipse.equinox.p2.engine/plugin.xml index 3d469f492..cee2c3856 100644 --- a/bundles/org.eclipse.equinox.p2.engine/plugin.xml +++ b/bundles/org.eclipse.equinox.p2.engine/plugin.xml @@ -4,6 +4,7 @@ <extension-point id="touchpoints" name="Touchpoints" schema="schema/touchpoints.exsd"/> <extension-point id="actions" name="Actions" schema="schema/actions.exsd"/> +<extension-point id="pgp" name="PGP" schema="schema/pgp.exsd"/> <extension id="metadataRepository" point="org.eclipse.equinox.p2.metadata.repository.metadataRepositories"> diff --git a/bundles/org.eclipse.equinox.p2.engine/pom.xml b/bundles/org.eclipse.equinox.p2.engine/pom.xml index cbf8386be..eb9562492 100644 --- a/bundles/org.eclipse.equinox.p2.engine/pom.xml +++ b/bundles/org.eclipse.equinox.p2.engine/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.engine/schema/pgp.exsd b/bundles/org.eclipse.equinox.p2.engine/schema/pgp.exsd new file mode 100644 index 000000000..fcb06d04a --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/schema/pgp.exsd @@ -0,0 +1,107 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.equinox.p2.engine" xmlns="http://www.w3.org/2001/XMLSchema"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.equinox.p2.engine" id="pgp" name="PGP"/> + </appInfo> + <documentation> + Configure PGP keys and trust for p2 operations + </documentation> + </annotation> + + <element name="extension"> + <annotation> + <appInfo> + <meta.element /> + </appInfo> + </annotation> + <complexType> + <sequence minOccurs="0" maxOccurs="unbounded"> + <element ref="trustedKeys"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="trustedKeys"> + <annotation> + <documentation> + PGP Keys that are trusted by default. + </documentation> + </annotation> + <complexType> + <attribute name="path" type="string"> + <annotation> + <documentation> + Path to a file containing PGP keys in armored form. + </documentation> + <appInfo> + <meta.attribute kind="resource"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + 2.7.300 + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="examples"/> + </appInfo> + <documentation> + [Enter extension point usage example here.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="apiinfo"/> + </appInfo> + <documentation> + [Enter API information here.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="implementation"/> + </appInfo> + <documentation> + [Enter information about supplied implementation of this extension point.] + </documentation> + </annotation> + + +</schema> diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CertificateChecker.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CertificateChecker.java index 15943a47c..3a1589f8b 100644 --- a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CertificateChecker.java +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CertificateChecker.java @@ -13,14 +13,17 @@ *******************************************************************************/ package org.eclipse.equinox.internal.p2.engine.phases; -import java.io.File; -import java.io.IOException; +import java.io.*; +import java.net.URL; +import java.nio.ByteBuffer; import java.security.GeneralSecurityException; -import java.security.cert.Certificate; +import java.security.cert.*; import java.util.*; import java.util.Map.Entry; +import java.util.function.Supplier; import java.util.stream.Collectors; -import org.bouncycastle.openpgp.*; +import java.util.stream.Stream; +import org.bouncycastle.openpgp.PGPPublicKey; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPPublicKeyStore; @@ -28,13 +31,16 @@ import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerif import org.eclipse.equinox.internal.p2.engine.*; import org.eclipse.equinox.p2.core.*; import org.eclipse.equinox.p2.core.UIServices.TrustInfo; -import org.eclipse.equinox.p2.engine.*; +import org.eclipse.equinox.p2.engine.IProfile; +import org.eclipse.equinox.p2.engine.ProfileScope; +import org.eclipse.equinox.p2.metadata.IArtifactKey; import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; +import org.eclipse.equinox.p2.repository.artifact.spi.IArtifactUIServices; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; import org.eclipse.osgi.service.security.TrustEngine; import org.eclipse.osgi.signedcontent.*; import org.eclipse.osgi.util.NLS; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; +import org.osgi.framework.*; import org.osgi.service.prefs.BackingStoreException; import org.osgi.util.tracker.ServiceTracker; @@ -46,15 +52,49 @@ import org.osgi.util.tracker.ServiceTracker; public class CertificateChecker { private static final String DEBUG_PREFIX = "certificate checker"; //$NON-NLS-1$ + public static final String TRUST_ALWAYS_PROPERTY = "trustAlways"; //$NON-NLS-1$ + public static final String TRUSTED_KEY_STORE_PROPERTY = "pgp.trustedPublicKeys"; //$NON-NLS-1$ + public static final String TRUSTED_CERTIFICATES_PROPERTY = "trustedCertificates"; //$NON-NLS-1$ + + /*** + * Store the optional profile for PGP key handling + */ + private IProfile profile; + /** * Stores artifacts to check */ private Map<IArtifactDescriptor, File> artifacts = new HashMap<>(); private final IProvisioningAgent agent; + private final PGPPublicKeyService keyService; - private PGPPublicKeyStore trustedKeys; + // Lazily loading + private Supplier<PGPPublicKeyStore> trustedKeys = new Supplier<>() { + private PGPPublicKeyStore cache = null; + + public PGPPublicKeyStore get() { + if (cache == null) { + cache = getPreferenceTrustedKeys(); + getContributedTrustedKeys().keySet().forEach(cache::addKey); + } + return cache; + } + }; + + // Lazily loading in case we ever add an extension point for registering + // certificates. + private Supplier<Collection<? extends Certificate>> additionalTrustedCertificates = new Supplier<>() { + private Collection<? extends Certificate> cache = null; + + public Collection<? extends Certificate> get() { + if (cache == null) { + cache = getPreferenceTrustedCertificates(); + } + return cache; + } + }; public CertificateChecker() { this(null); @@ -63,11 +103,14 @@ public class CertificateChecker { public CertificateChecker(IProvisioningAgent agent) { this.agent = agent; artifacts = new HashMap<>(); + keyService = agent.getService(PGPPublicKeyService.class); + } public IStatus start() { final BundleContext context = EngineActivator.getContext(); - ServiceReference<SignedContentFactory> contentFactoryRef = context.getServiceReference(SignedContentFactory.class); + ServiceReference<SignedContentFactory> contentFactoryRef = context + .getServiceReference(SignedContentFactory.class); SignedContentFactory verifierFactory = context.getService(contentFactoryRef); try { return checkCertificates(verifierFactory); @@ -77,72 +120,95 @@ public class CertificateChecker { } private IStatus checkCertificates(SignedContentFactory verifierFactory) { + if (artifacts.isEmpty()) { + return Status.OK_STATUS; + } + + // If unsigned content is allowed then the flood gates are open so there is no + // point in checking for unrooted certificates nor for not-yet-trusted keys. + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=578583 + String policy = getUnsignedContentPolicy(); + if (EngineActivator.UNSIGNED_ALLOW.equals(policy)) { + return Status.OK_STATUS; + } + UIServices serviceUI = agent.getService(UIServices.class); - ArrayList<Certificate> untrustedCertificates = new ArrayList<>(); - Map<IArtifactDescriptor, Collection<PGPPublicKey>> untrustedPGPArtifacts = new HashMap<>(); - Map<IArtifactDescriptor, File> unsigned = new HashMap<>(); - ArrayList<Certificate[]> untrustedChain = new ArrayList<>(); - Map<Certificate, Collection<File>> untrustedArtifacts = new HashMap<>(); - IStatus status = Status.OK_STATUS; - if (artifacts.isEmpty() || serviceUI == null) { - return status; + if (serviceUI == null) { + return Status.OK_STATUS; + } + + if (isTrustAlways()) { + return Status.OK_STATUS; } - Set<Long> trustedKeysIds = new HashSet<>(); + + IArtifactUIServices artifactServiceUI = serviceUI instanceof IArtifactUIServices + ? (IArtifactUIServices) serviceUI + : (untrustedCertificateChains, untrustedPGPKeys, unsignedArtifacts, + artifactFiles) -> IArtifactUIServices.getTrustInfo(serviceUI, untrustedCertificateChains, + untrustedPGPKeys, unsignedArtifacts, artifactFiles); + + Map<List<Certificate>, Set<IArtifactKey>> untrustedCertificates = new LinkedHashMap<>(); + Map<PGPPublicKey, Set<IArtifactKey>> untrustedPGPKeys = new LinkedHashMap<>(); + Set<IArtifactKey> unsignedArtifacts = new LinkedHashSet<>(); + Set<PGPPublicKey> trustedKeySet = new HashSet<>(); + boolean isTrustedKeySetInitialized = false; + Map<IArtifactKey, File> artifactFiles = new LinkedHashMap<>(); for (Entry<IArtifactDescriptor, File> artifact : artifacts.entrySet()) { + IArtifactDescriptor artifactDescriptor = artifact.getKey(); + IArtifactKey artifactKey = artifactDescriptor.getArtifactKey(); File artifactFile = artifact.getValue(); + artifactFiles.put(artifactKey, artifactFile); try { SignedContent content = verifierFactory.getSignedContent(artifactFile); if (content.isSigned()) { SignerInfo[] signerInfo = content.getSignerInfos(); - if (Arrays.stream(signerInfo).noneMatch(SignerInfo::isTrusted)) { - // Only record the untrusted elements if there are no trusted elements. + // Only record the untrusted elements if there are no trusted elements. + // Also check previously trusted certificates from the preferences. + if (Arrays.stream(signerInfo).noneMatch(SignerInfo::isTrusted) + && Arrays.stream(signerInfo).map(SignerInfo::getCertificateChain).flatMap(Arrays::stream) + .noneMatch(cert -> additionalTrustedCertificates.get().contains(cert))) { for (SignerInfo element : signerInfo) { if (!element.isTrusted()) { - Certificate[] certificateChain = element.getCertificateChain(); - if (!untrustedCertificates.contains(certificateChain[0])) { - untrustedCertificates.add(certificateChain[0]); - untrustedChain.add(certificateChain); - } - if (DebugHelper.DEBUG_CERTIFICATE_CHECKER_UNTRUSTED) { - untrustedArtifacts.computeIfAbsent(certificateChain[0], key -> new ArrayList<>()) - .add(artifactFile); - } + List<Certificate> certificateChain = Arrays.asList(element.getCertificateChain()); + untrustedCertificates.computeIfAbsent(certificateChain, key -> new LinkedHashSet<>()) + .add(artifactKey); } } } } else { - Collection<PGPSignature> signatures = PGPSignatureVerifier.getSignatures(artifact.getKey()); - if (!signatures.isEmpty()) { - if (trustedKeys == null) { - trustedKeys = buildPGPTrustore(); - } - if (trustedKeysIds.isEmpty() && !trustedKeys.isEmpty()) { - trustedKeysIds.addAll(trustedKeys.all().stream() - .map(PGPPublicKey::getKeyID).map(Long::valueOf).collect(Collectors.toSet())); + // The keys are in this destination artifact's properties if and only if the + // PGPSignatureVerifier verified the signatures against these keys. + List<PGPPublicKey> verifiedKeys = PGPPublicKeyStore + .readPublicKeys( + artifactDescriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)) + .stream().map(keyService::addKey).collect(Collectors.toList()); + if (!verifiedKeys.isEmpty()) { + if (!isTrustedKeySetInitialized) { + isTrustedKeySetInitialized = true; + trustedKeySet.addAll(trustedKeys.get().all()); } - if (signatures.stream().map(PGPSignature::getKeyID).noneMatch(trustedKeysIds::contains)) { - untrustedPGPArtifacts.put(artifact.getKey(), - signatures.stream().map(PGPSignature::getKeyID) - .map(id -> findKey(id, artifact.getKey())) - .filter(Objects::nonNull) - .collect(Collectors.toList())); + // Only record the untrusted keys if none of the keys are trusted. + if (verifiedKeys.stream().noneMatch(trustedKeySet::contains)) { + verifiedKeys.forEach(key -> untrustedPGPKeys + .computeIfAbsent(key, k -> new LinkedHashSet<>()).add(artifactKey)); } } else { - unsigned.put(artifact.getKey(), artifactFile); + unsignedArtifacts.add(artifactKey); } } - } catch (GeneralSecurityException | PGPException e) { + } catch (GeneralSecurityException e) { return new Status(IStatus.ERROR, EngineActivator.ID, Messages.CertificateChecker_SignedContentError, e); } catch (IOException e) { - return new Status(IStatus.ERROR, EngineActivator.ID, Messages.CertificateChecker_SignedContentIOError, e); + return new Status(IStatus.ERROR, EngineActivator.ID, Messages.CertificateChecker_SignedContentIOError, + e); } } // log the unsigned artifacts if requested - if (DebugHelper.DEBUG_CERTIFICATE_CHECKER_UNSIGNED && !unsigned.isEmpty()) { + if (DebugHelper.DEBUG_CERTIFICATE_CHECKER_UNSIGNED && !unsignedArtifacts.isEmpty()) { StringBuilder message = new StringBuilder("The following artifacts are unsigned:\n"); //$NON-NLS-1$ - for (File file : unsigned.values()) { - message.append(NLS.bind(" {0}\n", file.getPath())); //$NON-NLS-1$ + for (IArtifactKey key : unsignedArtifacts) { + message.append(NLS.bind(" {0}\n", artifactFiles.get(key).getPath())); //$NON-NLS-1$ } DebugHelper.debug(DEBUG_PREFIX, message.toString()); } @@ -150,130 +216,161 @@ public class CertificateChecker { // log the untrusted certificates if requested if (DebugHelper.DEBUG_CERTIFICATE_CHECKER_UNTRUSTED && !untrustedCertificates.isEmpty()) { StringBuilder message = new StringBuilder("The following certificates are untrusted:\n"); //$NON-NLS-1$ - for (Certificate cert : untrustedArtifacts.keySet()) { - message.append(cert.toString() + "\n"); //$NON-NLS-1$ + for (Map.Entry<List<Certificate>, Set<IArtifactKey>> entry : untrustedCertificates.entrySet()) { + message.append(entry.getKey().get(0).toString() + "\n"); //$NON-NLS-1$ message.append(" used by the following artifacts:\n"); //$NON-NLS-1$ - for (File file : untrustedArtifacts.get(cert)) { - message.append(NLS.bind(" {0}\n", file.getPath())); //$NON-NLS-1$ + for (IArtifactKey key : entry.getValue()) { + message.append(NLS.bind(" {0}\n", artifactFiles.get(key).getPath())); //$NON-NLS-1$ } + } DebugHelper.debug(DEBUG_PREFIX, message.toString()); } - Set<PGPPublicKey> untrustedPGPKeys = untrustedPGPArtifacts.values().stream().flatMap(Collection::stream) - .collect(Collectors.toSet()); + if (DebugHelper.DEBUG_CERTIFICATE_CHECKER_UNTRUSTED && !untrustedPGPKeys.isEmpty()) { StringBuilder message = new StringBuilder("The following PGP Keys are untrusted:\n"); //$NON-NLS-1$ - for (PGPPublicKey untrustedKey : untrustedPGPKeys) { - message.append(Long.toHexString(untrustedKey.getKeyID()) + "\n"); //$NON-NLS-1$ + for (Map.Entry<PGPPublicKey, Set<IArtifactKey>> entry : untrustedPGPKeys.entrySet()) { + message.append(Long.toHexString(entry.getKey().getKeyID()) + "\n"); //$NON-NLS-1$ message.append(" used by the following artifacts:\n"); //$NON-NLS-1$ - for (Entry<IArtifactDescriptor, Collection<PGPPublicKey>> entry : untrustedPGPArtifacts.entrySet()) { - if (entry.getValue().stream().anyMatch(signer -> signer.getKeyID() == untrustedKey.getKeyID())) { - message.append(NLS.bind(" {0}\n", entry.getKey().getArtifactKey())); //$NON-NLS-1$ - } + for (IArtifactKey key : entry.getValue()) { + message.append(NLS.bind(" {0}\n", key)); //$NON-NLS-1$ } } DebugHelper.debug(DEBUG_PREFIX, message.toString()); } - String policy = getUnsignedContentPolicy(); - //if there is unsigned content and we should never allow it, then fail without further checking certificates - if (!unsigned.isEmpty() && EngineActivator.UNSIGNED_FAIL.equals(policy)) { - return new Status(IStatus.ERROR, EngineActivator.ID, NLS.bind(Messages.CertificateChecker_UnsignedNotAllowed, unsigned)); + // if there is unsigned content and we should never allow it, then fail without + // further checking certificates + if (!unsignedArtifacts.isEmpty() && EngineActivator.UNSIGNED_FAIL.equals(policy)) { + return new Status(IStatus.ERROR, EngineActivator.ID, NLS.bind( + Messages.CertificateChecker_UnsignedNotAllowed, + unsignedArtifacts.stream().map(key -> artifactFiles.get(key)).collect(Collectors.toList()))); } - String[] details = EngineActivator.UNSIGNED_ALLOW.equals(policy) || unsigned.isEmpty() ? null - : unsigned.values().stream().map(Object::toString).toArray(String[]::new); - Certificate[][] unTrustedCertificateChains = untrustedCertificates.isEmpty() ? null - : untrustedChain.toArray(Certificate[][]::new); // If there was no unsigned content, and nothing untrusted, no need to prompt. - if (details == null && unTrustedCertificateChains == null && untrustedPGPArtifacts.isEmpty()) { - return status; + if (unsignedArtifacts.isEmpty() && untrustedCertificates.isEmpty() && untrustedPGPKeys.isEmpty()) { + return Status.OK_STATUS; } - TrustInfo trustInfo = serviceUI.getTrustInfo(unTrustedCertificateChains, - untrustedPGPKeys, - details); + TrustInfo trustInfo = artifactServiceUI.getTrustInfo(untrustedCertificates, untrustedPGPKeys, unsignedArtifacts, + artifactFiles); - // If user doesn't trust unsigned content, cancel the operation - if (!unsigned.isEmpty() && !trustInfo.trustUnsignedContent()) { - return Status.CANCEL_STATUS; - } + setTrustAlways(trustInfo.trustAlways()); - Certificate[] trustedCertificates = trustInfo.getTrustedCertificates(); - // If we had untrusted chains and nothing was trusted, cancel the operation - if (unTrustedCertificateChains != null && trustedCertificates == null) { - return new Status(IStatus.CANCEL, EngineActivator.ID, Messages.CertificateChecker_CertificateRejected); - } - // Anything that was trusted should be removed from the untrusted list - if (trustedCertificates != null) { - for (Certificate trustedCertificate : trustedCertificates) { - untrustedCertificates.remove(trustedCertificate); + if (!trustInfo.trustAlways()) { + // If there is unsigned content and user doesn't trust unsigned content, cancel + // the operation. + if (!unsignedArtifacts.isEmpty() && !trustInfo.trustUnsignedContent()) { + return Status.CANCEL_STATUS; } - } - trustedKeysIds - .addAll(trustInfo.getTrustedPGPKeys().stream().map(PGPPublicKey::getKeyID).collect(Collectors.toSet())); - untrustedPGPArtifacts.values().removeIf( - pgpKeys -> pgpKeys.stream().anyMatch(untrusted -> trustedKeysIds.contains(untrusted.getKeyID()))); - - // If there is still untrusted content, cancel the operation - if (!untrustedCertificates.isEmpty() || !untrustedPGPArtifacts.isEmpty()) { - return new Status(IStatus.CANCEL, EngineActivator.ID, Messages.CertificateChecker_CertificateRejected); - } - // If we should persist the trusted certificates, add them to the trust engine - if (trustInfo.persistTrust()) { - IStatus certifactesStatus = trustInfo.getTrustedCertificates().length == 0 ? null - : persistTrustedCertificates(trustedCertificates); - trustInfo.getTrustedPGPKeys().forEach(trustedKeys::addKey); - IStatus pgpStatus = trustInfo.getTrustedPGPKeys().isEmpty() ? null : persistTrustedKeys(trustedKeys); - if (pgpStatus == null) { - return certifactesStatus; + + Certificate[] trustedCertificates = trustInfo.getTrustedCertificates(); + // If we had untrusted chains and nothing was trusted, cancel the operation + if (!untrustedCertificates.isEmpty() && trustedCertificates == null) { + return new Status(IStatus.CANCEL, EngineActivator.ID, Messages.CertificateChecker_CertificateRejected); + } + + // Anything that was trusted should be removed from the untrusted list + if (trustedCertificates != null) { + List<Certificate> trustedCertificateList = Arrays.asList(trustedCertificates); + untrustedCertificates.keySet().removeIf(it -> trustedCertificateList.contains(it.get(0))); } - if (certifactesStatus == null) { - return pgpStatus; + + trustedKeySet.addAll(trustInfo.getTrustedPGPKeys()); + + Set<IArtifactKey> trustedArtifactKeys = trustedKeySet.stream().map(untrustedPGPKeys::get) + .filter(Objects::nonNull).flatMap(Set::stream).collect(Collectors.toSet()); + untrustedPGPKeys.values().forEach(it -> it.removeAll(trustedArtifactKeys)); + untrustedPGPKeys.values().removeIf(Collection::isEmpty); + + // If there is still untrusted content, cancel the operation + if (!untrustedCertificates.isEmpty() || !untrustedPGPKeys.isEmpty()) { + return new Status(IStatus.CANCEL, EngineActivator.ID, Messages.CertificateChecker_CertificateRejected); } - return new MultiStatus(getClass(), IStatus.OK, new IStatus[] { pgpStatus, certifactesStatus }, - pgpStatus.getMessage() + '\n' + certifactesStatus.getMessage(), null); } - return status; - } + // If we should persist the trusted certificates and keys. + if (trustInfo.persistTrust()) { + List<IStatus> statuses = new ArrayList<>(); + Set<Certificate> unsavedCertificates = new LinkedHashSet<>(); + if (trustInfo.getTrustedCertificates() != null) { + unsavedCertificates.addAll(Arrays.asList(trustInfo.getTrustedCertificates())); + } + + if (!unsavedCertificates.isEmpty()) { + // Try to save to the trust engine first. + IStatus status = persistTrustedCertificatesInTrustEngine(unsavedCertificates); + if (status != null && !status.isOK()) { + statuses.add(status); + } - private PGPPublicKey findKey(long id, IArtifactDescriptor artifact) { - PGPPublicKey key = PGPSignatureVerifier.KNOWN_KEYS.getKey(id); - if (key != null) { - return key; + // If we couldn't save them in the trust engine, save them in the preferences. + if (!unsavedCertificates.isEmpty()) { + // Be sure we add the new certificates to the previously saved certificates. + unsavedCertificates.addAll(getPreferenceTrustedCertificates()); + IStatus preferenceStatus = persistTrustedCertificates(unsavedCertificates); + if (preferenceStatus != null && !preferenceStatus.isOK()) { + statuses.add(preferenceStatus); + } + } + } + + if (!trustInfo.getTrustedPGPKeys().isEmpty()) { + // Be sure we add the new keys to the previously saved keys. + PGPPublicKeyStore keyStore = getPreferenceTrustedKeys(); + trustInfo.getTrustedPGPKeys().forEach(keyStore::addKey); + IStatus status = persistTrustedKeys(keyStore); + if (status != null && !status.isOK()) { + statuses.add(status); + } + } + + if (!statuses.isEmpty()) { + if (statuses.size() == 1) { + return statuses.get(1); + } + String message = statuses.stream().map(IStatus::getMessage).collect(Collectors.joining("\n")); //$NON-NLS-1$ + return new MultiStatus(getClass(), IStatus.OK, statuses.toArray(IStatus[]::new), message, null); + } } - // in case keys from artifact were not imported yet in keystore, add them - PGPSignatureVerifier.KNOWN_KEYS - .addKeys(artifact.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); - return PGPSignatureVerifier.KNOWN_KEYS.getKey(id); + + return Status.OK_STATUS; } - private IStatus persistTrustedCertificates(Certificate[] trustedCertificates) { - if (trustedCertificates == null) - // I'm pretty sure this would be a bug; trustedCertificates should never be null here. - return new Status(IStatus.INFO, EngineActivator.ID, Messages.CertificateChecker_CertificateRejected); - ServiceTracker<TrustEngine, TrustEngine> trustEngineTracker = new ServiceTracker<>(EngineActivator.getContext(), TrustEngine.class, null); + /** + * This modifies the argument collection to remove the certificates that were + * successfully saved. Often no certificates are saved because this tries to + * store in the Java runtime cacerts and those are typically read-only. + */ + private IStatus persistTrustedCertificatesInTrustEngine( + Collection<? extends Certificate> unsavedTrustedCertificates) { + ServiceTracker<TrustEngine, TrustEngine> trustEngineTracker = new ServiceTracker<>(EngineActivator.getContext(), + TrustEngine.class, null); trustEngineTracker.open(); Object[] trustEngines = trustEngineTracker.getServices(); try { if (trustEngines == null) return null; - for (Certificate trustedCertificate : trustedCertificates) { + for (Iterator<? extends Certificate> it = unsavedTrustedCertificates.iterator(); it.hasNext();) { + Certificate trustedCertificate = it.next(); for (Object engine : trustEngines) { TrustEngine trustEngine = (TrustEngine) engine; if (trustEngine.isReadOnly()) continue; try { trustEngine.addTrustAnchor(trustedCertificate, trustedCertificate.toString()); - // this should mean we added an anchor successfully; continue to next certificate + // this should mean we added an anchor successfully; continue to next + // certificate + it.remove(); break; } catch (IOException e) { - //just return an INFO so the user can proceed with the install - return new Status(IStatus.INFO, EngineActivator.ID, Messages.CertificateChecker_KeystoreConnectionError, e); + // just return an INFO so the user can proceed with the install + return new Status(IStatus.INFO, EngineActivator.ID, + Messages.CertificateChecker_KeystoreConnectionError, e); } catch (GeneralSecurityException e) { - return new Status(IStatus.INFO, EngineActivator.ID, Messages.CertificateChecker_CertificateError, e); + return new Status(IStatus.INFO, EngineActivator.ID, + Messages.CertificateChecker_CertificateError, e); } } } @@ -283,6 +380,43 @@ public class CertificateChecker { return Status.OK_STATUS; } + public IStatus persistTrustedCertificates(Collection<? extends Certificate> trustedCertificates) { + if (profile != null) { + ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), + profile.getProfileId()); + IEclipsePreferences node = profileScope.getNode(EngineActivator.ID); + try { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$ + CertPath certPath = certificateFactory.generateCertPath(new ArrayList<>(trustedCertificates)); + byte[] encoded = certPath.getEncoded("PKCS7"); //$NON-NLS-1$ + node.putByteArray(TRUSTED_CERTIFICATES_PROPERTY, encoded); + node.flush(); + } catch (BackingStoreException | CertificateException ex) { + return new Status(IStatus.ERROR, EngineActivator.ID, ex.getMessage(), ex); + } + } + return Status.OK_STATUS; + } + + public Set<? extends Certificate> getPreferenceTrustedCertificates() { + if (profile != null) { + ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), + profile.getProfileId()); + IEclipsePreferences node = profileScope.getNode(EngineActivator.ID); + try { + byte[] encoded = node.getByteArray(TRUSTED_CERTIFICATES_PROPERTY, null); + if (encoded != null) { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$ + CertPath certPath = certificateFactory.generateCertPath(new ByteArrayInputStream(encoded), "PKCS7"); //$NON-NLS-1$ + return new LinkedHashSet<>(certPath.getCertificates()); + } + } catch (CertificateException ex) { + DebugHelper.debug(DEBUG_PREFIX, ex.getMessage()); + } + } + return Set.of(); + } + /** * Return the policy on unsigned content. */ @@ -294,37 +428,87 @@ public class CertificateChecker { } + public void setProfile(IProfile profile) { + this.profile = profile; + } + public void add(Map<IArtifactDescriptor, File> toAdd) { artifacts.putAll(toAdd); } - public PGPPublicKeyStore buildPGPTrustore() { + public Map<PGPPublicKey, Set<Bundle>> getContributedTrustedKeys() { + // Build the map based on fingerprints to properly avoid duplicates as we + // accumulate the full set of keys. + Map<ByteBuffer, Set<Bundle>> keys = new LinkedHashMap<>(); + + // Load from the extension registry. + for (IConfigurationElement extension : RegistryFactory.getRegistry() + .getConfigurationElementsFor(EngineActivator.ID, "pgp")) { //$NON-NLS-1$ + if ("trustedKeys".equals(extension.getName())) { //$NON-NLS-1$ + String pathInBundle = extension.getAttribute("path"); //$NON-NLS-1$ + if (pathInBundle != null) { + String name = extension.getContributor().getName(); + Stream.of(EngineActivator.getContext().getBundles()) + .filter(bundle -> bundle.getSymbolicName().equals(name)).findAny().ifPresent(bundle -> { + URL keyURL = bundle.getEntry(pathInBundle); + try (InputStream stream = keyURL.openStream()) { + PGPPublicKeyStore.readPublicKeys(stream).stream().map(keyService::addKey) + .forEach(key -> keys.computeIfAbsent(ByteBuffer.wrap(key.getFingerprint()), + k -> new LinkedHashSet<>()).add(bundle)); + } catch (IOException e) { + DebugHelper.debug(DEBUG_PREFIX, e.getMessage()); + } + }); + } + } + } + + Map<PGPPublicKey, Set<Bundle>> result = keys.entrySet().stream() + .collect(Collectors.toMap(entry -> keyService.getKey(entry.getKey().array()), Map.Entry::getValue)); + return result; + } + + public boolean isTrustAlways() { + if (profile != null) { + ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), + profile.getProfileId()); + return profileScope.getNode(EngineActivator.ID).getBoolean(TRUST_ALWAYS_PROPERTY, false); + } + return false; + } + + public void setTrustAlways(boolean trustAlways) { + if (profile != null) { + ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), + profile.getProfileId()); + profileScope.getNode(EngineActivator.ID).putBoolean(TRUST_ALWAYS_PROPERTY, trustAlways); + } + } + + public PGPPublicKeyStore getPreferenceTrustedKeys() { PGPPublicKeyStore trustStore = new PGPPublicKeyStore(); - IProfile profile = agent.getService(IProfileRegistry.class).getProfile(IProfileRegistry.SELF); - trustStore.addKeys(profile.getProperty(TRUSTED_KEY_STORE_PROPERTY)); - ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), profile.getProfileId()); - trustStore.addKeys(profileScope.getNode(EngineActivator.ID).get(TRUSTED_KEY_STORE_PROPERTY, null)); - //// SECURITY ISSUE: next lines become an attack vector as we have no guarantee - //// the metadata of those IUs is safe/were signed. - //// https://bugs.eclipse.org/bugs/show_bug.cgi?id=576705#c4 - // profile.query(QueryUtil.ALL_UNITS, new NullProgressMonitor()).forEach( - // iu -> - // store.addAll(PGPSignatureVerifier.readPublicKeys(iu.getProperty(TRUSTED_KEY_STORE_PROPERTY)))); - trustStore.all().forEach(PGPSignatureVerifier.KNOWN_KEYS::addKey); + if (profile != null) { + ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), + profile.getProfileId()); + PGPPublicKeyStore + .readPublicKeys(profileScope.getNode(EngineActivator.ID).get(TRUSTED_KEY_STORE_PROPERTY, null)) + .stream().map(keyService::addKey).forEach(trustStore::addKey); + } return trustStore; } public IStatus persistTrustedKeys(PGPPublicKeyStore trustStore) { - IProfile profile = agent.getService(IProfileRegistry.class).getProfile(IProfileRegistry.SELF); - ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), profile.getProfileId()); - IEclipsePreferences node = profileScope.getNode(EngineActivator.ID); - try { - node.put(TRUSTED_KEY_STORE_PROPERTY, trustStore.toArmoredString()); - node.flush(); - return Status.OK_STATUS; - } catch (IOException | BackingStoreException ex) { - return new Status(IStatus.ERROR, EngineActivator.ID, ex.getMessage(), ex); + if (profile != null) { + ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), + profile.getProfileId()); + IEclipsePreferences node = profileScope.getNode(EngineActivator.ID); + try { + node.put(TRUSTED_KEY_STORE_PROPERTY, trustStore.toArmoredString()); + node.flush(); + } catch (IOException | BackingStoreException ex) { + return new Status(IStatus.ERROR, EngineActivator.ID, ex.getMessage(), ex); + } } + return Status.OK_STATUS; } } - diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CheckTrust.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CheckTrust.java index e885ad1f6..5b81e7ba3 100644 --- a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CheckTrust.java +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CheckTrust.java @@ -60,6 +60,7 @@ public class CheckTrust extends InstallableUnitPhase { // Instantiate a check trust manager CertificateChecker certificateChecker = new CertificateChecker(agent); certificateChecker.add(artifactRequests); + certificateChecker.setProfile(profile); return certificateChecker.start(); } diff --git a/bundles/org.eclipse.equinox.p2.extensionlocation/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.extensionlocation/META-INF/MANIFEST.MF index 76a2520ae..290732d27 100644 --- a/bundles/org.eclipse.equinox.p2.extensionlocation/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.extensionlocation/META-INF/MANIFEST.MF @@ -2,11 +2,11 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.extensionlocation;singleton:=true -Bundle-Version: 1.4.0.qualifier +Bundle-Version: 1.4.100.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.extensionlocation.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin -Export-Package: org.eclipse.equinox.internal.p2.extensionlocation;x-friends:="org.eclipse.equinox.p2.reconciler.dropins,org.eclipse.equinox.p2.ui.importexport" +Export-Package: org.eclipse.equinox.internal.p2.extensionlocation;x-friends:="org.eclipse.equinox.p2.reconciler.dropins,org.eclipse.equinox.p2.ui,org.eclipse.equinox.p2.ui.importexport" Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.5.0,4.0.0)", org.eclipse.equinox.p2.metadata Bundle-RequiredExecutionEnvironment: JavaSE-11 diff --git a/bundles/org.eclipse.equinox.p2.extensionlocation/pom.xml b/bundles/org.eclipse.equinox.p2.extensionlocation/pom.xml index 474ce108b..2cadcc0b2 100644 --- a/bundles/org.eclipse.equinox.p2.extensionlocation/pom.xml +++ b/bundles/org.eclipse.equinox.p2.extensionlocation/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.extensionlocation</artifactId> - <version>1.4.0-SNAPSHOT</version> + <version>1.4.100-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.garbagecollector/pom.xml b/bundles/org.eclipse.equinox.p2.garbagecollector/pom.xml index 989378df4..ee0e4278b 100644 --- a/bundles/org.eclipse.equinox.p2.garbagecollector/pom.xml +++ b/bundles/org.eclipse.equinox.p2.garbagecollector/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.installer/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.installer/META-INF/MANIFEST.MF index d1be7dc49..63f6c3707 100644 --- a/bundles/org.eclipse.equinox.p2.installer/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.installer/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-SymbolicName: org.eclipse.equinox.p2.installer;singleton:=true Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-Localization: plugin -Bundle-Version: 1.3.200.qualifier +Bundle-Version: 1.3.300.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.installer.InstallerActivator Bundle-ActivationPolicy: lazy Require-Bundle: org.eclipse.osgi;bundle-version="[3.7.0,4.0.0)", diff --git a/bundles/org.eclipse.equinox.p2.installer/installer.product b/bundles/org.eclipse.equinox.p2.installer/installer.product index 6c2bd7198..83b557986 100644 --- a/bundles/org.eclipse.equinox.p2.installer/installer.product +++ b/bundles/org.eclipse.equinox.p2.installer/installer.product @@ -40,7 +40,7 @@ <plugin id="org.eclipse.core.expressions"/> <plugin id="org.eclipse.core.jobs"/> <plugin id="org.eclipse.core.net"/> - <plugin id="org.eclipse.core.net.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.core.net.linux" fragment="true"/> <plugin id="org.eclipse.core.runtime"/> <plugin id="org.eclipse.core.runtime.compatibility.registry" fragment="true"/> <plugin id="org.eclipse.ecf"/> @@ -76,7 +76,7 @@ <plugin id="org.eclipse.equinox.preferences"/> <plugin id="org.eclipse.equinox.registry"/> <plugin id="org.eclipse.equinox.security"/> - <plugin id="org.eclipse.equinox.security.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.equinox.security.linux" fragment="true"/> <plugin id="org.eclipse.equinox.security.macosx" fragment="true"/> <plugin id="org.eclipse.equinox.simpleconfigurator"/> <plugin id="org.eclipse.equinox.simpleconfigurator.manipulator"/> diff --git a/bundles/org.eclipse.equinox.p2.installer/installer.properties b/bundles/org.eclipse.equinox.p2.installer/installer.properties index c28de9f82..ef00a4347 100644 --- a/bundles/org.eclipse.equinox.p2.installer/installer.properties +++ b/bundles/org.eclipse.equinox.p2.installer/installer.properties @@ -11,8 +11,8 @@ # Contributors: # IBM Corporation - initial API and implementation ############################################################################### -eclipse.p2.metadata=http://download.eclipse.org/eclipse/updates/3.5 -eclipse.p2.artifacts=http://download.eclipse.org/eclipse/updates/3.5 +eclipse.p2.metadata=https://download.eclipse.org/eclipse/updates/3.5 +eclipse.p2.artifacts=https://download.eclipse.org/eclipse/updates/3.5 eclipse.p2.flavor=tooling eclipse.p2.profileName=Eclipse SDK eclipse.p2.launcherName=eclipse diff --git a/bundles/org.eclipse.equinox.p2.installer/pom.xml b/bundles/org.eclipse.equinox.p2.installer/pom.xml index 2e06dd169..27ae673d5 100644 --- a/bundles/org.eclipse.equinox.p2.installer/pom.xml +++ b/bundles/org.eclipse.equinox.p2.installer/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.installer</artifactId> - <version>1.3.200-SNAPSHOT</version> + <version>1.3.300-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.jarprocessor/META-INF/MANIFEST.MF index 2257e8ff3..283bf2bb4 100644 --- a/bundles/org.eclipse.equinox.p2.jarprocessor/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-SymbolicName: org.eclipse.equinox.p2.jarprocessor;singleton:=true Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-Localization: plugin -Bundle-Version: 1.2.100.qualifier +Bundle-Version: 1.2.300.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-11 Main-Class: org.eclipse.equinox.internal.p2.jarprocessor.Main Export-Package: org.eclipse.equinox.internal.p2.jarprocessor;x-friends:="org.eclipse.equinox.p2.artifact.repository,org.eclipse.pde.build", diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/forceQualifierUpdate.txt b/bundles/org.eclipse.equinox.p2.jarprocessor/forceQualifierUpdate.txt index 88375d023..1519a409d 100644 --- a/bundles/org.eclipse.equinox.p2.jarprocessor/forceQualifierUpdate.txt +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/forceQualifierUpdate.txt @@ -6,4 +6,6 @@ comparator errors on new infra Bug 566668 - Comparator errors in I20200904-0210 Bug 571555 - Comparator errors in I20210226-1800 Bug 571731 - Comparator errors in I20210305-0820 -Bug 575841 - Comparator errors in I20210906-1000
\ No newline at end of file +Bug 575841 - Comparator errors in I20210906-1000 +Bug 578351 - Lambda generation order is unstable in ecj +Bug 579126 - 4.24 I-Build: I20220307-0340 - Comparator Errors Found
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/pom.xml b/bundles/org.eclipse.equinox.p2.jarprocessor/pom.xml index caf173f63..7064c875d 100644 --- a/bundles/org.eclipse.equinox.p2.jarprocessor/pom.xml +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.jarprocessor</artifactId> - <version>1.2.100-SNAPSHOT</version> + <version>1.2.300-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.metadata.repository/pom.xml b/bundles/org.eclipse.equinox.p2.metadata.repository/pom.xml index afd2687b0..4058eb8f1 100644 --- a/bundles/org.eclipse.equinox.p2.metadata.repository/pom.xml +++ b/bundles/org.eclipse.equinox.p2.metadata.repository/pom.xml @@ -14,7 +14,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.metadata/pom.xml b/bundles/org.eclipse.equinox.p2.metadata/pom.xml index eb0923e93..bf0807827 100644 --- a/bundles/org.eclipse.equinox.p2.metadata/pom.xml +++ b/bundles/org.eclipse.equinox.p2.metadata/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.operations/pom.xml b/bundles/org.eclipse.equinox.p2.operations/pom.xml index 621d99d8a..ed63d9000 100644 --- a/bundles/org.eclipse.equinox.p2.operations/pom.xml +++ b/bundles/org.eclipse.equinox.p2.operations/pom.xml @@ -14,7 +14,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/.settings/org.eclipse.jdt.ui.prefs b/bundles/org.eclipse.equinox.p2.publisher.eclipse/.settings/org.eclipse.jdt.ui.prefs index 708515333..d3d075e63 100644 --- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/.settings/org.eclipse.jdt.ui.prefs +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/.settings/org.eclipse.jdt.ui.prefs @@ -23,7 +23,7 @@ sp_cleanup.always_use_this_for_non_static_method_access=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true -sp_cleanup.format_source_code_changes_only=false +sp_cleanup.format_source_code_changes_only=true sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF index 049dcc704..0a8d62359 100644 --- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %bundleName Bundle-SymbolicName: org.eclipse.equinox.p2.publisher.eclipse;singleton:=true -Bundle-Version: 1.4.1.qualifier +Bundle-Version: 1.4.2.qualifier Bundle-Activator: org.eclipse.pde.internal.publishing.Activator Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/pom.xml b/bundles/org.eclipse.equinox.p2.publisher.eclipse/pom.xml index 9068e97a1..cf46b80e9 100644 --- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/pom.xml +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/pom.xml @@ -4,12 +4,12 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.publisher.eclipse</artifactId> - <version>1.4.1-SNAPSHOT</version> + <version>1.4.2-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/FeaturesAction.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/FeaturesAction.java index cbc41b8c6..d11aaa39d 100644 --- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/FeaturesAction.java +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/FeaturesAction.java @@ -614,7 +614,14 @@ public class FeaturesAction extends AbstractPublisherAction { // artifact per feature IU. Collection<IArtifactKey> artifacts = featureIU.getArtifacts(); for (IArtifactKey artifactKey : artifacts) { - File file = new File(feature.getLocation()); + String location = feature.getLocation(); + if (location == null) { + if (PublisherHelper.isArtifactPublish(publisherInfo)) { + throw new IllegalArgumentException(NLS.bind(Messages.exception_sourcePath, feature.getId())); + } + continue; + } + File file = new File(location); ArtifactDescriptor ad = (ArtifactDescriptor) PublisherHelper.createArtifactDescriptor(info, artifactKey, file); processArtifactPropertiesAdvice(featureIU, ad, publisherInfo); ad.setProperty(IArtifactDescriptor.DOWNLOAD_CONTENTTYPE, IArtifactDescriptor.TYPE_ZIP); diff --git a/bundles/org.eclipse.equinox.p2.publisher/.settings/.api_filters b/bundles/org.eclipse.equinox.p2.publisher/.settings/.api_filters new file mode 100644 index 000000000..5fbae6834 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher/.settings/.api_filters @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<component id="org.eclipse.equinox.p2.publisher" version="2"> + <resource path="META-INF/MANIFEST.MF"> + <filter id="926941240"> + <message_arguments> + <message_argument value="1.7.0"/> + <message_argument value="1.6.200"/> + </message_arguments> + </filter> + </resource> +</component> diff --git a/bundles/org.eclipse.equinox.p2.publisher/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.publisher/META-INF/MANIFEST.MF index 2568eeee2..693d9e66b 100644 --- a/bundles/org.eclipse.equinox.p2.publisher/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.publisher/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %bundleName Bundle-SymbolicName: org.eclipse.equinox.p2.publisher;singleton:=true -Bundle-Version: 1.6.200.qualifier +Bundle-Version: 1.7.0.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.publisher.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/bundles/org.eclipse.equinox.p2.publisher/pom.xml b/bundles/org.eclipse.equinox.p2.publisher/pom.xml index aa9c72cd8..b16ba1bd1 100644 --- a/bundles/org.eclipse.equinox.p2.publisher/pom.xml +++ b/bundles/org.eclipse.equinox.p2.publisher/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.publisher</artifactId> - <version>1.6.200-SNAPSHOT</version> + <version>1.7.0-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/AbstractPublisherAction.java b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/AbstractPublisherAction.java index 497a8a261..5f1dc9d42 100644 --- a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/AbstractPublisherAction.java +++ b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/AbstractPublisherAction.java @@ -504,7 +504,7 @@ public abstract class AbstractPublisherAction implements IPublisherAction { // if all we are doing is indexing things then add the descriptor and get on // with it - if ((publisherInfo.getArtifactOptions() & IPublisherInfo.A_PUBLISH) == 0) { + if (!PublisherHelper.isArtifactPublish(publisherInfo)) { destination.addDescriptor(descriptor, new NullProgressMonitor()); return; } @@ -556,7 +556,7 @@ public abstract class AbstractPublisherAction implements IPublisherAction { return; // if all we are doing is indexing things then add the descriptor and get on // with it - if ((publisherInfo.getArtifactOptions() & IPublisherInfo.A_PUBLISH) == 0) { + if (!PublisherHelper.isArtifactPublish(publisherInfo)) { destination.addDescriptor(descriptor, new NullProgressMonitor()); return; } diff --git a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/IPublisherInfo.java b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/IPublisherInfo.java index 0e88610aa..33796f507 100644 --- a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/IPublisherInfo.java +++ b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/IPublisherInfo.java @@ -8,8 +8,8 @@ * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: + * + * Contributors: * Code 9 - initial API and implementation * IBM - ongoing development ******************************************************************************/ @@ -40,7 +40,7 @@ public interface IPublisherInfo { public static final int A_OVERWRITE = 4; /** - * A bitwise flag indicating that MD5 hash should not be generated when + * A bitwise flag indicating that MD5 hash should not be generated when * publishing an artifact. When this flag is not specified the MD5 hash will * be generated by default. */ @@ -61,9 +61,9 @@ public interface IPublisherInfo { public IMetadataRepository getMetadataRepository(); /** - * Returns whether or not artifacts themselves should be published. - * @return <code>true</code> if artifacts should be published. - * <code>false</code> otherwise. + * Returns a bitflag that controls the publish operation, what is a bitwhise OR + * of {@link #A_INDEX},{@value #A_PUBLISH}, {@link #A_NO_MD5}, + * {@link #A_OVERWRITE} or <code>0</code> if no flags are set. */ public int getArtifactOptions(); @@ -80,7 +80,7 @@ public interface IPublisherInfo { public <T extends IPublisherAdvice> Collection<T> getAdvice(String configSpec, boolean includeDefault, String id, Version version, Class<T> type); /** - * Add the given advice to the set of publishing advices. + * Add the given advice to the set of publishing advices. * @param advice the advice to retain */ public void addAdvice(IPublisherAdvice advice); @@ -95,19 +95,19 @@ public interface IPublisherInfo { public String[] getConfigurations(); /** - * Returns the artifact repository given to the publisher as context for - * the publisher actions. May be <code>null</code>. Note that multiple + * Returns the artifact repository given to the publisher as context for + * the publisher actions. May be <code>null</code>. Note that multiple * repositories may be represented as one composite repository. - * + * * @return the context artifact repository or <code>null</code> if none. */ public IArtifactRepository getContextArtifactRepository(); /** - * Returns the metadata repository given to the publisher as context for - * the publisher actions. May be <code>null</code>. Note that multiple + * Returns the metadata repository given to the publisher as context for + * the publisher actions. May be <code>null</code>. Note that multiple * repositories may be represented as one composite repository. - * + * * @return the context metadata repository or <code>null</code> if none. */ public IMetadataRepository getContextMetadataRepository(); diff --git a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/spi/p2/publisher/PublisherHelper.java b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/spi/p2/publisher/PublisherHelper.java index d62b50b90..f074794b7 100644 --- a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/spi/p2/publisher/PublisherHelper.java +++ b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/spi/p2/publisher/PublisherHelper.java @@ -112,7 +112,7 @@ public class PublisherHelper { descriptor.setProperty(IArtifactDescriptor.ARTIFACT_SIZE, Long.toString(pathOnDisk.length())); descriptor.setProperty(IArtifactDescriptor.DOWNLOAD_SIZE, Long.toString(pathOnDisk.length())); - boolean generateChecksums = info == null || (info.getArtifactOptions() & IPublisherInfo.A_NO_MD5) == 0; + boolean generateChecksums = info == null || isArtifactGenerateChecksums(info); if (generateChecksums) { calculateChecksums(pathOnDisk, descriptor); } @@ -252,4 +252,49 @@ public class PublisherHelper { return new VersionRange(min, includeMin, max, includeMax); } + + /** + * + * @return <code>true</code> if md5 sums should be generated <code>false</code> + * otherwise + * @since 1.7.0 + */ + public static boolean isArtifactGenerateChecksums(IPublisherInfo publisherInfo) { + if ((publisherInfo.getArtifactOptions() & IPublisherInfo.A_NO_MD5) != 0) { + return false; + } + return true; + } + + /** + * + * @return <code>true</code> if existing artifacts should be overwritten + * <code>false</code> otherwise + * @since 1.7.0 + */ + public static boolean isArtifactOverwrite(IPublisherInfo publisherInfo) { + return (publisherInfo.getArtifactOptions() & IPublisherInfo.A_OVERWRITE) != 0; + } + + /** + * + * @return <code>true</code> if artifacts should published <code>false</code> + * otherwise + * @since 1.7.0 + */ + public static boolean isArtifactPublish(IPublisherInfo publisherInfo) { + int artifactOptions = publisherInfo.getArtifactOptions(); + return artifactOptions == 0 || (artifactOptions & IPublisherInfo.A_PUBLISH) != 0; + } + + /** + * + * @return <code>true</code> if the artifact index should be updated + * <code>false</code> otherwise + * @since 1.7.0 + */ + public static boolean isArtifactIndex(IPublisherInfo publisherInfo) { + int artifactOptions = publisherInfo.getArtifactOptions(); + return artifactOptions == 0 || (artifactOptions & IPublisherInfo.A_INDEX) != 0; + } } diff --git a/bundles/org.eclipse.equinox.p2.reconciler.dropins/Bootstrap.product b/bundles/org.eclipse.equinox.p2.reconciler.dropins/Bootstrap.product index 833e2413c..f48bab7f0 100644 --- a/bundles/org.eclipse.equinox.p2.reconciler.dropins/Bootstrap.product +++ b/bundles/org.eclipse.equinox.p2.reconciler.dropins/Bootstrap.product @@ -30,7 +30,7 @@ <plugin id="org.eclipse.core.expressions"/> <plugin id="org.eclipse.core.jobs"/> <plugin id="org.eclipse.core.net"/> - <plugin id="org.eclipse.core.net.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.core.net.linux" fragment="true"/> <plugin id="org.eclipse.core.runtime"/> <plugin id="org.eclipse.core.runtime.compatibility.registry" fragment="true"/> <plugin id="org.eclipse.ecf"/> @@ -64,7 +64,7 @@ <plugin id="org.eclipse.equinox.preferences"/> <plugin id="org.eclipse.equinox.registry"/> <plugin id="org.eclipse.equinox.security"/> - <plugin id="org.eclipse.equinox.security.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.equinox.security.linux" fragment="true"/> <plugin id="org.eclipse.equinox.simpleconfigurator"/> <plugin id="org.eclipse.equinox.simpleconfigurator.manipulator"/> <plugin id="org.eclipse.equinox.util"/> diff --git a/bundles/org.eclipse.equinox.p2.reconciler.dropins/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.reconciler.dropins/META-INF/MANIFEST.MF index 5131cf0e6..104fdb8d6 100644 --- a/bundles/org.eclipse.equinox.p2.reconciler.dropins/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.reconciler.dropins/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.reconciler.dropins;singleton:=true -Bundle-Version: 1.4.0.qualifier +Bundle-Version: 1.4.100.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.reconciler.dropins.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/bundles/org.eclipse.equinox.p2.reconciler.dropins/pom.xml b/bundles/org.eclipse.equinox.p2.reconciler.dropins/pom.xml index bc21ce7f7..d2362a506 100644 --- a/bundles/org.eclipse.equinox.p2.reconciler.dropins/pom.xml +++ b/bundles/org.eclipse.equinox.p2.reconciler.dropins/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.reconciler.dropins</artifactId> - <version>1.4.0-SNAPSHOT</version> + <version>1.4.100-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.repository.tools/pom.xml b/bundles/org.eclipse.equinox.p2.repository.tools/pom.xml index 2eb240c5e..379a11b8a 100644 --- a/bundles/org.eclipse.equinox.p2.repository.tools/pom.xml +++ b/bundles/org.eclipse.equinox.p2.repository.tools/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.repository/.options b/bundles/org.eclipse.equinox.p2.repository/.options new file mode 100644 index 000000000..c2d3dc538 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/.options @@ -0,0 +1,3 @@ +org.eclipse.equinox.p2.repository/credentials/debug = false +org.eclipse.equinox.p2.repository/transport/debug = false +org.eclipse.equinox.p2.repository/keyservice/debug = false diff --git a/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF index ce71e8628..0f0800872 100644 --- a/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.repository;singleton:=true -Bundle-Version: 2.5.300.qualifier +Bundle-Version: 2.6.0.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.repository.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin @@ -42,6 +42,16 @@ Require-Bundle: org.eclipse.equinox.common, Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Import-Package: javax.crypto, + org.bouncycastle;version="1.69.0", + org.bouncycastle.bcpg;version="1.69.0", + org.bouncycastle.gpg.keybox;version="1.69.0", + org.bouncycastle.gpg.keybox.jcajce;version="1.69.0", + org.bouncycastle.openpgp;version="1.69.0", + org.bouncycastle.openpgp.jcajce;version="1.69.0", + org.bouncycastle.openpgp.operator;version="1.69.0", + org.bouncycastle.openpgp.operator.bc;version="1.69.0", + org.bouncycastle.openpgp.operator.jcajce;version="1.69.0", + org.bouncycastle.util.encoders;version="1.69.0", org.eclipse.core.runtime.jobs, org.eclipse.core.runtime.preferences;version="3.2.0", org.eclipse.equinox.internal.p2.core, @@ -61,5 +71,5 @@ Import-Package: javax.crypto, org.osgi.service.packageadmin;version="1.2.0", org.osgi.service.prefs;version="1.0.0", org.osgi.util.tracker;version="1.4.0" -Service-Component: OSGI-INF/cacheManager.xml +Service-Component: OSGI-INF/cacheManager.xml, OSGI-INF/pgpPublicKeyService.xml Automatic-Module-Name: org.eclipse.equinox.p2.repository diff --git a/bundles/org.eclipse.equinox.p2.repository/OSGI-INF/pgpPublicKeyService.xml b/bundles/org.eclipse.equinox.p2.repository/OSGI-INF/pgpPublicKeyService.xml new file mode 100644 index 000000000..e303cd93e --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/OSGI-INF/pgpPublicKeyService.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.eclipse.equinox.p2.repository.spi.pgpPublicKeyService"> + <implementation class="org.eclipse.equinox.internal.p2.repository.PGPKeyServiceComponent"/> + <service> + <provide interface="org.eclipse.equinox.p2.core.spi.IAgentServiceFactory"/> + </service> + <property name="p2.agent.servicename" type="String" value="org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService"/> +</scr:component> diff --git a/bundles/org.eclipse.equinox.p2.repository/build.properties b/bundles/org.eclipse.equinox.p2.repository/build.properties index f3b28f666..89e396d06 100644 --- a/bundles/org.eclipse.equinox.p2.repository/build.properties +++ b/bundles/org.eclipse.equinox.p2.repository/build.properties @@ -17,6 +17,7 @@ bin.includes = META-INF/,\ about.html,\ plugin.properties,\ OSGI-INF/cacheManager.xml,\ - OSGI-INF/ + OSGI-INF/,\ + .options src.includes = about.html source.. = src/ diff --git a/bundles/org.eclipse.equinox.p2.repository/pom.xml b/bundles/org.eclipse.equinox.p2.repository/pom.xml index f80aa9c5c..7b16ab7c0 100644 --- a/bundles/org.eclipse.equinox.p2.repository/pom.xml +++ b/bundles/org.eclipse.equinox.p2.repository/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.repository</artifactId> - <version>2.5.300-SNAPSHOT</version> + <version>2.6.0-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/PGPKeyServiceComponent.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/PGPKeyServiceComponent.java new file mode 100644 index 000000000..dfb73bc90 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/PGPKeyServiceComponent.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.repository; + +import org.eclipse.equinox.internal.provisional.p2.repository.DefaultPGPPublicKeyService; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.core.spi.IAgentServiceFactory; + +public class PGPKeyServiceComponent implements IAgentServiceFactory { + @Override + public Object createService(IProvisioningAgent agent) { + return new DefaultPGPPublicKeyService(agent); + } +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/DebugHelper.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/DebugHelper.java index bb422888a..e4955cefe 100644 --- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/DebugHelper.java +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/DebugHelper.java @@ -25,15 +25,18 @@ public class DebugHelper { public static final boolean DEBUG_REPOSITORY_CREDENTIALS; public static final boolean DEBUG_REPOSITORY_TRANSPORT; + public static final boolean DEBUG_KEY_SERVICE; static { DebugOptions options = ServiceHelper.getService(Activator.getContext(), DebugOptions.class); if (options != null) { DEBUG_REPOSITORY_CREDENTIALS = options.getBooleanOption(Activator.ID + "/credentials/debug", false); //$NON-NLS-1$ DEBUG_REPOSITORY_TRANSPORT = options.getBooleanOption(Activator.ID + "/transport/debug", false); //$NON-NLS-1$ + DEBUG_KEY_SERVICE = options.getBooleanOption(Activator.ID + "/keyservice/debug", false); //$NON-NLS-1$ } else { DEBUG_REPOSITORY_CREDENTIALS = false; DEBUG_REPOSITORY_TRANSPORT = false; + DEBUG_KEY_SERVICE = false; } } @@ -50,7 +53,7 @@ public class DebugHelper { System.out.println(buffer.toString()); } - public static void debug(String name, String message, Object[] keyValueArray) { + public static void debug(String name, String message, Object... keyValueArray) { if (keyValueArray == null || keyValueArray.length == 0) debug(name, message); else { diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/DefaultPGPPublicKeyService.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/DefaultPGPPublicKeyService.java new file mode 100644 index 000000000..3aa501986 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/DefaultPGPPublicKeyService.java @@ -0,0 +1,869 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.internal.provisional.p2.repository; + +import java.io.*; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.ByteBuffer; +import java.nio.file.*; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.util.*; +import java.util.function.*; +import org.bouncycastle.bcpg.ArmoredInputStream; +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.gpg.keybox.*; +import org.bouncycastle.gpg.keybox.jcajce.JcaKeyBoxBuilder; +import org.bouncycastle.openpgp.*; +import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory; +import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; +import org.eclipse.equinox.internal.p2.repository.Transport; +import org.eclipse.equinox.internal.p2.repository.helpers.DebugHelper; +import org.eclipse.equinox.p2.core.IAgentLocation; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; + +/** + * @since 2.6 + */ +public class DefaultPGPPublicKeyService extends PGPPublicKeyService { + + /** + * Enable debug tracing either via debug options or via a system property. + */ + private static final boolean DEBUG_KEY_SERVICE = DebugHelper.DEBUG_KEY_SERVICE + || Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty("p2.keyserver.debug")); //$NON-NLS-1$ + + /** + * The system property used to initialized the {@link #keyServer}. + */ + private static final String KEY_SERVERS_PROPERTY = "p2.keyservers"; //$NON-NLS-1$ + + /** + * The system property used to determine where to look for the GPG pubring. + * + * @see #getGPPDirectory() + */ + private static final String GPG_HOME_PROPERTY = "p2.gpg.home"; //$NON-NLS-1$ + + /** + * The system property used to determine whether to enable GPG pubring lookup. + * + * @see #gpg + * @see #setGPG(boolean) + */ + private static final String GPG_PROPERTY = "p2.gpg"; //$NON-NLS-1$ + + /** + * The number of elapsed milliseconds after which keys cached from a key server + * are considered stale such that they will be re-fetched if possible. + */ + private static final long STALE_AFTER_MILLIS = Long.getLong("p2.keyserver.cache.stale", 24) * 1000 * 60 * 60; //$NON-NLS-1$ + + /** + * Reuse p2's transport layer for fetching keys from the key server. + */ + private final Transport transport; + + /** + * Keys {@link #addKey(PGPPublicKey) added} to this key service are cached via + * this map. + */ + private final Map<Long, LocalKeyCache> localKeys = new LinkedHashMap<>(); + + /** + * A folder with locally cached keys, indexed on {@link PGPPublicKey#getKeyID() + * key ID}. + */ + private final Path keyCache; + + /** + * The current key servers. + */ + private final Map<String, PGPKeyServer> keyServers = new LinkedHashMap<>(); + + /** + * Whether to load from GPG's pubring. + */ + private boolean gpg; + + /** + * Creates an instance associated with the given agent. + * + * @param agent the agent for which a key service is provided. + */ + public DefaultPGPPublicKeyService(IProvisioningAgent agent) { + IAgentLocation agentLocation = agent.getService(IAgentLocation.class); + URI dataArea = agentLocation.getDataArea(org.eclipse.equinox.internal.p2.repository.Activator.ID); + keyCache = Paths.get(dataArea).resolve("pgp"); //$NON-NLS-1$ + try { + Files.createDirectories(keyCache); + } catch (IOException e) { + throw new RuntimeException(e); + } + + if (DEBUG_KEY_SERVICE) { + DebugHelper.debug("KeyServer", "Cache", "location", keyCache); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + String keyServersProperty = System.getProperty(KEY_SERVERS_PROPERTY, ""); //$NON-NLS-1$ + if (!keyServersProperty.isBlank()) { + Set<String> keyServersSet = new LinkedHashSet<>(); + for (String keyServer : keyServersProperty.split("[,; \t]+")) { //$NON-NLS-1$ + if (!keyServer.isEmpty()) { + keyServersSet.add(keyServer); + } + } + + setKeyServers(keyServersSet); + } + + setGPG(Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty(GPG_PROPERTY, Boolean.TRUE.toString())) + || !System.getProperty(GPG_HOME_PROPERTY, "").isBlank()); //$NON-NLS-1$ + + transport = agent.getService(Transport.class); + } + + public Set<String> getKeyServers() { + return Collections.unmodifiableSet(keyServers.keySet()); + } + + public void setKeyServers(Set<String> keyServers) { + Map<String, PGPKeyServer> newKeyServers = new LinkedHashMap<>(); + for (String keyServer : keyServers) { + PGPKeyServer pgpKeyServer = this.keyServers.get(keyServer); + if (pgpKeyServer == null) { + pgpKeyServer = new PGPKeyServer(keyServer, this.keyCache) { + @Override + protected boolean isStale(Path path) { + return DefaultPGPPublicKeyService.this.isStale(path); + } + + @Override + protected IStatus download(URI uri, OutputStream receiver, IProgressMonitor monitor) { + return DefaultPGPPublicKeyService.this.download(uri, receiver, monitor); + } + + @Override + protected void log(Throwable throwable) { + DefaultPGPPublicKeyService.this.log(throwable); + } + }; + } + newKeyServers.put(keyServer, pgpKeyServer); + } + + this.keyServers.clear(); + this.keyServers.putAll(newKeyServers); + } + + @Override + public PGPPublicKey getKey(String fingerprint) { + int length = fingerprint.length(); + if (length >= 16) { + long keyID = Long.parseUnsignedLong(fingerprint.substring(length - 16, length), 16); + Collection<PGPPublicKey> keys = getKeys(keyID); + for (PGPPublicKey key : keys) { + if (toHexFingerprint(key).equalsIgnoreCase(fingerprint)) { + return key; + } + } + } + return null; + } + + @Override + public Collection<PGPPublicKey> getKeys(long keyID) { + List<PGPPublicKey> keys = new ArrayList<>(); + for (PGPKeyServer keyServer : keyServers.values()) { + keys.addAll(keyServer.getKeys(keyID)); + } + + keys.addAll(getLocalKeyCache(keyID).get()); + + keys.addAll(getDefaultKeys(keyID)); + + return reconcileKeys(keys); + } + + public boolean isGGP() { + return gpg; + } + + public void setGPG(boolean gpg) { + this.gpg = gpg; + } + + protected List<PGPPublicKey> getDefaultKeys(long keyID) { + return gpg ? getGPGPubringKeys(keyID) : Collections.emptyList(); + } + + protected List<PGPPublicKey> reconcileKeys(List<PGPPublicKey> keys) { + if (keys.size() <= 1) { + return new ArrayList<>(keys); + } + + Map<ByteBuffer, PGPPublicKey> encodings = new LinkedHashMap<>(); + Map<ByteBuffer, PGPPublicKey> fingerprints = new LinkedHashMap<>(); + for (PGPPublicKey key : keys) { + try { + ByteBuffer encoding = ByteBuffer.wrap(key.getEncoded()); + PGPPublicKey existingKey = encodings.put(encoding, key); + if (existingKey == null) { + ByteBuffer fingerprint = ByteBuffer.wrap(key.getFingerprint()); + PGPPublicKey otherKey = fingerprints.put(fingerprint, key); + if (otherKey != null) { + fingerprints.put(fingerprint, choose(otherKey, key)); + } + } + } catch (IOException e) { + log(e); + } + } + + return new ArrayList<>(fingerprints.values()); + } + + /** + * While {@link #reconcileKeys(List) reconciling}, when two keys have the same + * fingerprint, this method must be chosen in favor of the other to be retained + * in the result. + * + * @param key1 the first key from which to choose. + * @param key2 the second key from which to choose. + * @return the key with the newest or most complete details. + */ + protected PGPPublicKey choose(PGPPublicKey key1, PGPPublicKey key2) { + // Favor the one with the newest information. + long signatureTime1 = getNewestSignature(key1); + long signatureTime2 = getNewestSignature(key2); + if (signatureTime1 > signatureTime2) { + return key1; + } else if (signatureTime1 < signatureTime2) { + return key2; + } + + // Favor the one with the most information. + int signatureCount1 = getSignatureCount(key1); + int signatureCount2 = getSignatureCount(key2); + if (signatureCount1 > signatureCount2) { + return key1; + } else if (signatureCount1 < signatureCount2) { + return key2; + } + + return key1; + } + + protected static int getSignatureCount(PGPPublicKey key) { + int result = 0; + for (Iterator<PGPSignature> signatures = key.getSignatures(); signatures.hasNext(); signatures.next()) { + ++result; + } + for (Iterator<PGPSignature> signatures = key.getKeySignatures(); signatures.hasNext(); signatures.next()) { + ++result; + } + return result; + } + + protected static long getNewestSignature(PGPPublicKey key) { + long result = 0; + for (Iterator<PGPSignature> signatures = key.getSignatures(); signatures.hasNext();) { + PGPSignature signature = signatures.next(); + long time = signature.getCreationTime().getTime(); + result = Math.max(result, time); + } + for (Iterator<PGPSignature> signatures = key.getKeySignatures(); signatures.hasNext();) { + PGPSignature signature = signatures.next(); + long time = signature.getCreationTime().getTime(); + result = Math.max(result, time); + } + + return result; + } + + @Override + public PGPPublicKey addKey(PGPPublicKey key) { + long keyID = key.getKeyID(); + LocalKeyCache localKeyCache = getLocalKeyCache(keyID); + localKeyCache.add(key); + + Collection<PGPPublicKey> keys = getKeys(keyID); + byte[] fingerprint = key.getFingerprint(); + for (PGPPublicKey otherKey : keys) { + if (Arrays.equals(otherKey.getFingerprint(), fingerprint)) { + return otherKey; + } + } + + // We should never get this far. + return key; + } + + protected boolean isStale(Path path) { + try { + FileTime lastModifiedTime = Files.getLastModifiedTime(path); + long lastModified = lastModifiedTime.toMillis(); + long currentTime = System.currentTimeMillis(); + return currentTime - lastModified > STALE_AFTER_MILLIS; + } catch (IOException e) { + return true; + } + } + + @Override + public Set<PGPPublicKey> getVerifiedCertifications(PGPPublicKey key) { + Set<PGPPublicKey> certifications = new LinkedHashSet<>(); + LOOP: for (Iterator<PGPSignature> signatures = key.getSignatures(); signatures.hasNext();) { + PGPSignature signature = signatures.next(); + long signingKeyID = signature.getKeyID(); + for (PGPPublicKey signingKey : getKeys(signingKeyID)) { + switch (signature.getSignatureType()) { + case PGPSignature.SUBKEY_BINDING: + case PGPSignature.PRIMARYKEY_BINDING: { + try { + signature.init(new BcPGPContentVerifierBuilderProvider(), signingKey); + if (signature.verifyCertification(signingKey, key) + && isCreatedBeforeRevocation(signature, signingKey)) { + certifications.add(signingKey); + continue LOOP; + } + } catch (PGPException e) { + //$FALL-THROUGH$ + } + break; + } + case PGPSignature.DEFAULT_CERTIFICATION: + case PGPSignature.NO_CERTIFICATION: + case PGPSignature.CASUAL_CERTIFICATION: + case PGPSignature.POSITIVE_CERTIFICATION: { + for (Iterator<String> userIDs = key.getUserIDs(); userIDs.hasNext();) { + String userID = userIDs.next(); + try { + signature.init(new BcPGPContentVerifierBuilderProvider(), signingKey); + if (signature.verifyCertification(userID, key) + && isCreatedBeforeRevocation(signature, signingKey)) { + certifications.add(signingKey); + continue LOOP; + } + } catch (PGPException e) { + //$FALL-THROUGH$ + } + } + break; + } + } + } + } + return certifications; + } + + @Override + public Date getVerifiedRevocationDate(PGPPublicKey key) { + for (Iterator<PGPSignature> signatures = key.getSignatures(); signatures.hasNext();) { + PGPSignature signature = signatures.next(); + long signingKeyID = signature.getKeyID(); + for (PGPPublicKey signingKey : getKeys(signingKeyID)) { + switch (signature.getSignatureType()) { + case PGPSignature.KEY_REVOCATION: + case PGPSignature.CERTIFICATION_REVOCATION: { + try { + signature.init(new BcPGPContentVerifierBuilderProvider(), signingKey); + if (signature.verifyCertification(key)) { + return signature.getCreationTime(); + } + } catch (PGPException e) { + //$FALL-THROUGH$ + } + break; + } + } + } + } + return null; + } + + private LocalKeyCache getLocalKeyCache(long keyID) { + LocalKeyCache localKeyCache = localKeys.get(keyID); + if (localKeyCache == null) { + String hexKeyID = toHex(keyID); + Path cache = keyCache.resolve(hexKeyID + ".asc"); //$NON-NLS-1$ + localKeyCache = new LocalKeyCache(cache) { + @Override + protected List<PGPPublicKey> reconcileKeys(List<PGPPublicKey> keys) { + return DefaultPGPPublicKeyService.this.reconcileKeys(keys); + } + + @Override + protected void log(Throwable throwable) { + DefaultPGPPublicKeyService.this.log(throwable); + } + }; + localKeys.put(keyID, localKeyCache); + } + return localKeyCache; + } + + protected Collection<PGPPublicKey> fetchKeys(URI uri, Path cache) throws IOException { + try { + ByteArrayOutputStream reciever = new ByteArrayOutputStream(); + IStatus download = download(uri, reciever, new NullProgressMonitor()); + if (!download.isOK()) { + Throwable exception = download.getException(); + if (exception != null) { + throw new IOException(download.getMessage(), exception); + } + throw new IOException(download.getMessage()); + } + List<PGPPublicKey> result = new ArrayList<>(); + byte[] bytes = reciever.toByteArray(); + try (InputStream input = new ArmoredInputStream(new ByteArrayInputStream(bytes))) { + result.addAll(loadKeys(input)); + } + + try (OutputStream out = newAtomicOutputStream(cache)) { + out.write(bytes); + } + + return result; + } catch (IOException ex) { + if (Files.isRegularFile(cache)) { + try (InputStream input = new ArmoredInputStream(new BufferedInputStream(Files.newInputStream(cache)))) { + return loadKeys(input); + } catch (IOException ex1) { + try { + // Assume the cache is corrupt so delete it. + Files.delete(cache); + } catch (IOException ex2) { + log(ex2); + } + // Rethrow original network failure exception + throw new IOException("Error while processing " + uri + " as well while processing the cache " //$NON-NLS-1$ //$NON-NLS-2$ + + cache + ": " + ex1.getMessage(), ex); //$NON-NLS-1$ + } + } + throw new IOException("Error while processing " + uri, ex); //$NON-NLS-1$ + } + } + + protected IStatus download(URI uri, OutputStream receiver, IProgressMonitor monitor) { + return transport.download(uri, receiver, monitor); + } + + protected void log(Throwable throwable) { + if (DEBUG_KEY_SERVICE) { + LogHelper.log(new Status(IStatus.ERROR, org.eclipse.equinox.internal.p2.repository.Activator.ID, + throwable.getMessage(), throwable)); + } + } + + protected static OutputStream newAtomicOutputStream(Path cache) throws IOException { + Path temp = Files.createTempFile(cache.getParent(), "out", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$ + return new BufferedOutputStream(Files.newOutputStream(temp)) { + @Override + public void close() throws IOException { + super.close(); + Files.move(temp, cache, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); + } + }; + } + + protected static List<PGPPublicKey> loadKeys(InputStream input) throws IOException { + try { + List<PGPPublicKey> result = new ArrayList<>(); + for (Object o : new JcaPGPObjectFactory(input)) { + if (o instanceof PGPPublicKeyRingCollection) { + collectKeys((PGPPublicKeyRingCollection) o, result::add); + } else if (o instanceof PGPPublicKeyRing) { + collectKeys((PGPPublicKeyRing) o, result::add); + } else if (o instanceof PGPPublicKey) { + result.add((PGPPublicKey) o); + } + } + return result; + } catch (RuntimeException ex) { + throw new IOException(ex); + } + } + + private static void collectKeys(PGPPublicKeyRingCollection pgpPublicKeyRingCollection, + Consumer<PGPPublicKey> collector) { + pgpPublicKeyRingCollection.forEach(keyring -> collectKeys(keyring, collector)); + } + + private static void collectKeys(PGPPublicKeyRing pgpPublicKeyRing, Consumer<PGPPublicKey> collector) { + pgpPublicKeyRing.getPublicKeys().forEachRemaining(collector::accept); + } + + private static abstract class LocalKeyCache { + private Path cache; + private FileTime lastModifiedTime; + private List<PGPPublicKey> keys; + + public LocalKeyCache(Path cache) { + this.cache = cache; + } + + protected abstract void log(Throwable throwable); + + protected abstract List<PGPPublicKey> reconcileKeys(List<PGPPublicKey> keysToReconcile); + + public List<PGPPublicKey> get() { + if (keys != null) { + try { + FileTime newLastModifiedTime = Files.getLastModifiedTime(cache); + if (lastModifiedTime == null || lastModifiedTime.compareTo(newLastModifiedTime) < 0) { + lastModifiedTime = newLastModifiedTime; + } else { + return keys; + } + } catch (Exception e) { + //$FALL-THROUGH$ + } + } + + if (!Files.isRegularFile(cache)) { + return List.of(); + } + + try (InputStream input = new ArmoredInputStream(new BufferedInputStream(Files.newInputStream(cache)))) { + keys = loadKeys(input); + return keys; + } catch (IOException ex) { + log(ex); + try { + // Assume the cache is corrupt so delete it. + Files.delete(cache); + } catch (IOException ex2) { + log(ex2); + } + return List.of(); + } + } + + public void add(PGPPublicKey key) { + List<PGPPublicKey> oldKeys = get(); + List<PGPPublicKey> newKeys = new ArrayList<>(oldKeys); + newKeys.add(key); + newKeys = reconcileKeys(newKeys); + if (!oldKeys.equals(newKeys)) { + try (OutputStream underlyingStream = newAtomicOutputStream(cache); + OutputStream output = new ArmoredOutputStream(underlyingStream)) { + for (PGPPublicKey newKey : newKeys) { + newKey.encode(output); + } + } catch (IOException e) { + log(e); + return; + } + keys = newKeys; + } + } + } + + private static abstract class PGPKeyServer { + private final Map<Long, List<PGPPublicKey>> keyIDMap = new LinkedHashMap<>(); + + private final String keyServer; + + private final Path keyCache; + + public PGPKeyServer(String keyServer, Path baseCache) { + this.keyServer = keyServer; + keyCache = baseCache.resolve(keyServer.replace(':', '_')); + if (!Files.isDirectory(this.keyCache)) { + try { + Files.createDirectory(keyCache); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + protected abstract boolean isStale(Path path); + + protected abstract IStatus download(URI uri, OutputStream receiver, IProgressMonitor monitor); + + protected abstract void log(Throwable throwable); + + public List<PGPPublicKey> getKeys(long keyID) { + List<PGPPublicKey> keys = keyIDMap.get(keyID); + String hexKeyID = toHex(keyID); + Path cache = keyCache.resolve(hexKeyID + ".asc"); //$NON-NLS-1$ + boolean needsRemoteFetch = !Files.isRegularFile(cache) || isStale(cache); + if (keys == null || needsRemoteFetch) { + try { + Iterable<PGPPublicKey> fetchedKeys; + if (needsRemoteFetch) { + String link = "https://" + keyServer + "/pks/lookup?op=get&search=0x" + hexKeyID; //$NON-NLS-1$ //$NON-NLS-2$ + if (DEBUG_KEY_SERVICE) { + DebugHelper.debug("KeyServer", "Searching", "uri", link); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + URI uri = new URI(link); + fetchedKeys = fetchKeys(uri, cache); + } else { + try (InputStream input = new ArmoredInputStream( + new BufferedInputStream(Files.newInputStream(cache)))) { + fetchedKeys = loadKeys(input); + } + } + + List<PGPPublicKey> newKeys = new ArrayList<>(); + for (PGPPublicKey fetchedKey : fetchedKeys) { + long fetchedKeyID = fetchedKey.getKeyID(); + if (fetchedKeyID == keyID) { + newKeys.add(fetchedKey); + } + } + + keyIDMap.put(keyID, newKeys); + keys = newKeys; + } catch (URISyntaxException | IOException e) { + log(e); + if (keys == null || keys.isEmpty()) { + List<PGPPublicKey> newKeys = List.of(); + keyIDMap.put(keyID, newKeys); + keys = newKeys; + } + } + } + + return Collections.unmodifiableList(keys); + } + + protected Collection<PGPPublicKey> fetchKeys(URI uri, Path cache) throws IOException { + try { + ByteArrayOutputStream reciever = new ByteArrayOutputStream(); + IStatus download = download(uri, reciever, new NullProgressMonitor()); + if (!download.isOK()) { + // If the file is not found, save an empty file to prevent repeated attempts to + // download from this URI. + Throwable exception = download.getException(); + if (exception instanceof FileNotFoundException) { + log(exception); + } else { + if (exception != null) { + throw new IOException(download.getMessage(), exception); + } + throw new IOException(download.getMessage()); + } + } + List<PGPPublicKey> result; + byte[] bytes = reciever.toByteArray(); + try { + try (InputStream input = new ArmoredInputStream(new ByteArrayInputStream(bytes))) { + result = loadKeys(input); + } + } catch (IOException ex) { + log(ex); + // If the bytes can't be processed cache an empty file to prevent repeated + // attempts. + bytes = new byte[0]; + result = List.of(); + } + + try (OutputStream out = newAtomicOutputStream(cache)) { + out.write(bytes); + } + + return result; + } catch (IOException ex) { + // If the key server fails, load the cache if it exists. + if (Files.isRegularFile(cache)) { + try (InputStream input = new ArmoredInputStream( + new BufferedInputStream(Files.newInputStream(cache)))) { + return loadKeys(input); + } catch (IOException ex1) { + try { + // Assume the cache is corrupt so delete it. + Files.delete(cache); + } catch (IOException ex2) { + log(ex2); + } + // Rethrow original network failure exception with additional details + throw new IOException("Error while processing " + uri + " as well while processing the cache " //$NON-NLS-1$ //$NON-NLS-2$ + + cache + ": " + ex1.getMessage(), ex); //$NON-NLS-1$ + } + } + throw new IOException("Error while processing " + uri, ex); //$NON-NLS-1$ + } + } + + } + + private static List<PGPPublicKey> getGPGPubringKeys(long keyID) { + return GPGPubringCache.getKeys(keyID); + } + + private static class GPGPubringCache { + private static final Supplier<PGPPublicKeyRingCollection> GPG_PUBRING = getGPGPubring(); + private static volatile PGPPublicKeyRingCollection cachePubring; + private static volatile Map<Long, List<PGPPublicKey>> cache; + + public static List<PGPPublicKey> getKeys(long keyID) { + PGPPublicKeyRingCollection pubring = GPG_PUBRING.get(); + if (pubring != cachePubring) { + Map<Long, List<PGPPublicKey>> newCache = new LinkedHashMap<>(); + for (Iterator<PGPPublicKeyRing> keyRings = pubring.getKeyRings(); keyRings.hasNext();) { + for (PGPPublicKey key : keyRings.next()) { + long keyID2 = key.getKeyID(); + List<PGPPublicKey> keys = newCache.computeIfAbsent(keyID2, it -> new ArrayList<>()); + keys.add(key); + } + } + cache = newCache; + cachePubring = pubring; + } + + List<PGPPublicKey> result = cache.get(keyID); + return result == null ? List.of() : result; + } + } + + private static abstract class GPGPubringSupplier implements Supplier<PGPPublicKeyRingCollection> { + + private final Path pubring; + + private PGPPublicKeyRingCollection keyRingCollection; + + private FileTime lastModifiedTime; + + public GPGPubringSupplier(Path pubring) { + this.pubring = pubring; + try { + keyRingCollection = new PGPPublicKeyRingCollection(Collections.emptyList()); + } catch (IOException | PGPException e) { + // Cannot happen for an empty collection. + throw new RuntimeException(e); + } + } + + @Override + public PGPPublicKeyRingCollection get() { + try { + FileTime newLastModifiedTime = Files.getLastModifiedTime(pubring); + if (lastModifiedTime == null || lastModifiedTime.compareTo(newLastModifiedTime) < 0) { + lastModifiedTime = newLastModifiedTime; + keyRingCollection = buildPubring(); + } + } catch (Exception e) { + //$FALL-THROUGH$ + } + return keyRingCollection; + } + + protected abstract PGPPublicKeyRingCollection buildPubring() throws Exception; + } + + private static Supplier<PGPPublicKeyRingCollection> getGPGPubring() { + Path gpgDirectory = getGPPDirectory(); + Path pubringGpg = gpgDirectory.resolve("pubring.gpg"); //$NON-NLS-1$ + Path pubringKbx = gpgDirectory.resolve("pubring.kbx"); //$NON-NLS-1$ + + if (Files.isRegularFile(pubringGpg)) { + return new GPGPubringSupplier(pubringGpg) { + @Override + protected PGPPublicKeyRingCollection buildPubring() throws Exception { + try (InputStream input = new BufferedInputStream(Files.newInputStream(pubringGpg))) { + PGPPublicKeyRingCollection keyRingCollection = new PGPPublicKeyRingCollection(input, + new JcaKeyFingerprintCalculator()); + return keyRingCollection; + } + } + }; + } else if (Files.isRegularFile(pubringKbx)) { + return new GPGPubringSupplier(pubringKbx) { + @Override + protected PGPPublicKeyRingCollection buildPubring() throws Exception { + try (InputStream input = new BufferedInputStream(Files.newInputStream(pubringKbx))) { + KeyBox keyBox = new JcaKeyBoxBuilder().build(input); + List<PGPPublicKeyRing> pgpPublicKeyRings = new ArrayList<>(); + for (KeyBlob keyBlob : keyBox.getKeyBlobs()) { + switch (keyBlob.getType()) { + case OPEN_PGP_BLOB: { + PGPPublicKeyRing pgpPublicKeyRing = ((PublicKeyRingBlob) keyBlob).getPGPPublicKeyRing(); + pgpPublicKeyRings.add(pgpPublicKeyRing); + } + default: { + //$FALL-THROUGH$ + } + } + } + PGPPublicKeyRingCollection keyRingCollection = new PGPPublicKeyRingCollection( + pgpPublicKeyRings); + return keyRingCollection; + } + } + }; + } else { + PGPPublicKeyRingCollection empty; + try { + empty = new PGPPublicKeyRingCollection(Collections.emptyList()); + } catch (IOException | PGPException e) { + // Cannot happen for an empty collection. + throw new RuntimeException(e); + } + return () -> empty; + } + } + + @SuppressWarnings("nls") + private static Path getGPPDirectory() { + // Handle ~ as might be used on macos and linux. + Function<String, Path> resolveTilde = s -> { + if (s.startsWith("~/") || s.startsWith("~" + File.separatorChar)) { + return new File(System.getProperty("user.home"), s.substring(2)).getAbsoluteFile().toPath(); + } + return Paths.get(s); + }; + + // Allow the user to specify the GPG home used by p2 specifically. + Path path = checkDirectory(System.getProperty(GPG_HOME_PROPERTY), resolveTilde); + if (path != null) { + return path; + } + + path = checkDirectory(System.getenv("GNUPGHOME"), resolveTilde); + if (path != null) { + return path; + } + + if ("win32".equals(System.getProperty("osgi.os"))) { + // On Windows prefer %APPDATA%\gnupg if it exists, even if Cygwin is used. + path = checkDirectory(System.getenv("APPDATA"), //$NON-NLS-1$ + s -> Paths.get(s).resolve("gnupg")); //$NON-NLS-1$ + if (path != null) { + return path; + } + } + // All systems, including Cygwin and even Windows if %APPDATA%\gnupg doesn't + // exist. + return resolveTilde.apply("~/.gnupg"); //$NON-NLS-1$ + } + + private static Path checkDirectory(String dir, Function<String, Path> toPath) { + if (dir != null && !dir.isBlank()) { + try { + Path directory = toPath.apply(dir); + if (Files.isDirectory(directory)) { + return directory; + } + } catch (RuntimeException e) { + //$FALL-THROUGH$ + } + } + return null; + } +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/spi/IArtifactUIServices.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/spi/IArtifactUIServices.java new file mode 100644 index 000000000..b5cc328f5 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/spi/IArtifactUIServices.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.p2.repository.artifact.spi; + +import java.io.File; +import java.security.cert.Certificate; +import java.util.*; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.eclipse.equinox.p2.core.UIServices; +import org.eclipse.equinox.p2.core.UIServices.TrustInfo; +import org.eclipse.equinox.p2.metadata.IArtifactKey; + +/** + * An interface optionally implemented by {@link UIServices} to provide richer + * information for the users. In particular, the users often wish to know which + * artifacts are signed by which certificates or which keys when they are + * {@link UIServices#getTrustInfo(Certificate[][], Collection, String[]) + * prompted} whether to trust such certificates or keys. + * + * @since 2.6 + * + * @see UIServices#getTrustInfo(Certificate[][], Collection, String[]) + */ +public interface IArtifactUIServices { + + /** + * Opens a UI prompt to capture information about trusted content. + * + * @param untrustedCertificateChains a map from untrusted certificate chains to + * the set of keys of the artifacts signed by + * that chain. + * @param untrustedPGPKeys a map of untrusted PGP public keys to the + * set of keys of the artifacts signed by that + * key. + * @param unsignedArtifacts a set of keys of the artifacts that are not + * signed. + * @param artifactFiles a map from artifact keys to the file + * associated with that artifact key. + * + * @return the TrustInfo that describes the user's choices for trusting + * certificates, keys, and unsigned content. + * + * @see #getTrustInfo(UIServices, Map, Map, Set, Map) + * @see UIServices + */ + TrustInfo getTrustInfo( // + Map<List<Certificate>, Set<IArtifactKey>> untrustedCertificateChains, // + Map<PGPPublicKey, Set<IArtifactKey>> untrustedPGPKeys, // + Set<IArtifactKey> unsignedArtifacts, // + Map<IArtifactKey, File> artifactFiles); + + /** + * Opens a UI prompt to capture information about trusted content. This + * implementation is useful for delegating to an old-style + * {@link UIServices#getTrustInfo(Certificate[][], Collection, String[]) + * UIServices implementation} that does not support the IArtifactUIServices + * interface. + * + * @param uiServices the delegate UI services. + * @param untrustedCertificateChains a map from untrusted certificate chains to + * the set of keys of the artifacts signed by + * that chain. + * @param untrustedPGPKeys a map of untrusted PGP public keys to the + * set of keys of the artifacts signed by that + * key. + * @param unsignedArtifacts a set of keys of the artifacts that are not + * signed. + * @param artifactFiles a map from artifact keys to the file + * associated with that artifact key. + * + * @return the TrustInfo that describes the user's choices for trusting + * certificates, keys, and unsigned content. + * + * @see #getTrustInfo(Map, Map, Set, Map) + * @see UIServices#getTrustInfo(Certificate[][], Collection, String[]) + */ + static TrustInfo getTrustInfo(UIServices uiServices, // + Map<List<Certificate>, Set<IArtifactKey>> untrustedCertificateChains, // + Map<PGPPublicKey, Set<IArtifactKey>> untrustedPGPKeys, // + Set<IArtifactKey> unsignedArtifacts, // + Map<IArtifactKey, File> artifactFiles) { + Certificate[][] unTrustedCertificateChainsArray = untrustedCertificateChains.keySet().stream() + .map(c -> c.toArray(Certificate[]::new)).toArray(Certificate[][]::new); + String[] details = unsignedArtifacts.isEmpty() ? null + : unsignedArtifacts.stream().map(artifactFiles::get).map(Objects::toString).toArray(String[]::new); + return uiServices.getTrustInfo(unTrustedCertificateChainsArray, untrustedPGPKeys.keySet(), details); + } +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/spi/PGPPublicKeyService.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/spi/PGPPublicKeyService.java new file mode 100644 index 000000000..6fbdff286 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/spi/PGPPublicKeyService.java @@ -0,0 +1,243 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.p2.repository.spi; + +import java.util.*; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.util.encoders.Hex; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.core.spi.IAgentServiceFactory; + +/** + * A service for managing and searching {@link PGPPublicKey keys}. + * Implementations may make use of a + * <a href="https://datatracker.ietf.org/doc/html/draft-shaw-openpgp-hkp-00">key + * server</a> to fetch up-to-date information about keys. Implementations should + * generally provide support for caching and efficient lookup of keys, + * especially lookup based on {@link PGPPublicKey#getKeyID() key ID} because + * signatures generally use {@link PGPSignature#getKeyID() key IDs} and this is + * the primary use case. + * + * <p> + * Implementors of this service are responsible for registering the + * implementation with the {@link IProvisioningAgent provisioning agent} either + * {@link IProvisioningAgent#registerService(String, Object) explicitly} or via + * an {@link IAgentServiceFactory agent service factory}. + * </p> + * + * @see PGPPublicKey#getKeyID() + * @see PGPSignature#getKeyID() + * @see IAgentServiceFactory + * @see IProvisioningAgent#registerService(String, Object) + * + * @since 2.6 + */ +public abstract class PGPPublicKeyService { + /** + * The name used for obtaining a reference to the key service. + * + * @see IProvisioningAgent#getService(Class) + * @see IProvisioningAgent#getService(String) + */ + public static final String SERVICE_NAME = PGPPublicKeyService.class.getName(); + + /** + * Returns the key associated with the given + * {@link PGPPublicKey#getFingerprint() fingerprint}. + * + * @param fingerprint the fingerprint for which to search. + * @return the key with the matching fingerprint. + * + * @see PGPPublicKey#getFingerprint() + */ + public PGPPublicKey getKey(byte[] fingerprint) { + return getKey(toHex(fingerprint)); + } + + /** + * Returns the key associated with the given + * {@link PGPPublicKey#getFingerprint() fingerprint} represented as a + * {@link #toHex(byte[]) hexadecimal} string value. + * + * @param fingerprint the fingerprint for which to search. + * @return the key with the matching fingerprint. + * + * @see PGPPublicKey#getFingerprint() + * @see #toHex(byte[]) + */ + public abstract PGPPublicKey getKey(String fingerprint); + + /** + * Returns the keys associated with the given {@link PGPPublicKey#getKeyID() key + * ID}. In general, key ID collisions are possible so implementations must be + * tolerant of that. + * + * @param keyID the key ID for which to search. + * @return the keys with the matching key IDs. + * + * @see PGPPublicKey#getKeyID() + * @see PGPSignature#getKeyID() + */ + public abstract Collection<PGPPublicKey> getKeys(long keyID); + + /** + * Adds the given key to this key service. An implementations may fetch more + * up-to-date information about this key from a key server and may return a + * different key than the one passed in here. In general an implementation may + * also return an existing key, with the same fingerprint, already known to the + * key service. + * + * @param key the key to add. + * @return the normalized key available in this key service. + */ + public abstract PGPPublicKey addKey(PGPPublicKey key); + + /** + * Returns the set of keys that have been verified to have signed the given key. + * These are the links in the web of trust. + * + * @param key the key for which to find keys that have signed it. + * @return the set of keys that have been verified to have signed the given key. + * + * @see PGPSignature#verifyCertification(String, PGPPublicKey) + * @see PGPSignature#verifyCertification(PGPPublicKey, PGPPublicKey) + */ + public abstract Set<PGPPublicKey> getVerifiedCertifications(PGPPublicKey key); + + /** + * If this key has a revocation signature that is verified to have been signed + * by the public key of that revocation signature, this returns the + * {@link PGPSignature#getCreationTime() creation time} of that signature, + * otherwise it returns <code>null</code>. + * + * @param key the key to test for revocation. + * @return when this key was verifiably revoked, or <code>null</code> if it is + * not revoked. + * + * @see PGPSignature#getKeyID() + * @see PGPPublicKey#hasRevocation() + * @see PGPSignature#getCreationTime() + * @see PGPSignature#KEY_REVOCATION + * @see PGPSignature#SUBKEY_REVOCATION + */ + public abstract Date getVerifiedRevocationDate(PGPPublicKey key); + + /** + * Returns whether the signature's {@link PGPSignature creation time} is before + * the key's {@link #getVerifiedRevocationDate(PGPPublicKey) verified revocation + * date}, if that key has one. + * + * @param signature the signature to test. + * @param key the corresponding key of this signature against which to + * test. + * @return <code>true</code> if the signature was created before the key was + * revoked or if the key is not revoked, <code>false</code> otherwise. + * + * @throws IllegalArgumentException if the signature's + * {@link PGPSignature#getKeyID() key} is not + * the same as the key's + * {@link PGPPublicKey#getKeyID() key ID} + */ + public boolean isCreatedBeforeRevocation(PGPSignature signature, PGPPublicKey key) { + if (signature.getKeyID() != key.getKeyID()) { + throw new IllegalArgumentException("The signature's key ID must be the same as the key's key ID"); //$NON-NLS-1$ + } + Date verifiedRevocationDate = getVerifiedRevocationDate(key); + if (verifiedRevocationDate != null) { + long signatureCreationTime = signature.getCreationTime().getTime(); + if (signatureCreationTime >= verifiedRevocationDate.getTime()) { + return false; + } + } + return true; + } + + /** + * Returns the hexadecimal representation of the key's + * {@link PGPPublicKey#getFingerprint() fingerprint}. + * + * @param key the key for which to get the hexadecimal fingerprint. + * + * @return the hexadecimal representation of the key's fingerprint. + */ + public static String toHexFingerprint(PGPPublicKey key) { + return Hex.toHexString(key.getFingerprint()); + } + + /** + * Returns the hexadecimal representation of the given bytes. + * + * @param bytes the bytes to convert to a hexadecimal representation. + * @return the hexadecimal representation of the given bytes. + */ + public static String toHex(byte[] bytes) { + return Hex.toHexString(bytes); + } + + /** + * Returns the hexadecimal representation of the given long value, typically a + * key ID, padded with leading zeros to a length of 16. + * + * @param keyID the long value, typically a key ID, to convert to a hexadecimal + * representation. + * @return the hexadecimal representation of the given long value, padded with + * leading zeros. + */ + public static String toHex(long keyID) { + return String.format("%1$016x", keyID); //$NON-NLS-1$ + } + + /** + * If the signature's {@link PGPSignature#getCreationTime() creation time} is + * before the key's {@link PGPPublicKey#getCreationTime() creation time}, this + * returns a negative value equal to the number of milliseconds that the + * signature was created before the key was created. If the key has a + * {@link PGPPublicKey#getValidSeconds() validity period}, i.e., if the key + * expires, and the signature's creation time is after the key's expiration, + * returns a positive value equal to the number of milliseconds that the + * signature was created after the key's expiration. Otherwise, the signature + * was created during the period of time that the key was valid and this returns + * <code>0</code>. + * + * @param signature the signature to test. + * @param key the corresponding key of this signature against which to + * test. + * @return a negative value representing the number of milliseconds the + * signature was created before the key was created, a positive value + * representing the number of milliseconds the signature as created + * after the key expired, or <code>0</code> if the signature was created + * during the valid period of the key. + * @throws IllegalArgumentException if the signature's + * {@link PGPSignature#getKeyID() key} is not + * the same as the key's + * {@link PGPPublicKey#getKeyID() key ID} + */ + public static long compareSignatureTimeToKeyValidityTime(PGPSignature signature, PGPPublicKey key) { + if (signature.getKeyID() != key.getKeyID()) { + throw new IllegalArgumentException("The signature's key ID must be the same as the key's key ID"); //$NON-NLS-1$ + } + long keyCreationTime = key.getCreationTime().getTime(); + long signatureCreationTime = signature.getCreationTime().getTime(); + long delta = signatureCreationTime - keyCreationTime; + if (delta < 0) { + return delta; + } + long validSeconds = key.getValidSeconds(); + if (validSeconds != 0) { + delta = signatureCreationTime - (keyCreationTime + validSeconds * 1000); + if (delta > 0) { + return delta; + } + } + return 0; + } +} diff --git a/bundles/org.eclipse.equinox.p2.sar/pom.xml b/bundles/org.eclipse.equinox.p2.sar/pom.xml index 2a1c6de4c..fb9f99428 100644 --- a/bundles/org.eclipse.equinox.p2.sar/pom.xml +++ b/bundles/org.eclipse.equinox.p2.sar/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.tests.discovery/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.tests.discovery/META-INF/MANIFEST.MF index 6ccf36ca3..361f71e5e 100644 --- a/bundles/org.eclipse.equinox.p2.tests.discovery/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.tests.discovery/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.equinox.p2.tests.discovery;singleton:=true -Bundle-Version: 1.3.100.qualifier +Bundle-Version: 1.3.200.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Export-Package: org.eclipse.equinox.p2.discovery.tests;x-internal:=true, diff --git a/bundles/org.eclipse.equinox.p2.tests.discovery/plugin.xml b/bundles/org.eclipse.equinox.p2.tests.discovery/plugin.xml index 968754bef..f6f6a95f0 100644 --- a/bundles/org.eclipse.equinox.p2.tests.discovery/plugin.xml +++ b/bundles/org.eclipse.equinox.p2.tests.discovery/plugin.xml @@ -86,7 +86,7 @@ style="push"> <parameter name="org.eclipse.equinox.p2.ui.discovery.commands.DirectoryParameter" - value="http://www.eclipse.org/mylyn/discovery/directory-3.3.xml"> + value="https://www.eclipse.org/mylyn/discovery/directory-3.3.xml"> </parameter> <parameter name="org.eclipse.equinox.p2.ui.discovery.commands.TagsParameter" @@ -99,7 +99,7 @@ style="push"> <parameter name="org.eclipse.equinox.p2.ui.discovery.commands.RepositoryParameter" - value="http://download.eclipse.org/tools/mylyn/update/e3.4"> + value="https://download.eclipse.org/tools/mylyn/update/e3.4"> </parameter> </command> </menuContribution> diff --git a/bundles/org.eclipse.equinox.p2.tests.discovery/pom.xml b/bundles/org.eclipse.equinox.p2.tests.discovery/pom.xml index 1a7e81a20..ce3ff89b1 100644 --- a/bundles/org.eclipse.equinox.p2.tests.discovery/pom.xml +++ b/bundles/org.eclipse.equinox.p2.tests.discovery/pom.xml @@ -4,12 +4,12 @@ <parent> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> <groupId>org.eclipse</groupId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.tests.discovery</artifactId> - <version>1.3.100-SNAPSHOT</version> + <version>1.3.200-SNAPSHOT</version> <packaging>eclipse-test-plugin</packaging> <properties> diff --git a/bundles/org.eclipse.equinox.p2.tests.optimizers/pom.xml b/bundles/org.eclipse.equinox.p2.tests.optimizers/pom.xml index 3e89aab0c..089a4d389 100644 --- a/bundles/org.eclipse.equinox.p2.tests.optimizers/pom.xml +++ b/bundles/org.eclipse.equinox.p2.tests.optimizers/pom.xml @@ -4,7 +4,7 @@ <parent> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> <groupId>org.eclipse</groupId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.tests.reconciler.product/pom.xml b/bundles/org.eclipse.equinox.p2.tests.reconciler.product/pom.xml index 95344861e..13eefc82a 100644 --- a/bundles/org.eclipse.equinox.p2.tests.reconciler.product/pom.xml +++ b/bundles/org.eclipse.equinox.p2.tests.reconciler.product/pom.xml @@ -11,7 +11,7 @@ <parent> <groupId>org.eclipse</groupId> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> diff --git a/bundles/org.eclipse.equinox.p2.tests.reconciler.product/reconciler.product b/bundles/org.eclipse.equinox.p2.tests.reconciler.product/reconciler.product index 6cd3000e1..0532b3ab5 100644 --- a/bundles/org.eclipse.equinox.p2.tests.reconciler.product/reconciler.product +++ b/bundles/org.eclipse.equinox.p2.tests.reconciler.product/reconciler.product @@ -38,8 +38,8 @@ <plugin id="org.apache.commons.jxpath"/> <plugin id="org.apache.commons.logging"/> <plugin id="org.apache.felix.scr"/> - <plugin id="org.apache.httpcomponents.httpclient"/> - <plugin id="org.apache.httpcomponents.httpcore"/> + <plugin id="org.apache.httpcomponents.client5.httpclient5"/> + <plugin id="org.apache.httpcomponents.core5.httpcore5"/> <plugin id="org.apache.xmlgraphics"/> <plugin id="org.eclipse.core.commands"/> <plugin id="org.eclipse.core.contenttype"/> @@ -49,7 +49,7 @@ <plugin id="org.eclipse.core.expressions"/> <plugin id="org.eclipse.core.jobs"/> <plugin id="org.eclipse.core.net"/> - <plugin id="org.eclipse.core.net.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.core.net.linux" fragment="true"/> <plugin id="org.eclipse.core.runtime"/> <plugin id="org.eclipse.e4.core.commands"/> <plugin id="org.eclipse.e4.core.contexts"/> @@ -78,7 +78,7 @@ <plugin id="org.eclipse.ecf.filetransfer"/> <plugin id="org.eclipse.ecf.identity"/> <plugin id="org.eclipse.ecf.provider.filetransfer"/> - <plugin id="org.eclipse.ecf.provider.filetransfer.httpclient45"/> + <plugin id="org.eclipse.ecf.provider.filetransfer.httpclient5"/> <plugin id="org.eclipse.ecf.provider.filetransfer.source"/> <plugin id="org.eclipse.ecf.provider.filetransfer.ssl" fragment="true"/> <plugin id="org.eclipse.ecf.ssl" fragment="true"/> @@ -120,7 +120,7 @@ <plugin id="org.eclipse.equinox.preferences"/> <plugin id="org.eclipse.equinox.registry"/> <plugin id="org.eclipse.equinox.security"/> - <plugin id="org.eclipse.equinox.security.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.equinox.security.linux" fragment="true"/> <plugin id="org.eclipse.equinox.security.ui"/> <plugin id="org.eclipse.equinox.simpleconfigurator"/> <plugin id="org.eclipse.equinox.simpleconfigurator.manipulator"/> diff --git a/bundles/org.eclipse.equinox.p2.tests.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.tests.ui/META-INF/MANIFEST.MF index 37792cc02..58c4adbf1 100644 --- a/bundles/org.eclipse.equinox.p2.tests.ui/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.tests.ui/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.tests.ui -Bundle-Version: 1.3.300.qualifier +Bundle-Version: 1.3.400.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Require-Bundle: org.eclipse.core.runtime;bundle-version="3.4.100", diff --git a/bundles/org.eclipse.equinox.p2.tests.ui/pom.xml b/bundles/org.eclipse.equinox.p2.tests.ui/pom.xml index 2f8b7876a..f941878fb 100644 --- a/bundles/org.eclipse.equinox.p2.tests.ui/pom.xml +++ b/bundles/org.eclipse.equinox.p2.tests.ui/pom.xml @@ -15,13 +15,13 @@ <parent> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> <groupId>org.eclipse</groupId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.tests.ui</artifactId> - <version>1.3.300-SNAPSHOT</version> + <version>1.3.400-SNAPSHOT</version> <packaging>eclipse-test-plugin</packaging> <properties> diff --git a/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/actions/ElementUtilsTest.java b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/actions/ElementUtilsTest.java index d0125ad1e..a4cfbd144 100644 --- a/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/actions/ElementUtilsTest.java +++ b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/actions/ElementUtilsTest.java @@ -58,15 +58,15 @@ public class ElementUtilsTest extends ProfileModificationActionTest { public void testUpdateUsingElements() throws URISyntaxException { // Two visible repos, one is added, the other is not - URI known1 = new URI("http://example.com/known1"); - URI known2 = new URI("http://example.com/known2"); + URI known1 = new URI("https://example.com/known1"); + URI known2 = new URI("https://example.com/known2"); IMetadataRepositoryManager manager = getMetadataRepositoryManager(); manager.addRepository(known1); // Add system repos that should not be known or affected by ElementUtils // One is an enabled system repo, one is disabled system repo - URI uri = new URI("http://example.com/1"); - URI uri2 = new URI("http://example.com/2"); + URI uri = new URI("https://example.com/1"); + URI uri2 = new URI("https://example.com/2"); manager.addRepository(uri); manager.setRepositoryProperty(uri, IRepository.PROP_SYSTEM, Boolean.toString(true)); manager.addRepository(uri2); @@ -82,7 +82,7 @@ public class ElementUtilsTest extends ProfileModificationActionTest { MetadataRepositoryElement[] elements = children.toArray(new MetadataRepositoryElement[children.size()]); // Add a visible repo not known by the elements - URI uri3 = new URI("http://example.com/3"); + URI uri3 = new URI("https://example.com/3"); manager.addRepository(uri3); // Now update the repo using the elements. diff --git a/bundles/org.eclipse.equinox.p2.tests.verifier/pom.xml b/bundles/org.eclipse.equinox.p2.tests.verifier/pom.xml index 909390b0d..08bb6782c 100644 --- a/bundles/org.eclipse.equinox.p2.tests.verifier/pom.xml +++ b/bundles/org.eclipse.equinox.p2.tests.verifier/pom.xml @@ -4,7 +4,7 @@ <parent> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> <groupId>org.eclipse</groupId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.tests/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.tests/META-INF/MANIFEST.MF index 7859591e4..73c3d22e7 100644 --- a/bundles/org.eclipse.equinox.p2.tests/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.tests/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.tests;singleton:=true -Bundle-Version: 1.8.600.qualifier +Bundle-Version: 1.8.700.qualifier Bundle-ClassPath: . Bundle-Activator: org.eclipse.equinox.p2.tests.TestActivator Bundle-Vendor: %providerName @@ -45,7 +45,7 @@ Require-Bundle: org.eclipse.equinox.frameworkadmin, org.eclipse.ecf.filetransfer;bundle-version="4.0.0", org.eclipse.ecf.identity;bundle-version="3.1.0", org.eclipse.ecf.provider.filetransfer;bundle-version="3.1.0", - org.eclipse.ecf.provider.filetransfer.httpclient45, + org.eclipse.ecf.provider.filetransfer.httpclient5, org.eclipse.equinox.p2.reconciler.dropins;bundle-version="1.1.0", org.eclipse.ant.core;bundle-version="3.2.200", org.apache.ant;bundle-version="1.7.1", @@ -114,6 +114,5 @@ Import-Package: org.eclipse.ant.core, org.eclipse.osgi.util;version="1.1.0", org.osgi.framework;version="1.3.0", org.osgi.service.packageadmin;version="1.2.0", - org.osgi.util.tracker;version="1.3.0", - org.easymock;version="4.0.0" + org.osgi.util.tracker;version="1.3.0" Automatic-Module-Name: org.eclipse.equinox.p2.tests diff --git a/bundles/org.eclipse.equinox.p2.tests/plugin.xml b/bundles/org.eclipse.equinox.p2.tests/plugin.xml index 841b529ea..857699598 100644 --- a/bundles/org.eclipse.equinox.p2.tests/plugin.xml +++ b/bundles/org.eclipse.equinox.p2.tests/plugin.xml @@ -153,4 +153,10 @@ suffix="@testArtifactRepositoryRegistry"> </filter> </extension> + <extension + point="org.eclipse.equinox.p2.engine.pgp"> + <trustedKeys + path="testData/pgp/signer2-publickey.asc"> + </trustedKeys> + </extension> </plugin> diff --git a/bundles/org.eclipse.equinox.p2.tests/pom.xml b/bundles/org.eclipse.equinox.p2.tests/pom.xml index f0006e807..894085114 100644 --- a/bundles/org.eclipse.equinox.p2.tests/pom.xml +++ b/bundles/org.eclipse.equinox.p2.tests/pom.xml @@ -10,13 +10,13 @@ <parent> <groupId>org.eclipse</groupId> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.tests</artifactId> - <version>1.8.600-SNAPSHOT</version> + <version>1.8.700-SNAPSHOT</version> <packaging>eclipse-test-plugin</packaging> <properties> diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractProvisioningTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractProvisioningTest.java index dcc84bbfb..ab74b9572 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractProvisioningTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractProvisioningTest.java @@ -739,7 +739,7 @@ public abstract class AbstractProvisioningTest extends TestCase { } } - public static void writeBuffer(File outputFile, StringBuffer buffer) throws IOException { + public static void writeBuffer(File outputFile, StringBuilder buffer) throws IOException { outputFile.getParentFile().mkdirs(); try (FileOutputStream stream = new FileOutputStream(outputFile)) { stream.write(buffer.toString().getBytes()); @@ -1367,7 +1367,7 @@ public abstract class AbstractProvisioningTest extends TestCase { IInstallableUnit sourceIU = it.next(); IQueryResult destinationCollector = destination.query(QueryUtil.createIUQuery(sourceIU), null); assertEquals(message, 1, queryResultSize(destinationCollector)); - assertTrue(message, sourceIU.equals(destinationCollector.iterator().next())); + assertEquals(message, sourceIU, destinationCollector.iterator().next()); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestData.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestData.java index c423c5dd5..d2a8bc870 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestData.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestData.java @@ -135,15 +135,16 @@ public class TestData { * @throws IOException */ public static void assertEquals(ZipInputStream expected, ZipInputStream actual) throws IOException { - Map<String, Object[]> jar1 = getEntries(expected); - Map<String, Object[]> jar2 = getEntries(actual); - for (String name : jar1.keySet()) { - Object[] file1 = jar1.get(name); - Object[] file2 = jar2.remove(name); - Assert.assertNotNull(file2); - - ZipEntry entry1 = (ZipEntry) file1[0]; - ZipEntry entry2 = (ZipEntry) file2[0]; + Map<String, Object[]> expectedEntries = getEntries(expected); + Map<String, Object[]> actualEntries = getEntries(actual); + for (String name : expectedEntries.keySet()) { + Object[] expectedFiles = expectedEntries.get(name); + Object[] actualFiles = actualEntries.remove(name); + Assert.assertNotNull(name + " entry is missing in actual zip stream (actual=" + actualEntries.keySet() + + ", expected=" + expectedEntries.keySet() + ")", actualFiles); + + ZipEntry entry1 = (ZipEntry) expectedFiles[0]; + ZipEntry entry2 = (ZipEntry) actualFiles[0]; // compare the entries Assert.assertEquals(entry1.getName(), entry2.getName()); Assert.assertEquals(entry1.getSize(), entry2.getSize()); @@ -154,11 +155,11 @@ public class TestData { Assert.assertEquals(entry1.getMethod(), entry2.getMethod()); // check the content of the entries - Assert.assertArrayEquals((byte[]) file1[1], (byte[]) file2[1]); + Assert.assertArrayEquals((byte[]) expectedFiles[1], (byte[]) actualFiles[1]); } // ensure that we have consumed all of the entries in the second JAR - Assert.assertEquals(0, jar2.size()); + Assert.assertEquals(0, actualEntries.size()); } /** diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/PGPSignatureVerifierTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/PGPSignatureVerifierTest.java index 36b50a545..dd3356767 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/PGPSignatureVerifierTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/PGPSignatureVerifierTest.java @@ -13,31 +13,57 @@ package org.eclipse.equinox.p2.tests.artifact.processors; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; import java.nio.file.Files; +import java.util.Set; import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IStatus; -import org.eclipse.equinox.internal.p2.artifact.processors.pgp.Messages; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerifier; import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; +import org.eclipse.equinox.internal.provisional.p2.repository.DefaultPGPPublicKeyService; +import org.eclipse.equinox.p2.core.ProvisionException; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; import org.eclipse.equinox.p2.repository.artifact.IProcessingStepDescriptor; import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor; import org.eclipse.equinox.p2.repository.artifact.spi.ProcessingStepDescriptor; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; +import org.eclipse.equinox.p2.tests.TestAgentProvider; import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; public class PGPSignatureVerifierTest { + @Rule + public TestAgentProvider agentProvider = new TestAgentProvider(); + + @Before + public void initialize() { + try { + PGPPublicKeyService keyService = agentProvider.getService(PGPPublicKeyService.class); + if (keyService instanceof DefaultPGPPublicKeyService) { + DefaultPGPPublicKeyService defaultPGPPublicKeyService = (DefaultPGPPublicKeyService) keyService; + defaultPGPPublicKeyService.setKeyServers(Set.of()); + defaultPGPPublicKeyService.setGPG(false); + } + } catch (ProvisionException e) { + //$FALL-THROUGH$ + } + } + // @formatter:off /* - * About test keys: - * * Install the public&private keys locally - * * then generate signatures with eg `gpg -u signer2@fakeuser.eclipse.org -a --output signed_by_signer_2 --detach-sig testArtifact` + * About test keys: * Install the public&private keys locally * then generate + * signatures with eg `gpg -u signer2@fakeuser.eclipse.org -a --output + * signed_by_signer_2 --detach-sig testArtifact` */ // @formatter:on @@ -50,6 +76,23 @@ public class PGPSignatureVerifierTest { return res; } + private static class ArtifactOutputStream extends ByteArrayOutputStream implements IAdaptable { + IArtifactDescriptor descriptor = new ArtifactDescriptor( + new ArtifactKey("whatever", "whatever", Version.parseVersion("1.0.0"))); + + public IArtifactDescriptor getDescriptor() { + return descriptor; + } + + @Override + public <T> T getAdapter(Class<T> adapter) { + if (adapter.isInstance(descriptor)) { + return adapter.cast(descriptor); + } + return null; + } + } + private String read(String resource) throws IOException, URISyntaxException { return Files.readString(new File(FileLocator.toFileURL(getClass().getResource(resource)).toURI()).toPath()); } @@ -58,8 +101,11 @@ public class PGPSignatureVerifierTest { public void testOK() throws Exception { IProcessingStepDescriptor processingStepDescriptor = new ProcessingStepDescriptor(null, null, false); IArtifactDescriptor artifact = createArtifact("signed_by_signer_1", "public_signer1.pgp"); + @SuppressWarnings("resource") PGPSignatureVerifier verifier = new PGPSignatureVerifier(); - verifier.initialize(null, processingStepDescriptor, artifact); + verifier.initialize(agentProvider.getAgent(), processingStepDescriptor, artifact); + ArtifactOutputStream artifactOutputStream = new ArtifactOutputStream(); + verifier.link(artifactOutputStream, new NullProgressMonitor()); Assert.assertTrue(verifier.getStatus().toString(), verifier.getStatus().isOK()); try (InputStream bytes = getClass().getResourceAsStream("testArtifact")) { bytes.transferTo(verifier); @@ -67,6 +113,12 @@ public class PGPSignatureVerifierTest { Assert.assertTrue(verifier.getStatus().isOK()); verifier.close(); Assert.assertTrue(verifier.getStatus().isOK()); + + IArtifactDescriptor descriptor = artifactOutputStream.getDescriptor(); + Assert.assertNotNull("Signatures should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME)); + Assert.assertNotNull("Keys should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); } @Test @@ -74,10 +126,22 @@ public class PGPSignatureVerifierTest { IProcessingStepDescriptor processingStepDescriptor = new ProcessingStepDescriptor(null, null, false); IArtifactDescriptor artifact = createArtifact("signed_by_signer_1", "public_signer2.pgp"); try (PGPSignatureVerifier verifier = new PGPSignatureVerifier()) { - verifier.initialize(null, processingStepDescriptor, artifact); - IStatus status = verifier.getStatus(); - assertEquals(IStatus.ERROR, status.getSeverity()); - assertTrue(status.getMessage().contains("Public key not found for")); + verifier.initialize(agentProvider.getAgent(), processingStepDescriptor, artifact); + ArtifactOutputStream artifactOutputStream = new ArtifactOutputStream(); + verifier.link(artifactOutputStream, new NullProgressMonitor()); + Assert.assertTrue(verifier.getStatus().toString(), verifier.getStatus().isOK()); + try (InputStream bytes = getClass().getResourceAsStream("testArtifact")) { + bytes.transferTo(verifier); + } + Assert.assertTrue(verifier.getStatus().isOK()); + verifier.close(); + Assert.assertTrue(verifier.getStatus().isOK()); + + IArtifactDescriptor descriptor = artifactOutputStream.getDescriptor(); + Assert.assertNull("No signatures should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME)); + Assert.assertNull("No keys should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); } } @@ -86,7 +150,7 @@ public class PGPSignatureVerifierTest { IProcessingStepDescriptor processingStepDescriptor = new ProcessingStepDescriptor(null, null, false); IArtifactDescriptor artifact = createArtifact("signed_by_signer_1_tampered", "public_signer1.pgp"); try (PGPSignatureVerifier verifier = new PGPSignatureVerifier()) { - verifier.initialize(null, processingStepDescriptor, artifact); + verifier.initialize(agentProvider.getAgent(), processingStepDescriptor, artifact); // signature has random modification, making it invalid by itself Assert.assertFalse(verifier.getStatus().isOK()); } @@ -96,8 +160,9 @@ public class PGPSignatureVerifierTest { public void testSignatureForAnotherArtifact() throws Exception { IProcessingStepDescriptor processingStepDescriptor = new ProcessingStepDescriptor(null, null, false); IArtifactDescriptor artifact = createArtifact("signed_by_signer_1_otherArtifact", "public_signer1.pgp"); + @SuppressWarnings("resource") PGPSignatureVerifier verifier = new PGPSignatureVerifier(); - verifier.initialize(null, processingStepDescriptor, artifact); + verifier.initialize(agentProvider.getAgent(), processingStepDescriptor, artifact); Assert.assertTrue(verifier.getStatus().isOK()); try (InputStream bytes = getClass().getResourceAsStream("testArtifact")) { bytes.transferTo(verifier); @@ -106,6 +171,6 @@ public class PGPSignatureVerifierTest { verifier.close(); IStatus status = verifier.getStatus(); assertEquals(IStatus.ERROR, status.getSeverity()); - assertTrue(status.getMessage().contains(Messages.Error_SignatureAndFileDontMatch)); + assertTrue(status.getMessage().matches(".*signature.*invalid.*")); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/PGPVerifierTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/PGPVerifierTest.java index c010af306..0d4525b23 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/PGPVerifierTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/PGPVerifierTest.java @@ -10,15 +10,26 @@ *******************************************************************************/ package org.eclipse.equinox.p2.tests.artifact.repository; +import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.Set; +import java.util.stream.Stream; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerifier; import org.eclipse.equinox.internal.p2.artifact.repository.MirrorRequest; import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; +import org.eclipse.equinox.internal.provisional.p2.repository.DefaultPGPPublicKeyService; +import org.eclipse.equinox.p2.core.IAgentLocation; import org.eclipse.equinox.p2.metadata.Version; +import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; import org.eclipse.equinox.p2.tests.AbstractProvisioningTest; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -30,7 +41,6 @@ public class PGPVerifierTest extends AbstractProvisioningTest { @Before public void setUp() throws Exception { super.setUp(); - PGPSignatureVerifier.KNOWN_KEYS.clear(); } private void loadPGPTestRepo(String repoName) throws Exception { @@ -42,29 +52,75 @@ public class PGPVerifierTest extends AbstractProvisioningTest { @Test public void testAllGood() throws Exception { - IStatus mirrorStatus = performMirrorFrom("repoPGPOK"); + MirrorRequest mirrorRequest = performMirrorFrom("repoPGPOK"); + IStatus mirrorStatus = mirrorRequest.getResult(); assertOK(mirrorStatus); + + IArtifactDescriptor[] artifactDescriptors = targetRepo.getArtifactDescriptors(mirrorRequest.getArtifactKey()); + Assert.assertEquals(1, artifactDescriptors.length); + IArtifactDescriptor descriptor = artifactDescriptors[0]; + Assert.assertNotNull("Signatures should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME)); + Assert.assertNotNull("Keys should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); } @Test public void testAllGoodWithEncodedProperties() throws Exception { - IStatus mirrorStatus = performMirrorFrom("repoPGPOK_encoded"); + MirrorRequest mirrorRequest = performMirrorFrom("repoPGPOK_encoded"); + IStatus mirrorStatus = mirrorRequest.getResult(); assertOK(mirrorStatus); + + IArtifactDescriptor[] artifactDescriptors = targetRepo.getArtifactDescriptors(mirrorRequest.getArtifactKey()); + Assert.assertEquals(1, artifactDescriptors.length); + IArtifactDescriptor descriptor = artifactDescriptors[0]; + Assert.assertNotNull("Signatures should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME)); + Assert.assertNotNull("Keys should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); } - private IStatus performMirrorFrom(String repoName) throws Exception { + private MirrorRequest performMirrorFrom(String repoName) throws Exception { + // Clear the remembered keys/cache of the agent. + IAgentLocation agentLocation = getAgent().getService(IAgentLocation.class); + Path repositoryCache = Paths + .get(agentLocation.getDataArea(org.eclipse.equinox.internal.p2.repository.Activator.ID)); + if (Files.isDirectory(repositoryCache)) { + try (Stream<Path> walk = Files.walk(repositoryCache)) { + walk.sorted(Comparator.reverseOrder()).forEach(path -> { + try { + Files.delete(path); + } catch (IOException e) { + // Ignore + } + }); + } + } + DefaultPGPPublicKeyService keyService = new DefaultPGPPublicKeyService(getAgent()); + keyService.setGPG(false); + keyService.setKeyServers(Set.of()); + + getAgent().registerService(PGPPublicKeyService.SERVICE_NAME, keyService); loadPGPTestRepo(repoName); ArtifactKey key = new ArtifactKey("osgi.bundle", "blah", Version.create("1.0.0.123456")); MirrorRequest mirrorRequest = new MirrorRequest(key, targetRepo, NO_PROPERTIES, NO_PROPERTIES, getTransport()); mirrorRequest.perform(sourceRepo, getMonitor()); - return mirrorRequest.getResult(); + return mirrorRequest; } @Test public void testMissingPublicKey() throws Exception { - IStatus mirrorStatus = performMirrorFrom("repoMissingPublicKey"); - assertNotOK(mirrorStatus); - assertTrue(mirrorStatus.toString().contains("Public key not found")); + MirrorRequest mirrorRequest = performMirrorFrom("repoMissingPublicKey"); + IStatus mirrorStatus = mirrorRequest.getResult(); + assertOK(mirrorStatus); + + IArtifactDescriptor[] artifactDescriptors = targetRepo.getArtifactDescriptors(mirrorRequest.getArtifactKey()); + Assert.assertEquals(1, artifactDescriptors.length); + IArtifactDescriptor descriptor = artifactDescriptors[0]; + Assert.assertNull("Signatures should not be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME)); + Assert.assertNull("Keys should not be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); } @Override diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/TransferTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/TransferTest.java index 718db58cc..dd7b38315 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/TransferTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/TransferTest.java @@ -57,7 +57,9 @@ public class TransferTest extends AbstractProvisioningTest { try { fos.close(); if (f != null) { - String[] ecfPlugins = new String[] {"org.eclipse.ecf", "org.eclipse.ecf.identity", "org.eclipse.ecf.filetransfer", "org.eclipse.ecf.provider.filetransfer", "org.eclipse.ecf.provider.filetransfer.httpclient45"}; + String[] ecfPlugins = new String[] { "org.eclipse.ecf", "org.eclipse.ecf.identity", + "org.eclipse.ecf.filetransfer", "org.eclipse.ecf.provider.filetransfer", + "org.eclipse.ecf.provider.filetransfer.httpclient5" }; StringBuilder buffer = new StringBuilder(); for (String ecfPlugin : ecfPlugins) { Bundle bundle = Platform.getBundle(ecfPlugin); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/DirectorApplicationTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/DirectorApplicationTest.java index 973a234ff..9ac6d4b8a 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/DirectorApplicationTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/DirectorApplicationTest.java @@ -838,4 +838,24 @@ public class DirectorApplicationTest extends AbstractProvisioningTest { return avoidTrustPromptService.getTrustInfo(untrustedChain, null); } + public void testPGPSignedArtifact() throws Exception { + File srcRepo = getTestData(null, "/testData/pgp/repoPGPOK"); + + IArtifactRepositoryManager artifactManager = getAgent().getService(IArtifactRepositoryManager.class); + IMetadataRepositoryManager metadataManager = getAgent().getService(IMetadataRepositoryManager.class); + assertNotNull(artifactManager); + assertNotNull(metadataManager); + + File destinationRepo = new File(getTempFolder(), "DirectorApp Destination"); + String[] args = getSingleRepoArgs(null, srcRepo, srcRepo, destinationRepo, "blah"); + + destinationRepo.mkdirs(); + + StringBuffer buffer = runDirectorApp(null, args); + assertFalse(buffer.toString(), buffer.toString().contains("failed")); + + artifactManager.removeRepository(srcRepo.toURI()); + metadataManager.removeRepository(srcRepo.toURI()); + delete(destinationRepo); + } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/CertificateCheckerTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/CertificateCheckerTest.java index 5f4049a5a..ba56cc6ea 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/CertificateCheckerTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/CertificateCheckerTest.java @@ -16,8 +16,12 @@ package org.eclipse.equinox.p2.tests.engine; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.security.cert.Certificate; +import java.util.Comparator; import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; import org.eclipse.core.runtime.IStatus; import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerifier; import org.eclipse.equinox.internal.p2.core.AgentLocation; @@ -25,12 +29,14 @@ import org.eclipse.equinox.internal.p2.core.ProvisioningAgent; import org.eclipse.equinox.internal.p2.engine.EngineActivator; import org.eclipse.equinox.internal.p2.engine.phases.CertificateChecker; import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; +import org.eclipse.equinox.internal.provisional.p2.repository.DefaultPGPPublicKeyService; import org.eclipse.equinox.p2.core.IAgentLocation; import org.eclipse.equinox.p2.core.ProvisionException; import org.eclipse.equinox.p2.core.UIServices; import org.eclipse.equinox.p2.engine.IProfileRegistry; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; import org.eclipse.equinox.p2.tests.AbstractProvisioningTest; import org.eclipse.equinox.p2.tests.TestActivator; import org.eclipse.equinox.p2.tests.TestData; @@ -39,7 +45,7 @@ import org.eclipse.equinox.p2.tests.TestData; * Tests for {@link CertificateChecker}. */ public class CertificateCheckerTest extends AbstractProvisioningTest { - private static final String PGP_PUBLIC_KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + private static final String PGP_SIGNER1_PUBLIC_KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "mQGNBGB1bugBDADQ5l7YnS9hNFRkBKSrvVNHt/TxeHaNNIHkdTC56I1QdThsOt4Y\n" + "oQRI27AEOaY1GFEi6+QqwxALcMMMSTgkCRs2NFGqlWMVzNYE7bJMWChVa7uQ/9CG\n" + "1HRbXwVwQx3hFgU4kmw1Kl/IH4LX76d9gAMyFANPjYZJSjbAv54wOlKruDRgpQFF\n" @@ -78,7 +84,7 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { + "cKLSMOR+Tji6n7FjOcVl6VoDKTjdxj9OgAlbZ7W9jEArrUjDdCk/m4jq9h9phpli\n" + "BHoul/nauwtlUnQes1V+39Rk9l7gddKWg3dlwg6CjB5MkmcaeyxgANcyKgrunpg=\n" + "=JYpC\n" + "-----END PGP PUBLIC KEY BLOCK-----\n"; - private static final String PGP_SIGNATURE = "-----BEGIN PGP SIGNATURE-----\n" + "\n" + private static final String PGP_SIGNER1_SIGNATURE = "-----BEGIN PGP SIGNATURE-----\n" + "\n" + "iQHRBAABCAA7FiEE6ZbGcKp/ZUCb9dtWE5442Q3tEfAFAmB4Bf8dHHNpZ25lcjFA\n" + "ZmFrZXVzZXIuZWNsaXBzZS5vcmcACgkQE5442Q3tEfBPuAwAhE4zA7BswKFhEtzm\n" + "DS3EbyRr/U13sV01YxqGtxYDCfrOt8TGVPXJSvo0AVP4vLFc5b+0jtVFoarFJNBu\n" @@ -88,8 +94,48 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { + "2XpuKxPXGavKfpSvssVQIZ6aWi5W6wp5lZAQQddZvYAv3Gi5CZZcUT7ayFJYdD23\n" + "p9jz76G7MXm0f0uNT9B57T72QryokUIEIJYsCb6lNjWUQB4cd0+JesM7sHwweOQ3\n" + "7iTFc+WgVJkP0e695mm1tcvtQHUPbIItYJUsndyLgGInzglxN8+F4U4k8uapydI9\n" - + "RmV2NVAifYp8z95Am5AnlG8lqjwrWk5bMbJH82QsQESrNT/h\n" + "=8Vrn\n" - + "-----END PGP SIGNATURE-----\n"; + + "RmV2NVAifYp8z95Am5AnlG8lqjwrWk5bMbJH82QsQESrNT/h\n" + "=8Vrn\n" + "-----END PGP SIGNATURE-----\n"; + + private static final String PGP_SIGNER2_PUBLIC_KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n" + "\r\n" + + "mQINBGHUyxYBEADATeNx4XA4H2fP9mD5xwlIyh7qvHLezgXpqCwNS9ATqBwnfrCV\r\n" + + "06a+pfSLsLoXrP/sdaB5WhijfuxTis18RMfoDVwGMRqyD2GiBCl2vwJDg3BUwHnc\r\n" + + "H7W6XkWKO7dkPmF+TUbD3cTWZ7cvrPmMjinmXaq8htuktGuE2VEGZRnuG1m+ChDM\r\n" + + "PnSb1ioFS2+MJv13P2fagVk2qC95DkPJGpMk3CY3ghLDEaY/KaJl+8axAlUUUk9N\r\n" + + "d3k/KVxxf5k3g26EVQkWWgH2mmolptGO101xW64iked97Cy4NK2yafOF/wmpsavx\r\n" + + "PGpOewnDgAJBBPkum6mPH0vcOZgxmLyh4uqfPfr3IaBQlbJLN2dXaDsV83j5t1wZ\r\n" + + "2qxOPcWBfORm6W7dC0TQgCXbEG0geMBpJtvnMX83Q2ORqDpjbHRJsV2k+8NxaXON\r\n" + + "pYXGr+Lj/9n0xfNEDXhCdGab0XP2tVZ5jfr2OQ5dAomEaPqK5Kq7akoWMddpDLNC\r\n" + + "G4IvH8G0cxwruJk00uwd6Nd2NVqVMRYCsBbA89VanUnutLUIpVnnOAetlX9blXHO\r\n" + + "JtmiCPGgHyp+iYGhKYVzfuZQyFhonbi0AhidJDvbHsoLT3p4Mcog1B9y6MODBE7R\r\n" + + "jwrU+qMqYsYeFhGYKbYyXv9TfEyUvtCQ/GnTtRJAQyicFdOdbK37WecS6QARAQAB\r\n" + + "tDdFY2xpcHNlIHAyIHRlc3QgU2lnbmVyIDIgPHNpZ25lcjJAZmFrZXVzZXIuZWNs\r\n" + + "aXBzZS5vcmc+iQJYBBMBCABCFiEEzZ1aK4a8T+GDlFHh4vaU9BsKs3AFAmHUyxYC\r\n" + + "GwMFCQPCZwAFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4HAheAAAoJEOL2lPQbCrNw\r\n" + + "wH4QAIiCaw1mREgt4ldz7hQvmGxdMuQwVKZPzbOIAlYbZBo0q9SmeMf/CBCO90hg\r\n" + + "LFmJmsZV4KUU5NKI7UwkDVrpUCl00Ok6+gtiUTId2tRcwXI+25I478VX27j6OkQj\r\n" + + "7Xr6giv8cn8nstt5CF6xxeqrxvpmnZC0u30jE6CL6SdXSd0vViFDPQj3KgGJCRc9\r\n" + + "St+LHB3XJTsadihzQnscqI4E2i5Z3Uj1GogqxtR59M1NAXubl5dySM0qHhwn8O+6\r\n" + + "lcgCCeuyMLLde1M1v8w07jdRUM+IFqHrRnE89EPH3MQeZbQ3UfFXK2r7wx2BJCqL\r\n" + + "Irtn68kz834ByKchGR6DqaAw0q+iF2QkgzYxpwai41BgmtUCYnj+HxQNIF4KTzDe\r\n" + + "nd8mDAPWttGCoVuV2Tyu9peYOaqyAZ2PZwUEH5MqihPCbenU17RLXzRu/IT/SH47\r\n" + + "NGrN3yKGgLZr2EVWPWFibpoxP4G4NUCHsY75uiL2EWPVSjK/+OOeHIE5k3U3lYwB\r\n" + + "7clhBwMkIhQHJ+a0SHRkKixkwrQDw4veKY4LaD0NCBLHFoV5L9orH1ToGM729kr/\r\n" + + "+4I1VQFkL3KvfLjmRbTUgwHeqEquQ96JtqowbNwlpujfHXQKDNsuiKGP5OazXll5\r\n" + + "sH2CR7e4ePqhhzxjLvi9e/79Khq+08eqllS3rs06EXEAJYTo\r\n" + "=807V\r\n" + + "-----END PGP PUBLIC KEY BLOCK-----\r\n" + ""; + private static final String PGP_SIGNER2_SIGNATURE = "-----BEGIN PGP SIGNATURE-----\n" + "\n" + + "iQIzBAABCAAdFiEEzZ1aK4a8T+GDlFHh4vaU9BsKs3AFAmHUy4UACgkQ4vaU9BsK\n" + + "s3DjuhAAvlCtqhK/7/aAG0/cXtlpu0fPC176OmEmBGTjCsrGdWwuRHsqXbLnMBVZ\n" + + "0D1m38MDvuYZfJuP7arw7USKp+Jy54Bv/YwvHLl74YTx1BN9hAN9QvyLxLZOjdm1\n" + + "/ipiWUuUgGa/brxEZNQqSR0w17TqXkIJHeFFS3T/rNH/Ckom5vQhAEm9HwJYeGdt\n" + + "tJ5BUl7BS1rrEOs+xmzqLu6AaERREc5gGRniJe7aP2Ke+/wL6oOLG3v/6vsJSM2e\n" + + "t+Olo4Ugc6JbdNrwvTO8kkTxsi0gp2CPhKl3RZVnbGoe4tXHawmk2V3eVTa0w6iP\n" + + "ARPJ/xH2dDsRi4Kz3OkcyQOI24jGmaqpUrx3+f2BnEbcVs4cHIJc+O2gh1WUz6uY\n" + + "Zw7qtK0W3H+E7RuJLCScgasPZZPBzyA6B3o2J3bfXnG5r41aJEuiq3otgllrBakG\n" + + "u7fX00h8lylgRrlCb4mquZxxRsrl+ac6U5eYdDMkK5VNkXgrus8FedIh3vmqI7RR\n" + + "ou9GEjho4kebt1Y1yTAQnxWBtTUG2hFt6VirKydI7+ZcCZmbD9lrZT6xVQOCkyUQ\n" + + "Cwy7vNPWkpMBBRVLoaThD2+7znDpb6wNYf9mDCcCK8tyuVCDSYEFX6jlqv1yfpNl\n" + + "QlYE1biLAHl09NH397Ta/9cWdfu68I7Mv4Ev2zu45OGa83h820M=\n" + "=9Ha/\n" + "-----END PGP SIGNATURE-----\n"; class CertificateTestService extends UIServices { public boolean unsignedReturnValue = true; @@ -117,14 +163,22 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { CertificateTestService serviceUI; File unsigned; private ProvisioningAgent testAgent; + private Path agentLocation; @Override protected void setUp() throws Exception { serviceUI = new CertificateTestService(); testAgent = new ProvisioningAgent(); testAgent.registerService(UIServices.SERVICE_NAME, serviceUI); + agentLocation = Files.createTempDirectory("certificateTest"); + testAgent.setLocation(agentLocation.toUri()); testAgent.setBundleContext(TestActivator.getContext()); checker = new CertificateChecker(testAgent); + PGPPublicKeyService keyService = testAgent.getService(PGPPublicKeyService.class); + if (keyService instanceof DefaultPGPPublicKeyService) { + ((DefaultPGPPublicKeyService) keyService).setKeyServers(Set.of()); + } + try { unsigned = TestData.getFile("CertificateChecker", "unsigned.jar"); } catch (IOException e) { @@ -134,12 +188,26 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { assertTrue("1.0", unsigned.exists()); } + @Override + protected void tearDown() throws Exception { + try (Stream<Path> walk = Files.walk(agentLocation)) { + walk.sorted(Comparator.reverseOrder()).forEach(path -> { + try { + Files.delete(path); + } catch (IOException e) { + // Ignore + } + }); + } + } + /** - * Tests that installing unsigned content is not allowed when the policy says it must fail. + * Tests that installing unsigned content is not allowed when the policy says it + * must fail. */ public void testPolicyAllow() { try { - //if the service is consulted it will say no + // if the service is consulted it will say no serviceUI.unsignedReturnValue = false; System.getProperties().setProperty(EngineActivator.PROP_UNSIGNED_POLICY, EngineActivator.UNSIGNED_ALLOW); checker.add(Map.of(new ArtifactDescriptor(new ArtifactKey("what", "ever", Version.create("1"))), unsigned)); @@ -151,7 +219,8 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { } /** - * Tests that installing unsigned content is not allowed when the policy says it must fail. + * Tests that installing unsigned content is not allowed when the policy says it + * must fail. */ public void testPolicyFail() { try { @@ -166,7 +235,8 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { } /** - * Tests that installing unsigned content with the "prompt" policy and the prompt succeeds. + * Tests that installing unsigned content with the "prompt" policy and the + * prompt succeeds. */ public void testPolicyPromptSuccess() { try { @@ -194,7 +264,8 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { } /** - * Tests that installing unsigned content with the "prompt" policy and the prompt says no. + * Tests that installing unsigned content with the "prompt" policy and the + * prompt says no. */ public void testPolicyPromptCancel() { try { @@ -210,16 +281,17 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { } /** - * Tests that trust checks that occur in a headless environment are properly treated - * as permissive, but not persistent, the same way as it would be if the service registration - * were not there. + * Tests that trust checks that occur in a headless environment are properly + * treated as permissive, but not persistent, the same way as it would be if the + * service registration were not there. */ public void testBug291049() { try { // Intentionally replace our service with a null service testAgent.registerService(UIServices.SERVICE_NAME, null); checker.add(Map.of(new ArtifactDescriptor(new ArtifactKey("what", "ever", Version.create("1"))), unsigned)); - // TODO need to add some untrusted files here, too. To prove that we treated them as trusted temporarily + // TODO need to add some untrusted files here, too. To prove that we treated + // them as trusted temporarily System.getProperties().setProperty(EngineActivator.PROP_UNSIGNED_POLICY, EngineActivator.UNSIGNED_PROMPT); IStatus result = checker.start(); assertTrue("1.0", result.isOK()); @@ -241,10 +313,9 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { unsigned = TestData.getFile("pgp/repoPGPOK/plugins", "blah_1.0.0.123456.jar"); ArtifactDescriptor artifactDescriptor = new ArtifactDescriptor( new ArtifactKey("what", "ever", Version.create("1"))); - artifactDescriptor.addProperties( - Map.of(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME, PGP_SIGNATURE, // - PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME, - PGP_PUBLIC_KEY)); + artifactDescriptor + .addProperties(Map.of(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME, PGP_SIGNER1_SIGNATURE, // + PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME, PGP_SIGNER1_PUBLIC_KEY)); checker.add(Map.of(artifactDescriptor, unsigned)); System.getProperties().setProperty(EngineActivator.PROP_UNSIGNED_POLICY, EngineActivator.UNSIGNED_PROMPT); IStatus result = checker.start(); @@ -255,22 +326,14 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { } } - public void testPGPSignedArtifactTrustedKeyInProfile() throws ProvisionException, IOException { + public void testPGPSignedArtifactTrustedKeyInProvideCapability() throws IOException { try { - // create a test profile - testAgent.registerService("FORCED_SELF", IProfileRegistry.SELF); - testAgent.registerService(IAgentLocation.SERVICE_NAME, new AgentLocation( - Files.createTempDirectory( - CertificateCheckerTest.class.getName() + "testPGPSignedArtifactTrustedKey-profile") - .toUri())); - testAgent.getService(IProfileRegistry.class).addProfile(IProfileRegistry.SELF, - Map.of(CertificateChecker.TRUSTED_KEY_STORE_PROPERTY, PGP_PUBLIC_KEY)); - unsigned = TestData.getFile("pgp/repoPGPOK/plugins", "blah_1.0.0.123456.jar"); ArtifactDescriptor artifactDescriptor = new ArtifactDescriptor( new ArtifactKey("what", "ever", Version.create("1"))); - artifactDescriptor.addProperties( - Map.of(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME, PGP_SIGNATURE)); + artifactDescriptor + .addProperties(Map.of(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME, PGP_SIGNER2_SIGNATURE, + PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME, PGP_SIGNER2_PUBLIC_KEY)); checker.add(Map.of(artifactDescriptor, unsigned)); System.getProperties().setProperty(EngineActivator.PROP_UNSIGNED_POLICY, EngineActivator.UNSIGNED_PROMPT); IStatus result = checker.start(); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/generator/GeneratorTests.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/generator/GeneratorTests.java index 4cd3e61c3..e09e8bc4d 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/generator/GeneratorTests.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/generator/GeneratorTests.java @@ -27,7 +27,9 @@ import org.eclipse.equinox.p2.publisher.eclipse.FeaturesAndBundlesPublisherAppli import org.eclipse.equinox.p2.publisher.eclipse.InstallPublisherApplication; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; -import org.eclipse.equinox.p2.tests.*; +import org.eclipse.equinox.p2.tests.AbstractProvisioningTest; +import org.eclipse.equinox.p2.tests.StringBufferStream; +import org.eclipse.equinox.p2.tests.TestActivator; import org.eclipse.osgi.util.NLS; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; @@ -90,7 +92,7 @@ public class GeneratorTests extends AbstractProvisioningTest { for (int i = 0; i < limit; i++) { BundleContext context = TestActivator.getContext(); Bundle bundle = context.getBundle(i); - File bundleFile = FileLocator.getBundleFile(bundle); + File bundleFile = FileLocator.getBundleFileLocation(bundle).get(); if (!bundleFile.isFile()) { //only jars please ++limit; diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/metadata/repository/MetadataRepositoryManagerTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/metadata/repository/MetadataRepositoryManagerTest.java index 22f8f9150..6ac039147 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/metadata/repository/MetadataRepositoryManagerTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/metadata/repository/MetadataRepositoryManagerTest.java @@ -387,17 +387,14 @@ public class MetadataRepositoryManagerTest extends AbstractProvisioningTest { IAgentLocation agentLocation = ServiceHelper.getService(TestActivator.getContext(), IAgentLocation.class); URI dataArea = agentLocation.getDataArea("org.eclipse.equinox.p2.repository/cache/"); File dataAreaFile = URIUtil.toFile(dataArea); - File cacheFileXML = new File(dataAreaFile, "content" + repoLocation.hashCode() + ".xml"); - File cacheFileJAR = new File(dataAreaFile, "content" + repoLocation.hashCode() + ".jar"); - File cacheFile; + File cacheFile = new File(dataAreaFile, + Integer.toString(URIUtil.append(repoLocation, "content.xml.xz").hashCode())); // as implemented in + // XZedSimpleMetadataRepository + // and CacheManager // load a remote repository and check that a local cache was created manager.loadRepository(repoLocation, null); - assertTrue("Cache file was not created.", cacheFileXML.exists() || cacheFileJAR.exists()); - if (cacheFileXML.exists()) - cacheFile = cacheFileXML; - else - cacheFile = cacheFileJAR; + assertTrue("Cache file was not created.", cacheFile.exists()); // modify the last modified date to be older than the remote file cacheFile.setLastModified(0); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/MetadataMirrorApplicationTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/MetadataMirrorApplicationTest.java index 004b5db20..94bdec524 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/MetadataMirrorApplicationTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/MetadataMirrorApplicationTest.java @@ -14,7 +14,9 @@ package org.eclipse.equinox.p2.tests.mirror; import java.io.File; -import java.net.*; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; import java.util.HashMap; import java.util.Map; import org.eclipse.equinox.internal.p2.metadata.repository.CompositeMetadataRepository; @@ -516,7 +518,7 @@ public class MetadataMirrorApplicationTest extends AbstractProvisioningTest { public void testMetadataMirrorToInvalid() { URI invalidDestRepository; try { - invalidDestRepository = new URI("http://eclipse.org/equinox/foobar/abcdefg"); + invalidDestRepository = new URI("https://eclipse.org/equinox/foobar/abcdefg"); basicRunMirrorApplication("14.1", sourceRepoLocation.toURL(), invalidDestRepository.toURL(), true); //we expect an illegal state exception to be thrown and should never get here fail("14.0 IllegalStateExpection not thrown"); @@ -535,7 +537,7 @@ public class MetadataMirrorApplicationTest extends AbstractProvisioningTest { delete(invalidRepository); try { - URI invalidDestRepository = new URI("http://eclipse.org/equinox/foobar/abcdefg"); + URI invalidDestRepository = new URI("https://eclipse.org/equinox/foobar/abcdefg"); basicRunMirrorApplication("15.1", invalidRepository.toURL(), invalidDestRepository.toURL(), true); //we expect a provisioning exception to be thrown and should never get here fail("15.0 ProvisionExpection not thrown"); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/NewMirrorApplicationMetadataTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/NewMirrorApplicationMetadataTest.java index 173bb4ca0..4d8c2ac71 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/NewMirrorApplicationMetadataTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/NewMirrorApplicationMetadataTest.java @@ -14,8 +14,12 @@ package org.eclipse.equinox.p2.tests.mirror; import java.io.File; -import java.net.*; -import java.util.*; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import org.eclipse.equinox.internal.p2.metadata.repository.CompositeMetadataRepository; import org.eclipse.equinox.internal.simpleconfigurator.utils.URIUtil; import org.eclipse.equinox.p2.core.ProvisionException; @@ -551,7 +555,7 @@ public class NewMirrorApplicationMetadataTest extends AbstractProvisioningTest { public void testMetadataMirrorToInvalid() { URI invalidDestRepository = null; try { - invalidDestRepository = new URI("http://eclipse.org/equinox/foobar/abcdefg"); + invalidDestRepository = new URI("https://eclipse.org/equinox/foobar/abcdefg"); basicRunMirrorApplication("14.1", sourceRepoLocation.toURL(), invalidDestRepository.toURL(), true); //we expect an illegal state exception to be thrown and should never get here fail("14.0 IllegalStateExpection not thrown"); @@ -574,7 +578,7 @@ public class NewMirrorApplicationMetadataTest extends AbstractProvisioningTest { delete(invalidRepository); try { - URI invalidDestRepository = new URI("http://eclipse.org/equinox/foobar/abcdefg"); + URI invalidDestRepository = new URI("https://eclipse.org/equinox/foobar/abcdefg"); basicRunMirrorApplication("15.1", invalidRepository.toURL(), invalidDestRepository.toURL(), true); //we expect a provisioning exception to be thrown and should never get here fail("15.0 ProvisionExpection not thrown"); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ANYConfigCUsActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ANYConfigCUsActionTest.java index bb92cf4ef..d6085fd8a 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ANYConfigCUsActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ANYConfigCUsActionTest.java @@ -14,13 +14,17 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.expect; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.matches; +import static org.mockito.Mockito.when; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import org.easymock.EasyMock; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.equinox.internal.p2.metadata.InstallableUnit; import org.eclipse.equinox.internal.p2.publisher.eclipse.DataLoader; @@ -212,7 +216,8 @@ public class ANYConfigCUsActionTest extends ActionTest { fail("Unable to create product file advice", e); //$NON-NLS-1$ } - expect(publisherInfo.getAdvice(EasyMock.matches(configSpec), EasyMock.eq(false), (String) EasyMock.anyObject(), (Version) EasyMock.anyObject(), EasyMock.eq(IExecutableAdvice.class))).andReturn(launchingList).anyTimes(); + when(publisherInfo.getAdvice(matches(configSpec), eq(false), anyString(), any(Version.class), + eq(IExecutableAdvice.class))).thenReturn(launchingList); //configure IConfigAdvice ConfigData configData = loader.getConfigData(); @@ -220,14 +225,15 @@ public class ANYConfigCUsActionTest extends ActionTest { ArrayList<IConfigAdvice> configList = new ArrayList<>(); configList.add(configAdvice); configList.add(productAdvice); - expect(publisherInfo.getAdvice(EasyMock.matches(configSpec), EasyMock.eq(false), (String) EasyMock.anyObject(), (Version) EasyMock.anyObject(), EasyMock.eq(IConfigAdvice.class))).andReturn(configList).anyTimes(); + when(publisherInfo.getAdvice(matches(configSpec), eq(false), anyString(), any(Version.class), + eq(IConfigAdvice.class))).thenReturn(configList); //setup metadata repository IInstallableUnit[] ius = {mockIU("foo", null), mockIU("bar", null)}; //$NON-NLS-1$ //$NON-NLS-2$ metadataRepo = new TestMetadataRepository(getAgent(), ius); - expect(publisherInfo.getMetadataRepository()).andReturn(metadataRepo).anyTimes(); - expect(publisherInfo.getContextMetadataRepository()).andReturn(null).anyTimes(); + when(publisherInfo.getMetadataRepository()).thenReturn(metadataRepo); + when(publisherInfo.getContextMetadataRepository()).thenReturn(null); } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AccumulateConfigDataActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AccumulateConfigDataActionTest.java index 480303b82..ffe6009d5 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AccumulateConfigDataActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AccumulateConfigDataActionTest.java @@ -15,21 +15,25 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; +import static org.mockito.Mockito.verify; + import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Map; -import org.easymock.Capture; -import org.easymock.EasyMock; +import java.util.stream.Collectors; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.equinox.frameworkadmin.BundleInfo; import org.eclipse.equinox.internal.provisional.frameworkadmin.ConfigData; +import org.eclipse.equinox.p2.publisher.IPublisherAdvice; import org.eclipse.equinox.p2.publisher.eclipse.AccumulateConfigDataAction; import org.eclipse.equinox.p2.publisher.eclipse.ConfigAdvice; import org.eclipse.equinox.p2.publisher.eclipse.LaunchingAdvice; import org.eclipse.equinox.p2.tests.TestActivator; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; public class AccumulateConfigDataActionTest extends ActionTest { @@ -43,13 +47,11 @@ public class AccumulateConfigDataActionTest extends ActionTest { private static String launcherName = "launcherName"; //$NON-NLS-1$ private static String launcherVersion = "0.0.0"; //$NON-NLS-1$ - Capture<ConfigAdvice> configAdviceCapture; - Capture<LaunchingAdvice> launchingAdviceCapture; + ArgumentCaptor<IPublisherAdvice> capture; @Override public void setUp() throws Exception { - configAdviceCapture = Capture.newInstance(); - launchingAdviceCapture = Capture.newInstance(); + capture = ArgumentCaptor.forClass(IPublisherAdvice.class); setupPublisherInfo(); setupPublisherResult(); testAction = new AccumulateConfigDataAction(publisherInfo, configSpec, configLocation, executableLocation); @@ -57,13 +59,15 @@ public class AccumulateConfigDataActionTest extends ActionTest { public void testAccumulateConfigDataAction() throws Exception { testAction.perform(publisherInfo, publisherResult, new NullProgressMonitor()); + verify(publisherInfo, Mockito.atLeastOnce()).addAdvice(capture.capture()); verifyConfigAdvice(); verifyLaunchAdvice(); debug("Completed AccumulateConfigDataActionTest."); //$NON-NLS-1$ } private void verifyLaunchAdvice() throws URISyntaxException { - LaunchingAdvice captured = launchingAdviceCapture.getValue(); + LaunchingAdvice captured = (LaunchingAdvice) capture.getAllValues().stream() + .filter(LaunchingAdvice.class::isInstance).collect(Collectors.toList()).get(0); String[] programArgs = captured.getProgramArguments(); assertTrue(programArgs.length == 4); assertTrue(programArgs[0].equalsIgnoreCase("-startup")); //$NON-NLS-1$ @@ -81,7 +85,8 @@ public class AccumulateConfigDataActionTest extends ActionTest { } private void verifyConfigAdvice() throws Exception { - ConfigAdvice captured = configAdviceCapture.getValue(); + ConfigAdvice captured = (ConfigAdvice) capture.getAllValues().stream().filter(ConfigAdvice.class::isInstance) + .collect(Collectors.toList()).get(0); Map<String, String> prop = captured.getProperties(); assertTrue(prop.get("eclipse.buildId").equalsIgnoreCase("TEST-ID")); //$NON-NLS-1$ //$NON-NLS-2$ assertTrue(prop.get("eclipse.p2.profile").equalsIgnoreCase("PlatformProfile")); //$NON-NLS-1$//$NON-NLS-2$ @@ -109,8 +114,5 @@ public class AccumulateConfigDataActionTest extends ActionTest { ConfigAdvice configAdvice = new ConfigAdvice(configData, configSpec); ArrayList<ConfigAdvice> configList = new ArrayList<>(); configList.add(configAdvice); - - publisherInfo.addAdvice(EasyMock.and(EasyMock.isA(ConfigAdvice.class), EasyMock.capture(configAdviceCapture))); - publisherInfo.addAdvice(EasyMock.and(EasyMock.isA(LaunchingAdvice.class), EasyMock.capture(launchingAdviceCapture))); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ActionTest.java index 1741c7878..25290d337 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ActionTest.java @@ -14,10 +14,9 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.io.ByteArrayOutputStream; import java.io.File; @@ -118,9 +117,9 @@ public abstract class ActionTest extends AbstractProvisioningTest { for (IRequirement act : actual) { if (expected.getMatches().equals(act.getMatches())) { String descr = "IRequirement " + expected.getMatches(); - Assert.assertEquals("Min of " + descr, expected.getMin(), act.getMin()); - Assert.assertEquals("Max of " + descr, expected.getMax(), act.getMax()); - Assert.assertEquals("Greedy of " + descr, expected.isGreedy(), act.isGreedy()); + assertEquals("Min of " + descr, expected.getMin(), act.getMin()); + assertEquals("Max of " + descr, expected.getMax(), act.getMax()); + assertEquals("Greedy of " + descr, expected.isGreedy(), act.isGreedy()); return; } } @@ -128,13 +127,12 @@ public abstract class ActionTest extends AbstractProvisioningTest { } protected IInstallableUnit mockIU(String id, Version version) { - IInstallableUnit result = createMock(IInstallableUnit.class); - expect(result.getId()).andReturn(id).anyTimes(); + IInstallableUnit result = mock(IInstallableUnit.class); + when(result.getId()).thenReturn(id); if (version == null) version = Version.emptyVersion; - expect(result.getVersion()).andReturn(version).anyTimes(); - expect(result.getFilter()).andReturn(null).anyTimes(); - replay(result); + when(result.getVersion()).thenReturn(version); + when(result.getFilter()).thenReturn(null); return result; } @@ -170,31 +168,20 @@ public abstract class ActionTest extends AbstractProvisioningTest { * Call this method to setup Publisher Info, not <code>insertPublisherInfoBehavior</code> */ public void setupPublisherInfo() { - publisherInfo = createPublisherInfoMock(); + publisherInfo = mock(IPublisherInfo.class); String[] config = getArrayFromString(configSpec, COMMA_SEPARATOR); - expect(publisherInfo.getConfigurations()).andReturn(config).anyTimes(); + when(publisherInfo.getConfigurations()).thenReturn(config); insertPublisherInfoBehavior(); - replay(publisherInfo); - } - - /** - * Creates the mock object for the IPublisherInfo. Subclasses - * can override to create a nice or strict mock instead. - * @return The publisher info mock - * @see org.easymock.EasyMock#createNiceMock(Class) - * @see org.easymock.EasyMock#createStrictMock(Class) - */ - protected IPublisherInfo createPublisherInfoMock() { - return createMock(IPublisherInfo.class); } /** * Do not call this method, it is called by <code>setupPublisherInfo</code>. */ protected void insertPublisherInfoBehavior() { - expect(publisherInfo.getMetadataRepository()).andReturn(createTestMetdataRepository(new IInstallableUnit[0])).anyTimes(); - expect(publisherInfo.getContextMetadataRepository()).andReturn(createTestMetdataRepository(new IInstallableUnit[0])).anyTimes(); + when(publisherInfo.getMetadataRepository()).thenReturn(createTestMetdataRepository(new IInstallableUnit[0])); + when(publisherInfo.getContextMetadataRepository()) + .thenReturn(createTestMetdataRepository(new IInstallableUnit[0])); } public void cleanup() { diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AdviceMatcher.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AdviceMatcher.java deleted file mode 100644 index f23e1f53b..000000000 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AdviceMatcher.java +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2017 IBM Corporation and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.equinox.p2.tests.publisher.actions; - -import org.easymock.EasyMock; -import org.easymock.IArgumentMatcher; -import org.eclipse.equinox.p2.metadata.Version; -import org.eclipse.equinox.p2.publisher.IPublisherAdvice; - -/** - * A matcher that matches advice applicable to a given id and version. - */ -public class AdviceMatcher implements IArgumentMatcher { - private final Version version; - private final String id; - private static Class<?> clazz; - - public static IPublisherAdvice adviceMatches(String id, Version version, Class<?> clazz) { - AdviceMatcher.clazz = clazz; - EasyMock.reportMatcher(new AdviceMatcher(id, version)); - return null; - } - - public AdviceMatcher(String id, Version version) { - this.id = id; - this.version = version; - } - - @Override - public void appendTo(StringBuffer buf) { - buf.append("AdviceMatcher[" + id + ',' + version + ']'); - } - - @Override - public boolean matches(Object arg) { - if (!(arg instanceof IPublisherAdvice)) - return false; - if (!(clazz.isAssignableFrom(arg.getClass()))) - return false; - IPublisherAdvice advice = (IPublisherAdvice) arg; - return advice.isApplicable("", false, id, version); - } -} diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/BundlesActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/BundlesActionTest.java index 72bde8105..fd95c834b 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/BundlesActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/BundlesActionTest.java @@ -16,15 +16,16 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.and; -import static org.easymock.EasyMock.capture; -import static org.easymock.EasyMock.expect; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.errorStatus; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.statusWithMessageWhich; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.io.BufferedInputStream; import java.io.File; @@ -39,9 +40,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.zip.ZipInputStream; -import org.easymock.Capture; -import org.easymock.CaptureType; -import org.easymock.EasyMock; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; @@ -60,6 +58,7 @@ import org.eclipse.equinox.p2.metadata.IUpdateDescriptor; import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.metadata.VersionRange; +import org.eclipse.equinox.p2.metadata.expression.IMatchExpression; import org.eclipse.equinox.p2.publisher.AbstractPublisherApplication; import org.eclipse.equinox.p2.publisher.AdviceFileAdvice; import org.eclipse.equinox.p2.publisher.IPublisherInfo; @@ -80,6 +79,8 @@ import org.eclipse.equinox.p2.tests.TestActivator; import org.eclipse.equinox.p2.tests.TestData; import org.eclipse.equinox.p2.tests.publisher.TestArtifactRepository; import org.eclipse.equinox.spi.p2.publisher.PublisherHelper; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; public class BundlesActionTest extends ActionTest { private static final String OSGI = PublisherHelper.OSGI_BUNDLE_CLASSIFIER; @@ -143,39 +144,27 @@ public class BundlesActionTest extends ActionTest { private static final VersionRange TEST1_IU_D_VERSION_RANGE = VersionRange.create("1.3.0");//$NON-NLS-1$ protected TestArtifactRepository artifactRepository = new TestArtifactRepository(getAgent()); - - private Capture<ITouchpointAdvice> tpAdvice1, tpAdvice2; - private Capture<IUpdateDescriptorAdvice> udAdvice3; - private Capture<ICapabilityAdvice> capAdvice5; - - @Override - public void setupPublisherInfo() { - tpAdvice1 = Capture.newInstance(CaptureType.ALL); - tpAdvice2 = Capture.newInstance(CaptureType.ALL); - - udAdvice3 = Capture.newInstance(CaptureType.ALL); - capAdvice5 = Capture.newInstance(CaptureType.ALL); - - super.setupPublisherInfo(); - } + private ArgumentCaptor<AdviceFileAdvice> updateDescriptorCapture; public void testAll() throws Exception { File[] files = TEST_BASE.listFiles(); - testAction = new BundlesAction(files); + testAction = Mockito.spy(new BundlesAction(files)); setupPublisherResult(); setupPublisherInfo(); artifactRepository.setProperty(AbstractPublisherApplication.PUBLISH_PACK_FILES_AS_SIBLINGS, "true");//$NON-NLS-1$ + updateDescriptorCapture = ArgumentCaptor.forClass(AdviceFileAdvice.class); + IStatus status = testAction.perform(publisherInfo, publisherResult, new NullProgressMonitor()); + verify(publisherInfo, Mockito.atLeastOnce()).addAdvice(updateDescriptorCapture.capture()); + assertEquals(Status.OK_STATUS, status); - assertEquals(Status.OK_STATUS, testAction.perform(publisherInfo, publisherResult, new NullProgressMonitor())); verifyBundlesAction(); cleanup(); - debug("Completed BundlesActionTest.");//$NON-NLS-1$ } public void testTranslationFragment() { File foo_fragment = new File(TestActivator.getTestDataFolder(), "FragmentPublisherTest/foo.fragment");//$NON-NLS-1$ File foo = new File(TestActivator.getTestDataFolder(), "FragmentPublisherTest/foo");//$NON-NLS-1$ - BundlesAction bundlesAction = new BundlesAction(new File[] {foo_fragment}); + BundlesAction bundlesAction = new BundlesAction(new File[] { foo_fragment }); PublisherInfo info = new PublisherInfo(); PublisherResult results = new PublisherResult(); @@ -185,7 +174,7 @@ public class BundlesActionTest extends ActionTest { info = new PublisherInfo(); results = new PublisherResult(); - bundlesAction = new BundlesAction(new File[] {foo}); + bundlesAction = new BundlesAction(new File[] { foo }); bundlesAction.perform(info, results, new NullProgressMonitor()); ius = results.getIUs(null, null); assertEquals(1, ius.size()); @@ -197,7 +186,7 @@ public class BundlesActionTest extends ActionTest { utils.setTranslationSource(queryableArray); assertEquals("English Foo", utils.getIUProperty(iu, IInstallableUnit.PROP_NAME)); - bundlesAction = new BundlesAction(new File[] {foo_fragment}); + bundlesAction = new BundlesAction(new File[] { foo_fragment }); bundlesAction.perform(info, results, new NullProgressMonitor()); ius = results.getIUs(null, null); assertEquals(3, ius.size()); @@ -238,7 +227,8 @@ public class BundlesActionTest extends ActionTest { canonicalIdx = 0; } - try (ZipInputStream actual = artifactRepository.getZipInputStream(descriptors[canonicalIdx]); ZipInputStream expected = new ZipInputStream(new FileInputStream(TEST_FILE2))) { + try (ZipInputStream actual = artifactRepository.getZipInputStream(descriptors[canonicalIdx]); + ZipInputStream expected = new ZipInputStream(new FileInputStream(TEST_FILE2))) { TestData.assertEquals(expected, actual); } @@ -248,12 +238,14 @@ public class BundlesActionTest extends ActionTest { IArtifactKey key1 = ArtifactKey.parse("osgi.bundle,test1,0.1.0");//$NON-NLS-1$ ZipInputStream zis = artifactRepository.getZipInputStream(key1); - Map<String, Object[]> fileMap = getFileMap(new HashMap<>(), new File[] {TEST_FILE1}, new Path(TEST_FILE1.getAbsolutePath())); + Map<String, Object[]> fileMap = getFileMap(new HashMap<>(), new File[] { TEST_FILE1 }, + new Path(TEST_FILE1.getAbsolutePath())); TestData.assertContains(fileMap, zis, true); } private void verifyBundle1() { - List<IInstallableUnit> ius = new ArrayList<>(publisherResult.getIUs(TEST1_PROVBUNDLE_NAME, IPublisherResult.ROOT)); + List<IInstallableUnit> ius = new ArrayList<>( + publisherResult.getIUs(TEST1_PROVBUNDLE_NAME, IPublisherResult.ROOT)); assertEquals(1, ius.size()); IInstallableUnit bundle1IU = ius.get(0); @@ -272,25 +264,28 @@ public class BundlesActionTest extends ActionTest { verifyProvidedCapability(providedCapabilities, OSGI_IDENTITY, TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION); verifyProvidedCapability(providedCapabilities, OSGI, TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION); verifyProvidedCapability(providedCapabilities, TEST1_PROV_Z_NAMESPACE, TEST1_PROVZ_NAME, TEST2_PROVZ_VERSION); - verifyProvidedCapability(providedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "source", Version.create("1.0.0"));//$NON-NLS-1$//$NON-NLS-2$ + verifyProvidedCapability(providedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "source", //$NON-NLS-1$ + Version.create("1.0.0"));//$NON-NLS-1$ assertEquals(5, providedCapabilities.size()); - Collection<ITouchpointData> data = bundle1IU.getTouchpointData(); + List<AdviceFileAdvice> tData = updateDescriptorCapture.getAllValues(); boolean found = false; - for (ITouchpointData td : data) { - ITouchpointInstruction configure = td.getInstruction("configure"); - if (configure == null) - continue; - String body = configure.getBody(); - if (body != null && body.indexOf("download.eclipse.org/releases/ganymede") > 0) { - found = true; + for (AdviceFileAdvice iTouchpointAdvice : tData) { + ITouchpointInstruction configure = iTouchpointAdvice.getTouchpointData(NO_TP_DATA) + .getInstruction("configure"); + if (configure != null) { + String body = configure.getBody(); + if (body != null && body.indexOf("download.eclipse.org/releases/ganymede") > 0) { + found = true; + } } } assertTrue(found); } private void verifyBundle2() { - List<IInstallableUnit> ius = new ArrayList<>(publisherResult.getIUs(TEST2_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); + List<IInstallableUnit> ius = new ArrayList<>( + publisherResult.getIUs(TEST2_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); assertEquals(1, ius.size()); IInstallableUnit bundleIu = ius.get(0); @@ -307,14 +302,16 @@ public class BundlesActionTest extends ActionTest { // check provided capabilities Collection<IProvidedCapability> providedCapabilities = bundleIu.getProvidedCapabilities(); - verifyProvidedCapability(providedCapabilities, PROVBUNDLE_NAMESPACE, TEST2_PROV_BUNDLE_NAME, PROVBUNDLE2_VERSION); + verifyProvidedCapability(providedCapabilities, PROVBUNDLE_NAMESPACE, TEST2_PROV_BUNDLE_NAME, + PROVBUNDLE2_VERSION); verifyProvidedCapability(providedCapabilities, OSGI, TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION); verifyProvidedCapability(providedCapabilities, OSGI_IDENTITY, TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION); verifyProvidedCapability(providedCapabilities, TEST2_PROV_Z_NAMESPACE, TEST2_PROV_Z_NAME, TEST2_PROVZ_VERSION); verifyProvidedCapability(providedCapabilities, TEST2_PROV_Y_NAMESPACE, TEST2_PROV_Y_NAME, TEST2_PROVY_VERSION); verifyProvidedCapability(providedCapabilities, TEST2_PROV_X_NAMESPACE, TEST2_PROV_X_NAME, TEST2_PROVX_VERSION); - verifyProvidedCapability(providedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "bundle", Version.create("1.0.0"));//$NON-NLS-1$//$NON-NLS-2$ - assertEquals(7, providedCapabilities.size()); /*number of tested elements*/ + verifyProvidedCapability(providedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "bundle", //$NON-NLS-1$ + Version.create("1.0.0"));//$NON-NLS-1$ + assertEquals(7, providedCapabilities.size()); /* number of tested elements */ // check %bundle name is correct Map<String, String> prop = bundleIu.getProperties(); @@ -336,27 +333,41 @@ public class BundlesActionTest extends ActionTest { } private void verifyBundle3() { - // also a regression test for bug 393051: manifest headers use uncommon (but valid) capitalization - ArrayList<IInstallableUnit> ius = new ArrayList<>(publisherResult.getIUs(TEST3_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); + // also a regression test for bug 393051: manifest headers use uncommon (but + // valid) capitalization + ArrayList<IInstallableUnit> ius = new ArrayList<>( + publisherResult.getIUs(TEST3_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); assertEquals(1, ius.size()); - - IInstallableUnit bundleIu = ius.get(0); - - IUpdateDescriptor updateDescriptor = bundleIu.getUpdateDescriptor(); - String name = RequiredCapability.extractName(updateDescriptor.getIUsBeingUpdated().iterator().next()); + IUpdateDescriptor updateDescriptor = null; + boolean found = false; + for (AdviceFileAdvice advice : updateDescriptorCapture.getAllValues()) { + IUpdateDescriptor descriptor = advice.getUpdateDescriptor(new InstallableUnitDescription()); + if (descriptor != null) { + Collection<IMatchExpression<IInstallableUnit>> iUsBeingUpdated = descriptor.getIUsBeingUpdated(); + if (iUsBeingUpdated != null) { + String name = RequiredCapability.extractName(descriptor.getIUsBeingUpdated().iterator().next()); + if (TEST3_PROV_BUNDLE_NAME.equals(name)) { + updateDescriptor = descriptor; + found = true; + break; + } + } + } + } + assertTrue(found); VersionRange range = RequiredCapability.extractRange(updateDescriptor.getIUsBeingUpdated().iterator().next()); String description = updateDescriptor.getDescription(); int severity = updateDescriptor.getSeverity(); VersionRange expectedRange = new VersionRange("(0.0.1," + BUNDLE3_VERSION + "]"); - assertEquals(TEST3_PROV_BUNDLE_NAME, name); assertEquals(expectedRange, range); assertEquals("Some description about this update", description.trim()); assertEquals(8, severity); } private void verifyBundle4() { - ArrayList<IInstallableUnit> ius = new ArrayList<>(publisherResult.getIUs(TEST4_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); + ArrayList<IInstallableUnit> ius = new ArrayList<>( + publisherResult.getIUs(TEST4_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); assertEquals(1, ius.size()); IInstallableUnit bundleIu = ius.get(0); @@ -365,23 +376,29 @@ public class BundlesActionTest extends ActionTest { // check required capabilities Collection<IRequirement> requirements = bundleIu.getRequirements(); - verifyRequirement(requirements, JAVA_PACKAGE, TEST4_REQ_PACKAGE_OPTIONAL_NAME, DEFAULT_VERSION_RANGE, null, 0, 1, false); - verifyRequirement(requirements, JAVA_PACKAGE, TEST4_REQ_PACKAGE_OPTGREEDY_NAME, DEFAULT_VERSION_RANGE, null, 0, 1, true); + verifyRequirement(requirements, JAVA_PACKAGE, TEST4_REQ_PACKAGE_OPTIONAL_NAME, DEFAULT_VERSION_RANGE, null, 0, + 1, false); + verifyRequirement(requirements, JAVA_PACKAGE, TEST4_REQ_PACKAGE_OPTGREEDY_NAME, DEFAULT_VERSION_RANGE, null, 0, + 1, true); verifyRequirement(requirements, OSGI, TEST4_REQ_BUNDLE_OPTIONAL_NAME, DEFAULT_VERSION_RANGE, null, 0, 1, false); verifyRequirement(requirements, OSGI, TEST4_REQ_BUNDLE_OPTGREEDY_NAME, DEFAULT_VERSION_RANGE, null, 0, 1, true); assertEquals(4, requirements.size()); } private void verifyBundle5() { - ArrayList<IInstallableUnit> ius = new ArrayList<>(publisherResult.getIUs(TEST5_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); + ArrayList<IInstallableUnit> ius = new ArrayList<>( + publisherResult.getIUs(TEST5_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); assertEquals(1, ius.size()); IInstallableUnit bundle5IU = ius.get(0); - + for (AdviceFileAdvice adv : updateDescriptorCapture.getAllValues()) { + IRequirement[] reqs = adv.getRequiredCapabilities(new InstallableUnitDescription()); + if (reqs != null) { + verifyRequirement(List.of(reqs), "bar", "foo", VersionRange.emptyRange, null, 6, 7, true); + } + } Collection<IRequirement> requirements = bundle5IU.getRequirements(); verifyRequirement(requirements, OSGI_EE, TEST5_REQ_EE, null, 1, 1, true); - verifyRequirement(requirements, "bar", "foo", VersionRange.emptyRange, null, 6, 7, true); - assertEquals(2, requirements.size()); } @Override @@ -395,7 +412,8 @@ public class BundlesActionTest extends ActionTest { @Override protected void insertPublisherInfoBehavior() { - //super sets publisherInfo.getMetadataRepository and publisherInfo.getContextMetadataRepository + // super sets publisherInfo.getMetadataRepository and + // publisherInfo.getContextMetadataRepository super.insertPublisherInfoBehavior(); Map<String, String> sarProperties = new HashMap<>(); sarProperties.put("key1", "value1");//$NON-NLS-1$//$NON-NLS-2$ @@ -405,23 +423,21 @@ public class BundlesActionTest extends ActionTest { sdkProperties.put("key1", "value1");//$NON-NLS-1$//$NON-NLS-2$ sdkProperties.put("key2", "value2");//$NON-NLS-1$//$NON-NLS-2$ - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH).anyTimes(); - expect(publisherInfo.getAdvice(null, false, null, null, ICapabilityAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); + when(publisherInfo.getArtifactRepository()).thenReturn(artifactRepository); + when(publisherInfo.getArtifactOptions()) + .thenReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH); + when(publisherInfo.getAdvice(null, false, null, null, ICapabilityAdvice.class)).thenReturn(new ArrayList<>()); expectOtherAdviceQueries(TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION); expectPropertyAdviceQuery(TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION, sarProperties); expectUpdateDescriptorAdviceQuery(TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION, null); - expectTouchpointAdviceQuery(TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION, tpAdvice1.getValues()); expectOtherAdviceQueries(TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION); expectPropertyAdviceQuery(TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION, sdkProperties); expectUpdateDescriptorAdviceQuery(TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION, null); - expectTouchpointAdviceQuery(TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION, tpAdvice2.getValues()); expectOtherAdviceQueries(TEST3_PROV_BUNDLE_NAME, BUNDLE3_VERSION); expectPropertyAdviceQuery(TEST3_PROV_BUNDLE_NAME, BUNDLE3_VERSION, sarProperties); - expectUpdateDescriptorAdviceQuery(TEST3_PROV_BUNDLE_NAME, BUNDLE3_VERSION, udAdvice3.getValues()); expectTouchpointAdviceQuery(TEST3_PROV_BUNDLE_NAME, BUNDLE3_VERSION, null); expectOtherAdviceQueries(TEST4_PROV_BUNDLE_NAME, BUNDLE4_VERSION); @@ -429,49 +445,43 @@ public class BundlesActionTest extends ActionTest { expectUpdateDescriptorAdviceQuery(TEST4_PROV_BUNDLE_NAME, BUNDLE4_VERSION, null); expectTouchpointAdviceQuery(TEST4_PROV_BUNDLE_NAME, BUNDLE4_VERSION, null); - expectCapabilityAdviceQuery(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION, capAdvice5.getValues()); +// expectCapabilityAdviceQuery(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION, capAdvice5.getValues()); expectOtherAdviceQueries(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION); expectPropertyAdviceQuery(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION, sarProperties); expectUpdateDescriptorAdviceQuery(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION, null); expectTouchpointAdviceQuery(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION, null); - //capture any touchpoint advice, and return the captured advice when the action asks for it - publisherInfo.addAdvice(and(AdviceMatcher.adviceMatches(TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION, ITouchpointAdvice.class), capture(tpAdvice1))); - EasyMock.expectLastCall().anyTimes(); - - publisherInfo.addAdvice(and(AdviceMatcher.adviceMatches(TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION, ITouchpointAdvice.class), capture(tpAdvice2))); - EasyMock.expectLastCall().anyTimes(); - - publisherInfo.addAdvice(and(AdviceMatcher.adviceMatches(TEST3_PROV_BUNDLE_NAME, BUNDLE3_VERSION, AdviceFileAdvice.class), capture(udAdvice3))); - - publisherInfo.addAdvice(and(AdviceMatcher.adviceMatches(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION, AdviceFileAdvice.class), capture(capAdvice5))); - EasyMock.expectLastCall().anyTimes(); } private void expectOtherAdviceQueries(String bundleName, Version bundleVersion) { - expect(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, ICapabilityAdvice.class)) - .andReturn(Collections.emptyList()); - expect(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, IAdditionalInstallableUnitAdvice.class)) - .andReturn(Collections.emptyList()); - expect(publisherInfo.getAdvice(null, true, bundleName, bundleVersion, IBundleShapeAdvice.class)).andReturn(null); + when(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, ICapabilityAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, IAdditionalInstallableUnitAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, true, bundleName, bundleVersion, IBundleShapeAdvice.class)).thenReturn(null); } - private void expectCapabilityAdviceQuery(String bundleName, Version bundleVersion, Collection<ICapabilityAdvice> answer) { - if (answer == null) - answer = Collections.emptyList(); - expect(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, ICapabilityAdvice.class)).andReturn(answer); - } +// private void expectCapabilityAdviceQuery(String bundleName, Version bundleVersion, +// Collection<ICapabilityAdvice> answer) { +// if (answer == null) +// answer = Collections.emptyList(); +// when(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, ICapabilityAdvice.class)) +// .thenReturn(answer); +// } - private void expectUpdateDescriptorAdviceQuery(String bundleName, Version bundleVersion, Collection<IUpdateDescriptorAdvice> answer) { + private void expectUpdateDescriptorAdviceQuery(String bundleName, Version bundleVersion, + Collection<IUpdateDescriptorAdvice> answer) { if (answer == null) answer = Collections.emptyList(); - expect(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, IUpdateDescriptorAdvice.class)).andReturn(answer); + when(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, IUpdateDescriptorAdvice.class)) + .thenReturn(answer); } - private void expectTouchpointAdviceQuery(String bundleName, Version bundleVersion, Collection<ITouchpointAdvice> answer) { + private void expectTouchpointAdviceQuery(String bundleName, Version bundleVersion, List<ITouchpointAdvice> answer) { if (answer == null) answer = Collections.emptyList(); - expect(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, ITouchpointAdvice.class)).andReturn(answer).anyTimes(); + when(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, ITouchpointAdvice.class)) + .thenReturn(answer); } private void expectPropertyAdviceQuery(String bundleName, Version bundleVersion, Map<String, String> answer) { @@ -480,20 +490,22 @@ public class BundlesActionTest extends ActionTest { propertyAdvices = Collections.singletonList(createPropertyAdvice(answer)); else propertyAdvices = Collections.emptyList(); - expect(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, IPropertyAdvice.class)).andReturn(propertyAdvices).times(2); + when(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, IPropertyAdvice.class)) + .thenReturn(propertyAdvices); } private IPropertyAdvice createPropertyAdvice(Map<String, String> properties) { - IPropertyAdvice mockAdvice = EasyMock.createMock(IPropertyAdvice.class); - expect(mockAdvice.getInstallableUnitProperties((InstallableUnitDescription) EasyMock.anyObject())).andReturn(null).anyTimes(); - expect(mockAdvice.getArtifactProperties((IInstallableUnit) EasyMock.anyObject(), (IArtifactDescriptor) EasyMock.anyObject())).andReturn(properties).anyTimes(); - EasyMock.replay(mockAdvice); + IPropertyAdvice mockAdvice = mock(IPropertyAdvice.class); + when(mockAdvice.getInstallableUnitProperties(any(InstallableUnitDescription.class))).thenReturn(null); + when(mockAdvice.getArtifactProperties(any(IInstallableUnit.class), any(IArtifactDescriptor.class))) + .thenReturn(properties); return mockAdvice; } public void testDynamicImport() throws Exception { File testData = getTestData("dymamicImport", "testData/dynamicImport"); - IInstallableUnit iu = BundlesAction.createBundleIU(BundlesAction.createBundleDescription(testData), null, new PublisherInfo()); + IInstallableUnit iu = BundlesAction.createBundleIU(BundlesAction.createBundleDescription(testData), null, + new PublisherInfo()); Collection<IRequirement> requirements = iu.getRequirements(); verifyRequirement(requirements, OSGI_EE, TESTDYN_REQ_EE, null, 1, 1, true); @@ -510,11 +522,13 @@ public class BundlesActionTest extends ActionTest { // overall status shall be error... assertThat(status, errorStatus()); List<IStatus> childStatuses = Arrays.asList(status.getChildren()); - assertThat(childStatuses, hasItem(statusWithMessageWhich(containsString("The manifest line \"foo\" is invalid; it has no colon ':' character after the header key.")))); + assertThat(childStatuses, hasItem(statusWithMessageWhich(containsString( + "The manifest line \"foo\" is invalid; it has no colon ':' character after the header key.")))); assertThat(childStatuses.size(), is(1)); // ... but the valid bundle must still be published - Collection<IInstallableUnit> ius = publisherResult.getIUs("org.eclipse.p2.test.validManifest", IPublisherResult.ROOT); + Collection<IInstallableUnit> ius = publisherResult.getIUs("org.eclipse.p2.test.validManifest", + IPublisherResult.ROOT); assertThat(ius.size(), is(1)); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/CaptureList.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/CaptureList.java deleted file mode 100644 index e317d621c..000000000 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/CaptureList.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2017 IBM Corporation and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.equinox.p2.tests.publisher.actions; - -import java.util.AbstractList; -import org.easymock.Capture; - -/** - * An object that adapts an EasyMock Capture to a List. - */ -public class CaptureList<E> extends AbstractList<E> { - private Capture<E> capture; - - public CaptureList(Capture<E> capture) { - this.capture = capture; - } - - @Override - public E get(int arg0) { - return capture.getValue(); - } - - @Override - public int size() { - return capture.hasCaptured() ? 1 : 0; - } - -} diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ConfigCUsActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ConfigCUsActionTest.java index 24b6997bb..99afa871c 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ConfigCUsActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ConfigCUsActionTest.java @@ -15,13 +15,17 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.expect; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.matches; +import static org.mockito.Mockito.when; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import org.easymock.EasyMock; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.equinox.internal.p2.metadata.InstallableUnit; import org.eclipse.equinox.internal.p2.publisher.eclipse.DataLoader; @@ -147,7 +151,8 @@ public class ConfigCUsActionTest extends ActionTest { ConfigAdvice configAdvice = new ConfigAdvice(configData, configSpec); ArrayList<IConfigAdvice> configList = new ArrayList<>(); configList.add(configAdvice); - expect(publisherInfo.getAdvice(EasyMock.matches(configSpec), EasyMock.eq(false), (String) EasyMock.anyObject(), (Version) EasyMock.anyObject(), EasyMock.eq(IConfigAdvice.class))).andReturn(configList).anyTimes(); + when(publisherInfo.getAdvice(matches(configSpec), eq(false), anyString(), any(Version.class), + eq(IConfigAdvice.class))).thenReturn(configList); //configure IExecutableAdvice LauncherData launcherData = loader.getLauncherData(); @@ -164,14 +169,15 @@ public class ConfigCUsActionTest extends ActionTest { // TODO Auto-generated catch block } - expect(publisherInfo.getAdvice(EasyMock.matches(configSpec), EasyMock.eq(false), (String) EasyMock.anyObject(), (Version) EasyMock.anyObject(), EasyMock.eq(IExecutableAdvice.class))).andReturn(launchingList).anyTimes(); + when(publisherInfo.getAdvice(matches(configSpec), eq(false), anyString(), any(Version.class), + eq(IExecutableAdvice.class))).thenReturn(launchingList); //setup metadata repository IInstallableUnit[] ius = {mockIU("foo", null), mockIU("bar", null)}; //$NON-NLS-1$ //$NON-NLS-2$ metadataRepo = new TestMetadataRepository(getAgent(), ius); - expect(publisherInfo.getMetadataRepository()).andReturn(metadataRepo).anyTimes(); - expect(publisherInfo.getContextMetadataRepository()).andReturn(null).anyTimes(); + when(publisherInfo.getMetadataRepository()).thenReturn(metadataRepo); + when(publisherInfo.getContextMetadataRepository()).thenReturn(null); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxExecutableActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxExecutableActionTest.java index 41bf705a0..532bab05e 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxExecutableActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxExecutableActionTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2017 Code 9 and others. + * Copyright (c) 2008, 2021 Code 9 and others. * * This * program and the accompanying materials are made available under the terms of @@ -15,9 +15,11 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.anyBoolean; -import static org.easymock.EasyMock.anyObject; -import static org.easymock.EasyMock.expect; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.when; import java.io.File; import java.io.FileInputStream; @@ -30,6 +32,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.LinkedList; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; @@ -62,10 +65,9 @@ public class EquinoxExecutableActionTest extends ActionTest { private static final File LINUX_EXEC = new File(TestActivator.getTestDataFolder(), "EquinoxExecutableActionTest/linux/"); //$NON-NLS-1$ private static final File WIN_EXEC = new File(TestActivator.getTestDataFolder(), "EquinoxExecutableActionTest/win/"); //$NON-NLS-1$ private final String EXECUTABLE_NAME = "LauncherName"; //$NON-NLS-1$ - private Collection<IBrandingAdvice> brandingAdvice = new LinkedList<>(); private String macConfigCocoa = "cocoa.macosx.x86"; //$NON-NLS-1$ - private String winConfig = "win32.win32.x86"; //$NON-NLS-1$ - private String linuxConfig = "linux.gtk.x86"; //$NON-NLS-1$ + private String winConfig = "win32.win32.x86_64"; //$NON-NLS-1$ + private String linuxConfig = "linux.gtk.x86_64"; //$NON-NLS-1$ private ExecutablesDescriptor executablesDescriptor; private IArtifactRepository artifactRepository; private Version version = Version.create("1.2.3"); //$NON-NLS-1$ @@ -76,9 +78,6 @@ public class EquinoxExecutableActionTest extends ActionTest { public void setUp() throws Exception { setupPublisherInfo(); setupPublisherResult(); - } - - private void setupArtifactRepository() { artifactRepository = new TestArtifactRepository(getAgent()); } @@ -118,7 +117,10 @@ public class EquinoxExecutableActionTest extends ActionTest { private void testExecutableAction(String idBase, final String osArg, String config, File exec, File icon) { id = idBase; - setupBrandingAdvice(osArg, configSpec, exec, icon); + when(publisherInfo.getArtifactRepository()).thenReturn(artifactRepository); + when(publisherInfo.getArtifactOptions()).thenReturn(IPublisherInfo.A_PUBLISH); + when(publisherInfo.getAdvice(anyString(), anyBoolean(), nullable(String.class), nullable(Version.class), + eq(IBrandingAdvice.class))).then(invocation -> setupBrandingAdvice(osArg, icon)); executablesDescriptor = ExecutablesDescriptor.createDescriptor(osArg, "eclipse", exec); testAction = new EquinoxExecutableAction(executablesDescriptor, config, idBase, version, flavorArg); testAction.perform(publisherInfo, publisherResult, new NullProgressMonitor()); @@ -128,10 +130,10 @@ public class EquinoxExecutableActionTest extends ActionTest { private void verifyResults(String idBase, String confSpec) { ArrayList<IInstallableUnit> iuList = new ArrayList<>(publisherResult.getIUs(null, IPublisherResult.ROOT)); + assertEquals(3, iuList.size()); verifyEclipseIU(iuList, idBase, confSpec); verifyCU(iuList, idBase, confSpec); verifyExecIU(iuList, idBase, confSpec); - assertTrue(iuList.size() == 3); } private void verifyCU(ArrayList<IInstallableUnit> iuList, String idBase, String confSpec) { @@ -162,16 +164,16 @@ public class EquinoxExecutableActionTest extends ActionTest { private void verifyEclipseIU(ArrayList<IInstallableUnit> iuList, String idBase, String confSpec) { for (IInstallableUnit possibleEclipse : iuList) { if (possibleEclipse.getId().equals((idBase + ".executable." + confSpec + "." + EXECUTABLE_NAME))) { //$NON-NLS-1$//$NON-NLS-2$ - assertTrue(possibleEclipse.getVersion().equals(version)); + assertEquals(version, possibleEclipse.getVersion()); Collection<IProvidedCapability> providedCapability = possibleEclipse.getProvidedCapabilities(); verifyProvidedCapability(providedCapability, IInstallableUnit.NAMESPACE_IU_ID, idBase + ".executable." + confSpec + "." + EXECUTABLE_NAME, version); //$NON-NLS-1$ //$NON-NLS-2$ - assertTrue(providedCapability.size() == 1); + assertEquals(1, providedCapability.size()); Collection<IRequirement> req = possibleEclipse.getRequirements(); - assertTrue(req.size() == 0); + assertEquals(0, req.size()); return;//pass } } - fail(); + fail("No executable installable unit."); } private void verifyExecIU(ArrayList<IInstallableUnit> iuList, String idBase, String confSpec) { @@ -274,15 +276,8 @@ public class EquinoxExecutableActionTest extends ActionTest { } } - @Override - protected void insertPublisherInfoBehavior() { - setupArtifactRepository(); - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_PUBLISH).anyTimes(); - expect(publisherInfo.getAdvice((String) anyObject(), anyBoolean(), (String) anyObject(), (Version) anyObject(), (Class<IBrandingAdvice>) anyObject())).andReturn(brandingAdvice); - } - - private void setupBrandingAdvice(final String osArg, final String config, final File exec, final File icon) { + private List<IBrandingAdvice> setupBrandingAdvice(final String osArg, final File icon) { + List<IBrandingAdvice> brandingAdvice = new LinkedList<>(); brandingAdvice.add(new IBrandingAdvice() { @Override public boolean isApplicable(String configSpec, boolean includeDefault, String id, Version version) { @@ -304,5 +299,6 @@ public class EquinoxExecutableActionTest extends ActionTest { return EXECUTABLE_NAME; } }); + return brandingAdvice; } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxLauncherCUActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxLauncherCUActionTest.java index 95442d175..ff0b846fb 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxLauncherCUActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxLauncherCUActionTest.java @@ -15,9 +15,8 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Collection; @@ -56,13 +55,12 @@ public class EquinoxLauncherCUActionTest extends ActionTest { } protected IInstallableUnit mockIU(String id, Version version, boolean fragment) { - IInstallableUnit result = createMock(IInstallableUnit.class); - expect(result.getId()).andReturn(id).anyTimes(); + IInstallableUnit result = mock(IInstallableUnit.class); + when(result.getId()).thenReturn(id); if (version == null) version = Version.emptyVersion; - expect(result.getVersion()).andReturn(version).anyTimes(); - expect(result.getFilter()).andReturn(null).anyTimes(); - replay(result); + when(result.getVersion()).thenReturn(version); + when(result.getFilter()).thenReturn(null); return result; } @@ -108,7 +106,10 @@ public class EquinoxLauncherCUActionTest extends ActionTest { ArrayList<IVersionAdvice> versionList = new ArrayList<>(); versionList.add(versionAdvice); - expect(publisherInfo.getAdvice(null, true, EquinoxLauncherCUAction.ORG_ECLIPSE_EQUINOX_LAUNCHER, null, IVersionAdvice.class)).andReturn(versionList); - expect(publisherInfo.getAdvice(configSpec, true, EquinoxLauncherCUAction.ORG_ECLIPSE_EQUINOX_LAUNCHER + "." + configSpec, null, IVersionAdvice.class)).andReturn(versionList); //$NON-NLS-1$ + when(publisherInfo.getAdvice(null, true, EquinoxLauncherCUAction.ORG_ECLIPSE_EQUINOX_LAUNCHER, null, + IVersionAdvice.class)).thenReturn(versionList); + when(publisherInfo.getAdvice(configSpec, true, + EquinoxLauncherCUAction.ORG_ECLIPSE_EQUINOX_LAUNCHER + "." + configSpec, null, IVersionAdvice.class)) //$NON-NLS-1$ + .thenReturn(versionList); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/FeaturesActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/FeaturesActionTest.java index bae3a460e..279660892 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/FeaturesActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/FeaturesActionTest.java @@ -15,10 +15,10 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.and; -import static org.easymock.EasyMock.capture; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.isA; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.io.File; import java.io.IOException; @@ -26,10 +26,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.zip.ZipInputStream; -import org.easymock.Capture; -import org.easymock.EasyMock; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; @@ -62,6 +61,8 @@ import org.eclipse.equinox.p2.tests.TestData; import org.eclipse.equinox.p2.tests.TestMetadataRepository; import org.eclipse.equinox.p2.tests.publisher.TestArtifactRepository; import org.eclipse.equinox.spi.p2.publisher.PublisherHelper; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; public class FeaturesActionTest extends ActionTest { @@ -75,12 +76,12 @@ public class FeaturesActionTest extends ActionTest { private Version barVersion = Version.create("1.1.1"); //$NON-NLS-1$ private String BAR = "bar"; //$NON-NLS-1$ private String FOO = "foo"; //$NON-NLS-1$ - private Capture<ITouchpointAdvice> tpAdvice; + private ArgumentCaptor<ITouchpointAdvice> capture = ArgumentCaptor.forClass(ITouchpointAdvice.class); @Override public void setUp() throws Exception { - testAction = new FeaturesAction(new File[] {root}); - tpAdvice = Capture.newInstance(); + super.setUp(); + testAction = Mockito.spy(new FeaturesAction(new File[] { root })); setupPublisherInfo(); setupPublisherResult(); } @@ -90,37 +91,41 @@ public class FeaturesActionTest extends ActionTest { */ public void testSimple() throws Exception { testAction.perform(publisherInfo, publisherResult, new NullProgressMonitor()); + verify(publisherInfo, Mockito.atLeastOnce()).addAdvice(capture.capture()); verifyRepositoryContents(); debug("Completed FeaturesAction."); //$NON-NLS-1$ } public void testFeaturePatch() throws Exception { File testFolder = getTestFolder("FeaturesAction.testFilters"); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("<feature id=\"test.feature\" version=\"1.0.0\" > \n"); buffer.append(" <requires> \n"); - buffer.append(" <import feature=\"org.foo\" version=\"[1.0.0,2.0.0)\" match=\"versionRange\" patch=\"true\"/> \n"); + buffer.append( + " <import feature=\"org.foo\" version=\"[1.0.0,2.0.0)\" match=\"versionRange\" patch=\"true\"/> \n"); buffer.append(" </requires> \n"); buffer.append("</feature> \n"); File featureXML = new File(testFolder, "feature.xml"); writeBuffer(featureXML, buffer); publisherInfo = new PublisherInfo(); - FeaturesAction action = new FeaturesAction(new File[] {testFolder}); + FeaturesAction action = new FeaturesAction(new File[] { testFolder }); action.perform(publisherInfo, publisherResult, new NullProgressMonitor()); - IInstallableUnitPatch iu = (IInstallableUnitPatch) publisherResult.getIU("test.feature.feature.group", Version.parseVersion("1.0.0"), null); + IInstallableUnitPatch iu = (IInstallableUnitPatch) publisherResult.getIU("test.feature.feature.group", + Version.parseVersion("1.0.0"), null); IRequirement[][] applicabilityScope = iu.getApplicabilityScope(); assertEquals(1, applicabilityScope.length); IRequiredCapability require = (IRequiredCapability) applicabilityScope[0][0]; - IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.foo.feature.group", VersionRange.create("[1.0.0, 2.0.0)"), null, false, false, true); + IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, + "org.foo.feature.group", VersionRange.create("[1.0.0, 2.0.0)"), null, false, false, true); verifyRequirement(Collections.singleton(expected), require); } public void testMatchRange() throws Exception { File testFolder = getTestFolder("FeaturesAction.testFilters"); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("<feature id=\"test.feature\" version=\"1.0.0\" > \n"); buffer.append(" <requires> \n"); buffer.append(" <import plugin=\"org.plug\" version=\"[1.0.0,2.0.0)\" match=\"versionRange\" /> \n"); @@ -131,7 +136,7 @@ public class FeaturesActionTest extends ActionTest { writeBuffer(featureXML, buffer); publisherInfo = new PublisherInfo(); - FeaturesAction action = new FeaturesAction(new File[] {testFolder}); + FeaturesAction action = new FeaturesAction(new File[] { testFolder }); action.perform(publisherInfo, publisherResult, new NullProgressMonitor()); IInstallableUnit iu = publisherResult.getIU("test.feature.feature.group", Version.parseVersion("1.0.0"), null); @@ -141,10 +146,12 @@ public class FeaturesActionTest extends ActionTest { String requireName = ((IRequiredCapability) require).getName(); if (requireName.equals("org.foo.feature.group")) { - IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.foo.feature.group", VersionRange.create("[1.0.0, 2.0.0)"), null, false, false, true); + IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, + "org.foo.feature.group", VersionRange.create("[1.0.0, 2.0.0)"), null, false, false, true); verifyRequirement(Collections.singleton(expected), require); } else if (requireName.equals("org.plug")) { - IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.plug", VersionRange.create("[1.0.0, 2.0.0)"), null, false, false, true); + IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.plug", + VersionRange.create("[1.0.0, 2.0.0)"), null, false, false, true); verifyRequirement(Collections.singleton(expected), require); } } @@ -152,7 +159,7 @@ public class FeaturesActionTest extends ActionTest { public void testMatchGreaterOrEqual() throws Exception { File testFolder = getTestFolder("FeaturesAction.testFilters"); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("<feature id=\"test.feature\" version=\"1.0.0\" > \n"); buffer.append(" <requires> \n"); buffer.append(" <import plugin=\"org.plug\" version=\"1.0.0\" match=\"greaterOrEqual\" /> \n"); @@ -163,7 +170,7 @@ public class FeaturesActionTest extends ActionTest { writeBuffer(featureXML, buffer); publisherInfo = new PublisherInfo(); - FeaturesAction action = new FeaturesAction(new File[] {testFolder}); + FeaturesAction action = new FeaturesAction(new File[] { testFolder }); action.perform(publisherInfo, publisherResult, new NullProgressMonitor()); IInstallableUnit iu = publisherResult.getIU("test.feature.feature.group", Version.parseVersion("1.0.0"), null); @@ -173,10 +180,12 @@ public class FeaturesActionTest extends ActionTest { String requireName = ((IRequiredCapability) require).getName(); if (requireName.equals("org.foo.feature.group")) { - IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.foo.feature.group", VersionRange.create("1.0.0"), null, false, false, true); + IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, + "org.foo.feature.group", VersionRange.create("1.0.0"), null, false, false, true); verifyRequirement(Collections.singleton(expected), require); } else if (requireName.equals("org.plug")) { - IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.plug", VersionRange.create("1.0.0"), null, false, false, true); + IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.plug", + VersionRange.create("1.0.0"), null, false, false, true); verifyRequirement(Collections.singleton(expected), require); } } @@ -184,7 +193,7 @@ public class FeaturesActionTest extends ActionTest { public void testFilters() throws Exception { File testFolder = getTestFolder("FeaturesAction.testFilters"); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("<feature id=\"test.feature\" version=\"1.0.0\" > \n"); buffer.append(" <includes id=\"org.foo\" version=\"1.0.0\" filter=\"(osgi.os=win32)\"/> \n"); buffer.append(" <plugin id=\"org.plug\" version=\"1.0.0\" filter=\"(my.prop=foo)\" os=\"win32\" /> \n"); @@ -197,7 +206,7 @@ public class FeaturesActionTest extends ActionTest { writeBuffer(featureXML, buffer); publisherInfo = new PublisherInfo(); - FeaturesAction action = new FeaturesAction(new File[] {testFolder}); + FeaturesAction action = new FeaturesAction(new File[] { testFolder }); action.perform(publisherInfo, publisherResult, new NullProgressMonitor()); IInstallableUnit iu = publisherResult.getIU("test.feature.feature.group", Version.parseVersion("1.0.0"), null); @@ -207,7 +216,8 @@ public class FeaturesActionTest extends ActionTest { if (((IRequiredCapability) require).getName().equals("org.foo.feature.group")) { assertEquals(ExpressionUtil.parseLDAP("(osgi.os=win32)"), require.getFilter().getParameters()[0]); } else if (((IRequiredCapability) require).getName().equals("org.plug")) { - assertEquals(ExpressionUtil.parseLDAP("(&(my.prop=foo)(osgi.os=win32))"), require.getFilter().getParameters()[0]); + assertEquals(ExpressionUtil.parseLDAP("(&(my.prop=foo)(osgi.os=win32))"), + require.getFilter().getParameters()[0]); } else if (((IRequiredCapability) require).getName().equals("org.plug2")) { assertEquals(ExpressionUtil.parseLDAP("(my.prop=foo)"), require.getFilter().getParameters()[0]); } else if (((IRequiredCapability) require).getName().equals("org.foo2.feature.group")) { @@ -222,151 +232,173 @@ public class FeaturesActionTest extends ActionTest { } private void verifyMetadata() { - //{foo.feature.jar=[foo.feature.jar 1.0.0], bar.feature.jar=[bar.feature.jar 1.1.1], foo.feature.group=[foo.feature.group 1.0.0], bar.feature.group=[bar.feature.group 1.1.1]} - ArrayList<IInstallableUnit> fooIUs = new ArrayList<>(publisherResult.getIUs("foo.feature.jar", IPublisherResult.NON_ROOT)); //$NON-NLS-1$ - assertTrue(fooIUs.size() == 1); + // {foo.feature.jar=[foo.feature.jar 1.0.0], bar.feature.jar=[bar.feature.jar + // 1.1.1], foo.feature.group=[foo.feature.group 1.0.0], + // bar.feature.group=[bar.feature.group 1.1.1]} + ArrayList<IInstallableUnit> fooIUs = new ArrayList<>( + publisherResult.getIUs("foo.feature.jar", IPublisherResult.NON_ROOT)); //$NON-NLS-1$ + assertEquals(1, fooIUs.size()); IInstallableUnit foo = fooIUs.get(0); assertTrue(foo.getId().equalsIgnoreCase("foo.feature.jar")); //$NON-NLS-1$ - assertTrue(foo.getVersion().equals(fooVersion)); + assertEquals(fooVersion, foo.getVersion()); assertEquals("Foo Feature", foo.getProperty(IInstallableUnit.PROP_NAME)); assertEquals("Foo Description", foo.getProperty(IInstallableUnit.PROP_DESCRIPTION)); assertEquals("Foo License", foo.getLicenses().iterator().next().getBody()); assertEquals("Foo Copyright", foo.getCopyright().getBody()); - assertTrue(foo.getProperty("key1").equals("value1")); //$NON-NLS-1$ //$NON-NLS-2$ - assertTrue(foo.getProperty("key2").equals("value2")); //$NON-NLS-1$//$NON-NLS-2$ - assertTrue(foo.getArtifacts().iterator().next().equals(FOO_KEY)); - assertEquals(foo.getFilter().getParameters()[0], ExpressionUtil.parseLDAP("(org.eclipse.update.install.features=true)")); //$NON-NLS-1$ + assertEquals("value1", foo.getProperty("key1")); //$NON-NLS-1$ //$NON-NLS-2$ + assertEquals("value2", foo.getProperty("key2")); //$NON-NLS-1$//$NON-NLS-2$ + assertEquals(FOO_KEY, foo.getArtifacts().iterator().next()); + assertEquals(foo.getFilter().getParameters()[0], + ExpressionUtil.parseLDAP("(org.eclipse.update.install.features=true)")); //$NON-NLS-1$ - //check touchpointType + // check touchpointType assertTrue(foo.getTouchpointType().getId().equalsIgnoreCase("org.eclipse.equinox.p2.osgi")); //$NON-NLS-1$ - assertTrue(foo.getTouchpointType().getVersion().equals(fooVersion)); + assertEquals(fooVersion, foo.getTouchpointType().getVersion()); - //zipped=true + // zipped=true Collection<ITouchpointData> tpData = foo.getTouchpointData(); String fooValue = tpData.iterator().next().getInstructions().get("zipped").getBody(); //$NON-NLS-1$ assertTrue(fooValue.equalsIgnoreCase("true")); //$NON-NLS-1$ Collection<IRequirement> fooRequiredCapabilities = foo.getRequirements(); - assertTrue(fooRequiredCapabilities.size() == 0); + assertTrue(fooRequiredCapabilities.isEmpty()); Collection<IProvidedCapability> fooProvidedCapabilities = foo.getProvidedCapabilities(); - verifyProvidedCapability(fooProvidedCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "foo.feature.jar", fooVersion); //$NON-NLS-1$ - verifyProvidedCapability(fooProvidedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "feature", fooVersion); //$NON-NLS-1$ + verifyProvidedCapability(fooProvidedCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "foo.feature.jar", //$NON-NLS-1$ + fooVersion); + verifyProvidedCapability(fooProvidedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "feature", //$NON-NLS-1$ + fooVersion); verifyProvidedCapability(fooProvidedCapabilities, "org.eclipse.update.feature", FOO, fooVersion); //$NON-NLS-1$ - assertTrue(fooProvidedCapabilities.size() == 3); + assertEquals(3, fooProvidedCapabilities.size()); - //feature group IU for foo + // feature group IU for foo fooIUs = new ArrayList<>(publisherResult.getIUs("foo.feature.group", IPublisherResult.ROOT)); //$NON-NLS-1$ - assertTrue(fooIUs.size() == 1); + assertEquals(1, fooIUs.size()); IInstallableUnit fooGroup = fooIUs.get(0); - tpData = fooGroup.getTouchpointData(); - assertEquals(1, tpData.size()); - ITouchpointInstruction instruction = tpData.iterator().next().getInstruction("install"); + ITouchpointAdvice tData = capture.getValue(); + ITouchpointInstruction instruction = tData.getTouchpointData(NO_TP_DATA).getInstruction("install"); assertNotNull(instruction); - assertEquals("ln(targetDir:@artifact,linkTarget:foo/lib.1.so,linkName:lib.so);chmod(targetDir:@artifact,targetFile:lib/lib.so,permissions:755);", instruction.getBody()); + assertEquals( + "ln(targetDir:@artifact,linkTarget:foo/lib.1.so,linkName:lib.so);chmod(targetDir:@artifact,targetFile:lib/lib.so,permissions:755);", + instruction.getBody()); assertNull(fooGroup.getFilter()); - /*verify bar*/ - ArrayList<IInstallableUnit> barIUs = new ArrayList<>(publisherResult.getIUs("bar.feature.jar", IPublisherResult.NON_ROOT)); //$NON-NLS-1$ - assertTrue(barIUs.size() == 1); + /* verify bar */ + ArrayList<IInstallableUnit> barIUs = new ArrayList<>( + publisherResult.getIUs("bar.feature.jar", IPublisherResult.NON_ROOT)); //$NON-NLS-1$ + assertEquals(1, barIUs.size()); IInstallableUnit bar = barIUs.get(0); - assertTrue(bar.getId().equals("bar.feature.jar")); //$NON-NLS-1$ - assertTrue(bar.getVersion().equals(barVersion)); - assertTrue(bar.getProperty("key1").equals("value1")); //$NON-NLS-1$//$NON-NLS-2$ - assertTrue(bar.getProperty("key2").equals("value2")); //$NON-NLS-1$//$NON-NLS-2$ + assertEquals("bar.feature.jar", bar.getId()); //$NON-NLS-1$ + assertEquals(barVersion, bar.getVersion()); + assertEquals("value1", bar.getProperty("key1")); //$NON-NLS-1$//$NON-NLS-2$ + assertEquals("value2", bar.getProperty("key2")); //$NON-NLS-1$//$NON-NLS-2$ assertTrue(bar.getProperties().containsKey("org.eclipse.update.installHandler")); //$NON-NLS-1$ assertTrue(bar.getProperties().containsValue("handler=bar handler")); //$NON-NLS-1$ - assertTrue(bar.getArtifacts().iterator().next().equals(BAR_KEY)); - assertEquals(bar.getFilter().getParameters()[0], ExpressionUtil.parseLDAP("(org.eclipse.update.install.features=true)")); //$NON-NLS-1$ + assertEquals(BAR_KEY, bar.getArtifacts().iterator().next()); + assertEquals(bar.getFilter().getParameters()[0], + ExpressionUtil.parseLDAP("(org.eclipse.update.install.features=true)")); //$NON-NLS-1$ assertTrue(bar.isSingleton()); barIUs = new ArrayList<>(publisherResult.getIUs("bar.feature.group", IPublisherResult.ROOT)); //$NON-NLS-1$ - assertTrue(fooIUs.size() == 1); + assertEquals(1, fooIUs.size()); IInstallableUnit barGroup = barIUs.get(0); Collection<IRequirement> barRequiredCapabilities = barGroup.getRequirements(); - //contains(barRequiredCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "bar_root", new VersionRange(barVersion, true, barVersion, true), null, false /*multiple*/, false /*optional*/); //$NON-NLS-1$//$NON-NLS-2$ - verifyRequirement(barRequiredCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "bar.feature.jar", new VersionRange(barVersion, true, barVersion, true), "(org.eclipse.update.install.features=true)", 1, 1, true); //$NON-NLS-1$//$NON-NLS-2$ - verifyRequirement(barRequiredCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "org.bar.feature.feature.group", VersionRange.emptyRange, "(&(|(osgi.nl=de)(osgi.nl=en)(osgi.nl=fr)))", 1, 1, true); //$NON-NLS-1$//$NON-NLS-2$ - assertEquals(barGroup.getFilter().getParameters()[0], ExpressionUtil.parseLDAP("(&(|(osgi.os=macosx)(osgi.os=win32))(|(osgi.ws=carbon)(osgi.ws=win32))(|(osgi.arch=ppc)(osgi.arch=x86))(osgi.nl=en))")); - - //check zipped=true in touchpointData + // contains(barRequiredCapabilities, IInstallableUnit.NAMESPACE_IU_ID, + // "bar_root", new VersionRange(barVersion, true, barVersion, true), null, false + // /*multiple*/, false /*optional*/); //$NON-NLS-1$//$NON-NLS-2$ + verifyRequirement(barRequiredCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "bar.feature.jar", //$NON-NLS-1$ + new VersionRange(barVersion, true, barVersion, true), "(org.eclipse.update.install.features=true)", 1, //$NON-NLS-1$ + 1, true); + verifyRequirement(barRequiredCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "org.bar.feature.feature.group", //$NON-NLS-1$ + VersionRange.emptyRange, "(&(|(osgi.nl=de)(osgi.nl=en)(osgi.nl=fr)))", 1, 1, true); //$NON-NLS-1$ + assertEquals(barGroup.getFilter().getParameters()[0], ExpressionUtil.parseLDAP( + "(&(|(osgi.os=macosx)(osgi.os=win32))(|(osgi.ws=carbon)(osgi.ws=win32))(|(osgi.arch=ppc)(osgi.arch=x86))(osgi.nl=en))")); + + // check zipped=true in touchpointData String barValue = bar.getTouchpointData().iterator().next().getInstructions().get("zipped").getBody(); //$NON-NLS-1$ assertTrue(barValue.equalsIgnoreCase("true")); //$NON-NLS-1$ - //check touchpointType + // check touchpointType assertTrue(bar.getTouchpointType().getId().equalsIgnoreCase("org.eclipse.equinox.p2.osgi")); //$NON-NLS-1$ - assertTrue(bar.getTouchpointType().getVersion().equals(fooVersion)); - //String namespace, String name, VersionRange range, String filter, boolean optional, boolean multiple, boolean greedy) + assertEquals(fooVersion, bar.getTouchpointType().getVersion()); + // String namespace, String name, VersionRange range, String filter, boolean + // optional, boolean multiple, boolean greedy) barRequiredCapabilities = bar.getRequirements(); - assertTrue(barRequiredCapabilities.size() == 0); + assertTrue(barRequiredCapabilities.isEmpty()); Collection<IProvidedCapability> barProvidedCapabilities = bar.getProvidedCapabilities(); - verifyProvidedCapability(barProvidedCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "bar.feature.jar", barVersion); //$NON-NLS-1$ - verifyProvidedCapability(barProvidedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "feature", fooVersion); //$NON-NLS-1$ + verifyProvidedCapability(barProvidedCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "bar.feature.jar", //$NON-NLS-1$ + barVersion); + verifyProvidedCapability(barProvidedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "feature", //$NON-NLS-1$ + fooVersion); verifyProvidedCapability(barProvidedCapabilities, "org.eclipse.update.feature", BAR, barVersion); //$NON-NLS-1$ - assertTrue(barProvidedCapabilities.size() == 3); + assertEquals(3, barProvidedCapabilities.size()); } private void verifyArtifacts() throws IOException { ZipInputStream actualStream = artifactRepository.getZipInputStream(FOO_KEY); - Map<String, Object[]> expected = getFileMap(new HashMap<>(), new File[] {new File(root, FOO)}, new Path(new File(root, FOO).getAbsolutePath())); + Map<String, Object[]> expected = getFileMap(new HashMap<>(), new File[] { new File(root, FOO) }, + new Path(new File(root, FOO).getAbsolutePath())); TestData.assertContains(expected, actualStream, true); - expected = getFileMap(new HashMap<>(), new File[] {new File(root, BAR)}, new Path(new File(root, BAR).getAbsolutePath())); + expected = getFileMap(new HashMap<>(), new File[] { new File(root, BAR) }, + new Path(new File(root, BAR).getAbsolutePath())); actualStream = artifactRepository.getZipInputStream(BAR_KEY); TestData.assertContains(expected, actualStream, true); } @Override protected void insertPublisherInfoBehavior() { - //setup metadataRepository with barIU - metadataRepository = new TestMetadataRepository(getAgent(), new IInstallableUnit[] {mockIU(BAR, null)}); - - ArrayList<IPropertyAdvice> adviceCollection = fillAdvice(new ArrayList<>()); - expect(publisherInfo.getAdvice(null, false, "bar.feature.jar", barVersion, IPropertyAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar", barVersion, IPropertyAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar", barVersion, IFeatureRootAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, IPropertyAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, ITouchpointAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, ICapabilityAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, - IAdditionalInstallableUnitAdvice.class)).andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo.feature.jar", fooVersion, IPropertyAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, IUpdateDescriptorAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo", fooVersion, IPropertyAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo", fooVersion, IFeatureRootAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, IPropertyAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, ICapabilityAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, - IAdditionalInstallableUnitAdvice.class)).andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, IUpdateDescriptorAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH).anyTimes(); - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getMetadataRepository()).andReturn(metadataRepository).anyTimes(); - expect(publisherInfo.getContextMetadataRepository()).andReturn(null).anyTimes(); - - //capture any touchpoint advice, and return the captured advice when the action asks for it - publisherInfo.addAdvice(and(isA(ITouchpointAdvice.class), capture(tpAdvice))); - EasyMock.expectLastCall().anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, ITouchpointAdvice.class)).andReturn(new CaptureList<>(tpAdvice)).anyTimes(); + // setup metadataRepository with barIU + metadataRepository = new TestMetadataRepository(getAgent(), new IInstallableUnit[] { mockIU(BAR, null) }); + + List<IPropertyAdvice> adviceCollection = fillAdvice(new ArrayList<>()); + when(publisherInfo.getAdvice(null, false, "bar.feature.jar", barVersion, IPropertyAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(null, false, "bar", barVersion, IPropertyAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(null, false, "bar", barVersion, IFeatureRootAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, IPropertyAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, ITouchpointAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, ICapabilityAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, + IAdditionalInstallableUnitAdvice.class)).thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "foo.feature.jar", fooVersion, IPropertyAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, IUpdateDescriptorAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "foo", fooVersion, IPropertyAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(null, false, "foo", fooVersion, IFeatureRootAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, IPropertyAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, ICapabilityAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, + IAdditionalInstallableUnitAdvice.class)).thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, IUpdateDescriptorAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getArtifactOptions()) + .thenReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH); + when(publisherInfo.getArtifactRepository()).thenReturn(artifactRepository); + when(publisherInfo.getMetadataRepository()).thenReturn(metadataRepository); + when(publisherInfo.getContextMetadataRepository()).thenReturn(null); } - private ArrayList<IPropertyAdvice> fillAdvice(ArrayList<IPropertyAdvice> adviceCollection) { + private List<IPropertyAdvice> fillAdvice(ArrayList<IPropertyAdvice> adviceCollection) { Map<String, String> prop = new HashMap<>(); prop.put("key1", "value1"); //$NON-NLS-1$//$NON-NLS-2$ prop.put("key2", "value2"); //$NON-NLS-1$//$NON-NLS-2$ - IPropertyAdvice propertyAdvice = EasyMock.createMock(IPropertyAdvice.class); - expect(propertyAdvice.getInstallableUnitProperties((InstallableUnitDescription) EasyMock.anyObject())).andReturn(prop).anyTimes(); - expect(propertyAdvice.getArtifactProperties((IInstallableUnit) EasyMock.anyObject(), (IArtifactDescriptor) EasyMock.anyObject())).andReturn(null).anyTimes(); - EasyMock.replay(propertyAdvice); + IPropertyAdvice propertyAdvice = mock(IPropertyAdvice.class); + when(propertyAdvice.getInstallableUnitProperties(any(InstallableUnitDescription.class))).thenReturn(prop); + when(propertyAdvice.getArtifactProperties(any(IInstallableUnit.class), any(IArtifactDescriptor.class))) + .thenReturn(null); adviceCollection.add(propertyAdvice); return adviceCollection; } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/JREActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/JREActionTest.java index d1d3a4a20..dc5719e14 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/JREActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/JREActionTest.java @@ -16,7 +16,6 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.expect; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.errorStatus; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.okStatus; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.statusWithMessageWhich; @@ -27,6 +26,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; +import static org.mockito.Mockito.when; import java.io.ByteArrayOutputStream; import java.io.File; @@ -276,7 +276,7 @@ public class JREActionTest extends ActionTest { @Override protected void insertPublisherInfoBehavior() { - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_PUBLISH).anyTimes(); + when(publisherInfo.getArtifactRepository()).thenReturn(artifactRepository); + when(publisherInfo.getArtifactOptions()).thenReturn(IPublisherInfo.A_PUBLISH); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/LocalUpdateSiteActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/LocalUpdateSiteActionTest.java index 706760c4e..53d5f03b8 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/LocalUpdateSiteActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/LocalUpdateSiteActionTest.java @@ -14,9 +14,11 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.anyBoolean; -import static org.easymock.EasyMock.anyObject; -import static org.easymock.EasyMock.expect; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; import java.io.File; import java.util.Collection; @@ -29,7 +31,6 @@ import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.metadata.ITouchpointData; import org.eclipse.equinox.p2.metadata.ITouchpointInstruction; import org.eclipse.equinox.p2.metadata.Version; -import org.eclipse.equinox.p2.publisher.IPublisherAdvice; import org.eclipse.equinox.p2.publisher.IPublisherInfo; import org.eclipse.equinox.p2.tests.TestData; import org.eclipse.equinox.p2.tests.publisher.TestArtifactRepository; @@ -48,10 +49,11 @@ public class LocalUpdateSiteActionTest extends ActionTest { @Override protected void insertPublisherInfoBehavior() { super.insertPublisherInfoBehavior(); - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH).anyTimes(); - expect(publisherInfo.getAdvice((String) anyObject(), anyBoolean(), (String) anyObject(), (Version) anyObject(), - (Class<IPublisherAdvice>) anyObject())).andReturn(Collections.emptyList()).anyTimes(); + when(publisherInfo.getArtifactRepository()).thenReturn(artifactRepository); + when(publisherInfo.getArtifactOptions()) + .thenReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH); + when(publisherInfo.getAdvice(anyString(), anyBoolean(), anyString(), any(Version.class), any(Class.class))) + .thenReturn(Collections.emptyList()); } /** diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionCapturingTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionCapturingTest.java index eb9a2d1b8..898c5f0fb 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionCapturingTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionCapturingTest.java @@ -16,65 +16,35 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.anyBoolean; -import static org.easymock.EasyMock.anyObject; -import static org.easymock.EasyMock.createNiceMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.expectLastCall; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.okStatus; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.verify; import java.io.File; -import java.util.Collections; -import org.easymock.Capture; -import org.easymock.EasyMock; +import java.util.stream.Collectors; import org.eclipse.core.runtime.IStatus; import org.eclipse.equinox.frameworkadmin.BundleInfo; import org.eclipse.equinox.internal.p2.publisher.eclipse.ProductFile; -import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.publisher.IPublisherAdvice; -import org.eclipse.equinox.p2.publisher.IPublisherInfo; -import org.eclipse.equinox.p2.publisher.actions.RootIUAdvice; import org.eclipse.equinox.p2.publisher.eclipse.IConfigAdvice; import org.eclipse.equinox.p2.publisher.eclipse.IExecutableAdvice; import org.eclipse.equinox.p2.publisher.eclipse.ProductAction; import org.eclipse.equinox.p2.publisher.eclipse.ProductFileAdvice; import org.eclipse.equinox.p2.tests.TestData; import org.eclipse.equinox.p2.tests.publisher.TestArtifactRepository; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; public class ProductActionCapturingTest extends ActionTest { File executablesFeatureLocation = null; String source = ""; - private Capture<RootIUAdvice> rootIUAdviceCapture; - private Capture<ProductFileAdvice> productFileAdviceCapture; protected TestArtifactRepository artifactRepository = new TestArtifactRepository(getAgent()); @Override - protected IPublisherInfo createPublisherInfoMock() { - //override to create a nice mock, because we don't care about other method calls. - return createNiceMock(IPublisherInfo.class); - } - - @Override - protected void insertPublisherInfoBehavior() { - // capture these calls for assertions - publisherInfo.addAdvice(EasyMock.and(EasyMock.isA(RootIUAdvice.class), EasyMock.capture(rootIUAdviceCapture))); - publisherInfo.addAdvice(EasyMock.and(EasyMock.isA(ProductFileAdvice.class), EasyMock.capture(productFileAdviceCapture))); - - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_PUBLISH).anyTimes(); - //Return an empty list every time getAdvice is called - expect(publisherInfo.getAdvice((String) anyObject(), anyBoolean(), (String) anyObject(), (Version) anyObject(), (Class<IPublisherAdvice>) anyObject())).andReturn(Collections.emptyList()); - expectLastCall().anyTimes(); - } - - @Override public void setUp() throws Exception { - rootIUAdviceCapture = Capture.newInstance(); - productFileAdviceCapture = Capture.newInstance(); setupPublisherInfo(); setupPublisherResult(); } @@ -84,15 +54,18 @@ public class ProductActionCapturingTest extends ActionTest { * IConfigAdvice (start levels, auto-start). */ public void testSetBundleConfigData() throws Exception { + ArgumentCaptor<IPublisherAdvice> productFileAdviceCapture = ArgumentCaptor.forClass(IPublisherAdvice.class); addContextIU("org.eclipse.rcp.feature.group", "3.5.0.v20081110-9C9tEvNEla71LZ2jFz-RFB-t"); ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "startLevel.product").toString()); - testAction = new ProductAction(source, productFile, flavorArg, executablesFeatureLocation); + testAction = Mockito.spy(new ProductAction(source, productFile, flavorArg, executablesFeatureLocation)); IStatus status = testAction.perform(publisherInfo, publisherResult, null); + verify(publisherInfo, Mockito.atLeastOnce()).addAdvice(productFileAdviceCapture.capture()); assertThat(status, is(okStatus())); - IConfigAdvice configAdvice = productFileAdviceCapture.getValue(); + IConfigAdvice configAdvice = (IConfigAdvice) productFileAdviceCapture.getAllValues().stream() + .filter(IConfigAdvice.class::isInstance).collect(Collectors.toList()).get(0); BundleInfo[] bundles = configAdvice.getBundles(); assertEquals("1.0", 2, bundles.length); assertEquals("1.1", "org.eclipse.equinox.common", bundles[0].getSymbolicName()); @@ -110,14 +83,17 @@ public class ProductActionCapturingTest extends ActionTest { * Tests that correct advice is created for the org.eclipse.platform product. */ public void testPlatformProduct() throws Exception { + ArgumentCaptor<IPublisherAdvice> productFileAdviceCapture = ArgumentCaptor.forClass(IPublisherAdvice.class); ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "platform.product").toString()); addContextIU("org.eclipse.platform.feature.group", "1.2.3"); - testAction = new ProductAction(source, productFile, flavorArg, executablesFeatureLocation); + testAction = Mockito.spy(new ProductAction(source, productFile, flavorArg, executablesFeatureLocation)); IStatus status = testAction.perform(publisherInfo, publisherResult, null); + verify(publisherInfo, Mockito.atLeastOnce()).addAdvice(productFileAdviceCapture.capture()); assertThat(status, is(okStatus())); - IExecutableAdvice launchAdvice = productFileAdviceCapture.getValue(); + IExecutableAdvice launchAdvice = (IExecutableAdvice) productFileAdviceCapture.getAllValues().stream() + .filter(ProductFileAdvice.class::isInstance).collect(Collectors.toList()).get(0); assertEquals("1.0", "eclipse", launchAdvice.getExecutableName()); String[] programArgs = launchAdvice.getProgramArguments(); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java index 3405c53e5..994cfafcd 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java @@ -16,7 +16,6 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.createNiceMock; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.errorStatus; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.okStatus; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.statusWithMessageWhich; @@ -71,21 +70,18 @@ public class ProductActionTest extends ActionTest { String source = ""; protected TestArtifactRepository artifactRepository = new TestArtifactRepository(getAgent()); - @Override protected IPublisherInfo createPublisherInfoMock() { - //override to create a nice mock, because we don't care about other method calls. - return createNiceMock(IPublisherInfo.class); - } - - @Override public void setUp() throws Exception { + @Override + public void setUp() throws Exception { setupPublisherInfo(); setupPublisherResult(); } - @Override public void setupPublisherInfo() { + @Override + public void setupPublisherInfo() { PublisherInfo publisherInfoImpl = new PublisherInfo(); publisherInfoImpl.setArtifactRepository(artifactRepository); publisherInfoImpl.setArtifactOptions(IPublisherInfo.A_PUBLISH); - publisherInfoImpl.setConfigurations(new String[] {configSpec}); + publisherInfoImpl.setConfigurations(new String[] { configSpec }); publisherInfo = publisherInfoImpl; } @@ -95,18 +91,20 @@ public class ProductActionTest extends ActionTest { * splash screen, icon, etc. */ public void testBrandedApplication() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "brandedProduct/branded.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "brandedProduct/branded.product").toString()); addContextIU("org.eclipse.platform.feature.group", "1.2.3"); performProductAction(productFile); Collection<IInstallableUnit> ius = publisherResult.getIUs("branded.product", IPublisherResult.NON_ROOT); assertEquals("1.0", 1, ius.size()); - //TODO assert branding was done correctly + // TODO assert branding was done correctly } public void testLicense() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "productWithLicense.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "productWithLicense.product").toString()); performProductAction(productFile); Collection<IInstallableUnit> ius = publisherResult.getIUs("licenseIU.product", IPublisherResult.NON_ROOT); assertEquals("1.0", 1, ius.size()); @@ -116,7 +114,8 @@ public class ProductActionTest extends ActionTest { } public void testLicenseNoURL() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "licenseNoURL.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "licenseNoURL.product").toString()); performProductAction(productFile); Collection<IInstallableUnit> ius = publisherResult.getIUs("licenseIU.product", IPublisherResult.NON_ROOT); assertEquals("1.0", 1, ius.size()); @@ -126,7 +125,8 @@ public class ProductActionTest extends ActionTest { } public void testLicenseNoText() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "licenseNoText.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "licenseNoText.product").toString()); performProductAction(productFile); Collection<IInstallableUnit> ius = publisherResult.getIUs("licenseIU.product", IPublisherResult.NON_ROOT); assertEquals("1.0", 1, ius.size()); @@ -136,7 +136,8 @@ public class ProductActionTest extends ActionTest { } public void testMissingLicense() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "productWithNoLicense.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "productWithNoLicense.product").toString()); performProductAction(productFile); Collection<IInstallableUnit> ius = publisherResult.getIUs("licenseIU.product", IPublisherResult.NON_ROOT); assertEquals("1.0", 1, ius.size()); @@ -145,8 +146,10 @@ public class ProductActionTest extends ActionTest { } public void testMultiProductPublishing() throws Exception { - ProductFile productFile1 = new ProductFile(TestData.getFile("ProductActionTest", "boundedVersionConfigurations.product").toString()); - ProductFile productFile2 = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + ProductFile productFile1 = new ProductFile( + TestData.getFile("ProductActionTest", "boundedVersionConfigurations.product").toString()); + ProductFile productFile2 = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); addContextIU("org.eclipse.core.runtime", "4.0.0"); performProductAction(productFile1); @@ -158,7 +161,8 @@ public class ProductActionTest extends ActionTest { } public void testMultiPlatformCUs_DifferentPlatforms() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); setConfiguration(LINUX_CONFIG_SPEC); addContextIU("org.eclipse.core.runtime", "0.0.0", WIN_FILTER); @@ -169,7 +173,8 @@ public class ProductActionTest extends ActionTest { } public void testMultiPlatformCUs_SamePlatforms() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); setConfiguration(LINUX_CONFIG_SPEC); addContextIU("org.eclipse.core.runtime", "0.0.0", LINUX_FILTER); @@ -180,7 +185,8 @@ public class ProductActionTest extends ActionTest { } public void testMultiPlatformCUs_SamePlatforms_NoVersion() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); setConfiguration(LINUX_CONFIG_SPEC); addContextIU("org.eclipse.core.runtime", null, LINUX_FILTER); @@ -191,11 +197,13 @@ public class ProductActionTest extends ActionTest { } public void testMultiPlatformCUs_SamePlatforms_BoundedVersions() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); setConfiguration(LINUX_CONFIG_SPEC); - // Set a specific version number, the one in the .product file uses 0.0.0. Let's see if it binds properly - //filter is different from linuxConfigSpec, but will still match + // Set a specific version number, the one in the .product file uses 0.0.0. Let's + // see if it binds properly + // filter is different from linuxConfigSpec, but will still match addContextIU("org.eclipse.core.runtime", "4.0.0", "(osgi.os=linux)"); performProductAction(productFile); @@ -205,36 +213,45 @@ public class ProductActionTest extends ActionTest { } public void testCUsHost() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); setConfiguration(LINUX_CONFIG_SPEC); - // Set a specific version number, the one in the .product file uses 0.0.0. Let's see if it binds properly - //filter is different from linuxConfigSpec, but will still match + // Set a specific version number, the one in the .product file uses 0.0.0. Let's + // see if it binds properly + // filter is different from linuxConfigSpec, but will still match addContextIU("org.eclipse.core.runtime", "4.0.0", "(osgi.os=linux)"); performProductAction(productFile); - IInstallableUnitFragment fragment = (IInstallableUnitFragment) getUniquePublishedIU(flavorArg + LINUX_CONFIG_SPEC + "org.eclipse.core.runtime"); - assertEquals("1.1", "org.eclipse.core.runtime", RequiredCapability.extractName(fragment.getHost().iterator().next().getMatches())); - assertEquals("1.2", Version.create("4.0.0"), RequiredCapability.extractRange(fragment.getHost().iterator().next().getMatches()).getMinimum()); + IInstallableUnitFragment fragment = (IInstallableUnitFragment) getUniquePublishedIU( + flavorArg + LINUX_CONFIG_SPEC + "org.eclipse.core.runtime"); + assertEquals("1.1", "org.eclipse.core.runtime", + RequiredCapability.extractName(fragment.getHost().iterator().next().getMatches())); + assertEquals("1.2", Version.create("4.0.0"), + RequiredCapability.extractRange(fragment.getHost().iterator().next().getMatches()).getMinimum()); assertEquals("1.3", Version.create("1.0.0"), fragment.getVersion()); } public void testMultiConfigspecProductPublishing() throws IOException, Exception { ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "platform.product").toString()); - ((PublisherInfo) publisherInfo).setConfigurations(new String[] {"carbon.macos.x86", "cocoa.macos.x86"}); + ((PublisherInfo) publisherInfo).setConfigurations(new String[] { "carbon.macos.x86", "cocoa.macos.x86" }); addContextIU("org.eclipse.platform.feature.group", "1.2.3"); performProductAction(productFile); - Collection<IConfigAdvice> advice = publisherInfo.getAdvice("carbon.macos.x86", false, null, null, IConfigAdvice.class); + Collection<IConfigAdvice> advice = publisherInfo.getAdvice("carbon.macos.x86", false, null, null, + IConfigAdvice.class); assertEquals("1.0", 1, advice.size()); } public void testANYConfigSpecPublishing_GeneralBundle() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); - String configSpecANY = AbstractPublisherAction.createConfigSpec("ANY", "ANY", "ANY"); // configuration spec to create CUs without filters + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + String configSpecANY = AbstractPublisherAction.createConfigSpec("ANY", "ANY", "ANY"); // configuration spec to + // create CUs without + // filters setConfiguration(configSpecANY); addContextIU("org.eclipse.core.runtime", "4.0.0"); @@ -242,16 +259,22 @@ public class ProductActionTest extends ActionTest { performProductAction(productFile); // there is a CU for the IU because it applies to all platforms - IInstallableUnitFragment fragment = (IInstallableUnitFragment) getUniquePublishedIU(flavorArg + configSpecANY + "org.eclipse.core.runtime"); - assertEquals("1.1", "org.eclipse.core.runtime", RequiredCapability.extractName(fragment.getHost().iterator().next().getMatches())); - assertEquals("1.2", Version.create("4.0.0"), RequiredCapability.extractRange(fragment.getHost().iterator().next().getMatches()).getMinimum()); + IInstallableUnitFragment fragment = (IInstallableUnitFragment) getUniquePublishedIU( + flavorArg + configSpecANY + "org.eclipse.core.runtime"); + assertEquals("1.1", "org.eclipse.core.runtime", + RequiredCapability.extractName(fragment.getHost().iterator().next().getMatches())); + assertEquals("1.2", Version.create("4.0.0"), + RequiredCapability.extractRange(fragment.getHost().iterator().next().getMatches()).getMinimum()); assertEquals("1.3", Version.create("1.0.0"), fragment.getVersion()); assertNull("1.3", fragment.getFilter()); } public void testANYConfigSpecPublishing_PlatformSpecificBundle() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); - String configSpecANY = AbstractPublisherAction.createConfigSpec("ANY", "ANY", "ANY"); // configuration spec to create CUs without filters + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + String configSpecANY = AbstractPublisherAction.createConfigSpec("ANY", "ANY", "ANY"); // configuration spec to + // create CUs without + // filters setConfiguration(configSpecANY); addContextIU("org.eclipse.core.runtime", "4.0.0", WIN_FILTER); // any valid filter can be set here @@ -276,13 +299,15 @@ public class ProductActionTest extends ActionTest { testAction = new ProductAction(source, productFile, flavorArg, executablesFeatureLocation); PublisherInfo info = new PublisherInfo(); info.setContextMetadataRepository(repository); - // TODO this line doesn't have any effect -> is this a bug in the implementation? + // TODO this line doesn't have any effect -> is this a bug in the + // implementation? info.addAdvice(new QueryableFilterAdvice(info.getContextMetadataRepository())); IStatus status = testAction.perform(info, publisherResult, null); assertThat(status, is(okStatus())); - IQueryResult<IInstallableUnit> results = publisherResult.query(QueryUtil.createIUQuery("org.eclipse.platform.ide", Version.create("3.5.0.I20081118")), null); + IQueryResult<IInstallableUnit> results = publisherResult + .query(QueryUtil.createIUQuery("org.eclipse.platform.ide", Version.create("3.5.0.I20081118")), null); assertEquals("1.0", 1, queryResultSize(results)); IInstallableUnit unit = results.iterator().next(); Collection<IRequirement> requiredCapabilities = unit.getRequirements(); @@ -296,12 +321,14 @@ public class ProductActionTest extends ActionTest { } } assertTrue("1.1", capability != null); - assertEquals("1.2", InstallableUnit.parseFilter("(org.eclipse.update.install.features=true)"), capability.getFilter()); + assertEquals("1.2", InstallableUnit.parseFilter("(org.eclipse.update.install.features=true)"), + capability.getFilter()); } public void testProductWithAdviceFile() throws Exception { // product file that has a corresponding advice file (p2.inf). - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest/productWithAdvice", "productWithAdvice.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest/productWithAdvice", "productWithAdvice.product").toString()); testAction = new ProductAction(source, productFile, flavorArg, executablesFeatureLocation); IStatus status = testAction.perform(new PublisherInfo(), publisherResult, null); assertThat(status, is(okStatus())); @@ -310,27 +337,30 @@ public class ProductActionTest extends ActionTest { Collection<ITouchpointData> data = product.getTouchpointData(); assertEquals("1.1", 1, data.size()); String configure = data.iterator().next().getInstruction("configure").getBody(); - assertEquals("1.2", "addRepository(type:0,location:http${#58}//download.eclipse.org/releases/fred);addRepository(type:1,location:http${#58}//download.eclipse.org/releases/fred);", configure); - - //update.id = com.zoobar - //update.range = [4.0,4.3) - //update.severity = 0 - //update.description = This is the description + assertEquals("1.2", + "addRepository(type:0,location:http${#58}//download.eclipse.org/releases/fred);addRepository(type:1,location:http${#58}//download.eclipse.org/releases/fred);", + configure); + + // update.id = com.zoobar + // update.range = [4.0,4.3) + // update.severity = 0 + // update.description = This is the description IUpdateDescriptor update = product.getUpdateDescriptor(); assertEquals("2.0", 0, update.getSeverity()); assertEquals("2.1", "This is the description", update.getDescription()); - //unit that fits in range + // unit that fits in range assertTrue("2.2", update.isUpdateOf(createIU("com.zoobar", Version.createOSGi(4, 1, 0)))); - //unit that is too old for range + // unit that is too old for range assertFalse("2.3", update.isUpdateOf(createIU("com.zoobar", Version.createOSGi(3, 1, 0)))); - //version that is too new and outside of range + // version that is too new and outside of range assertFalse("2.4", update.isUpdateOf(createIU("com.zoobar", Version.createOSGi(6, 1, 0)))); - //unit with matching version but not matching id + // unit with matching version but not matching id assertFalse("2.6", update.isUpdateOf(createIU("com.other", Version.createOSGi(4, 1, 0)))); } public void testFiltersOfInclusions() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "productIncludingFragments.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "productIncludingFragments.product").toString()); addContextIU("generalbundle", "1.0.1"); addContextIU("fragment.win", "1.0.2", WIN_FILTER); // no fragment.linux in the context @@ -338,39 +368,48 @@ public class ProductActionTest extends ActionTest { IStatus status = performProductActionAndReturnStatus(productFile); IInstallableUnit productIU = getUniquePublishedIU("productIncludingFragments.uid"); - assertThat(productIU.getRequirements(), hasItem(createIURequirement("generalbundle", createStrictVersionRange("1.0.1")))); - assertThat(productIU.getRequirements(), hasItem(createIURequirement("fragment.win", createStrictVersionRange("1.0.2"), WIN_FILTER))); + assertThat(productIU.getRequirements(), + hasItem(createIURequirement("generalbundle", createStrictVersionRange("1.0.1")))); + assertThat(productIU.getRequirements(), + hasItem(createIURequirement("fragment.win", createStrictVersionRange("1.0.2"), WIN_FILTER))); - // this is bug 390361: the Linux fragment is required without filter, so the product cannot be installed for Windows ... + // this is bug 390361: the Linux fragment is required without filter, so the + // product cannot be installed for Windows ... assertThat(productIU.getRequirements(), hasItem(createIURequirement("fragment.linux", ANY_VERSION))); // ... therefore the action shall report an error assertThat(status, is(errorStatus())); - assertThat(Arrays.asList(status.getChildren()), hasItem(statusWithMessageWhich(containsString("Included element fragment.linux 0.0.0 is missing")))); + assertThat(Arrays.asList(status.getChildren()), + hasItem(statusWithMessageWhich(containsString("Included element fragment.linux 0.0.0 is missing")))); } public void testMessageForProductWithIgnoredContent() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "mixedContentIgnored.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "mixedContentIgnored.product").toString()); IStatus status = performProductActionAndReturnStatus(productFile); - // expect a warning about redundant, ignored content in product file -> requested in bug 325611 + // expect a warning about redundant, ignored content in product file -> + // requested in bug 325611 assertThat(Arrays.asList(status.getChildren()), hasItem(statusWithMessageWhich(containsString("are ignored")))); // TODO the message should have a code identifying it } public void testJREIncluded() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "brandedProduct/branded.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "brandedProduct/branded.product").toString()); addContextIU("org.eclipse.platform.feature.group", "1.2.3"); performProductAction(productFile); Collection<IInstallableUnit> ius = publisherResult.getIUs("branded.product", IPublisherResult.NON_ROOT); assertEquals(1, ius.size()); assertEquals("Missing a.jre.javase", 1, publisherResult.getIUs("a.jre.javase", IPublisherResult.ROOT).size()); - assertEquals("Missing config.a.jre.javase", 1, publisherResult.getIUs("config.a.jre.javase", IPublisherResult.ROOT).size()); + assertEquals("Missing config.a.jre.javase", 1, + publisherResult.getIUs("config.a.jre.javase", IPublisherResult.ROOT).size()); } public void testRequiredEEAsSpecified() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "productFileActionTest.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "productFileActionTest.product").toString()); addContextIU("org.eclipse.core.commands", "5.0.0"); performProductAction(productFile); @@ -407,7 +446,7 @@ public class ProductActionTest extends ActionTest { } private void setConfiguration(String configSpec) { - ((PublisherInfo) publisherInfo).setConfigurations(new String[] {configSpec}); + ((PublisherInfo) publisherInfo).setConfigurations(new String[] { configSpec }); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTestMac.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTestMac.java index a14b660b5..7673a092c 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTestMac.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTestMac.java @@ -13,30 +13,23 @@ *******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.anyBoolean; -import static org.easymock.EasyMock.anyObject; -import static org.easymock.EasyMock.createNiceMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.expectLastCall; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.okStatus; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.verify; import java.io.File; -import java.util.Collections; -import org.easymock.Capture; -import org.easymock.EasyMock; +import java.util.stream.Collectors; import org.eclipse.core.runtime.IStatus; import org.eclipse.equinox.internal.p2.publisher.eclipse.ProductFile; -import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.publisher.AbstractPublisherAction; import org.eclipse.equinox.p2.publisher.IPublisherAdvice; -import org.eclipse.equinox.p2.publisher.IPublisherInfo; -import org.eclipse.equinox.p2.publisher.actions.RootIUAdvice; import org.eclipse.equinox.p2.publisher.eclipse.IExecutableAdvice; import org.eclipse.equinox.p2.publisher.eclipse.ProductAction; import org.eclipse.equinox.p2.publisher.eclipse.ProductFileAdvice; import org.eclipse.equinox.p2.tests.TestData; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; /** * Tests for {@link ProductAction} specific to Mac. @@ -44,30 +37,11 @@ import org.eclipse.equinox.p2.tests.TestData; public class ProductActionTestMac extends ActionTest { private File executablesFeatureLocation = null; - private Capture<RootIUAdvice> rootIUAdviceCapture; - private Capture<ProductFileAdvice> productFileAdviceCapture; private String source = ""; @Override - protected IPublisherInfo createPublisherInfoMock() { - //override to create a nice mock, because we don't care about other method calls. - return createNiceMock(IPublisherInfo.class); - } - - @Override - protected void insertPublisherInfoBehavior() { - publisherInfo.addAdvice(EasyMock.and(EasyMock.isA(RootIUAdvice.class), EasyMock.capture(rootIUAdviceCapture))); - publisherInfo.addAdvice(EasyMock.and(EasyMock.isA(ProductFileAdvice.class), EasyMock.capture(productFileAdviceCapture))); - //Return an empty list every time getAdvice is called - expect(publisherInfo.getAdvice((String) anyObject(), anyBoolean(), (String) anyObject(), (Version) anyObject(), (Class<IPublisherAdvice>) anyObject())).andReturn(Collections.emptyList()); - expectLastCall().anyTimes(); - } - - @Override public void setUp() throws Exception { configSpec = AbstractPublisherAction.createConfigSpec("carbon", "macosx", "x86"); - rootIUAdviceCapture = Capture.newInstance(); - productFileAdviceCapture = Capture.newInstance(); setupPublisherInfo(); setupPublisherResult(); } @@ -76,13 +50,16 @@ public class ProductActionTestMac extends ActionTest { * Tests that correct advice is created for the org.eclipse.platform product. */ public void testPlatformProduct() throws Exception { + ArgumentCaptor<IPublisherAdvice> productFileAdviceCapture = ArgumentCaptor.forClass(IPublisherAdvice.class); ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "platform.product").toString()); addContextIU("org.eclipse.platform.feature.group", "3.8.3"); - testAction = new ProductAction(source, productFile, flavorArg, executablesFeatureLocation); + testAction = Mockito.spy(new ProductAction(source, productFile, flavorArg, executablesFeatureLocation)); IStatus status = testAction.perform(publisherInfo, publisherResult, null); + verify(publisherInfo, Mockito.atLeastOnce()).addAdvice(productFileAdviceCapture.capture()); assertThat(status, is(okStatus())); - IExecutableAdvice launchAdvice = productFileAdviceCapture.getValue(); + IExecutableAdvice launchAdvice = (IExecutableAdvice) productFileAdviceCapture.getAllValues().stream() + .filter(ProductFileAdvice.class::isInstance).collect(Collectors.toList()).get(0); assertEquals("1.0", "eclipse", launchAdvice.getExecutableName()); String[] programArgs = launchAdvice.getProgramArguments(); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductFileAdviceTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductFileAdviceTest.java index 3a93eb59a..91bc64d39 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductFileAdviceTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductFileAdviceTest.java @@ -208,7 +208,7 @@ public class ProductFileAdviceTest extends AbstractProvisioningTest { configProperties.put("osgi.bundles", "org.eclipse.equinox.simpleconfigurator@1:start"); writeProperties(new File(rootFolder, "config.ini"), configProperties); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("org.eclipse.equinox.common,3.5.100.v20090817,plugins/org.eclipse.equinox.common_3.5.100.v20090817.jar,2,true\n"); buffer.append("org.eclipse.equinox.simpleconfigurator,1.0.200.v20090729-1800,plugins/org.eclipse.equinox.simpleconfigurator_1.0.200.v20090729-1800.jar,1,true\n"); writeBuffer(new File(rootFolder, "org.eclipse.equinox.simpleconfigurator/bundles.info"), buffer); @@ -229,7 +229,7 @@ public class ProductFileAdviceTest extends AbstractProvisioningTest { File root = getTestFolder("configNullLauncher"); File testProduct = new File(root, "test.product"); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("<product id=\"test.product\" version=\"1\" useFeatures=\"false\"> \n"); buffer.append(" <configIni use=\"default\"> \n"); buffer.append(" <win32>config.ini</win32> \n"); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootFilesActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootFilesActionTest.java index 9fa2a5d65..030cd8b2a 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootFilesActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootFilesActionTest.java @@ -15,19 +15,30 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.expect; - -import java.io.*; -import java.util.*; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.zip.ZipInputStream; -import org.eclipse.core.runtime.*; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; import org.eclipse.equinox.internal.p2.core.helpers.FileUtils; import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; import org.eclipse.equinox.p2.metadata.IArtifactKey; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.publisher.AbstractPublisherAction; import org.eclipse.equinox.p2.publisher.IPublisherInfo; -import org.eclipse.equinox.p2.publisher.actions.*; +import org.eclipse.equinox.p2.publisher.actions.IRootFilesAdvice; +import org.eclipse.equinox.p2.publisher.actions.ITouchpointAdvice; +import org.eclipse.equinox.p2.publisher.actions.RootFilesAction; +import org.eclipse.equinox.p2.publisher.actions.RootFilesAdvice; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; import org.eclipse.equinox.p2.tests.TestActivator; import org.eclipse.equinox.p2.tests.TestData; @@ -68,10 +79,13 @@ public class RootFilesActionTest extends ActionTest { @Override public void insertPublisherInfoBehavior() { - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH).anyTimes(); - expect(publisherInfo.getAdvice(configSpec, true, null, null, IRootFilesAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(configSpec, false, flavorArg + topArg, versionArg, ITouchpointAdvice.class)).andReturn(null).anyTimes(); + when(publisherInfo.getArtifactRepository()).thenReturn(artifactRepository); + when(publisherInfo.getArtifactOptions()) + .thenReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH); + when(publisherInfo.getAdvice(configSpec, true, null, null, IRootFilesAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(configSpec, false, flavorArg + topArg, versionArg, ITouchpointAdvice.class)) + .thenReturn(null); } private void setupTestCase(int testArg) throws Exception { diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootIUActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootIUActionTest.java index 21b4dd4b2..637465c39 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootIUActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootIUActionTest.java @@ -15,7 +15,7 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.expect; +import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Collection; @@ -358,16 +358,22 @@ public class RootIUActionTest extends ActionTest { @Override public void insertPublisherInfoBehavior() { - expect(publisherInfo.getAdvice(null, false, rootIU, versionArg, ICapabilityAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); - expect(publisherInfo.getAdvice(null, true, null, null, IRootIUAdvice.class)).andReturn(rootIUAdviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, true, null, null, IVersionAdvice.class)).andReturn(null).anyTimes(); - expect(publisherInfo.getAdvice(null, false, rootIU, versionArg, ITouchpointAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, rootIU, versionArg, IUpdateDescriptorAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, rootIU, versionArg, IPropertyAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, rootIU, versionArg, IAdditionalInstallableUnitAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); - expect(publisherInfo.getAdvice(null, true, rootIU, versionArg, ILicenseAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); - expect(publisherInfo.getMetadataRepository()).andReturn(metadataRepository).anyTimes(); - expect(publisherInfo.getContextMetadataRepository()).andReturn(null).anyTimes(); + when(publisherInfo.getAdvice(null, false, rootIU, versionArg, ICapabilityAdvice.class)) + .thenReturn(new ArrayList<>()); + when(publisherInfo.getAdvice(null, true, null, null, IRootIUAdvice.class)).thenReturn(rootIUAdviceCollection); + when(publisherInfo.getAdvice(null, true, null, null, IVersionAdvice.class)).thenReturn(null); + when(publisherInfo.getAdvice(null, false, rootIU, versionArg, ITouchpointAdvice.class)) + .thenReturn(new ArrayList<>()); + when(publisherInfo.getAdvice(null, false, rootIU, versionArg, IUpdateDescriptorAdvice.class)) + .thenReturn(new ArrayList<>()); + when(publisherInfo.getAdvice(null, false, rootIU, versionArg, IPropertyAdvice.class)) + .thenReturn(new ArrayList<>()); + when(publisherInfo.getAdvice(null, false, rootIU, versionArg, IAdditionalInstallableUnitAdvice.class)) + .thenReturn(new ArrayList<>()); + when(publisherInfo.getAdvice(null, true, rootIU, versionArg, ILicenseAdvice.class)) + .thenReturn(new ArrayList<>()); + when(publisherInfo.getMetadataRepository()).thenReturn(metadataRepository); + when(publisherInfo.getContextMetadataRepository()).thenReturn(null); } @Override diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/reconciler/dropins/AbstractReconcilerTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/reconciler/dropins/AbstractReconcilerTest.java index f1b090aa9..81d99b186 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/reconciler/dropins/AbstractReconcilerTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/reconciler/dropins/AbstractReconcilerTest.java @@ -281,7 +281,7 @@ public class AbstractReconcilerTest extends AbstractProvisioningTest { detailedMessage.append(" install location is ").append(getInstallLocation()).append('\n'); String message = "Need to set the \"org.eclipse.equinox.p2.reconciler.tests.platform.archive\" system property with a valid path to the platform binary drop or copy the archive to be a sibling of the install folder."; assertNotNull(message + "\n" + detailedMessage, file); - assertTrue(message, file.exists()); + assertTrue(message + "\nThe file '" + file.getAbsolutePath() + "' does not exist", file.exists()); assertTrue("File is zero length: " + file.getAbsolutePath(), file.length() > 0); return file; } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/touchpoint/eclipse/SetLauncherNameActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/touchpoint/eclipse/SetLauncherNameActionTest.java index de6ddd662..f01f88db3 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/touchpoint/eclipse/SetLauncherNameActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/touchpoint/eclipse/SetLauncherNameActionTest.java @@ -14,7 +14,9 @@ package org.eclipse.equinox.p2.tests.touchpoint.eclipse; import java.io.File; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.eclipse.equinox.frameworkadmin.BundleInfo; import org.eclipse.equinox.internal.p2.engine.InstallableUnitOperand; import org.eclipse.equinox.internal.p2.touchpoint.eclipse.EclipseTouchpoint; @@ -99,7 +101,7 @@ public class SetLauncherNameActionTest extends AbstractProvisioningTest { //profile will start using "eclipse" by default, give it some content and see if it //survives a name change. File eclipseIni = new File(tempFolder, "eclipse.ini"); - StringBuffer ini = new StringBuffer(); + StringBuilder ini = new StringBuilder(); ini.append("-startup\n"); ini.append("plugins/org.eclipse.equinox.launcher_1.2.4.v1234.jar\n"); writeBuffer(eclipseIni, ini); diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/generator/Europa/site.xml b/bundles/org.eclipse.equinox.p2.tests/testData/generator/Europa/site.xml index a9bf0170d..d9102ed21 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/generator/Europa/site.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/generator/Europa/site.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<site digestURL="http://download.eclipse.org/releases/europa/" mirrorsURL="http://www.eclipse.org/downloads/download.php?file=/releases/europa/site.xml&format=xml&protocol=http" pack200="true"> +<site digestURL="https://download.eclipse.org/releases/europa/" mirrorsURL="http://www.eclipse.org/downloads/download.php?file=/releases/europa/site.xml&format=xml&protocol=http" pack200="true"> <description url="http://www.eclipse.org/europa/"> This Europa Discovery Site contains a number of Eclipse based projects diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform.source_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml b/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform.source_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml index b9cb4db57..5357a19cc 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform.source_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform.source_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml @@ -10,9 +10,9 @@ %license </license> <url> - <update label="%updateSiteName" url="http://update.eclipse.org/updates/3.3"/> - <discovery label="%updateSiteName" url="http://update.eclipse.org/updates/3.3"/> - <discovery label="%secondaryUpdateSiteName" url="http://download.eclipse.org/releases/europa"/> + <update label="%updateSiteName" url="https://update.eclipse.org/updates/3.3"/> + <discovery label="%updateSiteName" url="https://update.eclipse.org/updates/3.3"/> + <discovery label="%secondaryUpdateSiteName" url="https://download.eclipse.org/releases/europa"/> </url> <includes id="org.eclipse.rcp.source" version="3.3.0.v20070607-8y8eE8NEbsN3X_fjWS8HPNG"/> <plugin fragment="false" unpack="false" download-size="0" install-size="0" id="org.eclipse.platform.doc.isv" version="3.3.0.v20070621"/> diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml b/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml index e4974b851..8f1ffb44d 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml @@ -19,9 +19,9 @@ </license> <url> - <update label="%updateSiteName" url="http://update.eclipse.org/updates/3.3"/> - <discovery label="%updateSiteName" url="http://update.eclipse.org/updates/3.3"/> - <discovery label="%secondaryUpdateSiteName" url="http://download.eclipse.org/releases/europa"/> + <update label="%updateSiteName" url="https://update.eclipse.org/updates/3.3"/> + <discovery label="%updateSiteName" url="https://update.eclipse.org/updates/3.3"/> + <discovery label="%secondaryUpdateSiteName" url="https://download.eclipse.org/releases/europa"/> </url> <plugin diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.sdk_3.3.0.v20070607-7M7J-BIolz-OcxWxvWAPSfLPqevO/feature.xml b/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.sdk_3.3.0.v20070607-7M7J-BIolz-OcxWxvWAPSfLPqevO/feature.xml index 0b59abbde..87abcbbfd 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.sdk_3.3.0.v20070607-7M7J-BIolz-OcxWxvWAPSfLPqevO/feature.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.sdk_3.3.0.v20070607-7M7J-BIolz-OcxWxvWAPSfLPqevO/feature.xml @@ -19,9 +19,9 @@ </license> <url> - <update label="%updateSiteName" url="http://update.eclipse.org/updates/3.3"/> - <discovery label="%updateSiteName" url="http://update.eclipse.org/updates/3.3"/> - <discovery label="%secondaryUpdateSiteName" url="http://download.eclipse.org/releases/europa"/> + <update label="%updateSiteName" url="https://update.eclipse.org/updates/3.3"/> + <discovery label="%updateSiteName" url="https://update.eclipse.org/updates/3.3"/> + <discovery label="%secondaryUpdateSiteName" url="https://download.eclipse.org/releases/europa"/> </url> <includes diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/importexport/unknownformat.p2f b/bundles/org.eclipse.equinox.p2.tests/testData/importexport/unknownformat.p2f index 9ae98bf32..56e172939 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/importexport/unknownformat.p2f +++ b/bundles/org.eclipse.equinox.p2.tests/testData/importexport/unknownformat.p2f @@ -131,7 +131,7 @@ </unit> </units> <repositories> - <repository uri='http://download.eclipse.org/releases/helios'/> + <repository uri='https://download.eclipse.org/releases/helios'/> <repository uri='file:/folk/kzhu0/tmp/plugins/'/> <repository uri='http://hge.javaforge.com/hgeclipse'/> </repositories> diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/mirror/mirrorSourceRepoWithRefs/content.xml b/bundles/org.eclipse.equinox.p2.tests/testData/mirror/mirrorSourceRepoWithRefs/content.xml index 3a3575af8..d53fba1e4 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/mirror/mirrorSourceRepoWithRefs/content.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/mirror/mirrorSourceRepoWithRefs/content.xml @@ -6,10 +6,10 @@ <property name='p2.timestamp' value='1222693511921'/> </properties> <references size='4'> - <repository uri='http://download.eclipse.org/eclipse/updates/3.5/' url='http://download.eclipse.org/eclipse/updates/3.5/' type='0' options='0'/> - <repository uri='http://download.eclipse.org/eclipse/updates/3.5/' url='http://download.eclipse.org/eclipse/updates/3.5/' type='1' options='0'/> - <repository uri='http://download.eclipse.org/eclipse/updates/3.6/' url='http://download.eclipse.org/eclipse/updates/3.6/' type='0' options='0'/> - <repository uri='http://download.eclipse.org/eclipse/updates/3.6/' url='http://download.eclipse.org/eclipse/updates/3.6/' type='1' options='0'/> + <repository uri='https://download.eclipse.org/eclipse/updates/3.5/' url='https://download.eclipse.org/eclipse/updates/3.5/' type='0' options='0'/> + <repository uri='https://download.eclipse.org/eclipse/updates/3.5/' url='https://download.eclipse.org/eclipse/updates/3.5/' type='1' options='0'/> + <repository uri='https://download.eclipse.org/eclipse/updates/3.6/' url='https://download.eclipse.org/eclipse/updates/3.6/' type='0' options='0'/> + <repository uri='https://download.eclipse.org/eclipse/updates/3.6/' url='https://download.eclipse.org/eclipse/updates/3.6/' type='1' options='0'/> </references> <units size='8'> <unit id='helloworldfeature.feature.jar' version='1.0.1'> diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/pausefeature/artifacts.xml b/bundles/org.eclipse.equinox.p2.tests/testData/pausefeature/artifacts.xml index baf533cae..a9f083ecd 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/pausefeature/artifacts.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/pausefeature/artifacts.xml @@ -12,14 +12,14 @@ </properties> <mappings size='5'> <rule filter='(& (classifier=osgi.bundle) (format=packed))' - output='http://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/plugins/${id}_${version}.jar.pack.gz' /> - <rule filter='(& (classifier=osgi.bundle))' output='http://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/plugins/${id}_${version}.jar' /> - <rule filter='(& (classifier=binary))' output='http://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/binary/${id}_${version}' /> + output='https://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/plugins/${id}_${version}.jar.pack.gz' /> + <rule filter='(& (classifier=osgi.bundle))' output='https://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/plugins/${id}_${version}.jar' /> + <rule filter='(& (classifier=binary))' output='https://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/binary/${id}_${version}' /> <rule filter='(& (classifier=org.eclipse.update.feature) (format=packed))' - output='http://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/features/${id}_${version}.jar.pack.gz' /> + output='https://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/features/${id}_${version}.jar.pack.gz' /> <rule filter='(& (classifier=org.eclipse.update.feature))' - output='http://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/features/${id}_${version}.jar' /> + output='https://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/features/${id}_${version}.jar' /> </mappings> <artifacts size='13433'> <artifact classifier='binary' diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/pgp/signer2-publickey.asc b/bundles/org.eclipse.equinox.p2.tests/testData/pgp/signer2-publickey.asc new file mode 100644 index 000000000..0218bdce7 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/testData/pgp/signer2-publickey.asc @@ -0,0 +1,29 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGHUyxYBEADATeNx4XA4H2fP9mD5xwlIyh7qvHLezgXpqCwNS9ATqBwnfrCV +06a+pfSLsLoXrP/sdaB5WhijfuxTis18RMfoDVwGMRqyD2GiBCl2vwJDg3BUwHnc +H7W6XkWKO7dkPmF+TUbD3cTWZ7cvrPmMjinmXaq8htuktGuE2VEGZRnuG1m+ChDM +PnSb1ioFS2+MJv13P2fagVk2qC95DkPJGpMk3CY3ghLDEaY/KaJl+8axAlUUUk9N +d3k/KVxxf5k3g26EVQkWWgH2mmolptGO101xW64iked97Cy4NK2yafOF/wmpsavx +PGpOewnDgAJBBPkum6mPH0vcOZgxmLyh4uqfPfr3IaBQlbJLN2dXaDsV83j5t1wZ +2qxOPcWBfORm6W7dC0TQgCXbEG0geMBpJtvnMX83Q2ORqDpjbHRJsV2k+8NxaXON +pYXGr+Lj/9n0xfNEDXhCdGab0XP2tVZ5jfr2OQ5dAomEaPqK5Kq7akoWMddpDLNC +G4IvH8G0cxwruJk00uwd6Nd2NVqVMRYCsBbA89VanUnutLUIpVnnOAetlX9blXHO +JtmiCPGgHyp+iYGhKYVzfuZQyFhonbi0AhidJDvbHsoLT3p4Mcog1B9y6MODBE7R +jwrU+qMqYsYeFhGYKbYyXv9TfEyUvtCQ/GnTtRJAQyicFdOdbK37WecS6QARAQAB +tDdFY2xpcHNlIHAyIHRlc3QgU2lnbmVyIDIgPHNpZ25lcjJAZmFrZXVzZXIuZWNs +aXBzZS5vcmc+iQJYBBMBCABCFiEEzZ1aK4a8T+GDlFHh4vaU9BsKs3AFAmHUyxYC +GwMFCQPCZwAFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4HAheAAAoJEOL2lPQbCrNw +wH4QAIiCaw1mREgt4ldz7hQvmGxdMuQwVKZPzbOIAlYbZBo0q9SmeMf/CBCO90hg +LFmJmsZV4KUU5NKI7UwkDVrpUCl00Ok6+gtiUTId2tRcwXI+25I478VX27j6OkQj +7Xr6giv8cn8nstt5CF6xxeqrxvpmnZC0u30jE6CL6SdXSd0vViFDPQj3KgGJCRc9 +St+LHB3XJTsadihzQnscqI4E2i5Z3Uj1GogqxtR59M1NAXubl5dySM0qHhwn8O+6 +lcgCCeuyMLLde1M1v8w07jdRUM+IFqHrRnE89EPH3MQeZbQ3UfFXK2r7wx2BJCqL +Irtn68kz834ByKchGR6DqaAw0q+iF2QkgzYxpwai41BgmtUCYnj+HxQNIF4KTzDe +nd8mDAPWttGCoVuV2Tyu9peYOaqyAZ2PZwUEH5MqihPCbenU17RLXzRu/IT/SH47 +NGrN3yKGgLZr2EVWPWFibpoxP4G4NUCHsY75uiL2EWPVSjK/+OOeHIE5k3U3lYwB +7clhBwMkIhQHJ+a0SHRkKixkwrQDw4veKY4LaD0NCBLHFoV5L9orH1ToGM729kr/ ++4I1VQFkL3KvfLjmRbTUgwHeqEquQ96JtqowbNwlpujfHXQKDNsuiKGP5OazXll5 +sH2CR7e4ePqhhzxjLvi9e/79Khq+08eqllS3rs06EXEAJYTo +=807V +-----END PGP PUBLIC KEY BLOCK----- diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/associateSites.xml b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/associateSites.xml index 1238ec316..bddf9e047 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/associateSites.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/associateSites.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> <associateSites> -<associateSite url="http://download.eclipse.org/eclipse/updates/3.5" label="Eclipse Project Update Site"/> +<associateSite url="https://download.eclipse.org/eclipse/updates/3.5" label="Eclipse Project Update Site"/> <associateSite url="This is a bogus URI" label="broken"/> </associateSites>
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.testserver/pom.xml b/bundles/org.eclipse.equinox.p2.testserver/pom.xml index 5310f6302..15046df14 100644 --- a/bundles/org.eclipse.equinox.p2.testserver/pom.xml +++ b/bundles/org.eclipse.equinox.p2.testserver/pom.xml @@ -4,7 +4,7 @@ <parent> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> <groupId>org.eclipse</groupId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.testserver/webfiles/index.html b/bundles/org.eclipse.equinox.p2.testserver/webfiles/index.html index c07f8d265..9f1800e6e 100644 --- a/bundles/org.eclipse.equinox.p2.testserver/webfiles/index.html +++ b/bundles/org.eclipse.equinox.p2.testserver/webfiles/index.html @@ -8,10 +8,10 @@ log in. The credentials are: user: <b>Aladdin</b>, password: <b>open sesame</b>. <a href="http://localhost:8080/private/index.html">here</a></p> <p>The following real content is registered:</p> <ul> -<li>/proxy/private/ - goes to http://http://download.eclipse.org/eclipse/updates/3.4, but requires authentication.</li> -<li>/proxy/public/ - goes to http://http://download.eclipse.org/eclipse/updates/3.4, (useful in redirects).</li> -<li>/proxy/never/ - goes to http://http://download.eclipse.org/eclipse/updates/3.4, but always fail authentication.</li> -<li>/proxy/flipFlop/ - goes to http://http://download.eclipse.org/eclipse/updates/3.4, but fails authentication every second attempt.</li> +<li>/proxy/private/ - goes to https://download.eclipse.org/eclipse/updates/3.4, but requires authentication.</li> +<li>/proxy/public/ - goes to https://download.eclipse.org/eclipse/updates/3.4, (useful in redirects).</li> +<li>/proxy/never/ - goes to https://download.eclipse.org/eclipse/updates/3.4, but always fail authentication.</li> +<li>/proxy/flipFlop/ - goes to https://download.eclipse.org/eclipse/updates/3.4, but fails authentication every second attempt.</li> <li>/proxy/truncated - goes to updates/3.4, but truncates all files</li> <li>/proxy/molested - goes to updates/3.4, but generates gibberish for all files</li> <li>/proxy/decelerate - goes to updates/3.4, but delivers files in very small delayed packets - delay increases.</li> diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.eclipse/pom.xml b/bundles/org.eclipse.equinox.p2.touchpoint.eclipse/pom.xml index 2fb3c4137..7fff2566e 100644 --- a/bundles/org.eclipse.equinox.p2.touchpoint.eclipse/pom.xml +++ b/bundles/org.eclipse.equinox.p2.touchpoint.eclipse/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.natives/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.touchpoint.natives/META-INF/MANIFEST.MF index fa30088a2..3e794a0db 100644 --- a/bundles/org.eclipse.equinox.p2.touchpoint.natives/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.touchpoint.natives/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.touchpoint.natives;singleton:=true -Bundle-Version: 1.4.200.qualifier +Bundle-Version: 1.4.300.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.touchpoint.natives.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.natives/pom.xml b/bundles/org.eclipse.equinox.p2.touchpoint.natives/pom.xml index ccb91e06b..f90831920 100644 --- a/bundles/org.eclipse.equinox.p2.touchpoint.natives/pom.xml +++ b/bundles/org.eclipse.equinox.p2.touchpoint.natives/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.touchpoint.natives</artifactId> - <version>1.4.200-SNAPSHOT</version> + <version>1.4.300-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/messages.properties b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/messages.properties index a7204930b..298d24fa7 100644 --- a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/messages.properties +++ b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/messages.properties @@ -33,7 +33,7 @@ BackupStore_manual_restore_needed=Restore failed: {0}. Manual restore of backup BackupStore_missing_backup_directory=Missing backup directory - can not restore: {0} BackupStore_not_a_directory=File is not a directory: {0} BlockMacUpdate_0=Installation impossible -BlockMacUpdate_1=The installation/update you are trying to perform can not be completed because of structural changes to Eclipse. The installation/update will stop and will leave your existing Eclipse installation intact. You need to retrieve a new version of Eclipse from http://download.eclipse.org/. +BlockMacUpdate_1=The installation/update you are trying to perform can not be completed because of structural changes to Eclipse. The installation/update will stop and will leave your existing Eclipse installation intact. You need to retrieve a new version of Eclipse from https://download.eclipse.org/. action_0_status=The action {0} was performed. action_0_failed_on_file_1_reason_2=The action {0} on file {1} failed. Reason: {2} diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.transport.ecf/META-INF/MANIFEST.MF index 9d7b3fde5..31f2492ff 100644 --- a/bundles/org.eclipse.equinox.p2.transport.ecf/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.transport.ecf/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.transport.ecf -Bundle-Version: 1.3.200.qualifier +Bundle-Version: 1.3.300.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-11 Require-Bundle: org.eclipse.ecf;bundle-version="3.1.0", org.eclipse.ecf.filetransfer;bundle-version="4.0.0", diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/pom.xml b/bundles/org.eclipse.equinox.p2.transport.ecf/pom.xml index 34c1bcac3..7d8611d3e 100644 --- a/bundles/org.eclipse.equinox.p2.transport.ecf/pom.xml +++ b/bundles/org.eclipse.equinox.p2.transport.ecf/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.transport.ecf</artifactId> - <version>1.3.200-SNAPSHOT</version> + <version>1.3.300-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Activator.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Activator.java index d0e8b4e7b..7b6649c20 100644 --- a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Activator.java +++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Activator.java @@ -16,6 +16,7 @@ ******************************************************************************/ package org.eclipse.equinox.internal.p2.transport.ecf; +import java.util.Optional; import org.eclipse.ecf.filetransfer.service.IRetrieveFileTransferFactory; import org.eclipse.ecf.provider.filetransfer.IFileTransferProtocolToFactoryMapper; import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper; @@ -173,4 +174,18 @@ public class Activator implements BundleActivator { return false; } + public static String getProperty(String key) { + if (context != null) { + return context.getProperty(key); + } + return System.getProperty(key); + } + + public static Optional<Version> getVersion() { + return Optional.ofNullable(context) // + .map(BundleContext::getBundle) // + .or(() -> Optional.ofNullable(FrameworkUtil.getBundle(FileReader.class)))// + .map(Bundle::getVersion); + } + } diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileInfoReader.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileInfoReader.java index 8a45c5486..cfca3d832 100644 --- a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileInfoReader.java +++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileInfoReader.java @@ -163,7 +163,7 @@ public class FileInfoReader extends Job implements IRemoteFileSystemListener { try { container = ContainerFactory.getDefault().createContainer(); } catch (ContainerCreateException e) { - throw RepositoryStatusHelper.fromMessage(Messages.ecf_configuration_error); + throw RepositoryStatusHelper.fromExceptionMessage(e ,Messages.ecf_configuration_error); } IRemoteFileSystemBrowserContainerAdapter adapter = container.getAdapter(IRemoteFileSystemBrowserContainerAdapter.class); diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileReader.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileReader.java index 97c7e2fb4..7d95289f4 100644 --- a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileReader.java +++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileReader.java @@ -34,7 +34,7 @@ import org.eclipse.equinox.internal.p2.repository.*; import org.eclipse.equinox.internal.p2.repository.Messages; import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.osgi.util.NLS; -import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.Version; /** * FileReader is an ECF FileTransferJob implementation. @@ -64,11 +64,11 @@ public final class FileReader extends FileTransferJob implements IFileTransferLi static Map<String, Map<String, String>> options; static private String getProperty(String key, String defaultValue) { - String value = FrameworkUtil.getBundle(FileReader.class).getBundleContext().getProperty(key); - if (value == null) { - value = defaultValue; + String value = Activator.getProperty(key); + if (value != null) { + return value; } - return value; + return defaultValue; } static { @@ -80,7 +80,7 @@ public final class FileReader extends FileTransferJob implements IFileTransferLi String osgiArch = getProperty("org.osgi.framework.processor", "unknownArch");//$NON-NLS-1$//$NON-NLS-2$ String language = getProperty("osgi.nl", "unknownLanguage");//$NON-NLS-1$//$NON-NLS-2$ String osVersion = getProperty("org.osgi.framework.os.version", "unknownOSVersion"); //$NON-NLS-1$ //$NON-NLS-2$ - String p2Version = FrameworkUtil.getBundle(FileReader.class).getVersion().toString(); + String p2Version = Activator.getVersion().map(Version::toString).orElse("unknownVersion"); //$NON-NLS-1$ userAgent = "p2/" + p2Version + " (Java " + javaSpec + ' ' + javaVendor + "; " + osName + ' ' + osVersion + ' ' //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + osgiArch + "; " + language + ") "; //$NON-NLS-1$ //$NON-NLS-2$ String userAgentProvided = getProperty("p2.userAgent", null); //$NON-NLS-1$ diff --git a/bundles/org.eclipse.equinox.p2.ui.admin.rcp/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.ui.admin.rcp/META-INF/MANIFEST.MF index a48a7e72a..78f885414 100644 --- a/bundles/org.eclipse.equinox.p2.ui.admin.rcp/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.ui.admin.rcp/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: %bundleName Bundle-Vendor: %providerName Bundle-Localization: plugin Bundle-SymbolicName: org.eclipse.equinox.p2.ui.admin.rcp; singleton:=true -Bundle-Version: 1.3.100.qualifier +Bundle-Version: 1.3.200.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.ui.admin.rcp.Activator Bundle-RequiredExecutionEnvironment: JavaSE-11 Require-Bundle: org.eclipse.ui, diff --git a/bundles/org.eclipse.equinox.p2.ui.admin.rcp/pom.xml b/bundles/org.eclipse.equinox.p2.ui.admin.rcp/pom.xml index 9ac0df9f6..be007c637 100644 --- a/bundles/org.eclipse.equinox.p2.ui.admin.rcp/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui.admin.rcp/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.ui.admin.rcp</artifactId> - <version>1.3.100-SNAPSHOT</version> + <version>1.3.200-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.ui.admin.rcp/rcp.product b/bundles/org.eclipse.equinox.p2.ui.admin.rcp/rcp.product index 5c61916cd..9ba3bcdc0 100644 --- a/bundles/org.eclipse.equinox.p2.ui.admin.rcp/rcp.product +++ b/bundles/org.eclipse.equinox.p2.ui.admin.rcp/rcp.product @@ -165,7 +165,7 @@ Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in t <plugin id="org.eclipse.core.filesystem.macosx" fragment="true"/> <plugin id="org.eclipse.core.jobs"/> <plugin id="org.eclipse.core.net"/> - <plugin id="org.eclipse.core.net.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.core.net.linux" fragment="true"/> <plugin id="org.eclipse.core.resources"/> <plugin id="org.eclipse.core.runtime"/> <plugin id="org.eclipse.e4.core.commands"/> @@ -239,7 +239,7 @@ Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in t <plugin id="org.eclipse.equinox.preferences"/> <plugin id="org.eclipse.equinox.registry"/> <plugin id="org.eclipse.equinox.security"/> - <plugin id="org.eclipse.equinox.security.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.equinox.security.linux" fragment="true"/> <plugin id="org.eclipse.equinox.security.macosx" fragment="true"/> <plugin id="org.eclipse.equinox.security.ui"/> <plugin id="org.eclipse.equinox.simpleconfigurator"/> diff --git a/bundles/org.eclipse.equinox.p2.ui.admin/pom.xml b/bundles/org.eclipse.equinox.p2.ui.admin/pom.xml index cac9c880a..af1847806 100644 --- a/bundles/org.eclipse.equinox.p2.ui.admin/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui.admin/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.ui.discovery/pom.xml b/bundles/org.eclipse.equinox.p2.ui.discovery/pom.xml index 19b456343..54fa0b47d 100644 --- a/bundles/org.eclipse.equinox.p2.ui.discovery/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui.discovery/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.ui.importexport/pom.xml b/bundles/org.eclipse.equinox.p2.ui.importexport/pom.xml index c55bcd02f..0d01faecf 100644 --- a/bundles/org.eclipse.equinox.p2.ui.importexport/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui.importexport/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/META-INF/MANIFEST.MF index 93ad2243d..f6a0f04ed 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %bundleName Bundle-SymbolicName: org.eclipse.equinox.p2.ui.sdk.scheduler;singleton:=true -Bundle-Version: 1.5.200.qualifier +Bundle-Version: 1.5.300.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.ui.sdk.scheduler.AutomaticUpdatePlugin Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/pom.xml b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/pom.xml index c8360feff..f5f8a49f1 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.ui.sdk.scheduler</artifactId> - <version>1.5.200-SNAPSHOT</version> + <version>1.5.300-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/src/org/eclipse/equinox/internal/p2/ui/sdk/scheduler/AutomaticUpdateScheduler.java b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/src/org/eclipse/equinox/internal/p2/ui/sdk/scheduler/AutomaticUpdateScheduler.java index 504202512..6d56f31c9 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/src/org/eclipse/equinox/internal/p2/ui/sdk/scheduler/AutomaticUpdateScheduler.java +++ b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/src/org/eclipse/equinox/internal/p2/ui/sdk/scheduler/AutomaticUpdateScheduler.java @@ -33,6 +33,7 @@ import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.query.IQuery; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.ui.IStartup; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.statushandlers.StatusManager; /** @@ -54,8 +55,6 @@ public class AutomaticUpdateScheduler implements IStartup { private IUpdateListener listener; private IUpdateChecker checker; - private IProvisioningAgent agent; - @Override public void earlyStartup() { AutomaticUpdatePlugin.getDefault().setScheduler(this); @@ -64,7 +63,13 @@ public class AutomaticUpdateScheduler implements IStartup { @Override protected IStatus run(IProgressMonitor monitor) { - agent = ServiceHelper.getService(AutomaticUpdatePlugin.getContext(), IProvisioningAgent.class); + IProvisioningAgent agent = ServiceHelper.getService(AutomaticUpdatePlugin.getContext(), + IProvisioningAgent.class); + if (agent == null) { + String message = "No provisioning agent service found"; //$NON-NLS-1$ + AutomaticUpdatePlugin.getDefault().getLog().warn(message, new IllegalStateException(message)); + return Status.CANCEL_STATUS; + } IProfileRegistry registry = agent.getService(IProfileRegistry.class); IProfile currentProfile = registry.getProfile(IProfileRegistry.SELF); if (currentProfile != null @@ -72,7 +77,7 @@ public class AutomaticUpdateScheduler implements IStartup { return Status.OK_STATUS; } - removeUnusedPlugins(registry); + removeUnusedPlugins(registry, agent); scheduleUpdate(); return Status.OK_STATUS; } @@ -81,6 +86,11 @@ public class AutomaticUpdateScheduler implements IStartup { public boolean belongsTo(Object family) { return AutomaticUpdateScheduler.class == family; } + + @Override + public boolean shouldRun() { + return PlatformUI.isWorkbenchRunning(); + } }; updateJob.setSystem(true); @@ -92,8 +102,9 @@ public class AutomaticUpdateScheduler implements IStartup { * Invokes the garbage collector to discard unused plugins, if specified by a * corresponding preference. * + * @param agent non null */ - private void removeUnusedPlugins(IProfileRegistry registry) { + private void removeUnusedPlugins(IProfileRegistry registry, IProvisioningAgent agent) { // check if gc is enabled IPreferenceStore pref = AutomaticUpdatePlugin.getDefault().getPreferenceStore(); if (!pref.getBoolean(PreferenceConstants.PREF_GC_ON_STARTUP)) { @@ -149,10 +160,12 @@ public class AutomaticUpdateScheduler implements IStartup { } }; - IProvisioningAgent pagent = agent; + IProvisioningAgent pagent = ServiceHelper.getService(AutomaticUpdatePlugin.getContext(), + IProvisioningAgent.class); if (pagent == null) { - // Job not executed yet - pagent = ServiceHelper.getService(AutomaticUpdatePlugin.getContext(), IProvisioningAgent.class); + String message = "No provisioning agent service found"; //$NON-NLS-1$ + AutomaticUpdatePlugin.getDefault().getLog().warn(message, new IllegalStateException(message)); + return; } checker = pagent.getService(IUpdateChecker.class); if (checker == null) { diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.ui.sdk/META-INF/MANIFEST.MF index 988cfdd1b..4293409db 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.ui.sdk/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %bundleName Bundle-SymbolicName: org.eclipse.equinox.p2.ui.sdk;singleton:=true -Bundle-Version: 1.2.3.qualifier +Bundle-Version: 1.2.4.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.ui.sdk.ProvSDKUIActivator Bundle-Vendor: %providerName Bundle-Localization: plugin @@ -16,15 +16,16 @@ Import-Package: javax.xml.parsers, org.bouncycastle.openpgp;version="1.69.0", org.eclipse.compare;resolution:=optional, org.eclipse.compare.structuremergeviewer;resolution:=optional, + org.eclipse.equinox.internal.p2.artifact.processors.pgp, org.eclipse.equinox.internal.p2.engine.phases, org.eclipse.equinox.p2.core;version="[2.0.0,3.0.0)", - org.eclipse.equinox.internal.p2.artifact.processors.pgp, org.eclipse.equinox.p2.engine;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.engine.query;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.metadata;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.operations;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.planner;version="2.0.0", org.eclipse.equinox.p2.query;version="[2.0.0,3.0.0)", + org.eclipse.equinox.p2.repository.spi;version="2.0.0", org.eclipse.osgi.util;version="1.1.0", org.osgi.framework;version="1.6.0", org.w3c.dom, diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk/pom.xml b/bundles/org.eclipse.equinox.p2.ui.sdk/pom.xml index 32f6d437e..bcca1f5ac 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui.sdk/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.ui.sdk</artifactId> - <version>1.2.3-SNAPSHOT</version> + <version>1.2.4-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/ProvSDKMessages.java b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/ProvSDKMessages.java index bf0a1956c..26d979317 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/ProvSDKMessages.java +++ b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/ProvSDKMessages.java @@ -52,12 +52,30 @@ public class ProvSDKMessages extends NLS { public static String RemediationOperation_ResolveJobTask; public static String TrustPreferencePage_title; public static String TrustPreferencePage_export; - public static String TrustPreferencePage_idColumn; - public static String TrustPreferencePage_userColumn; public static String TrustPreferencePage_fileExportTitle; public static String TrustPreferencePage_pgpIntro; public static String TrustPreferencePage_fileImportTitle; public static String TrustPreferencePage_addPGPKeyButtonLabel; + public static String TrustPreferencePage_Contributor; + public static String TrustPreferencePage_CopyFingerprint; + public static String TrustPreferencePage_DataValidExpires; + public static String TrustPreferencePage_DateExpired; + public static String TrustPreferencePage_DateExpiredSince; + public static String TrustPreferencePage_DateNotYetvalid; + public static String TrustPreferencePage_DateNotYetValid; + public static String TrustPreferencePage_DateValid; + public static String TrustPreferencePage_Details; + public static String TrustPreferencePage_Export; + public static String TrustPreferencePage_FingerprintIdColumn; + public static String TrustPreferencePage_NameColumn; + public static String TrustPreferencePage_PreferenceContributor; public static String TrustPreferencePage_removePGPKeyButtonLabel; + public static String TrustPreferencePage_TrustAll; + public static String TrustPreferencePage_TrustAllConfirmationDescription; + public static String TrustPreferencePage_TrustAllConfirmationTitle; + public static String TrustPreferencePage_TrustAllNo; + public static String TrustPreferencePage_TrustAllYes; + public static String TrustPreferencePage_TypeColumn; + public static String TrustPreferencePage_ValidityColumn; } diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/TrustPreferencePage.java b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/TrustPreferencePage.java index 417ba2d68..bb0ef513e 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/TrustPreferencePage.java +++ b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/TrustPreferencePage.java @@ -10,33 +10,60 @@ *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui.sdk; +import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter; + import java.io.*; -import java.util.ArrayList; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.cert.*; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.*; import java.util.List; +import java.util.function.Function; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.openpgp.PGPPublicKey; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.*; import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPPublicKeyStore; import org.eclipse.equinox.internal.p2.engine.phases.CertificateChecker; -import org.eclipse.equinox.internal.p2.ui.ProvUIActivator; -import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.equinox.internal.p2.ui.dialogs.PGPPublicKeyViewDialog; +import org.eclipse.equinox.internal.p2.ui.viewers.CertificateLabelProvider; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.engine.IProfileRegistry; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; +import org.eclipse.jface.dialogs.*; +import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.Policy; import org.eclipse.jface.viewers.*; +import org.eclipse.jface.widgets.LabelFactory; +import org.eclipse.jface.widgets.WidgetFactory; +import org.eclipse.jface.window.Window; +import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.*; import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.*; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; +import org.osgi.framework.Bundle; +import org.osgi.framework.Constants; public class TrustPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + private static final String EXPORT_FILTER_PATH = "exportFilterPath"; //$NON-NLS-1$ + private static final String ADD_FILTER_PATH = "addFilterPath"; //$NON-NLS-1$ + private CertificateChecker certificateChecker; + private Set<Certificate> trustedCertificates; private PGPPublicKeyStore trustedKeys; - private boolean dirty = false; + private Map<PGPPublicKey, Set<Bundle>> contributedTrustedKeys; + private boolean dirty; private TableViewer viewer; public TrustPreferencePage() { @@ -50,108 +77,324 @@ public class TrustPreferencePage extends PreferencePage implements IWorkbenchPre @Override protected Control createContents(Composite parent) { + IProvisioningAgent provisioningAgent = ProvSDKUIActivator.getDefault().getProvisioningAgent(); + certificateChecker = new CertificateChecker(provisioningAgent); + certificateChecker + .setProfile(provisioningAgent.getService(IProfileRegistry.class).getProfile(IProfileRegistry.SELF)); + trustedCertificates = new LinkedHashSet<>(certificateChecker.getPreferenceTrustedCertificates()); + contributedTrustedKeys = certificateChecker.getContributedTrustedKeys(); + trustedKeys = certificateChecker.getPreferenceTrustedKeys(); + Composite res = new Composite(parent, SWT.NONE); + res.setLayout(new GridLayout(2, false)); - Label pgpLabel = new Label(res, SWT.WRAP); - pgpLabel.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false, 2, 1)); - pgpLabel.setText(ProvSDKMessages.TrustPreferencePage_pgpIntro); + // Ensure that the message supports wrapping for a long text message. + GridData data = new GridData(SWT.FILL, SWT.DEFAULT, true, false, 2, 1); + data.widthHint = convertWidthInCharsToPixels(90); + LabelFactory factory = WidgetFactory.label(SWT.WRAP).text(ProvSDKMessages.TrustPreferencePage_pgpIntro) + .font(parent.getFont()).layoutData(data); + factory.create(res); - res.setLayout(new GridLayout(2, false)); - viewer = new TableViewer(res); - viewer.getTable().setHeaderVisible(true); + TableColumnLayout tableColumnLayout = new TableColumnLayout(); + Composite tableComposite = WidgetFactory.composite(SWT.NONE) + .layoutData(new GridData(SWT.FILL, SWT.FILL, true, true)).layout(tableColumnLayout).create(res); + Table keyTable = WidgetFactory.table(SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION) + .headerVisible(true).linesVisible(true).font(parent.getFont()).create(tableComposite); + viewer = new TableViewer(keyTable); + keyTable.setHeaderVisible(true); viewer.setContentProvider(new ArrayContentProvider()); - TableViewerColumn idColumn = new TableViewerColumn(viewer, SWT.NONE); - idColumn.setLabelProvider(new ColumnLabelProvider() { - @Override - public String getText(Object element) { - return Long.toHexString(((PGPPublicKey) element).getKeyID()).toUpperCase(); + + // This column is packed later. + TableViewerColumn typeColumn = createColumn(viewer, ProvSDKMessages.TrustPreferencePage_TypeColumn, + key -> "PGP", cert -> "x509", tableColumnLayout, 1); //$NON-NLS-1$ //$NON-NLS-2$ + + createColumn(viewer, ProvSDKMessages.TrustPreferencePage_FingerprintIdColumn, + key -> PGPPublicKeyService.toHexFingerprint(key), + cert -> cert.getSerialNumber().toString(), tableColumnLayout, 10); + + createColumn(viewer, ProvSDKMessages.TrustPreferencePage_NameColumn, key -> { + List<String> userIds = new ArrayList<>(); + key.getUserIDs().forEachRemaining(userIds::add); + return String.join(", ", userIds); //$NON-NLS-1$ + }, cert -> CertificateLabelProvider.getText(cert), tableColumnLayout, 15); + + createColumn(viewer, ProvSDKMessages.TrustPreferencePage_Contributor, key -> { + { + Set<String> contributors = new LinkedHashSet<>(); + if (trustedKeys.all().contains(key)) { + contributors.add(ProvSDKMessages.TrustPreferencePage_PreferenceContributor); + } + Set<Bundle> bundles = contributedTrustedKeys.get(key); + if (bundles != null) { + Set<String> bundleContributors = new TreeSet<>(Policy.getComparator()); + bundles.stream().map(bundle -> getBundleName(bundle)).forEach(bundleContributors::add); + contributors.addAll(bundleContributors); + } + return String.join(", ", contributors); //$NON-NLS-1$ } - }); - idColumn.getColumn().setWidth(16 * 10); // number of chars in a key Id * some heuristic of width - idColumn.getColumn().setText(ProvSDKMessages.TrustPreferencePage_idColumn); - TableViewerColumn userColumn = new TableViewerColumn(viewer, SWT.NONE); - userColumn.setLabelProvider(new ColumnLabelProvider() { - @Override - public String getText(Object element) { - List<String> userIds = new ArrayList<>(); - ((PGPPublicKey) element).getUserIDs().forEachRemaining(userIds::add); - return String.join(",", userIds); //$NON-NLS-1$ + }, cert -> ProvSDKMessages.TrustPreferencePage_PreferenceContributor, tableColumnLayout, + contributedTrustedKeys.isEmpty() ? 8 : 15); + + createColumn(viewer, ProvSDKMessages.TrustPreferencePage_ValidityColumn, pgp -> { + if (pgp.getCreationTime().after(Date.from(Instant.now()))) { + return NLS.bind(ProvSDKMessages.TrustPreferencePage_DateNotYetValid, pgp.getCreationTime()); } - }); - userColumn.getColumn().setWidth(400); - userColumn.getColumn().setText(ProvSDKMessages.TrustPreferencePage_userColumn); - viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - certificateChecker = new CertificateChecker(ProvSDKUIActivator.getDefault().getProvisioningAgent()); - trustedKeys = certificateChecker.buildPGPTrustore(); - viewer.setInput(trustedKeys.all()); + long validSeconds = pgp.getValidSeconds(); + if (validSeconds == 0) { + return ProvSDKMessages.TrustPreferencePage_DateValid; + } + Instant expires = pgp.getCreationTime().toInstant().plus(validSeconds, ChronoUnit.SECONDS); + return expires.isBefore(Instant.now()) + ? NLS.bind(ProvSDKMessages.TrustPreferencePage_DateExpiredSince, expires) + : NLS.bind(ProvSDKMessages.TrustPreferencePage_DataValidExpires, expires); + }, x509 -> { + try { + x509.checkValidity(); + return ProvSDKMessages.TrustPreferencePage_DateValid; + } catch (CertificateExpiredException expired) { + return ProvSDKMessages.TrustPreferencePage_DateExpired; + } catch (CertificateNotYetValidException notYetValid) { + return ProvSDKMessages.TrustPreferencePage_DateNotYetvalid; + } + }, tableColumnLayout, 8); + + updateInput(); + Composite buttonComposite = createVerticalButtonBar(res); buttonComposite.setLayoutData(new GridData(SWT.DEFAULT, SWT.BEGINNING, false, false)); + Button exportButton = new Button(buttonComposite, SWT.PUSH); exportButton.setText(ProvSDKMessages.TrustPreferencePage_export); setVerticalButtonLayoutData(exportButton); + exportButton.setEnabled(false); + exportButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { - PGPPublicKey key = getSelectedKey(); - if (key == null) { - return; - } + Object element = viewer.getStructuredSelection().getFirstElement(); FileDialog dialog = new FileDialog(getShell(), SWT.SAVE); + dialog.setFilterPath(getFilterPath(EXPORT_FILTER_PATH)); dialog.setText(ProvSDKMessages.TrustPreferencePage_fileExportTitle); dialog.setFilterExtensions(new String[] { "*.asc" }); //$NON-NLS-1$ - dialog.setFileName(Long.toHexString(key.getKeyID()).toUpperCase() + ".asc"); //$NON-NLS-1$ - String path = dialog.open(); - if (path == null) { - return; - } - File destinationFile = new File(path); - try (OutputStream output = new ArmoredOutputStream(new FileOutputStream(destinationFile))) { - output.write(key.getEncoded()); - } catch (IOException ex) { - ProvSDKUIActivator.getDefault().getLog() - .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + FileDialog destination = new FileDialog(exportButton.getShell(), SWT.SAVE); + destination.setFilterPath(getFilterPath(EXPORT_FILTER_PATH)); + destination.setText(ProvSDKMessages.TrustPreferencePage_Export); + if (element instanceof X509Certificate) { + X509Certificate cert = (X509Certificate) element; + destination.setFilterExtensions(new String[] { "*.der" }); //$NON-NLS-1$ + destination.setFileName(cert.getSerialNumber().toString() + ".der"); //$NON-NLS-1$ + String path = destination.open(); + setFilterPath(EXPORT_FILTER_PATH, destination.getFilterPath()); + if (path == null) { + return; + } + File destinationFile = new File(path); + try (FileOutputStream output = new FileOutputStream(destinationFile)) { + output.write(cert.getEncoded()); + } catch (IOException | CertificateEncodingException ex) { + ProvSDKUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvSDKUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + } + } else { + PGPPublicKey key = (PGPPublicKey) element; + destination.setFilterExtensions(new String[] { "*.asc" }); //$NON-NLS-1$ + destination.setFileName(PGPPublicKeyService.toHexFingerprint(key) + ".asc"); //$NON-NLS-1$ + String path = destination.open(); + setFilterPath(EXPORT_FILTER_PATH, destination.getFilterPath()); + if (path == null) { + return; + } + File destinationFile = new File(path); + try (OutputStream output = new ArmoredOutputStream(new FileOutputStream(destinationFile))) { + key.encode(output); + } catch (IOException ex) { + ProvSDKUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvSDKUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + } } })); + Button addButton = new Button(buttonComposite, SWT.PUSH); addButton.setText(ProvSDKMessages.TrustPreferencePage_addPGPKeyButtonLabel); addButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { FileDialog dialog = new FileDialog(getShell(), SWT.OPEN); + dialog.setFilterPath(getFilterPath(ADD_FILTER_PATH)); dialog.setText(ProvSDKMessages.TrustPreferencePage_fileImportTitle); - dialog.setFilterExtensions(new String[] { "*.asc" }); //$NON-NLS-1$ + dialog.setFilterExtensions(new String[] { "*.asc;*.der" }); //$NON-NLS-1$ String path = dialog.open(); + setFilterPath(ADD_FILTER_PATH, dialog.getFilterPath()); if (path == null) { return; } - trustedKeys.add(new File(path)); - viewer.setInput(trustedKeys.all()); + + if (path.endsWith(".der")) { //$NON-NLS-1$ + try { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$ + try (InputStream input = Files.newInputStream(Paths.get(path))) { + Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(input); + trustedCertificates.addAll(certificates); + updateInput(); + viewer.setSelection(new StructuredSelection(certificates.toArray()), true); + } + } catch (IOException | CertificateException ex) { + ProvSDKUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvSDKUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + } + } else { + HashSet<Object> oldKeys = new HashSet<>(trustedKeys.all()); + trustedKeys.add(new File(path)); + + HashSet<Object> newKeys = new HashSet<>(trustedKeys.all()); + newKeys.removeAll(oldKeys); + updateInput(); + viewer.setSelection(new StructuredSelection(newKeys.toArray()), true); + } dirty = true; })); setVerticalButtonLayoutData(addButton); + Button removeButton = new Button(buttonComposite, SWT.PUSH); removeButton.setText(ProvSDKMessages.TrustPreferencePage_removePGPKeyButtonLabel); removeButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { - trustedKeys.remove(getSelectedKey()); - viewer.setInput(trustedKeys.all()); + for (Object key : getSelectedKeys()) { + if (key instanceof PGPPublicKey) { + trustedKeys.remove((PGPPublicKey) key); + } else { + trustedCertificates.remove(key); + } + } + updateInput(); dirty = true; })); + removeButton.setEnabled(false); setVerticalButtonLayoutData(removeButton); + + Runnable details = () -> { + Object element = viewer.getStructuredSelection().getFirstElement(); + if (element instanceof X509Certificate) { + // create and open dialog for certificate chain + CertificateLabelProvider.openDialog(getShell(), (X509Certificate) element); + } else { + new PGPPublicKeyViewDialog(getShell(), (PGPPublicKey) element, + provisioningAgent.getService(PGPPublicKeyService.class)).open(); + } + }; + + Button detailsButton = new Button(buttonComposite, SWT.PUSH); + detailsButton.setText(ProvSDKMessages.TrustPreferencePage_Details); + detailsButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> details.run())); + detailsButton.setEnabled(false); + setVerticalButtonLayoutData(detailsButton); + viewer.addPostSelectionChangedListener(e -> { - exportButton.setEnabled(getSelectedKey() != null); - removeButton.setEnabled(getSelectedKey() != null); + List<Object> selectedKeys = getSelectedKeys(); + exportButton.setEnabled(selectedKeys.size() == 1); + Collection<PGPPublicKey> keys = trustedKeys.all(); + removeButton.setEnabled( + selectedKeys.stream().anyMatch(o -> keys.contains(o) || trustedCertificates.contains(o))); + detailsButton.setEnabled(selectedKeys.size() == 1); }); - exportButton.setEnabled(getSelectedKey() != null); - removeButton.setEnabled(getSelectedKey() != null); + + Button trustAllButton = WidgetFactory.button(SWT.CHECK).text(ProvSDKMessages.TrustPreferencePage_TrustAll) + .font(JFaceResources.getDialogFont()).create(res); + setButtonLayoutData(trustAllButton).verticalSpan = 2; + trustAllButton.setSelection(certificateChecker.isTrustAlways()); + trustAllButton.addSelectionListener(widgetSelectedAdapter(e -> { + if (trustAllButton.getSelection()) { + // Prompt the user to ensure they really understand what they've chosen, the + // risk, and where the preference is stored if they wish to change it in the + // future. Also ensure that the default button is no so that they must + // explicitly click the yes button, not just hit enter. + MessageDialog messageDialog = new MessageDialog(getShell(), + ProvSDKMessages.TrustPreferencePage_TrustAllConfirmationTitle, null, + ProvSDKMessages.TrustPreferencePage_TrustAllConfirmationDescription, MessageDialog.QUESTION, + new String[] { ProvSDKMessages.TrustPreferencePage_TrustAllYes, + ProvSDKMessages.TrustPreferencePage_TrustAllNo }, + 1) { + @Override + public Image getImage() { + return getWarningImage(); + } + }; + int result = messageDialog.open(); + if (result != Window.OK) { + certificateChecker.setTrustAlways(false); + // Restore the setting. + trustAllButton.setSelection(false); + } else { + certificateChecker.setTrustAlways(true); + } + + } + })); + + viewer.addDoubleClickListener(e -> details.run()); + + typeColumn.getColumn().pack(); + + createMenu(); + return res; } - private PGPPublicKey getSelectedKey() { - ISelection sel = viewer.getSelection(); - if (!(sel instanceof IStructuredSelection)) { - return null; - } - Object o = ((IStructuredSelection) sel).getFirstElement(); - if (!(o instanceof PGPPublicKey)) { - return null; - } - return (PGPPublicKey) o; + private void createMenu() { + Control control = viewer.getControl(); + Menu menu = new Menu(control); + control.setMenu(menu); + MenuItem item = new MenuItem(menu, SWT.PUSH); + item.setText(ProvSDKMessages.TrustPreferencePage_CopyFingerprint); + item.addSelectionListener(widgetSelectedAdapter(e -> { + Object element = viewer.getStructuredSelection().getFirstElement(); + if (element instanceof PGPPublicKey) { + Clipboard clipboard = new Clipboard(getShell().getDisplay()); + clipboard.setContents(new Object[] { + PGPPublicKeyService.toHexFingerprint((PGPPublicKey) element) }, + new Transfer[] { TextTransfer.getInstance() }); + clipboard.dispose(); + } + })); + + viewer.addSelectionChangedListener( + e -> item.setEnabled(viewer.getStructuredSelection().getFirstElement() instanceof PGPPublicKey)); + } + + private TableViewerColumn createColumn(TableViewer tableViewer, String text, Function<PGPPublicKey, String> pgpMap, + Function<X509Certificate, String> x509Map, TableColumnLayout tableColumnLayout, int columnWeight) { + TableViewerColumn column = new TableViewerColumn(tableViewer, SWT.NONE); + column.getColumn().setText(text); + column.setLabelProvider(new PGPOrX509ColumnLabelProvider(pgpMap, x509Map)); + + tableColumnLayout.setColumnData(column.getColumn(), new ColumnWeightData(columnWeight)); + return column; + } + + private void updateInput() { + Set<PGPPublicKey> input = new TreeSet<>((k1, k2) -> { + return Arrays.compare(k1.getFingerprint(), k2.getFingerprint()); + }); + input = new LinkedHashSet<>(); + Collection<PGPPublicKey> all = trustedKeys.all(); + input = new TreeSet<>((k1, k2) -> { + boolean contains1 = all.contains(k1); + boolean contains2 = all.contains(k2); + if (contains1 != contains2) { + if (contains1) { + return -1; + } + return 1; + } + return PGPPublicKeyService.toHexFingerprint(k1).compareTo(PGPPublicKeyService.toHexFingerprint(k2)); + }); + input.addAll(all); + input.addAll(contributedTrustedKeys.keySet()); + + LinkedHashSet<Object> allInput = new LinkedHashSet<>(); + allInput.addAll(trustedCertificates); + allInput.addAll(input); + viewer.setInput(allInput); + } + + @SuppressWarnings("unchecked") + private List<Object> getSelectedKeys() { + return viewer.getStructuredSelection().toList(); } private Composite createVerticalButtonBar(Composite parent) { @@ -181,12 +424,68 @@ public class TrustPreferencePage extends PreferencePage implements IWorkbenchPre return data; } + private String getFilterPath(String key) { + IDialogSettings dialogSettings = DialogSettings + .getOrCreateSection(ProvSDKUIActivator.getDefault().getDialogSettings(), getClass().getName()); + String filterPath = dialogSettings.get(key); + if (filterPath == null) { + filterPath = System.getProperty("user.home"); //$NON-NLS-1$ + } + return filterPath; + } + + private void setFilterPath(String key, String filterPath) { + if (filterPath != null) { + IDialogSettings dialogSettings = DialogSettings + .getOrCreateSection(ProvSDKUIActivator.getDefault().getDialogSettings(), getClass().getName()); + dialogSettings.put(key, filterPath); + } + } + + private String getBundleName(Bundle bundle) { + String value = bundle.getHeaders().get(Constants.BUNDLE_NAME); + return value == null ? bundle.getSymbolicName() : Platform.getResourceString(bundle, value); + } + + @Override + protected void performDefaults() { + trustedCertificates = new LinkedHashSet<>(certificateChecker.getPreferenceTrustedCertificates()); + trustedKeys = certificateChecker.getPreferenceTrustedKeys(); + updateInput(); + super.performDefaults(); + } + @Override public boolean performOk() { if (dirty) { - return certificateChecker.persistTrustedKeys(trustedKeys).isOK(); + IStatus persistTrustedCertificates = certificateChecker.persistTrustedCertificates(trustedCertificates); + IStatus persistTrustedKeys = certificateChecker.persistTrustedKeys(trustedKeys); + dirty = false; + return persistTrustedKeys.isOK() && persistTrustedCertificates.isOK(); } return true; } + private static class PGPOrX509ColumnLabelProvider extends ColumnLabelProvider { + private Function<PGPPublicKey, String> pgpMap; + private Function<X509Certificate, String> x509map; + + public PGPOrX509ColumnLabelProvider(Function<PGPPublicKey, String> pgpMap, + Function<X509Certificate, String> x509map) { + this.pgpMap = pgpMap; + this.x509map = x509map; + } + + @Override + public String getText(Object element) { + if (element instanceof PGPPublicKey) { + return pgpMap.apply((PGPPublicKey) element); + } + if (element instanceof X509Certificate) { + return x509map.apply((X509Certificate) element); + } + return super.getText(element); + } + } + } diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/messages.properties b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/messages.properties index 0e344af3d..93df84f7f 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/messages.properties +++ b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/messages.properties @@ -37,12 +37,28 @@ RemediationOperation_ResolveJobName=Searching alternate solutions... RemediationOperation_ResolveJobTask=Some items cannot be at the highest version. Searching for the highest common denominator ... TrustPreferencePage_title=Trust TrustPreferencePage_export=\uD83D\uDCE5 E&xport... -TrustPreferencePage_idColumn=Id -TrustPreferencePage_userColumn=User TrustPreferencePage_fileExportTitle=Export PGP public key TrustPreferencePage_fileImportTitle=Import PGP public key to trust TrustPreferencePage_addPGPKeyButtonLabel=\u2795 &Add... +TrustPreferencePage_Contributor=Contributor +TrustPreferencePage_CopyFingerprint=Copy Fingerprint +TrustPreferencePage_DataValidExpires=\u2714\uFE0F Valid, expires {0} +TrustPreferencePage_DateExpired=\u274C Expired +TrustPreferencePage_DateExpiredSince=\u274C Expired since {0} +TrustPreferencePage_DateNotYetvalid=\u274C Not yet valid +TrustPreferencePage_DateNotYetValid=\u274C Not yet valid, starts {0} +TrustPreferencePage_DateValid=\u2714\uFE0F Valid +TrustPreferencePage_Details=\uD83D\uDD0D D&etails... +TrustPreferencePage_Export=Export... +TrustPreferencePage_FingerprintIdColumn=Fingerprint/Id +TrustPreferencePage_NameColumn=Name +TrustPreferencePage_PreferenceContributor=<preferences> TrustPreferencePage_removePGPKeyButtonLabel=\uD83D\uDDD1\uFE0F &Remove - -TrustPreferencePage_pgpIntro=The following PGP public keys are considered as trusted.\n\ -Artifacts that are signed and verified by one of those keys will be trusted and installed without further trust confirmation request.
\ No newline at end of file +TrustPreferencePage_pgpIntro=Artifacts verifiably signed by the following trusted certificates and PGP public keys will be installed without confirmation. +TrustPreferencePage_TrustAll=Trust all content +TrustPreferencePage_TrustAllConfirmationDescription=Are you certain you wish to trust all content, including unsigned content of unknown origin, with no confirmation for all future operations? +TrustPreferencePage_TrustAllConfirmationTitle=Always Trust Everything Confirmation +TrustPreferencePage_TrustAllNo=No, Prompt Me Instead +TrustPreferencePage_TrustAllYes=Yes, I Accept the Risk +TrustPreferencePage_TypeColumn=Type +TrustPreferencePage_ValidityColumn=Validity Dates diff --git a/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF index 429c0b980..433d2e820 100644 --- a/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %bundleName Bundle-SymbolicName: org.eclipse.equinox.p2.ui;singleton:=true -Bundle-Version: 2.7.300.qualifier +Bundle-Version: 2.7.400.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.ui.ProvUIActivator Bundle-Vendor: %providerName Bundle-Localization: plugin @@ -19,6 +19,7 @@ Export-Package: org.eclipse.equinox.internal.p2.ui; org.eclipse.equinox.internal.p2.ui.dialogs; x-friends:="org.eclipse.equinox.p2.ui.admin, org.eclipse.equinox.p2.ui.sdk.scheduler, + org.eclipse.equinox.p2.ui.sdk, org.eclipse.pde.ui, org.eclipse.equinox.p2.ui.importexport", org.eclipse.equinox.internal.p2.ui.model; @@ -28,7 +29,7 @@ Export-Package: org.eclipse.equinox.internal.p2.ui; org.eclipse.equinox.p2.ui.sdk, org.eclipse.equinox.p2.ui.importexport", org.eclipse.equinox.internal.p2.ui.query;x-friends:="org.eclipse.equinox.internal.p2.ui.analysis,org.eclipse.equinox.p2.ui.admin", - org.eclipse.equinox.internal.p2.ui.viewers;x-friends:="org.eclipse.equinox.p2.ui.admin,org.eclipse.equinox.p2.ui.sdk.scheduler,org.eclipse.equinox.p2.ui.importexport", + org.eclipse.equinox.internal.p2.ui.viewers;x-friends:="org.eclipse.equinox.p2.ui.admin,org.eclipse.equinox.p2.ui.sdk,org.eclipse.equinox.p2.ui.sdk.scheduler,org.eclipse.equinox.p2.ui.importexport", org.eclipse.equinox.p2.ui;version="2.6.0" Require-Bundle: org.eclipse.ui;bundle-version="3.107.0", org.eclipse.core.runtime;bundle-version="[3.18.0,4.0.0)", @@ -39,9 +40,11 @@ Import-Package: javax.xml.parsers, org.bouncycastle.bcpg;version="1.65.0", org.bouncycastle.openpgp;version="1.65.0", org.bouncycastle.util;version="1.65.1", + org.eclipse.equinox.internal.p2.artifact.processors.pgp, org.eclipse.equinox.internal.p2.artifact.repository, org.eclipse.equinox.internal.p2.core.helpers, org.eclipse.equinox.internal.p2.director, + org.eclipse.equinox.internal.p2.extensionlocation, org.eclipse.equinox.internal.p2.metadata, org.eclipse.equinox.internal.p2.metadata.repository, org.eclipse.equinox.internal.p2.operations, @@ -60,7 +63,9 @@ Import-Package: javax.xml.parsers, org.eclipse.equinox.p2.query;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository.artifact;version="[2.0.0,3.0.0)", + org.eclipse.equinox.p2.repository.artifact.spi;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository.metadata;version="[2.0.0,3.0.0)", + org.eclipse.equinox.p2.repository.spi;version="2.0.0", org.osgi.framework;version="1.6.0", org.osgi.service.packageadmin;version="1.2.0", org.w3c.dom, diff --git a/bundles/org.eclipse.equinox.p2.ui/plugin.xml b/bundles/org.eclipse.equinox.p2.ui/plugin.xml index 6f6031e2e..87e8645fd 100644 --- a/bundles/org.eclipse.equinox.p2.ui/plugin.xml +++ b/bundles/org.eclipse.equinox.p2.ui/plugin.xml @@ -12,6 +12,13 @@ <adapter type="org.eclipse.equinox.p2.repository.metadata.IMetadataRepository"/> <adapter type="org.eclipse.equinox.p2.repository.artifact.IArtifactRepository"/> </factory> + <factory + adaptableType="org.eclipse.ui.internal.about.AboutPluginsPage" + class="org.eclipse.equinox.internal.p2.ui.KeySigningInfoFactory"> + <adapter + type="org.eclipse.ui.internal.about.AboutBundleData$ExtendedSigningInfo"> + </adapter> + </factory> </extension> <extension diff --git a/bundles/org.eclipse.equinox.p2.ui/pom.xml b/bundles/org.eclipse.equinox.p2.ui/pom.xml index 027ad5f4a..13cace16e 100644 --- a/bundles/org.eclipse.equinox.p2.ui/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui/pom.xml @@ -14,11 +14,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.ui</artifactId> - <version>2.7.300-SNAPSHOT</version> + <version>2.7.400-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/IProvHelpContextIds.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/IProvHelpContextIds.java index cba53aa52..3c75aa5e9 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/IProvHelpContextIds.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/IProvHelpContextIds.java @@ -13,35 +13,37 @@ *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui; - /** * Help context ids for the P2 UI * <p> * This interface contains constants only; it is not intended to be implemented * or extended. * </p> + * * @since 3.4 * @noextend This interface is not intended to be extended by clients. * @noimplement This interface is not intended to be implemented by clients. */ public interface IProvHelpContextIds { - public static final String PREFIX = ProvUIActivator.PLUGIN_ID + "."; //$NON-NLS-1$ + String PREFIX = ProvUIActivator.PLUGIN_ID + "."; //$NON-NLS-1$ + + String REVERT_CONFIGURATION_WIZARD = PREFIX + "revert_configuration_wizard_context"; //$NON-NLS-1$ - public static final String REVERT_CONFIGURATION_WIZARD = PREFIX + "revert_configuration_wizard_context"; //$NON-NLS-1$ + String UNINSTALL_WIZARD = PREFIX + "uinstall_wizard_context"; //$NON-NLS-1$ - public static final String UNINSTALL_WIZARD = PREFIX + "uinstall_wizard_context"; //$NON-NLS-1$ + String UPDATE_WIZARD = PREFIX + "update_wizard_context"; //$NON-NLS-1$ - public static final String UPDATE_WIZARD = PREFIX + "update_wizard_context"; //$NON-NLS-1$ + String ADD_REPOSITORY_DIALOG = PREFIX + "add_repository_dialog_context"; //$NON-NLS-1$ - public static final String ADD_REPOSITORY_DIALOG = PREFIX + "add_repository_dialog_context"; //$NON-NLS-1$ + String INSTALL_WIZARD = PREFIX + "install_wizard_context"; //$NON-NLS-1$ - public static final String INSTALL_WIZARD = PREFIX + "install_wizard_context"; //$NON-NLS-1$ + String REPOSITORY_MANIPULATION_DIALOG = PREFIX + "repository_manipulation_dialog_context"; //$NON-NLS-1$ - public static final String REPOSITORY_MANIPULATION_DIALOG = PREFIX + "repository_manipulation_dialog_context"; //$NON-NLS-1$ + String INSTALLED_SOFTWARE = PREFIX + "installed_software_context"; //$NON-NLS-1$ - public static final String INSTALLED_SOFTWARE = PREFIX + "installed_software_context"; //$NON-NLS-1$ + String AVAILABLE_SOFTWARE = PREFIX + "available_software_context"; //$NON-NLS-1$ - public static final String AVAILABLE_SOFTWARE = PREFIX + "available_software_context"; //$NON-NLS-1$ + String TRUST_DIALOG = PREFIX + "trust_dialog_context"; //$NON-NLS-1$ } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/KeySigningInfoFactory.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/KeySigningInfoFactory.java new file mode 100644 index 000000000..1134dbb56 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/KeySigningInfoFactory.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.ui; + +import java.io.File; +import java.io.IOException; +import java.util.*; +import org.bouncycastle.openpgp.*; +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPPublicKeyStore; +import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerifier; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.metadata.IArtifactKey; +import org.eclipse.equinox.p2.query.IQueryResult; +import org.eclipse.equinox.p2.repository.artifact.*; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; +import org.eclipse.ui.internal.about.AboutBundleData; +import org.eclipse.ui.internal.about.AboutPluginsPage; +import org.osgi.framework.Bundle; + +/** + * A factory used by the {@link AboutPluginsPage} to provide extended signing + * information, in particular information about PGP signing. + * + * @since 2.7.400 + */ +public class KeySigningInfoFactory implements IAdapterFactory { + + private static final Class<?>[] CLASSES = new Class[] { AboutBundleData.ExtendedSigningInfo.class }; + + @Override + public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) { + if (adapterType == AboutBundleData.ExtendedSigningInfo.class) { + return adapterType.cast(new AboutBundleData.ExtendedSigningInfo() { + private final Map<File, Map<PGPSignature, PGPPublicKey>> bundlePoolArtficactSigningDetails = getBundlePoolArtficactPGPSigningDetails(); + + @Override + public boolean isSigned(Bundle bundle) { + return getDetails(bundle) != null; + } + + @Override + public String getSigningType(Bundle bundle) { + return ProvUIMessages.KeySigningInfoFactory_PGPSigningType; + } + + @Override + public String getSigningDetails(Bundle bundle) { + Map<PGPSignature, PGPPublicKey> details = getDetails(bundle); + if (details != null) { + PGPPublicKeyService keyService = getKeyService(); + List<String> lines = new ArrayList<>(); + for (PGPPublicKey key : details.values()) { + if (keyService != null) { + // Be sure to normalize/enhance the key so we properly don't show + // self-signatures. + key = keyService.addKey(key); + } + if (!lines.isEmpty()) { + lines.add(""); //$NON-NLS-1$ + } + addDetails(key, lines, ""); //$NON-NLS-1$ + if (keyService != null) { + Set<PGPPublicKey> verifiedCertifications = keyService.getVerifiedCertifications(key); + boolean first = true; + for (PGPPublicKey verifyingKey : verifiedCertifications) { + /// Don't show self-signatures. + if (!verifyingKey.equals(key)) { + if (first) { + lines.add(" " + ProvUIMessages.KeySigningInfoFactory_KeySignersSection); //$NON-NLS-1$ + first = false; + } + addDetails(verifyingKey, lines, " "); //$NON-NLS-1$ + } + } + } + } + return String.join("\n", lines); //$NON-NLS-1$ + } + return null; + } + + @Override + public Date getSigningTime(Bundle bundle) { + Map<PGPSignature, PGPPublicKey> details = getDetails(bundle); + return details == null ? null : details.keySet().iterator().next().getCreationTime(); + } + + private void addDetails(PGPPublicKey key, List<String> lines, String indentation) { + lines.add(indentation + ProvUIMessages.KeySigningInfoFactory_FingerprintItem + + PGPPublicKeyService.toHexFingerprint(key)); + for (Iterator<String> userIDs = key.getUserIDs(); userIDs.hasNext();) { + lines.add(indentation + ProvUIMessages.KeySigningInfoFactory_UserIDItem + userIDs.next()); + } + } + + private PGPPublicKeyService getKeyService() { + IProvisioningAgent agent = org.eclipse.equinox.internal.p2.extensionlocation.Activator + .getCurrentAgent(); + return agent == null ? null : agent.getService(PGPPublicKeyService.class); + } + + private Map<PGPSignature, PGPPublicKey> getDetails(Bundle bundle) { + try { + File bundleFile = FileLocator.getBundleFile(bundle).getCanonicalFile(); + Map<PGPSignature, PGPPublicKey> details = bundlePoolArtficactSigningDetails.get(bundleFile); + return details; + } catch (IOException | RuntimeException e) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, e.getMessage(), e)); + return null; + } + } + }); + } + + return null; + } + + @Override + public Class<?>[] getAdapterList() { + return CLASSES; + } + + /** + * Returns a map from artifact files to the PGP signature/key pairs that were + * used to {@link PGPSignatureVerifier#close() verify} the artifact while it was + * being downloaded. + * + * @return a map of all the PGP signed artifact files to their signature/key + * pairs. + */ + private static Map<File, Map<PGPSignature, PGPPublicKey>> getBundlePoolArtficactPGPSigningDetails() { + Map<File, Map<PGPSignature, PGPPublicKey>> result = new LinkedHashMap<>(); + try { + // Look up artifact metadata for all the bundle pool repository of the + // installation. + IFileArtifactRepository bundlePoolRepository = org.eclipse.equinox.internal.p2.extensionlocation.Activator + .getBundlePoolRepository(); + IQueryResult<IArtifactKey> allArtifactKeys = bundlePoolRepository.query(ArtifactKeyQuery.ALL_KEYS, + new NullProgressMonitor()); + for (IArtifactKey key : allArtifactKeys) { + for (IArtifactDescriptor descriptor : bundlePoolRepository.getArtifactDescriptors(key)) { + File file = bundlePoolRepository.getArtifactFile(descriptor); + if (file != null) { + try { + Collection<PGPSignature> signatures = PGPSignatureVerifier.getSignatures(descriptor); + if (!signatures.isEmpty()) { + Map<PGPSignature, PGPPublicKey> details = new LinkedHashMap<>(); + PGPPublicKeyStore keys = PGPSignatureVerifier.getKeys(descriptor); + for (PGPSignature signature : signatures) { + Collection<PGPPublicKey> signingKeys = keys.getKeys(signature.getKeyID()); + if (!signingKeys.isEmpty()) { + // There is vanishingly small chance that two keys with colliding key IDs were + // used on two different signatures on the same artifact. + details.put(signature, signingKeys.iterator().next()); + } + } + if (!details.isEmpty()) { + result.put(file.getCanonicalFile(), details); + } + } + } catch (IOException | PGPException | RuntimeException e) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, e.getMessage(), e)); + } + } + } + } + } catch (RuntimeException e) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, e.getMessage(), e)); + } + return result; + } +} diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java index bcee1d0ef..f79aacc0b 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java @@ -78,6 +78,7 @@ public class ProvUIMessages extends NLS { public static String IUGeneralInfoPropertyPage_VersionLabel; public static String IULicensePropertyPage_NoLicense; public static String IULicensePropertyPage_ViewLicenseLabel; + public static String PGPPublicKeyViewDialog_Title; public static String ProfileModificationAction_InvalidSelections; public static String ProfileModificationWizardPage_DetailsLabel; public static String ProfileSnapshots_Label; @@ -247,6 +248,10 @@ public class ProvUIMessages extends NLS { public static String SelectableIUsPage_Deselect_All; public static String InstallRemediationPage_Title; public static String InstallRemediationPage_Description; + public static String KeySigningInfoFactory_FingerprintItem; + public static String KeySigningInfoFactory_KeySignersSection; + public static String KeySigningInfoFactory_PGPSigningType; + public static String KeySigningInfoFactory_UserIDItem; public static String UpdateRemediationPage_Title; public static String UpdateRemediationPage_Description; public static String RemediationPage_SubDescription; @@ -274,12 +279,35 @@ public class ProvUIMessages extends NLS { public static String TrustCertificateDialog_Export; public static String TrustCertificateDialog_Title; public static String TrustCertificateDialog_Message; + public static String TrustCertificateDialog_MessageUnsigned; + public static String TrustCertificateDialog_MessageNameWarning; + public static String TrustCertificateDialog_MessagePGP; public static String TrustCertificateDialog_AcceptSelectedButtonLabel; + public static String TrustCertificateDialog_AlwaysTrust; + public static String TrustCertificateDialog_AlwaysTrustConfirmationMessage; + public static String TrustCertificateDialog_AlwaysTrustConfirmationTitle; + public static String TrustCertificateDialog_AlwaysTrustNo; + public static String TrustCertificateDialog_AlwaysTrustYes; + public static String TrustCertificateDialog_ArtifactId; public static String TrustCertificateDialog_SelectAll; public static String TrustCertificateDialog_DeselectAll; public static String TrustCertificateDialog_ObjectType; public static String TrustCertificateDialog_Id; public static String TrustCertificateDialog_Name; + public static String TrustCertificateDialog_Classifier; + public static String TrustCertificateDialog_CopyFingerprint; + public static String TrustCertificateDialog_dates; + public static String TrustCertificateDialog_NotApplicable; + public static String TrustCertificateDialog_NotYetValidStartDate; + public static String TrustCertificateDialog_expiredSince; + public static String TrustCertificateDialog_validExpires; + public static String TrustCertificateDialog_valid; + public static String TrustCertificateDialog_expired; + public static String TrustCertificateDialog_notYetValid; + public static String TrustCertificateDialog_RememberSigners; + public static String TrustCertificateDialog_Unknown; + public static String TrustCertificateDialog_Unsigned; + public static String TrustCertificateDialog_Version; // Operations public static String UpdateManagerCompatibility_ExportSitesTitle; public static String UpdateManagerCompatibility_ImportSitesTitle; diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ServiceUIComponent.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ServiceUIComponent.java index fcea5da5a..cad8f35b9 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ServiceUIComponent.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ServiceUIComponent.java @@ -13,9 +13,8 @@ *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui; -import org.eclipse.equinox.p2.core.UIServices; - import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.core.UIServices; import org.eclipse.equinox.p2.core.spi.IAgentServiceFactory; /** @@ -26,7 +25,7 @@ public class ServiceUIComponent implements IAgentServiceFactory { @Override public Object createService(IProvisioningAgent agent) { - return new ValidationDialogServiceUI(); + return new ValidationDialogServiceUI(agent); } } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ValidationDialogServiceUI.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ValidationDialogServiceUI.java index 482fe4844..ae451f4c7 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ValidationDialogServiceUI.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ValidationDialogServiceUI.java @@ -14,19 +14,29 @@ *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui; +import java.io.File; import java.net.URL; import java.security.cert.Certificate; import java.util.*; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; import org.bouncycastle.openpgp.PGPPublicKey; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.equinox.internal.p2.ui.dialogs.TrustCertificateDialog; import org.eclipse.equinox.internal.p2.ui.dialogs.UserValidationDialog; +import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.core.UIServices; +import org.eclipse.equinox.p2.metadata.IArtifactKey; +import org.eclipse.equinox.p2.repository.artifact.spi.IArtifactUIServices; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; import org.eclipse.equinox.p2.ui.LoadMetadataRepositoryJob; import org.eclipse.jface.dialogs.*; import org.eclipse.jface.viewers.TreeNode; +import org.eclipse.jface.window.IShellProvider; import org.eclipse.jface.window.Window; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; @@ -40,16 +50,45 @@ import org.eclipse.ui.PlatformUI; * declaration is made in the serviceui_component.xml file. * */ -public class ValidationDialogServiceUI extends UIServices { +public class ValidationDialogServiceUI extends UIServices implements IArtifactUIServices { + + private final IProvisioningAgent agent; + + private Display display; + + private Consumer<String> linkHandler = link -> { + if (PlatformUI.isWorkbenchRunning()) { + try { + URL url = new URL(link); + PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser().openURL(url); + } catch (Exception x) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, x.getMessage(), x)); + } + } + }; + + private IShellProvider shellProvider = () -> ProvUI.getDefaultParentShell(); + + public ValidationDialogServiceUI() { + this(null); + } + + public ValidationDialogServiceUI(IProvisioningAgent agent) { + this.agent = agent; + } static final class MessageDialogWithLink extends MessageDialog { private final String linkText; + private final Consumer<String> linkOpener; MessageDialogWithLink(Shell parentShell, String dialogTitle, Image dialogTitleImage, String dialogMessage, - int dialogImageType, String[] dialogButtonLabels, int defaultIndex, String linkText) { + int dialogImageType, String[] dialogButtonLabels, int defaultIndex, String linkText, + Consumer<String> linkOpener) { super(parentShell, dialogTitle, dialogTitleImage, dialogMessage, dialogImageType, dialogButtonLabels, defaultIndex); this.linkText = linkText; + this.linkOpener = linkOpener; } @Override @@ -60,13 +99,7 @@ public class ValidationDialogServiceUI extends UIServices { Link link = new Link(parent, SWT.NONE); link.setText(linkText); link.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { - try { - URL url = new URL(e.text); - PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser().openURL(url); - } catch (Exception x) { - ProvUIActivator.getDefault().getLog().log(// - new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, x.getMessage(), x)); - } + linkOpener.accept(e.text); })); return link; } @@ -96,8 +129,8 @@ public class ValidationDialogServiceUI extends UIServices { final AuthenticationInfo[] result = new AuthenticationInfo[1]; if (!suppressAuthentication() && !isHeadless()) { - PlatformUI.getWorkbench().getDisplay().syncExec(() -> { - Shell shell = ProvUI.getDefaultParentShell(); + getDisplay().syncExec(() -> { + Shell shell = shellProvider.getShell(); String message = NLS.bind(ProvUIMessages.ServiceUI_LoginDetails, location); UserValidationDialog dialog = new UserValidationDialog(shell, ProvUIMessages.ServiceUI_LoginRequired, null, message); @@ -141,10 +174,10 @@ public class ValidationDialogServiceUI extends UIServices { // detail should be trusted if (!isHeadless() && unsignedDetail != null && unsignedDetail.length > 0) { final boolean[] result = new boolean[] { false }; - PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { + getDisplay().syncExec(new Runnable() { @Override public void run() { - Shell shell = ProvUI.getDefaultParentShell(); + Shell shell = shellProvider.getShell(); OkCancelErrorDialog dialog = new OkCancelErrorDialog(shell, ProvUIMessages.ServiceUI_warning_title, null, createStatus(), IStatus.WARNING); result[0] = dialog.open() == IDialogConstants.OK_ID; @@ -169,17 +202,46 @@ public class ValidationDialogServiceUI extends UIServices { // We've established trust for unsigned content, now examine the untrusted // chains if (!isHeadless() && (untrustedChains.length > 0 || !untrustedPublicKeys.isEmpty())) { - final TrustCertificateDialog[] dialog = new TrustCertificateDialog[1]; - final TreeNode[] input = createTreeNodes(untrustedChains, untrustedPublicKeys); - PlatformUI.getWorkbench().getDisplay().syncExec(() -> { - Shell shell = ProvUI.getDefaultParentShell(); - TrustCertificateDialog trustCertificateDialog = new TrustCertificateDialog(shell, input); - dialog[0] = trustCertificateDialog; - trustCertificateDialog.open(); - }); - persistTrust = true; - if (dialog[0].getResult() != null) { - for (Object o : dialog[0].getResult()) { + return getTrustInfo( + Arrays.stream(untrustedChains).collect( + Collectors.toMap(Arrays::asList, it -> Set.of(), (e1, e2) -> e1, LinkedHashMap::new)), + untrustedPublicKeys.stream().collect( + Collectors.toMap(Function.identity(), it -> Set.of(), (e1, e2) -> e1, LinkedHashMap::new)), + null, null); + + } + + return new TrustInfo(trustedCertificates, trustedKeys, persistTrust, trustUnsigned); + } + + @Override + public TrustInfo getTrustInfo(Map<List<Certificate>, Set<IArtifactKey>> untrustedCertificates, + Map<PGPPublicKey, Set<IArtifactKey>> untrustedPGPKeys, // + Set<IArtifactKey> unsignedArtifacts, // + Map<IArtifactKey, File> artifacts) { + boolean trustUnsigned = true; + AtomicBoolean persistTrust = new AtomicBoolean(); + AtomicBoolean trustAlways = new AtomicBoolean(); + List<Certificate> trustedCertificates = new ArrayList<>(); + List<PGPPublicKey> trustedKeys = new ArrayList<>(); + if (!isHeadless()) { + TreeNode[] input = createTreeNodes(untrustedCertificates, untrustedPGPKeys, unsignedArtifacts, artifacts); + if (input.length != 0) { + trustUnsigned = unsignedArtifacts == null || unsignedArtifacts.isEmpty(); + List<Object> result = new ArrayList<>(); + getDisplay().syncExec(() -> { + Shell shell = shellProvider.getShell(); + TrustCertificateDialog trustCertificateDialog = new TrustCertificateDialog(shell, input); + if (trustCertificateDialog.open() == Window.OK) { + Object[] dialogResult = trustCertificateDialog.getResult(); + if (dialogResult != null) { + result.addAll(Arrays.asList(dialogResult)); + } + persistTrust.set(trustCertificateDialog.isRememberSelectedSigners()); + trustAlways.set(trustCertificateDialog.isTrustAlways()); + } + }); + for (Object o : result) { if (o instanceof TreeNode) { o = ((TreeNode) o).getValue(); } @@ -187,40 +249,93 @@ public class ValidationDialogServiceUI extends UIServices { trustedCertificates.add((Certificate) o); } else if (o instanceof PGPPublicKey) { trustedKeys.add((PGPPublicKey) o); + } else if (o == null) { + trustUnsigned = true; } } } } - return new TrustInfo(trustedCertificates, trustedKeys, persistTrust, trustUnsigned); + + return new TrustInfo(trustedCertificates, trustedKeys, persistTrust.get(), trustUnsigned, trustAlways.get()); } - private TreeNode[] createTreeNodes(Certificate[][] certificates, Collection<PGPPublicKey> untrustedPublicKeys) { - TreeNode[] children = new TreeNode[certificates.length + untrustedPublicKeys.size()]; - int i = 0; - for (i = 0; i < certificates.length; i++) { - TreeNode head = new TreeNode(certificates[i][0]); - TreeNode parent = head; - children[i] = head; - for (int j = 0; j < certificates[i].length; j++) { - TreeNode node = new TreeNode(certificates[i][j]); - node.setParent(parent); - parent.setChildren(new TreeNode[] { node }); - parent = node; + private TreeNode[] createTreeNodes(Map<List<Certificate>, Set<IArtifactKey>> untrustedCertificates, + Map<PGPPublicKey, Set<IArtifactKey>> untrustedPGPKeys, // + Set<IArtifactKey> unsignedArtifacts, // + Map<IArtifactKey, File> artifacts) { + + List<ExtendedTreeNode> children = new ArrayList<>(); + if (untrustedCertificates != null && !untrustedCertificates.isEmpty()) { + for (Map.Entry<List<Certificate>, Set<IArtifactKey>> entry : untrustedCertificates.entrySet()) { + ExtendedTreeNode parent = null; + List<Certificate> key = entry.getKey(); + Set<IArtifactKey> associatedArtifacts = entry.getValue(); + for (Certificate certificate : key) { + ExtendedTreeNode node = new ExtendedTreeNode(certificate, associatedArtifacts); + if (parent == null) { + children.add(node); + } else { + node.setParent(parent); + parent.setChildren(new TreeNode[] { node }); + } + parent = node; + } + } + } + + if (untrustedPGPKeys != null && !untrustedPGPKeys.isEmpty()) { + PGPPublicKeyService keyService = agent == null ? null : agent.getService(PGPPublicKeyService.class); + for (Map.Entry<PGPPublicKey, Set<IArtifactKey>> entry : untrustedPGPKeys.entrySet()) { + PGPPublicKey key = entry.getKey(); + Set<IArtifactKey> associatedArtifacts = entry.getValue(); + ExtendedTreeNode node = new ExtendedTreeNode(key, associatedArtifacts); + children.add(node); + expandChildren(node, key, keyService, new HashSet<>(), Integer.getInteger("p2.pgp.trust.depth", 3)); //$NON-NLS-1$ } } - for (PGPPublicKey key : untrustedPublicKeys) { - children[i] = new TreeNode(key); - i++; + + if (unsignedArtifacts != null && !unsignedArtifacts.isEmpty()) { + ExtendedTreeNode node = new ExtendedTreeNode(null, unsignedArtifacts); + children.add(node); + + } + + return children.toArray(TreeNode[]::new); + } + + private void expandChildren(TreeNode result, PGPPublicKey key, PGPPublicKeyService keyService, + Set<PGPPublicKey> visited, int remainingDepth) { + if (keyService != null && remainingDepth > 0 && visited.add(key)) { + Set<PGPPublicKey> certifications = keyService.getVerifiedCertifications(key); + if (certifications != null && !certifications.isEmpty()) { + List<TreeNode> children = new ArrayList<>(); + for (PGPPublicKey certifyingKey : certifications) { + if (visited.add(certifyingKey)) { + TreeNode treeNode = new TreeNode(certifyingKey); + children.add(treeNode); + } + } + + if (!children.isEmpty()) { + result.setChildren(children.toArray(TreeNode[]::new)); + children.forEach(child -> { + child.setParent(result); + PGPPublicKey certifyingKey = (PGPPublicKey) child.getValue(); + visited.remove(certifyingKey); + expandChildren(child, certifyingKey, keyService, visited, remainingDepth - 1); + visited.add(certifyingKey); + }); + } + } } - return children; } @Override public AuthenticationInfo getUsernamePassword(final String location, final AuthenticationInfo previousInfo) { final AuthenticationInfo[] result = new AuthenticationInfo[1]; if (!suppressAuthentication() && !isHeadless()) { - PlatformUI.getWorkbench().getDisplay().syncExec(() -> { - Shell shell = ProvUI.getDefaultParentShell(); + getDisplay().syncExec(() -> { + Shell shell = shellProvider.getShell(); String message = null; if (previousInfo.saveResult()) message = NLS.bind(ProvUIMessages.ProvUIMessages_SavedNotAccepted_EnterFor_0, location); @@ -246,18 +361,131 @@ public class ValidationDialogServiceUI extends UIServices { super.showInformationMessage(title, text, linkText); return; } - PlatformUI.getWorkbench().getDisplay().syncExec(() -> { - MessageDialog dialog = new MessageDialogWithLink(ProvUI.getDefaultParentShell(), title, null, text, - MessageDialog.INFORMATION, new String[] { IDialogConstants.OK_LABEL }, 0, linkText); + getDisplay().syncExec(() -> { + MessageDialog dialog = new MessageDialogWithLink(shellProvider.getShell(), title, null, text, + MessageDialog.INFORMATION, new String[] { IDialogConstants.OK_LABEL }, 0, linkText, linkHandler); dialog.open(); }); } + /** + * Returns a handler (used by + * {@link #showInformationMessage(String, String, String)}) to open a link in an + * external browser. + * + * @return a handler to open a link in an external browser. + * + * @since 2.7.400 + */ + public Consumer<String> getLinkHandler() { + return linkHandler; + } + + /** + * Sets the handler used by + * {@link #showInformationMessage(String, String, String)} to open a link in an + * external browser. + * + * @param linkHandler the handler for opening links in an external browser. + * + * @since 2.7.400 + */ + public void setLinkHandler(Consumer<String> linkHandler) { + this.linkHandler = linkHandler; + } + + /** + * Returns a shell that is appropriate to use as the parent for a modal dialog + * about to be opened. + * + * @return a shell that is appropriate to use as the parent for a modal dialog + * about to be opened. + * + * @see ProvUI#getDefaultParentShell() + * + * @since 2.7.400 + */ + public IShellProvider getShellProvider() { + return shellProvider; + } + + /** + * Set the provider that yields a shell that is appropriate to use as the parent + * for a modal dialog about to be opened. + * + * @param shellProvider the provider of a shell that is appropriate to use as + * the parent for a modal dialog about to be opened. + */ + public void setShellProvider(IShellProvider shellProvider) { + this.shellProvider = shellProvider; + } + + /** + * Returns the display of the workbench or the display set by + * {@link #setDisplay(Display)} in a non-workbench application. + * + * @since 2.7.400 + */ + public Display getDisplay() { + if (display == null && PlatformUI.isWorkbenchRunning()) { + display = PlatformUI.getWorkbench().getDisplay(); + } + return display; + } + + /** + * Can be called once to set the display in a non-workbench application. + * + * @throws IllegalStateException if there is a workbench running or the display + * has already been set differently. + * + * @since 2.7.400 + */ + public void setDisplay(Display display) { + if (this.display != null && this.display != display) { + throw new IllegalStateException("Cannot change the display"); //$NON-NLS-1$ + } + this.display = display; + } + private boolean isHeadless() { // If there is no UI available and we are still the IServiceUI, // assume that the operation should proceed. See // https://bugs.eclipse.org/bugs/show_bug.cgi?id=291049 - return !PlatformUI.isWorkbenchRunning(); + // + // But we shouldn't just assume that no workbench mean no display and no head. + // It should be possible to reuse this class in an application without a + // workbench, e.g., the Eclipse Installer. + return getDisplay() == null; + } + + private static class ExtendedTreeNode extends TreeNode implements IAdaptable { + private final Set<IArtifactKey> artifacts; + + public ExtendedTreeNode(Object value) { + super(value); + artifacts = null; + } + + public ExtendedTreeNode(Object value, Set<IArtifactKey> artifacts) { + super(value); + this.artifacts = artifacts; + } + + @Override + public <T> T getAdapter(Class<T> adapter) { + if (adapter.isInstance(this)) { + return adapter.cast(this); + } + if (adapter.isInstance(getValue())) { + return adapter.cast(value); + } + if (adapter == IArtifactKey[].class && artifacts != null) { + return adapter.cast(artifacts.toArray(IArtifactKey[]::new)); + } + + return null; + } } } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/PGPPublicKeyViewDialog.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/PGPPublicKeyViewDialog.java new file mode 100644 index 000000000..a6cbf3be1 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/PGPPublicKeyViewDialog.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.ui.dialogs; + +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.List; +import org.bouncycastle.bcpg.*; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSignature; +import org.eclipse.core.runtime.*; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.equinox.internal.p2.ui.ProvUIMessages; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.*; + +/** + * Presents information about a key in a format similar to what key servers + * display. + * + * @since 1.2.4 + */ +public class PGPPublicKeyViewDialog extends TitleAreaDialog { + + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); //$NON-NLS-1$ + + final private PGPPublicKey originalKey; + + final private PGPPublicKeyService keyService; + + private StyledText styledText; + + public PGPPublicKeyViewDialog(Shell parentShell, PGPPublicKey key, PGPPublicKeyService keyService) { + super(parentShell); + this.originalKey = key; + this.keyService = keyService; + DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); //$NON-NLS-1$ + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText(ProvUIMessages.PGPPublicKeyViewDialog_Title); + if (keyService != null) { + computeVerifiedCertifications(newShell); + } + } + + @Override + protected void setShellStyle(int newShellStyle) { + super.setShellStyle(newShellStyle | SWT.RESIZE | SWT.DIALOG_TRIM); + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite composite = (Composite) super.createDialogArea(parent); + GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); + composite.setLayoutData(data); + + styledText = new StyledText(composite, SWT.READ_ONLY | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); + styledText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + // Create a sightly smaller text (mono-space) font. + FontData[] fontData = JFaceResources.getTextFont().getFontData(); + for (FontData fontDataElement : fontData) { + fontDataElement.setHeight(fontDataElement.getHeight() - 1); + } + Font font = new Font(styledText.getDisplay(), fontData); + styledText.setFont(font); + styledText.addDisposeListener(e -> font.dispose()); + + GC gc = new GC(styledText); + gc.setFont(font); + data.widthHint = convertWidthInCharsToPixels(gc.getFontMetrics(), 110); + gc.dispose(); + + update(originalKey, Set.of()); + return composite; + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CLOSE_LABEL, true).setFocus(); + } + + @SuppressWarnings("nls") + protected void update(PGPPublicKey key, Set<PGPPublicKey> verifiedCertifications) { + StyledString content = new StyledString(); + String fingerprint = PGPPublicKeyService.toHexFingerprint(key); + + PublicKeyPacket publicKeyPacket = key.getPublicKeyPacket(); + publicKeyPacket.getAlgorithm(); + content.append(" "); + content.append(publicKeyPacket instanceof PublicSubkeyPacket ? "sub" : "pub", StyledString.QUALIFIER_STYLER); + content.append(" "); + + int algorithm = publicKeyPacket.getAlgorithm(); + switch (algorithm) { + case PublicKeyAlgorithmTags.RSA_GENERAL: + case PublicKeyAlgorithmTags.RSA_ENCRYPT: + case PublicKeyAlgorithmTags.RSA_SIGN: { + content.append("rsa"); + break; + } + case PublicKeyAlgorithmTags.DSA: { + content.append("dsa"); + break; + } + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: { + content.append("elgamal"); + break; + } + default: { + content.append("["); + content.append(Integer.toString(algorithm)); + content.append("]"); + break; + } + + } + int bitStrength = key.getBitStrength(); + content.append(Integer.toString(bitStrength)); + content.append("/"); + content.append(fingerprint); + + content.append(" "); + content.append(DATE_FORMAT.format(key.getCreationTime())); + + content.append(" "); + content.append("\n"); + + List<String> users = new ArrayList<>(); + key.getUserIDs().forEachRemaining(users::add); + if (!users.isEmpty()) { + for (String user : users) { + content.append(" "); + content.append("uid", StyledString.QUALIFIER_STYLER); + content.append(" "); + content.append(user, StyledString.COUNTER_STYLER); + content.append("\n"); + } + } + + Long subKeyOf = null; + + for (Iterator<PGPSignature> signatures = key.getSignatures(); signatures.hasNext();) { + PGPSignature signature = signatures.next(); + long keyID = signature.getKeyID(); + + if (signature.getSignatureType() == PGPSignature.SUBKEY_BINDING) { + subKeyOf = keyID; + } + + content.append(" "); + content.append("sig", StyledString.QUALIFIER_STYLER); + content.append(" "); + content.append(PGPPublicKeyService.toHex(keyID)); + content.append(" "); + Date creationTime = signature.getCreationTime(); + String formattedCreationTime = DATE_FORMAT.format(creationTime); + content.append(formattedCreationTime); + long signatureExpirationTime = signature.getHashedSubPackets().getSignatureExpirationTime(); + content.append(" "); + content.append(signatureExpirationTime == 0 ? formattedCreationTime.replaceAll(".", "_") + : DATE_FORMAT.format(new Date(creationTime.getTime() + 1000 * signatureExpirationTime))); + + content.append(" "); + Optional<PGPPublicKey> resolvedKey = verifiedCertifications.stream().filter(k -> k.getKeyID() == keyID) + .findFirst(); + + long keyExpirationTime = signature.getHashedSubPackets().getKeyExpirationTime(); + content.append(keyExpirationTime == 0 || resolvedKey == null || !resolvedKey.isPresent() + ? formattedCreationTime.replaceAll(".", "_") + : DATE_FORMAT.format( + new Date(resolvedKey.get().getCreationTime().getTime() + 1000 * keyExpirationTime))); + + if (resolvedKey != null && resolvedKey.isPresent()) { + content.append(" "); + content.append(getLabel(resolvedKey.get()), StyledString.COUNTER_STYLER); + } + + content.append("\n"); + } + + styledText.setText(content.getString()); + styledText.setStyleRanges(content.getStyleRanges()); + + List<String> title = new ArrayList<>(); + if (subKeyOf != null) { + long keyID = subKeyOf; + verifiedCertifications.stream().filter(k -> k.getKeyID() == keyID).findFirst() + .ifPresentOrElse(k -> title.add(getLabel(k)), () -> title.add(PGPPublicKeyService.toHex(keyID))); + } + title.add((subKeyOf == null ? "" : "sub ") + (users.isEmpty() ? fingerprint : users.get(0))); + + setTitle(String.join("\n", title)); + } + + private String getLabel(PGPPublicKey key) { + Iterator<String> userIDs = key.getUserIDs(); + if (userIDs.hasNext()) { + return userIDs.next(); + + } + return PGPPublicKeyService.toHexFingerprint(key); + } + + private void computeVerifiedCertifications(Shell shell) { + Display display = shell.getDisplay(); + new Job(PGPPublicKeyViewDialog.class.getName()) { + { + setSystem(true); + setPriority(Job.SHORT); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + synchronized (keyService) { + PGPPublicKey enhancedKey = keyService.addKey(originalKey); + Set<PGPPublicKey> verifiedCertifications = keyService.getVerifiedCertifications(originalKey); + display.asyncExec(() -> { + if (!shell.isDisposed()) { + update(enhancedKey, verifiedCertifications); + } + }); + } + return Status.OK_STATUS; + } + }.schedule(); + } +} diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java index 71479559e..47901c652 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java @@ -204,7 +204,7 @@ public abstract class ProvisioningOperationWizard extends Wizard { if (!operation.hasResolved()) { operation.resolveModal(monitor); } - if (operation.hasResolved()) { + if (operation.getProfileChangeRequest() != null) { this.localJRECheckPlan = ProvUI.toCompabilityWithCurrentJREProvisioningPlan(operation, null); if (!compatibleWithCurrentEE()) { couldNotResolveStatus = localJRECheckPlan.getStatus(); @@ -215,6 +215,10 @@ public abstract class ProvisioningOperationWizard extends Wizard { return false; } } + if (localJRECheckPlan == null) { + return true; + } + IStatus currentEEPlanStatus = localJRECheckPlan.getStatus(); if (currentEEPlanStatus.getSeverity() != IStatus.ERROR) { return true; @@ -337,7 +341,8 @@ public abstract class ProvisioningOperationWizard extends Wizard { try { runnableContext.run(true, true, monitor -> { operation.resolveModal(monitor); - if (getPolicy().getCheckAgainstCurrentExecutionEnvironment()) { + if (operation.getProfileChangeRequest() != null + && getPolicy().getCheckAgainstCurrentExecutionEnvironment()) { this.localJRECheckPlan = ProvUI.toCompabilityWithCurrentJREProvisioningPlan(operation, monitor); if (!compatibleWithCurrentEE()) { couldNotResolveStatus = localJRECheckPlan.getStatus(); diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/TrustCertificateDialog.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/TrustCertificateDialog.java index 4c4fc386e..2d6cc8237 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/TrustCertificateDialog.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/TrustCertificateDialog.java @@ -17,111 +17,181 @@ package org.eclipse.equinox.internal.p2.ui.dialogs; import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter; import java.io.*; -import java.security.cert.CertificateEncodingException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Iterator; +import java.security.cert.*; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.List; import java.util.function.Function; +import java.util.stream.Collectors; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.openpgp.PGPPublicKey; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.equinox.internal.p2.ui.ProvUIActivator; -import org.eclipse.equinox.internal.p2.ui.ProvUIMessages; +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.internal.p2.ui.*; import org.eclipse.equinox.internal.p2.ui.viewers.CertificateLabelProvider; import org.eclipse.equinox.internal.provisional.security.ui.X500PrincipalHelper; -import org.eclipse.equinox.internal.provisional.security.ui.X509CertificateViewDialog; +import org.eclipse.equinox.p2.metadata.IArtifactKey; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; +import org.eclipse.jface.dialogs.*; import org.eclipse.jface.dialogs.Dialog; -import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.layout.TableColumnLayout; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.Policy; import org.eclipse.jface.viewers.*; +import org.eclipse.jface.widgets.LabelFactory; +import org.eclipse.jface.widgets.WidgetFactory; +import org.eclipse.jface.window.Window; +import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.dnd.*; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.*; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.SelectionDialog; /** * A dialog that displays a certificate chain and asks the user if they trust - * the certificate providers. + * the certificate providers. It also supports prompting about unsigned content. */ public class TrustCertificateDialog extends SelectionDialog { + private static final String EXPORT_FILTER_PATH = "exportFilterPath"; //$NON-NLS-1$ - private Object inputElement; - private IStructuredContentProvider contentProvider; + private CheckboxTableViewer certifcateViewer; - private static final int SIZING_SELECTION_WIDGET_HEIGHT = 250; - private static final int SIZING_SELECTION_WIDGET_WIDTH = 300; + private TreeViewer certificateChainViewer; - CheckboxTableViewer listViewer; + private CheckboxTableViewer artifactViewer; - private TreeViewer certificateChainViewer; - protected TreeNode parentElement; - protected Object selectedCertificate; private Button detailsButton; + private Button exportButton; + + private boolean rememberSelectedSigners = true; + + private boolean trustAlways; + + private final Map<TreeNode, List<IArtifactKey>> artifactMap = new LinkedHashMap<>(); + public TrustCertificateDialog(Shell parentShell, Object input) { super(parentShell); - inputElement = input; - this.contentProvider = new TreeNodeContentProvider(); + + setShellStyle(SWT.DIALOG_TRIM | SWT.MODELESS | SWT.RESIZE | SWT.MAX | getDefaultOrientation()); + + if (input instanceof TreeNode[]) { + for (TreeNode node : (TreeNode[]) input) { + IArtifactKey[] associatedArtifacts = null; + if (node instanceof IAdaptable) { + associatedArtifacts = ((IAdaptable) node).getAdapter(IArtifactKey[].class); + } + artifactMap.put(node, associatedArtifacts == null ? List.of() : Arrays.asList(associatedArtifacts)); + } + } + setTitle(ProvUIMessages.TrustCertificateDialog_Title); - setMessage(ProvUIMessages.TrustCertificateDialog_Message); - setShellStyle(SWT.DIALOG_TRIM | SWT.MODELESS | SWT.RESIZE | getDefaultOrientation()); - } - private static class PGPOrX509ColumnLabelProvider extends ColumnLabelProvider { - private Function<PGPPublicKey, String> pgpMap; - private Function<X509Certificate, String> x509map; + boolean unsignedContent = artifactMap.keySet().stream().map(TreeNode::getValue).anyMatch(Objects::isNull); + boolean pgpContent = containsInstance(input, PGPPublicKey.class); + boolean certifcateContent = containsInstance(input, Certificate.class); - public PGPOrX509ColumnLabelProvider(Function<PGPPublicKey, String> pgpMap, - Function<X509Certificate, String> x509map) { - this.pgpMap = pgpMap; - this.x509map = x509map; + List<String> messages = new ArrayList<>(); + if (certifcateContent || pgpContent) { + messages.add(ProvUIMessages.TrustCertificateDialog_Message); + } + if (unsignedContent) { + messages.add(ProvUIMessages.TrustCertificateDialog_MessageUnsigned); + } + if (certifcateContent || pgpContent) { + messages.add(ProvUIMessages.TrustCertificateDialog_MessageNameWarning); + } + if (pgpContent) { + messages.add(ProvUIMessages.TrustCertificateDialog_MessagePGP); } + setMessage(String.join(" ", messages)); //$NON-NLS-1$ - @Override - public String getText(Object element) { - if (element instanceof TreeNode) { - element = ((TreeNode) element).getValue(); - } - if (element instanceof PGPPublicKey) { - return pgpMap.apply((PGPPublicKey) element); - } - if (element instanceof X509Certificate) { - return x509map.apply((X509Certificate) element); - } - return super.getText(element); + if (PlatformUI.isWorkbenchRunning()) { + PlatformUI.getWorkbench().getHelpSystem().setHelp(parentShell, IProvHelpContextIds.TRUST_DIALOG); + } + } + + public boolean isRememberSelectedSigners() { + return rememberSelectedSigners; + } + + public boolean isTrustAlways() { + return trustAlways; + } + + @Override + protected Label createMessageArea(Composite composite) { + // Ensure that the message supports wrapping for a long text message. + GridData data = new GridData(SWT.FILL, SWT.FILL, true, false); + data.widthHint = convertWidthInCharsToPixels(120); + LabelFactory factory = WidgetFactory.label(SWT.WRAP).font(composite.getFont()).layoutData(data); + if (getMessage() != null) { + factory.text(getMessage()); } + return factory.create(composite); } @Override protected Control createDialogArea(Composite parent) { - Composite composite = createUpperDialogArea(parent); - certificateChainViewer = new TreeViewer(composite, SWT.BORDER); - GridLayout layout = new GridLayout(); - certificateChainViewer.getTree().setLayout(layout); - GridData data = new GridData(GridData.FILL_BOTH); - data.grabExcessHorizontalSpace = true; - certificateChainViewer.getTree().setLayoutData(data); - certificateChainViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS); - certificateChainViewer.setContentProvider(new TreeNodeContentProvider()); - certificateChainViewer.setLabelProvider(new CertificateLabelProvider()); - certificateChainViewer.addSelectionChangedListener(getChainSelectionListener()); - if (inputElement instanceof Object[]) { - ISelection selection = null; - Object[] nodes = (Object[]) inputElement; - if (nodes.length > 0) { - selection = new StructuredSelection(nodes[0]); - certificateChainViewer.setInput(new TreeNode[] { (TreeNode) nodes[0] }); - selectedCertificate = nodes[0]; - } - listViewer.setSelection(selection); + Composite mainComposite = (Composite) super.createDialogArea(parent); + Dialog.applyDialogFont(mainComposite); + initializeDialogUnits(mainComposite); + + createMessageArea(mainComposite); + + SashForm sashForm = new SashForm(mainComposite, SWT.VERTICAL); + sashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + createCertificateViewerArea(createSashFormArea(sashForm)); + + // Create this area only if we have keys or certificates. + boolean containsCertificates = containsInstance(artifactMap.keySet(), PGPPublicKey.class) + || containsInstance(artifactMap.keySet(), Certificate.class); + if (containsCertificates) { + createCertficateChainViewerArea(createSashFormArea(sashForm)); } - listViewer.addDoubleClickListener(getDoubleClickListener()); - listViewer.addSelectionChangedListener(getParentSelectionListener()); - createButtons(composite); - detailsButton.setEnabled(selectedCertificate instanceof X509Certificate); - return composite; + + // Sort the set of all artifacts and create the lower area only if there are + // artifacts. + Comparator<Object> comparator = Policy.getComparator(); + Set<IArtifactKey> artifacts = artifactMap.values().stream().flatMap(Collection::stream) + .collect(Collectors.toCollection(() -> new TreeSet<>((a1, a2) -> { + int result = comparator.compare(a1.getId(), a2.getId()); + if (result == 0) { + result = a1.getVersion().compareTo(a2.getVersion()); + if (result == 0) { + result = a1.getClassifier().compareTo(a2.getClassifier()); + } + } + return result; + }))); + if (!artifacts.isEmpty()) { + crreateArtifactViewerArea(createSashFormArea(sashForm), artifacts); + } + + // Set weights based on the children's preferred size. + Control[] children = sashForm.getChildren(); + int[] weights = new int[children.length]; + for (int i = 0; i < children.length; ++i) { + weights[i] = children[i].computeSize(SWT.DEFAULT, SWT.DEFAULT, false).y; + } + sashForm.setWeights(weights); + + if (!getInitialElementSelections().isEmpty()) { + checkInitialSelections(); + } + + if (!artifactMap.isEmpty()) { + certifcateViewer.setSelection(new StructuredSelection(artifactMap.keySet().iterator().next())); + } + + return mainComposite; } @Override @@ -129,26 +199,21 @@ public class TrustCertificateDialog extends SelectionDialog { createButton(parent, IDialogConstants.OK_ID, ProvUIMessages.TrustCertificateDialog_AcceptSelectedButtonLabel, true); createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); - super.getOkButton().setEnabled(false); + updateOkButton(); } private void createButtons(Composite composite) { Composite buttonComposite = new Composite(composite, SWT.NONE); buttonComposite.setLayout(new RowLayout()); - // Details button to view certificate chain + detailsButton = new Button(buttonComposite, SWT.NONE); detailsButton.setText(ProvUIMessages.TrustCertificateDialog_Details); detailsButton.addSelectionListener(new SelectionListener() { @Override public void widgetDefaultSelected(SelectionEvent e) { - Object o = selectedCertificate; - if (selectedCertificate instanceof TreeNode) { - o = ((TreeNode) selectedCertificate).getValue(); - } - if (o instanceof X509Certificate) { - X509Certificate cert = (X509Certificate) o; - X509CertificateViewDialog certificateDialog = new X509CertificateViewDialog(getShell(), cert); - certificateDialog.open(); + X509Certificate cert = getInstance(certificateChainViewer.getSelection(), X509Certificate.class); + if (cert != null) { + CertificateLabelProvider.openDialog(getShell(), cert); } } @@ -158,46 +223,48 @@ public class TrustCertificateDialog extends SelectionDialog { } }); - Button exportButton = new Button(buttonComposite, SWT.NONE); + exportButton = new Button(buttonComposite, SWT.NONE); exportButton.setText(ProvUIMessages.TrustCertificateDialog_Export); exportButton.addSelectionListener(new SelectionListener() { @Override public void widgetDefaultSelected(SelectionEvent e) { - Object o = selectedCertificate; - if (selectedCertificate instanceof TreeNode) { - o = ((TreeNode) selectedCertificate).getValue(); - } - FileDialog destination = new FileDialog(detailsButton.getShell(), SWT.SAVE); - destination.setText(ProvUIMessages.TrustCertificateDialog_Export); - if (o instanceof X509Certificate) { - X509Certificate cert = (X509Certificate) o; - destination.setFilterExtensions(new String[] { "*.der" }); //$NON-NLS-1$ - destination.setFileName(cert.getSerialNumber().toString() + ".der"); //$NON-NLS-1$ - String path = destination.open(); - if (path == null) { - return; - } - File destinationFile = new File(path); - try (FileOutputStream output = new FileOutputStream(destinationFile)) { - output.write(cert.getEncoded()); - } catch (IOException | CertificateEncodingException ex) { - ProvUIActivator.getDefault().getLog() - .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, ex.getMessage(), ex)); - } - } else if (o instanceof PGPPublicKey) { - PGPPublicKey key = (PGPPublicKey) o; - destination.setFilterExtensions(new String[] { "*.asc" }); //$NON-NLS-1$ - destination.setFileName(Long.toHexString(key.getKeyID()) + ".asc"); //$NON-NLS-1$ - String path = destination.open(); - if (path == null) { - return; - } - File destinationFile = new File(path); - try (OutputStream output = new ArmoredOutputStream(new FileOutputStream(destinationFile))) { - output.write(key.getEncoded()); - } catch (IOException ex) { - ProvUIActivator.getDefault().getLog() - .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + ISelection selection = certificateChainViewer.getSelection(); + X509Certificate cert = getInstance(selection, X509Certificate.class); + PGPPublicKey key = getInstance(selection, PGPPublicKey.class); + if (cert != null || key != null) { + FileDialog destination = new FileDialog(exportButton.getShell(), SWT.SAVE); + destination.setFilterPath(getFilterPath(EXPORT_FILTER_PATH)); + destination.setText(ProvUIMessages.TrustCertificateDialog_Export); + if (cert != null) { + destination.setFilterExtensions(new String[] { "*.der" }); //$NON-NLS-1$ + destination.setFileName(cert.getSerialNumber().toString() + ".der"); //$NON-NLS-1$ + String path = destination.open(); + setFilterPath(EXPORT_FILTER_PATH, destination.getFilterPath()); + if (path == null) { + return; + } + File destinationFile = new File(path); + try (FileOutputStream output = new FileOutputStream(destinationFile)) { + output.write(cert.getEncoded()); + } catch (IOException | CertificateEncodingException ex) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + } + } else { + destination.setFilterExtensions(new String[] { "*.asc" }); //$NON-NLS-1$ + destination.setFileName(userFriendlyFingerPrint(key) + ".asc"); //$NON-NLS-1$ + String path = destination.open(); + setFilterPath(EXPORT_FILTER_PATH, destination.getFilterPath()); + if (path == null) { + return; + } + File destinationFile = new File(path); + try (OutputStream output = new ArmoredOutputStream(new FileOutputStream(destinationFile))) { + key.encode(output); + } catch (IOException ex) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + } } } } @@ -206,56 +273,277 @@ public class TrustCertificateDialog extends SelectionDialog { public void widgetSelected(SelectionEvent e) { widgetDefaultSelected(e); } - }); } - private Composite createUpperDialogArea(Composite parent) { - Composite composite = (Composite) super.createDialogArea(parent); - initializeDialogUnits(composite); - createMessageArea(composite); + private Composite createSashFormArea(SashForm sashForm) { + Composite composite = new Composite(sashForm, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING); + layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); + composite.setLayout(layout); + return composite; + } + + private void createCertificateViewerArea(Composite composite) { + + TableColumnLayout tableColumnLayout = new TableColumnLayout(true); + Composite tableComposite = WidgetFactory.composite(SWT.NONE) + .layoutData(new GridData(SWT.FILL, SWT.FILL, true, true)).layout(tableColumnLayout).create(composite); + Table table = WidgetFactory + .table(SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION | SWT.CHECK) + .headerVisible(true).linesVisible(true).font(composite.getFont()).create(tableComposite); + certifcateViewer = new CheckboxTableViewer(table); + certifcateViewer.setContentProvider(new TreeNodeContentProvider()); - listViewer = CheckboxTableViewer.newCheckList(composite, SWT.BORDER); GridData data = new GridData(GridData.FILL_BOTH); - data.heightHint = SIZING_SELECTION_WIDGET_HEIGHT; - data.widthHint = SIZING_SELECTION_WIDGET_WIDTH; - listViewer.getTable().setLayoutData(data); - - listViewer.setContentProvider(contentProvider); - TableViewerColumn typeColumn = new TableViewerColumn(listViewer, SWT.NONE); - typeColumn.getColumn().setWidth(80); - typeColumn.getColumn().setText(ProvUIMessages.TrustCertificateDialog_ObjectType); - typeColumn.setLabelProvider(new PGPOrX509ColumnLabelProvider(key -> "PGP", cert -> "x509")); //$NON-NLS-1$ //$NON-NLS-2$ - TableViewerColumn idColumn = new TableViewerColumn(listViewer, SWT.NONE); - idColumn.getColumn().setWidth(200); - idColumn.getColumn().setText(ProvUIMessages.TrustCertificateDialog_Id); - idColumn.setLabelProvider(new PGPOrX509ColumnLabelProvider(key -> Long.toHexString(key.getKeyID()), - cert -> cert.getSerialNumber().toString())); - TableViewerColumn signerColumn = new TableViewerColumn(listViewer, SWT.NONE); - signerColumn.getColumn().setText(ProvUIMessages.TrustCertificateDialog_Name); - signerColumn.getColumn().setWidth(400); - signerColumn.setLabelProvider(new PGPOrX509ColumnLabelProvider(pgp -> { - java.util.List<String> users = new ArrayList<>(); - pgp.getUserIDs().forEachRemaining(users::add); - return String.join(",", users); //$NON-NLS-1$ - }, x509 -> { - X500PrincipalHelper principalHelper = new X500PrincipalHelper(x509.getSubjectX500Principal()); - return principalHelper.getCN() + "; " + principalHelper.getOU() + "; " //$NON-NLS-1$ //$NON-NLS-2$ - + principalHelper.getO(); - })); - listViewer.getTable().setHeaderVisible(true); + data.heightHint = convertHeightInCharsToPixels(Math.min(artifactMap.keySet().size() + 1, 6)) * 3 / 2; + data.widthHint = convertWidthInCharsToPixels(120); + tableComposite.setLayoutData(data); + + // This column is packed later. + TableViewerColumn typeColumn = createColumn(certifcateViewer, ProvUIMessages.TrustCertificateDialog_ObjectType, + new PGPOrX509ColumnLabelProvider(key -> "PGP", cert -> "x509", //$NON-NLS-1$ //$NON-NLS-2$ + ProvUIMessages.TrustCertificateDialog_Unsigned), + tableColumnLayout, 1); + + createColumn(certifcateViewer, ProvUIMessages.TrustCertificateDialog_Id, + new PGPOrX509ColumnLabelProvider(TrustCertificateDialog::userFriendlyFingerPrint, + cert -> cert.getSerialNumber().toString(), ProvUIMessages.TrustCertificateDialog_NotApplicable), + tableColumnLayout, 10); + + createColumn(certifcateViewer, ProvUIMessages.TrustCertificateDialog_Name, + new PGPOrX509ColumnLabelProvider(pgp -> { + java.util.List<String> users = new ArrayList<>(); + pgp.getUserIDs().forEachRemaining(users::add); + return String.join(", ", users); //$NON-NLS-1$ + }, x509 -> { + X500PrincipalHelper principalHelper = new X500PrincipalHelper(x509.getSubjectX500Principal()); + return principalHelper.getCN() + "; " + principalHelper.getOU() + "; " //$NON-NLS-1$ //$NON-NLS-2$ + + principalHelper.getO(); + }, ProvUIMessages.TrustCertificateDialog_Unknown), tableColumnLayout, 15); + + createColumn(certifcateViewer, ProvUIMessages.TrustCertificateDialog_dates, + new PGPOrX509ColumnLabelProvider(pgp -> { + if (pgp.getCreationTime().after(Date.from(Instant.now()))) { + return NLS.bind(ProvUIMessages.TrustCertificateDialog_NotYetValidStartDate, + pgp.getCreationTime()); + } + long validSeconds = pgp.getValidSeconds(); + if (validSeconds == 0) { + return ProvUIMessages.TrustCertificateDialog_valid; + } + Instant expires = pgp.getCreationTime().toInstant().plus(validSeconds, ChronoUnit.SECONDS); + return expires.isBefore(Instant.now()) + ? NLS.bind(ProvUIMessages.TrustCertificateDialog_expiredSince, expires) + : NLS.bind(ProvUIMessages.TrustCertificateDialog_validExpires, expires); + }, x509 -> { + try { + x509.checkValidity(); + return ProvUIMessages.TrustCertificateDialog_valid; + } catch (CertificateExpiredException expired) { + return ProvUIMessages.TrustCertificateDialog_expired; + } catch (CertificateNotYetValidException notYetValid) { + return ProvUIMessages.TrustCertificateDialog_notYetValid; + } + }, ProvUIMessages.TrustCertificateDialog_NotApplicable), tableColumnLayout, 10); + + createMenu(certifcateViewer); addSelectionButtons(composite); - listViewer.setInput(inputElement); + certifcateViewer.addDoubleClickListener(e -> { + StructuredSelection selection = (StructuredSelection) e.getSelection(); + X509Certificate cert = getInstance(selection, X509Certificate.class); + if (cert != null) { + // create and open dialog for certificate chain + CertificateLabelProvider.openDialog(getShell(), cert); + } + }); - if (!getInitialElementSelections().isEmpty()) { - checkInitialSelections(); + certifcateViewer.addSelectionChangedListener(e -> { + if (certificateChainViewer != null) { + TreeNode treeNode = getInstance(e.getSelection(), TreeNode.class); + if (treeNode != null) { + certificateChainViewer.setInput(new TreeNode[] { treeNode }); + certificateChainViewer.setSelection(new StructuredSelection(treeNode)); + } else { + certificateChainViewer.setInput(new TreeNode[] {}); + } + } + + updateOkButton(); + }); + + certifcateViewer.setInput(artifactMap.keySet().toArray(TreeNode[]::new)); + + typeColumn.getColumn().pack(); + } + + private void createCertficateChainViewerArea(Composite composite) { + certificateChainViewer = new TreeViewer(composite, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); + GridData data = new GridData(GridData.FILL_BOTH); + int treeSize = artifactMap.keySet().stream().map(TrustCertificateDialog::computeTreeSize) + .max(Integer::compareTo).orElse(0); + data.heightHint = convertHeightInCharsToPixels(Math.min(treeSize, 5)); + data.widthHint = convertWidthInCharsToPixels(120); + certificateChainViewer.getTree().setLayoutData(data); + + certificateChainViewer.setAutoExpandLevel(3); + certificateChainViewer.setContentProvider(new TreeNodeContentProvider()); + certificateChainViewer.setLabelProvider(new CertificateLabelProvider() { + @Override + public String getText(Object element) { + if (element instanceof TreeNode) { + Object o = ((TreeNode) element).getValue(); + if (o instanceof PGPPublicKey) { + PGPPublicKey key = (PGPPublicKey) o; + String userFriendlyFingerPrint = userFriendlyFingerPrint(key); + java.util.List<String> users = new ArrayList<>(); + key.getUserIDs().forEachRemaining(users::add); + String userIDs = String.join(", ", users); //$NON-NLS-1$ + if (!userIDs.isEmpty()) { + return userFriendlyFingerPrint + " [" + userIDs + "]"; //$NON-NLS-1$//$NON-NLS-2$ + } + return userFriendlyFingerPrint; + } else if (o == null) { + return ProvUIMessages.TrustCertificateDialog_Unsigned; + } + } + + return super.getText(element); + } + }); + + certificateChainViewer.addSelectionChangedListener(event -> { + ISelection selection = event.getSelection(); + boolean containsCertificate = containsInstance(selection, X509Certificate.class); + detailsButton.setEnabled(containsCertificate); + exportButton.setEnabled(containsCertificate || containsInstance(selection, PGPPublicKey.class)); + }); + + createMenu(certificateChainViewer); + + createButtons(composite); + } + + private void crreateArtifactViewerArea(Composite composite, Set<IArtifactKey> artifacts) { + TableColumnLayout tableColumnLayout = new TableColumnLayout(true); + Composite tableComposite = WidgetFactory.composite(SWT.NONE) + .layoutData(new GridData(SWT.FILL, SWT.FILL, true, true)).layout(tableColumnLayout).create(composite); + Table table = WidgetFactory.table(SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION) + .headerVisible(true).linesVisible(true).font(composite.getFont()).create(tableComposite); + artifactViewer = new CheckboxTableViewer(table); + artifactViewer.setContentProvider(ArrayContentProvider.getInstance()); + + GridData data = new GridData(GridData.FILL_BOTH); + data.heightHint = convertHeightInCharsToPixels(Math.min(artifacts.size() + 1, 10)) * 3 / 2; + data.widthHint = convertWidthInCharsToPixels(120); + tableComposite.setLayoutData(data); + + Font font = table.getFont(); + FontData[] fontDatas = font.getFontData(); + for (FontData fontData : fontDatas) { + fontData.setStyle(fontData.getStyle() | SWT.BOLD); } + Font boldFont = new Font(table.getDisplay(), fontDatas); + composite.addDisposeListener(e -> boldFont.dispose()); + + Function<IArtifactKey, Font> fontProvider = e -> { + for (Object object : certifcateViewer.getCheckedElements()) { + List<IArtifactKey> list = artifactMap.get(object); + if (list != null && list.contains(e)) { + return boldFont; + } + } + return null; + }; - Dialog.applyDialogFont(composite); + certifcateViewer.addCheckStateListener(e -> artifactViewer.refresh(true)); + + artifactViewer.addPostSelectionChangedListener(e -> { + if (table.isFocusControl()) { + List<?> selection = e.getStructuredSelection().toList(); + List<TreeNode> newSelection = new ArrayList<>(); + LOOP: for (Map.Entry<TreeNode, List<IArtifactKey>> entry : artifactMap.entrySet()) { + List<IArtifactKey> value = entry.getValue(); + if (value != null) { + for (IArtifactKey key : value) { + if (selection.contains(key)) { + newSelection.add(entry.getKey()); + continue LOOP; + } + } + } + } - return composite; + certifcateViewer.setSelection(new StructuredSelection(newSelection), true); + } + }); + + certifcateViewer.addPostSelectionChangedListener(e -> { + if (!table.isFocusControl()) { + Set<IArtifactKey> associatedArtifacts = new LinkedHashSet<>(); + for (Object object : e.getStructuredSelection()) { + List<IArtifactKey> list = artifactMap.get(object); + if (list != null) { + associatedArtifacts.addAll(list); + } + } + + artifactViewer.setSelection(new StructuredSelection(associatedArtifacts.toArray()), true); + + // Reorder the artifacts so that the selected ones are first. + LinkedHashSet<IArtifactKey> newInput = new LinkedHashSet<>(artifacts); + newInput.retainAll(associatedArtifacts); + newInput.addAll(artifacts); + artifactViewer.setInput(newInput); + + artifactViewer.setSelection(new StructuredSelection(associatedArtifacts.toArray()), true); + } + }); + + TableViewerColumn classifierColumn = createColumn(artifactViewer, + ProvUIMessages.TrustCertificateDialog_Classifier, + new ArtifactLabelProvider(a -> a.getClassifier(), fontProvider), tableColumnLayout, 1); + createColumn(artifactViewer, ProvUIMessages.TrustCertificateDialog_ArtifactId, + new ArtifactLabelProvider(a -> a.getId(), fontProvider), tableColumnLayout, 10); + createColumn(artifactViewer, ProvUIMessages.TrustCertificateDialog_Version, + new ArtifactLabelProvider(a -> a.getVersion().toString(), fontProvider), tableColumnLayout, 10); + + artifactViewer.setInput(artifacts); + + classifierColumn.getColumn().pack(); + } + + private void createMenu(StructuredViewer viewer) { + Control control = viewer.getControl(); + Menu menu = new Menu(control); + control.setMenu(menu); + MenuItem item = new MenuItem(menu, SWT.PUSH); + item.setText(ProvUIMessages.TrustCertificateDialog_CopyFingerprint); + item.addSelectionListener(widgetSelectedAdapter(e -> { + PGPPublicKey key = getInstance(viewer.getSelection(), PGPPublicKey.class); + if (key != null) { + Clipboard clipboard = new Clipboard(getShell().getDisplay()); + clipboard.setContents(new Object[] { userFriendlyFingerPrint(key) }, + new Transfer[] { TextTransfer.getInstance() }); + clipboard.dispose(); + } + })); + viewer.addSelectionChangedListener( + e -> item.setEnabled(containsInstance(e.getSelection(), PGPPublicKey.class))); + } + + private TableViewerColumn createColumn(TableViewer tableViewer, String text, ColumnLabelProvider labelProvider, + TableColumnLayout tableColumnLayout, int columnWeight) { + TableViewerColumn column = new TableViewerColumn(tableViewer, SWT.NONE); + column.getColumn().setText(text); + column.setLabelProvider(labelProvider); + tableColumnLayout.setColumnData(column.getColumn(), new ColumnWeightData(columnWeight)); + return column; } /** @@ -265,7 +553,10 @@ public class TrustCertificateDialog extends SelectionDialog { private void checkInitialSelections() { Iterator<?> itemsToCheck = getInitialElementSelections().iterator(); while (itemsToCheck.hasNext()) { - listViewer.setChecked(itemsToCheck.next(), true); + certifcateViewer.setChecked(itemsToCheck.next(), true); + if (artifactViewer != null) { + artifactViewer.refresh(true); + } } } @@ -275,93 +566,239 @@ public class TrustCertificateDialog extends SelectionDialog { * @param composite org.eclipse.swt.widgets.Composite */ private void addSelectionButtons(Composite composite) { - Composite buttonComposite = new Composite(composite, SWT.NONE); - GridLayout layout = new GridLayout(); - layout.numColumns = 0; - layout.marginWidth = 0; - layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); - buttonComposite.setLayout(layout); - buttonComposite.setLayoutData(new GridData(SWT.END, SWT.TOP, true, false)); + int horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); + + Composite buttonArea = new Composite(composite, SWT.NONE); + GridLayout buttonAreaLayout = new GridLayout(); + buttonAreaLayout.numColumns = 2; + buttonAreaLayout.marginWidth = 0; + buttonAreaLayout.horizontalSpacing = horizontalSpacing; + buttonArea.setLayout(buttonAreaLayout); + buttonArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + + Composite leftButtonArea = new Composite(buttonArea, SWT.NONE); + GridLayout leftButtonAreaLayout = new GridLayout(); + leftButtonAreaLayout.numColumns = 0; + leftButtonAreaLayout.marginWidth = 0; + leftButtonAreaLayout.horizontalSpacing = horizontalSpacing; + leftButtonArea.setLayout(leftButtonAreaLayout); + leftButtonArea.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false)); + + if (containsInstance(artifactMap.keySet(), PGPPublicKey.class) + || containsInstance(artifactMap.keySet(), Certificate.class)) { + Button rememberSelectionButton = createCheckButton(leftButtonArea, + ProvUIMessages.TrustCertificateDialog_RememberSigners); + rememberSelectionButton.setSelection(rememberSelectedSigners); + rememberSelectionButton.addSelectionListener(widgetSelectedAdapter(e -> { + rememberSelectedSigners = rememberSelectionButton.getSelection(); + })); + } - Button selectButton = createButton(buttonComposite, IDialogConstants.SELECT_ALL_ID, - ProvUIMessages.TrustCertificateDialog_SelectAll, false); + Button trustAlwaysButton = createCheckButton(leftButtonArea, ProvUIMessages.TrustCertificateDialog_AlwaysTrust); + trustAlwaysButton.addSelectionListener(widgetSelectedAdapter(e -> { + if (trustAlwaysButton.getSelection()) { + // Prompt the user to ensure they really understand what they've chosen, the + // risk, and where the preference is stored if they wish to change it in the + // future. Also ensure that the default button is no so that they must + // explicitly click the yes button, not just hit enter. + MessageDialog messageDialog = new MessageDialog(getShell(), + ProvUIMessages.TrustCertificateDialog_AlwaysTrustConfirmationTitle, null, + ProvUIMessages.TrustCertificateDialog_AlwaysTrustConfirmationMessage, MessageDialog.QUESTION, + new String[] { ProvUIMessages.TrustCertificateDialog_AlwaysTrustYes, + ProvUIMessages.TrustCertificateDialog_AlwaysTrustNo }, + 1) { + @Override + public Image getImage() { + return getWarningImage(); + } + }; + int result = messageDialog.open(); + if (result != Window.OK) { + // Restore the checkbox state. + trustAlwaysButton.setSelection(false); + } else { + certifcateViewer.setAllChecked(true); + if (artifactViewer != null) { + artifactViewer.refresh(true); + } + updateOkButton(); + } + } + trustAlways = trustAlwaysButton.getSelection(); + })); - SelectionListener listener = widgetSelectedAdapter(e -> { - listViewer.setAllChecked(true); - getOkButton().setEnabled(true); - }); - selectButton.addSelectionListener(listener); + Composite rightButtonArea = new Composite(buttonArea, SWT.NONE); + GridLayout rightButtonAreaLayout = new GridLayout(); + rightButtonAreaLayout.numColumns = 0; + rightButtonAreaLayout.marginWidth = 0; + rightButtonAreaLayout.horizontalSpacing = horizontalSpacing; + rightButtonArea.setLayout(rightButtonAreaLayout); + rightButtonArea.setLayoutData(new GridData(SWT.END, SWT.TOP, true, false)); - Button deselectButton = createButton(buttonComposite, IDialogConstants.DESELECT_ALL_ID, - ProvUIMessages.TrustCertificateDialog_DeselectAll, false); + Button selectButton = createButton(rightButtonArea, IDialogConstants.SELECT_ALL_ID, + ProvUIMessages.TrustCertificateDialog_SelectAll, false); + selectButton.addSelectionListener(widgetSelectedAdapter(e -> { + certifcateViewer.setAllChecked(true); + if (artifactViewer != null) { + artifactViewer.refresh(true); + } + updateOkButton(); + })); - listener = widgetSelectedAdapter(e -> { - listViewer.setAllChecked(false); - getOkButton().setEnabled(false); - }); - deselectButton.addSelectionListener(listener); + Button deselectButton = createButton(rightButtonArea, IDialogConstants.DESELECT_ALL_ID, + ProvUIMessages.TrustCertificateDialog_DeselectAll, false); + deselectButton.addSelectionListener(widgetSelectedAdapter(e -> { + certifcateViewer.setAllChecked(false); + if (artifactViewer != null) { + artifactViewer.refresh(true); + } + updateOkButton(); + })); } - private ISelectionChangedListener getChainSelectionListener() { - return event -> { - ISelection selection = event.getSelection(); - if (selection instanceof StructuredSelection) { - selectedCertificate = ((StructuredSelection) selection).getFirstElement(); - detailsButton.setEnabled(selectedCertificate instanceof X509Certificate); - } - }; + protected Button createCheckButton(Composite parent, String label) { + ((GridLayout) parent.getLayout()).numColumns++; + Button button = WidgetFactory.button(SWT.CHECK).text(label).font(JFaceResources.getDialogFont()).create(parent); + setButtonLayoutData(button); + return button; } - public TreeViewer getCertificateChainViewer() { - return certificateChainViewer; + private String getFilterPath(String key) { + IDialogSettings dialogSettings = DialogSettings + .getOrCreateSection(ProvUIActivator.getDefault().getDialogSettings(), getClass().getName()); + String filterPath = dialogSettings.get(key); + if (filterPath == null) { + filterPath = System.getProperty("user.home"); //$NON-NLS-1$ + } + return filterPath; } - private IDoubleClickListener getDoubleClickListener() { - return event -> { - StructuredSelection selection = (StructuredSelection) event.getSelection(); - Object selectedElement = selection.getFirstElement(); - if (selectedElement instanceof TreeNode) { - TreeNode treeNode = (TreeNode) selectedElement; - // create and open dialog for certificate chain - X509CertificateViewDialog certificateViewDialog = new X509CertificateViewDialog(getShell(), - (X509Certificate) treeNode.getValue()); - certificateViewDialog.open(); - } - }; + private void setFilterPath(String key, String filterPath) { + if (filterPath != null) { + IDialogSettings dialogSettings = DialogSettings + .getOrCreateSection(ProvUIActivator.getDefault().getDialogSettings(), getClass().getName()); + dialogSettings.put(key, filterPath); + } } - private ISelectionChangedListener getParentSelectionListener() { - return event -> { - ISelection selection = event.getSelection(); - if (selection instanceof StructuredSelection) { - TreeNode firstElement = (TreeNode) ((StructuredSelection) selection).getFirstElement(); - getCertificateChainViewer().setInput(new TreeNode[] { firstElement }); - getOkButton().setEnabled(listViewer.getChecked(firstElement)); - getCertificateChainViewer().refresh(); + private void updateOkButton() { + Button okButton = getOkButton(); + if (okButton != null) { + certifcateViewer.getCheckedElements(); + Object[] checkedElements = certifcateViewer.getCheckedElements(); + Set<IArtifactKey> artifacts = artifactMap.values().stream().flatMap(Collection::stream) + .collect(Collectors.toSet()); + if (artifacts.isEmpty()) { + okButton.setEnabled(checkedElements.length > 0); + } else { + for (Object checkElement : checkedElements) { + artifacts.removeAll(artifactMap.get(checkElement)); + } + okButton.setEnabled(artifacts.isEmpty()); } - }; + } } - /** - * The <code>ListSelectionDialog</code> implementation of this - * <code>Dialog</code> method builds a list of the selected elements for later - * retrieval by the client and closes this dialog. - */ @Override protected void okPressed() { - // Get the input children. - Object[] children = contentProvider.getElements(inputElement); + setResult(Arrays.asList(certifcateViewer.getCheckedElements())); + super.okPressed(); + } + + private static class PGPOrX509ColumnLabelProvider extends ColumnLabelProvider { + private Function<PGPPublicKey, String> pgpMap; + private Function<X509Certificate, String> x509map; + private String unsignedValue; + + public PGPOrX509ColumnLabelProvider(Function<PGPPublicKey, String> pgpMap, + Function<X509Certificate, String> x509map, String unsignedValue) { + this.pgpMap = pgpMap; + this.x509map = x509map; + this.unsignedValue = unsignedValue; + } + + @Override + public String getText(Object element) { + if (element instanceof TreeNode) { + element = ((TreeNode) element).getValue(); + } + if (element instanceof PGPPublicKey) { + return pgpMap.apply((PGPPublicKey) element); + } + if (element instanceof X509Certificate) { + return x509map.apply((X509Certificate) element); + } - // Build a list of selected children. + if (element == null) { + return unsignedValue; + } + return super.getText(element); + } + } + + private static class ArtifactLabelProvider extends ColumnLabelProvider { + private Function<IArtifactKey, String> labelProvider; + private Function<IArtifactKey, Font> fontProvider; + + public ArtifactLabelProvider(Function<IArtifactKey, String> labelProvider, + Function<IArtifactKey, Font> fontProvider) { + this.labelProvider = labelProvider; + this.fontProvider = fontProvider; + } + + @Override + public String getText(Object element) { + return labelProvider.apply((IArtifactKey) element); + } + + @Override + public Font getFont(Object element) { + return fontProvider.apply((IArtifactKey) element); + } + } + + private static int computeTreeSize(TreeNode node) { + int count = 1; + TreeNode[] children = node.getChildren(); if (children != null) { - ArrayList<Object> list = new ArrayList<>(); - for (Object element : children) { - if (listViewer.getChecked(element)) { - list.add(element); + for (TreeNode child : children) { + count += computeTreeSize(child); + } + } + return count; + } + + private static <T> T getInstance(Object element, Class<T> type) { + if (type.isInstance(element)) { + return type.cast(element); + } else if (element instanceof Iterable) { + for (Object object : ((Iterable<?>) element)) { + T instance = getInstance(object, type); + if (instance != null) { + return instance; + } + } + } else if (element instanceof TreeNode) { + return getInstance(((TreeNode) element).getValue(), type); + } else if (element instanceof TreeNode[]) { + for (TreeNode child : (TreeNode[]) element) { + T instance = getInstance(child, type); + if (instance != null) { + return instance; } } - setResult(list); } - super.okPressed(); + return null; + } + + private static boolean containsInstance(Object element, Class<?> type) { + return getInstance(element, type) != null; + } + + private static String userFriendlyFingerPrint(PGPPublicKey key) { + if (key == null) { + return null; + } + return PGPPublicKeyService.toHexFingerprint(key); } } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties index 561551af2..1d574dc40 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties @@ -12,6 +12,7 @@ # IBM Corporation - initial API and implementation ############################################################################### +PGPPublicKeyViewDialog_Title=PGP Public Key Properties ProfileModificationAction_InvalidSelections=Problem determining user request. Profile id: {0}, Selection count: {1} ProfileModificationWizardPage_DetailsLabel=Details ProfileSnapshots_Label=Installation History @@ -257,6 +258,10 @@ RepositoryElement_NotFound=This repository is currently not available. RepositoryTracker_DuplicateLocation=Duplicate location MetadataRepositoryElement_RepositoryLoadError=Error loading repository {0} IUViewQueryContext_NoCategorizedItemsDescription=You can uncheck the 'Group items by category' check box to see items without categories. +KeySigningInfoFactory_FingerprintItem=Fingerprint= +KeySigningInfoFactory_KeySignersSection=Key Signers: +KeySigningInfoFactory_PGPSigningType=PGP Public Key +KeySigningInfoFactory_UserIDItem=UserID= QueriedElementWrapper_NoCategorizedItemsExplanation=There are no categorized items QueriedElementWrapper_NoItemsExplanation=There are no items available QueriedElementWrapper_SiteNotFound=Could not find {0} @@ -276,13 +281,38 @@ RevertDialog_CancelButtonLabel=Cancel RollbackProfileElement_CurrentInstallation=Current Installation -TrustCertificateDialog_Details=\uD83D\uDD0D &Details... +TrustCertificateDialog_Details=\uD83D\uDD0D D&etails... TrustCertificateDialog_Export=\uD83D\uDCE5 E&xport... TrustCertificateDialog_Title=Trust TrustCertificateDialog_Message=Do you trust these signers? -TrustCertificateDialog_AcceptSelectedButtonLabel=&Trust selected +TrustCertificateDialog_MessageUnsigned=\u26A0\uFE0F\u00A0Do you trust unsigned content of unknown origin? +TrustCertificateDialog_MessageNameWarning=\u26A0\uFE0F\u00A0The displayed originator names are not necessarily a reliable certification of origin. +TrustCertificateDialog_MessagePGP=For PG keys, verification is typically achieved by querying the key\'s fingerprint against a trusted key server. +TrustCertificateDialog_AcceptSelectedButtonLabel=&Trust Selected +TrustCertificateDialog_AlwaysTrust=Always trust all content +TrustCertificateDialog_AlwaysTrustConfirmationMessage=Are you certain you wish to accept all content, including unsigned content of unknown origin, with no further confirmation now and for all future operations?\n\n\ +Use the 'Select All' button to trust all content just for this operation.\n\n\ +This preference choice will be stored on the 'Install/Update > Trust' preference page. +TrustCertificateDialog_AlwaysTrustConfirmationTitle=Always Trust Everything Confirmation +TrustCertificateDialog_AlwaysTrustNo=No, Prompt Me Instead +TrustCertificateDialog_AlwaysTrustYes=Yes, I Accept the Risk +TrustCertificateDialog_ArtifactId=Id TrustCertificateDialog_SelectAll=&Select All TrustCertificateDialog_DeselectAll=&Deselect All TrustCertificateDialog_ObjectType=Type -TrustCertificateDialog_Id=Id +TrustCertificateDialog_Id=Id/Fingerprint TrustCertificateDialog_Name=Name +TrustCertificateDialog_Classifier=Classifier +TrustCertificateDialog_CopyFingerprint=Copy Fingerprint +TrustCertificateDialog_dates=Validity Dates +TrustCertificateDialog_NotApplicable=n/a +TrustCertificateDialog_NotYetValidStartDate=\u274C Not yet valid, starts {0} +TrustCertificateDialog_expiredSince=\u274C Expired since {0} +TrustCertificateDialog_validExpires=\u2714\uFE0F Valid, expires {0} +TrustCertificateDialog_valid=\u2714\uFE0F Valid +TrustCertificateDialog_expired=\u274C Expired +TrustCertificateDialog_notYetValid=\u274C Not yet valid +TrustCertificateDialog_RememberSigners=Remember selected signers +TrustCertificateDialog_Unknown=Unknown +TrustCertificateDialog_Unsigned=Unsigned +TrustCertificateDialog_Version=Version diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/viewers/CertificateLabelProvider.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/viewers/CertificateLabelProvider.java index 9eb67d01e..39cc991d8 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/viewers/CertificateLabelProvider.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/viewers/CertificateLabelProvider.java @@ -14,10 +14,11 @@ package org.eclipse.equinox.internal.p2.ui.viewers; import java.security.cert.X509Certificate; -import org.bouncycastle.openpgp.PGPPublicKey; import org.eclipse.equinox.internal.provisional.security.ui.X500PrincipalHelper; +import org.eclipse.equinox.internal.provisional.security.ui.X509CertificateViewDialog; import org.eclipse.jface.viewers.*; import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Shell; /** * A label provider that displays X509 certificates. @@ -35,33 +36,12 @@ public class CertificateLabelProvider implements ILabelProvider { Object o = ((TreeNode) element).getValue(); if (o instanceof X509Certificate) { X509Certificate cert = (X509Certificate) o; - X500PrincipalHelper principalHelper = new X500PrincipalHelper(cert.getSubjectX500Principal()); - return principalHelper.getCN() + "; " + principalHelper.getOU() + "; " //$NON-NLS-1$ //$NON-NLS-2$ - + principalHelper.getO(); - } else if (o instanceof PGPPublicKey) { - return userFriendlyFingerPrint((PGPPublicKey) o); + return getText(cert); } } return ""; //$NON-NLS-1$ } - private String userFriendlyFingerPrint(PGPPublicKey key) { - if (key == null) { - return null; - } - StringBuilder builder = new StringBuilder(); - boolean spaceSuffix = false; - for (byte b : key.getFingerprint()) { - builder.append(String.format("%02X", Byte.toUnsignedInt(b))); //$NON-NLS-1$ - if (spaceSuffix) { - builder.append(' '); - } - spaceSuffix = !spaceSuffix; - } - builder.deleteCharAt(builder.length() - 1); - return builder.toString(); - } - @Override public void addListener(ILabelProviderListener listener) { // do nothing @@ -82,4 +62,23 @@ public class CertificateLabelProvider implements ILabelProvider { // do nothing } + /** + * Returns a string that can be used as readable label for a certificate. This + * hides the internal implementation classes needed to produce this label. + */ + public static String getText(X509Certificate cert) { + X500PrincipalHelper principalHelper = new X500PrincipalHelper(cert.getSubjectX500Principal()); + return principalHelper.getCN() + "; " + principalHelper.getOU() + "; " //$NON-NLS-1$ //$NON-NLS-2$ + + principalHelper.getO(); + } + + /** + * Opens a dialog to present detailed information about a certificate. This + * hides the internal implementation classes needed open this dialog. + */ + public static void openDialog(Shell shell, X509Certificate cert) { + // create and open dialog for certificate chain + X509CertificateViewDialog certificateViewDialog = new X509CertificateViewDialog(shell, cert); + certificateViewDialog.open(); + } } diff --git a/bundles/org.eclipse.equinox.p2.updatechecker/pom.xml b/bundles/org.eclipse.equinox.p2.updatechecker/pom.xml index da2f37456..49d08db7f 100644 --- a/bundles/org.eclipse.equinox.p2.updatechecker/pom.xml +++ b/bundles/org.eclipse.equinox.p2.updatechecker/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.updatesite/pom.xml b/bundles/org.eclipse.equinox.p2.updatesite/pom.xml index 6f21a2913..1f7c8c962 100644 --- a/bundles/org.eclipse.equinox.p2.updatesite/pom.xml +++ b/bundles/org.eclipse.equinox.p2.updatesite/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.simpleconfigurator.manipulator/pom.xml b/bundles/org.eclipse.equinox.simpleconfigurator.manipulator/pom.xml index ee6dc888a..43581bbeb 100644 --- a/bundles/org.eclipse.equinox.simpleconfigurator.manipulator/pom.xml +++ b/bundles/org.eclipse.equinox.simpleconfigurator.manipulator/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.simpleconfigurator/pom.xml b/bundles/org.eclipse.equinox.simpleconfigurator/pom.xml index 4f5b876cf..5a9a88217 100644 --- a/bundles/org.eclipse.equinox.simpleconfigurator/pom.xml +++ b/bundles/org.eclipse.equinox.simpleconfigurator/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> |