Skip to content

Commit 4a396ed

Browse files
authored
cf: analyze API key when auth failures (#611)
1 parent 4063515 commit 4a396ed

File tree

5 files changed

+67
-3
lines changed

5 files changed

+67
-3
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package me.itzg.helpers.curseforge;
2+
3+
import org.slf4j.Logger;
4+
5+
public class ApiKeyHelper {
6+
7+
public static final String EXPECTED_API_KEY_PREFIX = "$2a$10$";
8+
9+
static String partiallyRedactApiKey(String apiKey) {
10+
final int trailingAmount = 2;
11+
// too short?
12+
if (apiKey.length() <= trailingAmount + EXPECTED_API_KEY_PREFIX.length()) {
13+
// show half of it
14+
return apiKey.substring(0, apiKey.length() / 2);
15+
}
16+
17+
return apiKey.substring(0, EXPECTED_API_KEY_PREFIX.length()) +
18+
"*****" + apiKey.substring(apiKey.length() - trailingAmount);
19+
}
20+
21+
public static void logKeyIssues(Logger log, String apiKey) {
22+
if (apiKey.startsWith("$$")) {
23+
log.error("The API key seems to have extra dollar sign escaping since "
24+
+ "it looked like '{}' but should start with '{}'.",
25+
partiallyRedactApiKey(apiKey), EXPECTED_API_KEY_PREFIX
26+
);
27+
}
28+
else if (!apiKey.startsWith(EXPECTED_API_KEY_PREFIX)) {
29+
log.error("The API key should start with '{}' but yours looked like '{}'."
30+
+ " Make sure to escape dollar signs with two each.",
31+
EXPECTED_API_KEY_PREFIX, partiallyRedactApiKey(apiKey)
32+
);
33+
}
34+
}
35+
}

src/main/java/me/itzg/helpers/curseforge/CurseForgeApiClient.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import me.itzg.helpers.curseforge.model.GetModResponse;
2525
import me.itzg.helpers.curseforge.model.ModsSearchResponse;
2626
import me.itzg.helpers.errors.GenericException;
27+
import me.itzg.helpers.errors.InvalidApiKeyException;
2728
import me.itzg.helpers.errors.InvalidParameterException;
2829
import me.itzg.helpers.errors.RateLimitException;
2930
import me.itzg.helpers.http.FailedRequestException;
@@ -174,7 +175,12 @@ public CurseForgeFile resolveModpackFile(
174175
)
175176
)
176177
.toObject(GetModFilesResponse.class)
177-
.execute();
178+
.assemble()
179+
.onErrorMap(FailedRequestException::isForbidden, this::errorMapForbidden)
180+
.block();
181+
if (resp == null) {
182+
throw new GenericException("Unable to resolve modpack's file");
183+
}
178184

179185
final List<CurseForgeFile> files = resp.getData();
180186

@@ -313,7 +319,7 @@ public Throwable errorMapForbidden(Throwable throwable) {
313319
return new RateLimitException(null, String.format("Access to %s has been rate-limited.", uriBuilder.getBaseUrl()), e);
314320
}
315321
else {
316-
return new InvalidParameterException(String.format("Access to %s is forbidden or rate-limit has been exceeded."
322+
return new InvalidApiKeyException(String.format("Access to %s is forbidden or rate-limit has been exceeded."
317323
+ " Ensure %s is set to a valid API key from %s or allow rate-limit to reset.",
318324
uriBuilder.getBaseUrl(), API_KEY_VAR, ETERNAL_DEVELOPER_CONSOLE_URL
319325
), e

src/main/java/me/itzg/helpers/curseforge/CurseForgeFilesCommand.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import me.itzg.helpers.curseforge.model.FileDependency;
2525
import me.itzg.helpers.curseforge.model.FileRelationType;
2626
import me.itzg.helpers.curseforge.model.ModLoaderType;
27+
import me.itzg.helpers.errors.InvalidApiKeyException;
2728
import me.itzg.helpers.errors.InvalidParameterException;
2829
import me.itzg.helpers.files.Manifests;
2930
import me.itzg.helpers.http.SharedFetchArgs;
@@ -128,11 +129,14 @@ public Integer call() throws Exception {
128129
newManifest =
129130
apiClient.loadCategoryInfo(Arrays.asList(CATEGORY_MC_MODS, CATEGORY_BUKKIT_PLUGINS))
130131
.flatMap(categoryInfo ->
132+
131133
processModFileRefs(categoryInfo, previousFiles, apiClient)
132134
.map(entries -> CurseForgeFilesManifest.builder()
133135
.entries(entries)
134136
.build()))
135-
.block();
137+
.doOnError(InvalidApiKeyException.class,
138+
throwable -> ApiKeyHelper.logKeyIssues(log, apiKey))
139+
.block();
136140
}
137141
}
138142
else {

src/main/java/me/itzg/helpers/curseforge/CurseForgeInstaller.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import me.itzg.helpers.curseforge.model.MinecraftModpackManifest;
4747
import me.itzg.helpers.curseforge.model.ModLoader;
4848
import me.itzg.helpers.errors.GenericException;
49+
import me.itzg.helpers.errors.InvalidApiKeyException;
4950
import me.itzg.helpers.errors.InvalidParameterException;
5051
import me.itzg.helpers.fabric.FabricLauncherInstaller;
5152
import me.itzg.helpers.files.Manifests;
@@ -239,6 +240,9 @@ void install(String slug, InstallationEntryPoint entryPoint) {
239240

240241
} catch (IOException e) {
241242
throw new GenericException("File system issue during installation", e);
243+
} catch (InvalidApiKeyException e) {
244+
ApiKeyHelper.logKeyIssues(log, apiKey);
245+
throw e;
242246
}
243247
}
244248

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package me.itzg.helpers.errors;
2+
3+
import picocli.CommandLine.ExitCode;
4+
5+
@EmitsExitCode(ExitCode.USAGE)
6+
public class InvalidApiKeyException extends InvalidParameterException {
7+
8+
public InvalidApiKeyException(String message) {
9+
super(message);
10+
}
11+
12+
public InvalidApiKeyException(String message, Throwable cause) {
13+
super(message, cause);
14+
}
15+
}

0 commit comments

Comments
 (0)