mirror of
https://github.com/yushijinhun/authlib-injector.git
synced 2024-11-15 06:11:09 +01:00
refactor
YggdrasilConfiguration -> APIMetadata InjectorInitializationException -> InitializationException javaagent/AuthlibInjectorPremain -> Premain
This commit is contained in:
parent
496baee488
commit
1ba5bbb678
9 changed files with 76 additions and 104 deletions
|
@ -28,8 +28,8 @@ jar {
|
|||
'Implementation-Vendor': 'yushijinhun',
|
||||
'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
|
||||
'Automatic-Module-Name': 'moe.yushi.authlibinjector',
|
||||
'Premain-Class': 'moe.yushi.authlibinjector.javaagent.AuthlibInjectorPremain',
|
||||
'Agent-Class': 'moe.yushi.authlibinjector.javaagent.AuthlibInjectorPremain',
|
||||
'Premain-Class': 'moe.yushi.authlibinjector.Premain',
|
||||
'Agent-Class': 'moe.yushi.authlibinjector.Premain',
|
||||
'Can-Retransform-Classes': true,
|
||||
'Can-Redefine-Classes': true,
|
||||
'Git-Commit': gitInfo.gitHashFull,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Haowei Wen <yushijinhun@gmail.com> and contributors
|
||||
* Copyright (C) 2020 Haowei Wen <yushijinhun@gmail.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
|
@ -37,9 +37,9 @@ import moe.yushi.authlibinjector.internal.org.json.simple.JSONObject;
|
|||
import moe.yushi.authlibinjector.util.JsonUtils;
|
||||
import moe.yushi.authlibinjector.util.KeyUtils;
|
||||
|
||||
public class YggdrasilConfiguration {
|
||||
public class APIMetadata {
|
||||
|
||||
public static YggdrasilConfiguration parse(String apiRoot, String metadataResponse) throws UncheckedIOException {
|
||||
public static APIMetadata parse(String apiRoot, String metadataResponse) throws UncheckedIOException {
|
||||
JSONObject response = asJsonObject(parseJson(metadataResponse));
|
||||
|
||||
List<String> skinDomains =
|
||||
|
@ -59,7 +59,7 @@ public class YggdrasilConfiguration {
|
|||
.map(it -> (Map<String, Object>) new TreeMap<>(asJsonObject(it)))
|
||||
.orElse(emptyMap());
|
||||
|
||||
return new YggdrasilConfiguration(apiRoot, unmodifiableList(skinDomains), unmodifiableMap(meta), decodedPublickey);
|
||||
return new APIMetadata(apiRoot, unmodifiableList(skinDomains), unmodifiableMap(meta), decodedPublickey);
|
||||
}
|
||||
|
||||
private String apiRoot;
|
||||
|
@ -67,7 +67,7 @@ public class YggdrasilConfiguration {
|
|||
private Optional<PublicKey> decodedPublickey;
|
||||
private Map<String, Object> meta;
|
||||
|
||||
public YggdrasilConfiguration(String apiRoot, List<String> skinDomains, Map<String, Object> meta, Optional<PublicKey> decodedPublickey) {
|
||||
public APIMetadata(String apiRoot, List<String> skinDomains, Map<String, Object> meta, Optional<PublicKey> decodedPublickey) {
|
||||
this.apiRoot = requireNonNull(apiRoot);
|
||||
this.skinDomains = requireNonNull(skinDomains);
|
||||
this.meta = requireNonNull(meta);
|
||||
|
@ -92,7 +92,6 @@ public class YggdrasilConfiguration {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return format("YggdrasilConfiguration [apiRoot={0}, skinDomains={1}, decodedPublickey={2}, meta={3}]", apiRoot, skinDomains, decodedPublickey, meta);
|
||||
return format("APIMetadata [apiRoot={0}, skinDomains={1}, decodedPublickey={2}, meta={3}]", apiRoot, skinDomains, decodedPublickey, meta);
|
||||
}
|
||||
|
||||
}
|
|
@ -18,8 +18,7 @@ package moe.yushi.authlibinjector;
|
|||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.Optional.of;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static moe.yushi.authlibinjector.util.IOUtils.asBytes;
|
||||
import static moe.yushi.authlibinjector.util.IOUtils.asString;
|
||||
import static moe.yushi.authlibinjector.util.IOUtils.removeNewLines;
|
||||
|
@ -42,7 +41,6 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import moe.yushi.authlibinjector.httpd.DefaultURLRedirector;
|
||||
import moe.yushi.authlibinjector.httpd.LegacySkinAPIFilter;
|
||||
|
@ -65,12 +63,6 @@ import moe.yushi.authlibinjector.yggdrasil.MojangYggdrasilAPIProvider;
|
|||
import moe.yushi.authlibinjector.yggdrasil.YggdrasilClient;
|
||||
|
||||
public final class AuthlibInjector {
|
||||
|
||||
/**
|
||||
* Stores the API URL, should be set before {@link #bootstrap(Consumer)} is invoked.
|
||||
*/
|
||||
public static final String PROP_API_ROOT = "authlibinjector.api";
|
||||
|
||||
private AuthlibInjector() {}
|
||||
|
||||
private static boolean booted = false;
|
||||
|
@ -78,13 +70,13 @@ public final class AuthlibInjector {
|
|||
private static boolean retransformSupported;
|
||||
private static ClassTransformer classTransformer;
|
||||
|
||||
public static synchronized void bootstrap(Instrumentation instrumentation) throws InjectorInitializationException {
|
||||
public static synchronized void bootstrap(Instrumentation instrumentation, String apiUrl) throws InitializationException {
|
||||
if (booted) {
|
||||
log(INFO, "Already started, skipping");
|
||||
return;
|
||||
}
|
||||
booted = true;
|
||||
AuthlibInjector.instrumentation = instrumentation;
|
||||
AuthlibInjector.instrumentation = requireNonNull(instrumentation);
|
||||
Config.init();
|
||||
|
||||
retransformSupported = instrumentation.isRetransformClassesSupported();
|
||||
|
@ -92,19 +84,14 @@ public final class AuthlibInjector {
|
|||
log(WARNING, "Retransform is not supported");
|
||||
}
|
||||
|
||||
log(INFO, "Version: " + getVersion());
|
||||
log(INFO, "Version: " + AuthlibInjector.class.getPackage().getImplementationVersion());
|
||||
|
||||
Optional<YggdrasilConfiguration> optionalConfig = configure();
|
||||
if (optionalConfig.isPresent()) {
|
||||
classTransformer = createTransformer(optionalConfig.get());
|
||||
APIMetadata apiMetadata = fetchAPIMetadata(apiUrl);
|
||||
classTransformer = createTransformer(apiMetadata);
|
||||
instrumentation.addTransformer(classTransformer, retransformSupported);
|
||||
|
||||
MC52974Workaround.init();
|
||||
MC52974_1710Workaround.init();
|
||||
} else {
|
||||
log(ERROR, "No authentication server specified");
|
||||
throw new InjectorInitializationException();
|
||||
}
|
||||
}
|
||||
|
||||
private static Optional<String> getPrefetchedResponse() {
|
||||
|
@ -118,27 +105,40 @@ public final class AuthlibInjector {
|
|||
return Optional.ofNullable(prefetched);
|
||||
}
|
||||
|
||||
private static Optional<YggdrasilConfiguration> configure() {
|
||||
String apiRoot = System.getProperty(PROP_API_ROOT);
|
||||
if (apiRoot == null)
|
||||
return empty();
|
||||
private static APIMetadata fetchAPIMetadata(String apiUrl) {
|
||||
if (apiUrl == null || apiUrl.isEmpty()) {
|
||||
log(ERROR, "No authentication server specified");
|
||||
throw new InitializationException();
|
||||
}
|
||||
|
||||
apiRoot = addHttpsIfMissing(apiRoot);
|
||||
log(INFO, "Authentication server: " + apiRoot);
|
||||
warnIfHttp(apiRoot);
|
||||
apiUrl = addHttpsIfMissing(apiUrl);
|
||||
log(INFO, "Authentication server: " + apiUrl);
|
||||
warnIfHttp(apiUrl);
|
||||
|
||||
String metadataResponse;
|
||||
|
||||
Optional<String> prefetched = getPrefetchedResponse();
|
||||
if (!prefetched.isPresent()) {
|
||||
if (prefetched.isPresent()) {
|
||||
|
||||
log(DEBUG, "Prefetched metadata detected");
|
||||
try {
|
||||
metadataResponse = new String(Base64.getDecoder().decode(removeNewLines(prefetched.get())), UTF_8);
|
||||
} catch (IllegalArgumentException e) {
|
||||
log(ERROR, "Unable to decode metadata: " + e + "\n"
|
||||
+ "Encoded metadata:\n"
|
||||
+ prefetched.get());
|
||||
throw new InitializationException(e);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
try {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(apiRoot).openConnection();
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(apiUrl).openConnection();
|
||||
|
||||
String ali = connection.getHeaderField("x-authlib-injector-api-location");
|
||||
if (ali != null) {
|
||||
URL absoluteAli = new URL(connection.getURL(), ali);
|
||||
if (!urlEqualsIgnoreSlash(apiRoot, absoluteAli.toString())) {
|
||||
if (!urlEqualsIgnoreSlash(apiUrl, absoluteAli.toString())) {
|
||||
|
||||
// usually the URL that ALI points to is on the same host
|
||||
// so the TCP connection can be reused
|
||||
|
@ -150,8 +150,8 @@ public final class AuthlibInjector {
|
|||
}
|
||||
|
||||
log(INFO, "Redirect to: " + absoluteAli);
|
||||
apiRoot = absoluteAli.toString();
|
||||
warnIfHttp(apiRoot);
|
||||
apiUrl = absoluteAli.toString();
|
||||
warnIfHttp(apiUrl);
|
||||
connection = (HttpURLConnection) absoluteAli.openConnection();
|
||||
}
|
||||
}
|
||||
|
@ -161,37 +161,28 @@ public final class AuthlibInjector {
|
|||
}
|
||||
} catch (IOException e) {
|
||||
log(ERROR, "Failed to fetch metadata: " + e);
|
||||
throw new InjectorInitializationException(e);
|
||||
throw new InitializationException(e);
|
||||
}
|
||||
|
||||
} else {
|
||||
log(DEBUG, "Prefetched metadata detected");
|
||||
try {
|
||||
metadataResponse = new String(Base64.getDecoder().decode(removeNewLines(prefetched.get())), UTF_8);
|
||||
} catch (IllegalArgumentException e) {
|
||||
log(ERROR, "Unable to decode metadata: " + e + "\n"
|
||||
+ "Encoded metadata:\n"
|
||||
+ prefetched.get());
|
||||
throw new InjectorInitializationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
log(DEBUG, "Metadata: " + metadataResponse);
|
||||
|
||||
if (!apiRoot.endsWith("/"))
|
||||
apiRoot += "/";
|
||||
if (!apiUrl.endsWith("/")) {
|
||||
apiUrl += "/";
|
||||
}
|
||||
|
||||
YggdrasilConfiguration configuration;
|
||||
APIMetadata metadata;
|
||||
try {
|
||||
configuration = YggdrasilConfiguration.parse(apiRoot, metadataResponse);
|
||||
metadata = APIMetadata.parse(apiUrl, metadataResponse);
|
||||
} catch (UncheckedIOException e) {
|
||||
log(ERROR, "Unable to parse metadata: " + e.getCause() + "\n"
|
||||
+ "Raw metadata:\n"
|
||||
+ metadataResponse);
|
||||
throw new InjectorInitializationException(e);
|
||||
throw new InitializationException(e);
|
||||
}
|
||||
log(DEBUG, "Parsed metadata: " + configuration);
|
||||
return of(configuration);
|
||||
log(DEBUG, "Parsed metadata: " + metadata);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private static void warnIfHttp(String url) {
|
||||
|
@ -216,7 +207,7 @@ public final class AuthlibInjector {
|
|||
return a.equals(b);
|
||||
}
|
||||
|
||||
private static List<URLFilter> createFilters(YggdrasilConfiguration config) {
|
||||
private static List<URLFilter> createFilters(APIMetadata config) {
|
||||
if (Config.httpdDisabled) {
|
||||
return emptyList();
|
||||
}
|
||||
|
@ -238,7 +229,7 @@ public final class AuthlibInjector {
|
|||
return filters;
|
||||
}
|
||||
|
||||
private static ClassTransformer createTransformer(YggdrasilConfiguration config) {
|
||||
private static ClassTransformer createTransformer(APIMetadata config) {
|
||||
URLProcessor urlProcessor = new URLProcessor(createFilters(config), new DefaultURLRedirector(config));
|
||||
|
||||
ClassTransformer transformer = new ClassTransformer();
|
||||
|
@ -265,10 +256,6 @@ public final class AuthlibInjector {
|
|||
return transformer;
|
||||
}
|
||||
|
||||
public static String getVersion() {
|
||||
return AuthlibInjector.class.getPackage().getImplementationVersion();
|
||||
}
|
||||
|
||||
public static void retransformClasses(String... classNames) {
|
||||
if (!retransformSupported) {
|
||||
return;
|
||||
|
|
|
@ -109,7 +109,7 @@ public final class Config {
|
|||
break;
|
||||
default:
|
||||
log(ERROR, "Unrecognized debug option: " + option);
|
||||
throw new InjectorInitializationException();
|
||||
throw new InitializationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ public final class Config {
|
|||
Matcher matcher = Pattern.compile("^(?<protocol>[^:]+)://(?<host>[^/]+)+:(?<port>\\d+)$").matcher(prop);
|
||||
if (!matcher.find()) {
|
||||
log(ERROR, "Unrecognized proxy URL: " + prop);
|
||||
throw new InjectorInitializationException();
|
||||
throw new InitializationException();
|
||||
}
|
||||
|
||||
String protocol = matcher.group("protocol");
|
||||
|
@ -196,7 +196,7 @@ public final class Config {
|
|||
|
||||
default:
|
||||
log(ERROR, "Unsupported proxy protocol: " + protocol);
|
||||
throw new InjectorInitializationException();
|
||||
throw new InitializationException();
|
||||
}
|
||||
log(INFO, "Mojang proxy: " + mojangProxy);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Haowei Wen <yushijinhun@gmail.com> and contributors
|
||||
* Copyright (C) 2020 Haowei Wen <yushijinhun@gmail.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
|
@ -16,12 +16,12 @@
|
|||
*/
|
||||
package moe.yushi.authlibinjector;
|
||||
|
||||
public class InjectorInitializationException extends RuntimeException {
|
||||
public class InitializationException extends RuntimeException {
|
||||
|
||||
public InjectorInitializationException() {
|
||||
public InitializationException() {
|
||||
}
|
||||
|
||||
public InjectorInitializationException(Throwable cause) {
|
||||
public InitializationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -14,22 +14,21 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package moe.yushi.authlibinjector.javaagent;
|
||||
package moe.yushi.authlibinjector;
|
||||
|
||||
import static moe.yushi.authlibinjector.util.Logging.log;
|
||||
import static moe.yushi.authlibinjector.util.Logging.Level.DEBUG;
|
||||
import static moe.yushi.authlibinjector.util.Logging.Level.ERROR;
|
||||
import static moe.yushi.authlibinjector.util.Logging.Level.INFO;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import moe.yushi.authlibinjector.AuthlibInjector;
|
||||
import moe.yushi.authlibinjector.InjectorInitializationException;
|
||||
|
||||
public class AuthlibInjectorPremain {
|
||||
public final class Premain {
|
||||
private Premain() {}
|
||||
|
||||
public static void premain(String arg, Instrumentation instrumentation) {
|
||||
try {
|
||||
initInjector(arg, instrumentation, false);
|
||||
} catch (InjectorInitializationException e) {
|
||||
} catch (InitializationException e) {
|
||||
log(DEBUG, "A known exception has occurred", e);
|
||||
System.exit(1);
|
||||
} catch (Throwable e) {
|
||||
|
@ -42,25 +41,18 @@ public class AuthlibInjectorPremain {
|
|||
try {
|
||||
log(INFO, "Launched from agentmain");
|
||||
initInjector(arg, instrumentation, true);
|
||||
} catch (InjectorInitializationException e) {
|
||||
} catch (InitializationException e) {
|
||||
log(DEBUG, "A known exception has occurred", e);
|
||||
} catch (Throwable e) {
|
||||
log(ERROR, "An exception has occurred", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void initInjector(String arg, Instrumentation instrumentation, boolean retransform) {
|
||||
setupConfig(arg);
|
||||
AuthlibInjector.bootstrap(instrumentation);
|
||||
private static void initInjector(String arg, Instrumentation instrumentation, boolean retransform) {
|
||||
AuthlibInjector.bootstrap(instrumentation, arg);
|
||||
|
||||
if (retransform) {
|
||||
AuthlibInjector.retransformAllClasses();
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupConfig(String arg) {
|
||||
if (arg != null && !arg.isEmpty()) {
|
||||
System.setProperty(AuthlibInjector.PROP_API_ROOT, arg);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Haowei Wen <yushijinhun@gmail.com> and contributors
|
||||
* Copyright (C) 2020 Haowei Wen <yushijinhun@gmail.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
|
@ -19,15 +19,14 @@ package moe.yushi.authlibinjector.httpd;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import moe.yushi.authlibinjector.YggdrasilConfiguration;
|
||||
import moe.yushi.authlibinjector.APIMetadata;
|
||||
|
||||
public class DefaultURLRedirector implements URLRedirector {
|
||||
|
||||
private Map<String, String> domainMapping = new HashMap<>();
|
||||
private String apiRoot;
|
||||
|
||||
public DefaultURLRedirector(YggdrasilConfiguration config) {
|
||||
public DefaultURLRedirector(APIMetadata config) {
|
||||
initDomainMapping();
|
||||
|
||||
apiRoot = config.getApiRoot();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Haowei Wen <yushijinhun@gmail.com> and contributors
|
||||
* Copyright (C) 2020 Haowei Wen <yushijinhun@gmail.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
|
@ -17,16 +17,14 @@
|
|||
package moe.yushi.authlibinjector.yggdrasil;
|
||||
|
||||
import static moe.yushi.authlibinjector.util.UUIDUtils.toUnsignedUUID;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import moe.yushi.authlibinjector.YggdrasilConfiguration;
|
||||
import moe.yushi.authlibinjector.APIMetadata;
|
||||
|
||||
public class CustomYggdrasilAPIProvider implements YggdrasilAPIProvider {
|
||||
|
||||
private String apiRoot;
|
||||
|
||||
public CustomYggdrasilAPIProvider(YggdrasilConfiguration configuration) {
|
||||
public CustomYggdrasilAPIProvider(APIMetadata configuration) {
|
||||
this.apiRoot = configuration.getApiRoot();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Haowei Wen <yushijinhun@gmail.com> and contributors
|
||||
* Copyright (C) 2020 Haowei Wen <yushijinhun@gmail.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
|
@ -19,18 +19,15 @@ package moe.yushi.authlibinjector.test;
|
|||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import moe.yushi.authlibinjector.YggdrasilConfiguration;
|
||||
import moe.yushi.authlibinjector.APIMetadata;
|
||||
import moe.yushi.authlibinjector.httpd.DefaultURLRedirector;
|
||||
|
||||
public class DefaultURLRedirectorTest {
|
||||
|
||||
private String apiRoot = "https://yggdrasil.example.com/";
|
||||
private DefaultURLRedirector redirector = new DefaultURLRedirector(new YggdrasilConfiguration(apiRoot, emptyList(), emptyMap(), Optional.empty()));
|
||||
private DefaultURLRedirector redirector = new DefaultURLRedirector(new APIMetadata(apiRoot, emptyList(), emptyMap(), Optional.empty()));
|
||||
|
||||
private void testTransform(String domain, String path, String output) {
|
||||
assertEquals(redirector.redirect(domain, path).get(), output);
|
||||
|
|
Loading…
Reference in a new issue