Skip to content
This repository was archived by the owner on Oct 13, 2024. It is now read-only.

Commit a4cd532

Browse files
committed
Added Artifact Hash Checking
1 parent a157c71 commit a4cd532

File tree

9 files changed

+208
-52
lines changed

9 files changed

+208
-52
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Bukkit Plugins. Example code can be seen here:
77
.setApplicationName("MyPlugin")
88
.addArtifact(ofArtifact("me:lucko", "jar-relocator", "1.5"))
99
.addRelocation(ofRelocation("me:lucko", "io:github:pulsebeat02:lucko"))
10-
.createEMCDepManagement();
10+
.create();
1111
management.load();
1212
```
1313

@@ -16,13 +16,13 @@ Bukkit Plugins. Example code can be seen here:
1616
1) Add the repository:
1717
```kotlin
1818
repositories {
19-
maven("https://pulsebeat02.jfrog.io/artifactory/pulse-gradle-release-local");
19+
maven("https://pulsebeat02.jfrog.io/artifactory/pulse-gradle-release-local/");
2020
}
2121
```
2222

2323
2) Add the dependency:
2424
```kotlin
2525
dependencies {
26-
implementation("io.github.pulsebeat02", "emc-dependency-management", "v1.0.1")
26+
implementation("io.github.pulsebeat02", "emc-dependency-management", "v1.0.0")
2727
}
2828
```

src/main/java/io/github/pulsebeat02/emcdependencymanagement/EMCDepManagement.java

+49-2
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,19 @@
3939

4040
public final class EMCDepManagement {
4141

42+
private final SimpleLogger logger;
4243
private final Collection<Artifact> artifacts;
4344
private final Collection<Relocation> relocations;
4445
private final Collection<Repository> repositories;
4546
private final Path folder;
4647

4748
EMCDepManagement(
49+
final SimpleLogger logger,
4850
final Collection<Artifact> artifacts,
4951
final Collection<Relocation> relocations,
5052
final Collection<Repository> repositories,
5153
final Path folder) {
54+
this.logger = logger;
5255
this.artifacts = artifacts == null ? new ArrayList<>() : artifacts;
5356
this.relocations = relocations == null ? new ArrayList<>() : relocations;
5457
this.repositories = repositories == null ? new ArrayList<>() : repositories;
@@ -82,7 +85,7 @@ private void relocate(final Collection<Path> paths) throws IOException {
8285

8386
private Collection<Path> installedJars(final Collection<Artifact> download) throws IOException {
8487
final JarInstaller installer =
85-
JarInstaller.ofInstaller(download, this.repositories, this.folder);
88+
JarInstaller.ofInstaller(this.logger, download, this.repositories, this.folder);
8689
return installer.install();
8790
}
8891

@@ -97,15 +100,53 @@ private Collection<Path> getJars() throws IOException {
97100
}
98101
}
99102

103+
public SimpleLogger getLogger() {
104+
return this.logger;
105+
}
106+
107+
public Collection<Artifact> getArtifacts() {
108+
return this.artifacts;
109+
}
110+
111+
public Collection<Relocation> getRelocations() {
112+
return this.relocations;
113+
}
114+
115+
public Collection<Repository> getRepositories() {
116+
return this.repositories;
117+
}
118+
119+
public Path getFolder() {
120+
return this.folder;
121+
}
122+
100123
public static class Builder {
101124

125+
private SimpleLogger logger;
102126
private Collection<Artifact> artifacts;
103127
private Collection<Relocation> relocations;
104128
private Collection<Repository> repositories;
105129
private Path folder;
106130
private String name;
107131

108132
{
133+
this.logger =
134+
new SimpleLogger() {
135+
@Override
136+
public void info(final String line) {
137+
System.out.printf("[INFO] %s%n", line);
138+
}
139+
140+
@Override
141+
public void warning(final String line) {
142+
System.out.printf("[WARN] %s%n", line);
143+
}
144+
145+
@Override
146+
public void error(final String line) {
147+
System.err.printf("[ERROR] %s%n", line);
148+
}
149+
};
109150
this.artifacts = new ArrayList<>();
110151
this.relocations = new ArrayList<>();
111152
this.repositories = new ArrayList<>();
@@ -151,10 +192,16 @@ public Builder setFolder(final Path folder) {
151192
return this;
152193
}
153194

154-
public EMCDepManagement createEMCDepManagement() throws IOException {
195+
public Builder setLogger(final SimpleLogger logger) {
196+
this.logger = logger;
197+
return this;
198+
}
199+
200+
public EMCDepManagement create() throws IOException {
155201
final Path file = (this.folder == null ? this.getFolder() : this.folder).resolve(this.name);
156202
this.createFile(file);
157203
return new EMCDepManagement(
204+
this.logger,
158205
new ArrayList<>(this.artifacts),
159206
new ArrayList<>(this.relocations),
160207
new ArrayList<>(this.repositories),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.github.pulsebeat02.emcdependencymanagement;
2+
3+
public interface SimpleLogger {
4+
5+
void info(final String line);
6+
7+
void warning(final String line);
8+
9+
void error(final String line);
10+
}

src/main/java/io/github/pulsebeat02/emcdependencymanagement/component/Artifact.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*/
2424
package io.github.pulsebeat02.emcdependencymanagement.component;
2525

26-
import static io.github.pulsebeat02.emcdependencymanagement.util.Packages.correctPackage;
26+
import static io.github.pulsebeat02.emcdependencymanagement.util.PackageUtils.correctPackage;
2727

2828
public final class Artifact {
2929

src/main/java/io/github/pulsebeat02/emcdependencymanagement/component/Relocation.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*/
2424
package io.github.pulsebeat02.emcdependencymanagement.component;
2525

26-
import static io.github.pulsebeat02.emcdependencymanagement.util.Packages.correctPackage;
26+
import static io.github.pulsebeat02.emcdependencymanagement.util.PackageUtils.correctPackage;
2727

2828
public final class Relocation {
2929

src/main/java/io/github/pulsebeat02/emcdependencymanagement/component/downloader/JarInstaller.java

+66-21
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,29 @@
11
/**
22
* MIT License
33
*
4-
* Copyright (c) 2021 Brandon Li
4+
* <p>Copyright (c) 2021 Brandon Li
55
*
6-
* Permission is hereby granted, free of charge, to any person obtaining a copy
7-
* of this software and associated documentation files (the "Software"), to deal
8-
* in the Software without restriction, including without limitation the rights
9-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10-
* copies of the Software, and to permit persons to whom the Software is
6+
* <p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software
7+
* and associated documentation files (the "Software"), to deal in the Software without restriction,
8+
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
9+
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
1110
* furnished to do so, subject to the following conditions:
1211
*
13-
* The above copyright notice and this permission notice shall be included in all
14-
* copies or substantial portions of the Software.
12+
* <p>The above copyright notice and this permission notice shall be included in all copies or
13+
* substantial portions of the Software.
1514
*
16-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22-
* SOFTWARE.
15+
* <p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
16+
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2320
*/
2421
package io.github.pulsebeat02.emcdependencymanagement.component.downloader;
2522

23+
import io.github.pulsebeat02.emcdependencymanagement.SimpleLogger;
2624
import io.github.pulsebeat02.emcdependencymanagement.component.Artifact;
2725
import io.github.pulsebeat02.emcdependencymanagement.component.Repository;
26+
import io.github.pulsebeat02.emcdependencymanagement.util.FileUtils;
2827
import java.io.IOException;
2928
import java.io.InputStream;
3029
import java.net.HttpURLConnection;
@@ -35,20 +34,25 @@
3534
import java.util.Arrays;
3635
import java.util.Collection;
3736
import java.util.HashSet;
37+
import java.util.Locale;
3838
import java.util.Optional;
39+
import java.util.Scanner;
3940
import java.util.Set;
4041

4142
public final class JarInstaller {
4243

44+
private final SimpleLogger logger;
4345
private final Collection<Artifact> artifacts;
4446
private final Collection<Repository> repositories;
4547
private final Set<Path> paths;
4648
private final Path target;
4749

4850
JarInstaller(
51+
final SimpleLogger logger,
4952
final Collection<Artifact> artifacts,
5053
final Collection<Repository> repositories,
5154
final Path target) {
55+
this.logger = logger;
5256
this.artifacts = artifacts;
5357
this.repositories = repositories;
5458
this.target = target;
@@ -58,10 +62,11 @@ public final class JarInstaller {
5862
}
5963

6064
public static JarInstaller ofInstaller(
65+
final SimpleLogger logger,
6166
final Collection<Artifact> artifacts,
6267
final Collection<Repository> repositories,
6368
final Path target) {
64-
return new JarInstaller(artifacts, repositories, target);
69+
return new JarInstaller(logger, artifacts, repositories, target);
6570
}
6671

6772
public Collection<Path> install() throws IOException {
@@ -87,18 +92,46 @@ private void downloadJarExceptionally(final String url) {
8792
try {
8893
this.downloadJar(url);
8994
} catch (final IOException e) {
90-
e.printStackTrace();
95+
this.logger.error(String.format("Failed to download JAR located at url %s!", url));
9196
}
9297
}
9398

9499
private void downloadJar(final String url) throws IOException {
100+
101+
final Path jarPath = this.downloadFile(url);
102+
if (this.checkHash(jarPath, url)) {
103+
this.downloadJar(url);
104+
}
105+
106+
this.paths.add(jarPath);
107+
}
108+
109+
private boolean checkHash(final Path jarPath, final String url) throws IOException {
110+
111+
final String originalHash = this.getCheckSumArtifact(url);
112+
final String newHash = FileUtils.getUppercaseHash(jarPath);
113+
if (originalHash.isEmpty()) {
114+
this.logger.warning(
115+
String.format("Could not retrieve SHA1 hash for artifact %s! Skipping hash check!", url));
116+
return false;
117+
}
118+
119+
if (!originalHash.equals(newHash)) {
120+
Files.deleteIfExists(jarPath);
121+
return true;
122+
}
123+
124+
return false;
125+
}
126+
127+
private Path downloadFile(final String url) throws IOException {
95128
final URL website = new URL(url);
96129
final String jar = this.getFilename(url);
97130
final Path jarPath = this.target.resolve(jar);
98131
try (final InputStream in = website.openStream()) {
99132
Files.copy(in, jarPath, StandardCopyOption.REPLACE_EXISTING);
100133
}
101-
this.paths.add(jarPath);
134+
return jarPath;
102135
}
103136

104137
private String getFilename(final String url) {
@@ -117,9 +150,7 @@ private Optional<String> tryRepository(final Repository repository, final String
117150
}
118151

119152
private boolean isValidUrl(final String url) throws IOException {
120-
final HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
121-
con.setRequestMethod("HEAD");
122-
return con.getResponseCode() == HttpURLConnection.HTTP_OK;
153+
return this.createConnection(url).getResponseCode() == HttpURLConnection.HTTP_OK;
123154
}
124155

125156
private String getAppendedUrl(final Artifact artifact) {
@@ -130,6 +161,20 @@ private String getAppendedUrl(final Artifact artifact) {
130161
return String.format("%s/%s/%s/%s", groupId, artifactId, version, jar);
131162
}
132163

164+
private String getCheckSumArtifact(final String url) throws IOException {
165+
final String hashUrl = String.format("%s.sha1", url);
166+
try (final Scanner scanner =
167+
new Scanner(new URL(hashUrl).openStream(), "UTF-8").useDelimiter("\\A")) {
168+
return scanner.next().toUpperCase(Locale.ROOT);
169+
}
170+
}
171+
172+
private HttpURLConnection createConnection(final String url) throws IOException {
173+
final HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
174+
con.setRequestMethod("HEAD");
175+
return con;
176+
}
177+
133178
public Collection<Artifact> getArtifacts() {
134179
return this.artifacts;
135180
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package io.github.pulsebeat02.emcdependencymanagement.util;
2+
3+
import java.io.InputStream;
4+
import java.nio.charset.StandardCharsets;
5+
import java.nio.file.Files;
6+
import java.nio.file.Path;
7+
import java.security.MessageDigest;
8+
import java.util.Locale;
9+
10+
public final class FileUtils {
11+
12+
private static final byte[] HEX_ARRAY;
13+
14+
static {
15+
HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
16+
}
17+
18+
private FileUtils() {}
19+
20+
public static String getUppercaseHash(final Path file) {
21+
try {
22+
return bytesToHex(createByteSHA(file)).toUpperCase(Locale.ROOT);
23+
} catch (final Exception e) {
24+
throw new AssertionError(String.format("Failed to get hash of file for %s!", file));
25+
}
26+
}
27+
28+
private static byte[] createByteSHA(final Path file) throws Exception {
29+
final MessageDigest digest = MessageDigest.getInstance("SHA-1");
30+
final InputStream fis = Files.newInputStream(file);
31+
int n = 0;
32+
final byte[] buffer = new byte[8192];
33+
while (n != -1) {
34+
n = fis.read(buffer);
35+
if (n > 0) {
36+
digest.update(buffer, 0, n);
37+
}
38+
}
39+
return digest.digest();
40+
}
41+
42+
private static String bytesToHex(final byte[] bytes) {
43+
final byte[] hexChars = new byte[bytes.length << 1];
44+
for (int j = 0; j < bytes.length; j++) {
45+
final int v = bytes[j] & 0xFF;
46+
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
47+
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
48+
}
49+
return new String(hexChars, StandardCharsets.UTF_8);
50+
}
51+
}

src/main/java/io/github/pulsebeat02/emcdependencymanagement/util/Packages.java renamed to src/main/java/io/github/pulsebeat02/emcdependencymanagement/util/PackageUtils.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
*/
2424
package io.github.pulsebeat02.emcdependencymanagement.util;
2525

26-
public final class Packages {
26+
public final class PackageUtils {
2727

28-
private Packages() {}
28+
private PackageUtils() {}
2929

3030
public static String correctPackage(final String name) {
3131
return name.replace(':', '.');

0 commit comments

Comments
 (0)