๐ (federation): Use bare-host acct form in WebFinger, accept both forms
Changes
3 files changed, +34 -8
MODIFY
src/main/java/de/workaround/federation/ActivityPubClient.java
+5 -3
@@ -126,13 +126,15 @@
126
126
127
127
/**
128
128
* Resolves {@code acct:{identifier}@{host}} via the host's WebFinger endpoint to the actor id in
129
- * its {@code self} link. The host may carry a non-default port (dev two-host trials); https is
130
- * used unless the dev-insecure flag permits http.
129
+ * its {@code self} link. The host may carry a non-default port (dev two-host trials) โ the port
130
+ * addresses the endpoint but is stripped from the {@code acct:} resource, which names the bare
131
+ * host per WebFinger convention. https is used unless the dev-insecure flag permits http.
131
132
*/
132
133
public Optional<String> resolveWebFinger(String identifier, String host)
133
134
{
134
135
String scheme = config.devAllowInsecure() ? "http" : "https";
135
- String resource = "acct:" + identifier + "@" + host;
136
+ String acctHost = host.contains(":") ? host.substring(0, host.indexOf(':')) : host;
137
+ String resource = "acct:" + identifier + "@" + acctHost;
136
138
String url = scheme + "://" + host + "/.well-known/webfinger?resource="
137
139
+ URLEncoder.encode(resource, java.nio.charset.StandardCharsets.UTF_8);
138
140
URI uri;
MODIFY
src/main/java/de/workaround/federation/WebFingerResource.java
+8 -5
@@ -59,7 +59,7 @@
59
59
}
60
60
String identifier = acct.substring(0, at);
61
61
String host = acct.substring(at + 1);
62
- if (!host.equalsIgnoreCase(baseHost()))
62
+ if (!isOurHost(host))
63
63
{
64
64
throw new NotFoundException();
65
65
}
@@ -90,11 +90,14 @@
90
90
}
91
91
}
92
92
93
- private String baseHost()
93
+ /**
94
+ * Accepts the acct host as the bare host (standard WebFinger) or as host:port โ the latter so
95
+ * dev two-host trials on one machine, where only the port distinguishes instances, still match.
96
+ */
97
+ private boolean isOurHost(String host)
94
98
{
95
- // Authority, not host: keeps a non-default port significant (acct:owner/name@host:port),
96
- // which the dev two-host trial on one machine relies on.
97
- return URI.create(config.baseUrl()).getAuthority();
99
+ URI base = URI.create(config.baseUrl());
100
+ return host.equalsIgnoreCase(base.getHost()) || host.equalsIgnoreCase(base.getAuthority());
98
101
}
99
102
100
103
}
MODIFY
src/test/java/de/workaround/federation/WebFingerResolveTest.java
+21 -0
@@ -72,6 +72,27 @@
72
72
assertTrue(client.resolveWebFinger("nobody/nothing-" + unique(), "localhost:8082").isEmpty());
73
73
}
74
74
75
+ /**
76
+ * The served endpoint accepts the acct host as the bare host (standard WebFinger, what our
77
+ * client sends) and as host:port (lenient, for clients that include the authority).
78
+ */
79
+ @Test
80
+ void acceptsBareHostAndAuthorityAcctForms()
81
+ {
82
+ User owner = persistUser("wf-carl-" + unique());
83
+ Repository repo = createRepo(owner, "lib-" + unique());
84
+ String handle = owner.username + "/" + repo.name;
85
+
86
+ io.restassured.RestAssured.given()
87
+ .queryParam("resource", "acct:" + handle + "@localhost")
88
+ .when().get("http://localhost:8082/.well-known/webfinger")
89
+ .then().statusCode(200);
90
+ io.restassured.RestAssured.given()
91
+ .queryParam("resource", "acct:" + handle + "@localhost:8082")
92
+ .when().get("http://localhost:8082/.well-known/webfinger")
93
+ .then().statusCode(200);
94
+ }
95
+
75
96
@Transactional
76
97
Repository createRepo(User owner, String name)
77
98
{