Skip to content

Commit 51b5c98

Browse files
S7evinKneilalexander
authored andcommitted
Implement MSC3916 (#3397)
Needs matrix-org/gomatrixserverlib#437
1 parent 2f9cfc5 commit 51b5c98

File tree

13 files changed

+375
-73
lines changed

13 files changed

+375
-73
lines changed

Diff for: clientapi/routing/routing.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ func Setup(
9090
unstableFeatures := map[string]bool{
9191
"org.matrix.e2e_cross_signing": true,
9292
"org.matrix.msc2285.stable": true,
93+
"org.matrix.msc3916.stable": true,
9394
}
9495
for _, msc := range cfg.MSCs.MSCs {
9596
unstableFeatures["org.matrix."+msc] = true
@@ -707,7 +708,7 @@ func Setup(
707708
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
708709

709710
v3mux.Handle("/auth/{authType}/fallback/web",
710-
httputil.MakeHTMLAPI("auth_fallback", enableMetrics, func(w http.ResponseWriter, req *http.Request) {
711+
httputil.MakeHTTPAPI("auth_fallback", userAPI, enableMetrics, func(w http.ResponseWriter, req *http.Request) {
711712
vars := mux.Vars(req)
712713
AuthFallback(w, req, vars["authType"], cfg)
713714
}),

Diff for: federationapi/routing/routing.go

+31
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package routing
1616

1717
import (
1818
"context"
19+
"encoding/json"
1920
"fmt"
2021
"net/http"
2122
"sync"
@@ -601,6 +602,36 @@ func MakeFedAPI(
601602
return httputil.MakeExternalAPI(metricsName, h)
602603
}
603604

605+
// MakeFedHTTPAPI makes an http.Handler that checks matrix federation authentication.
606+
func MakeFedHTTPAPI(
607+
serverName spec.ServerName,
608+
isLocalServerName func(spec.ServerName) bool,
609+
keyRing gomatrixserverlib.JSONVerifier,
610+
f func(http.ResponseWriter, *http.Request),
611+
) http.Handler {
612+
h := func(w http.ResponseWriter, req *http.Request) {
613+
fedReq, errResp := fclient.VerifyHTTPRequest(
614+
req, time.Now(), serverName, isLocalServerName, keyRing,
615+
)
616+
617+
enc := json.NewEncoder(w)
618+
logger := util.GetLogger(req.Context())
619+
if fedReq == nil {
620+
621+
logger.Debugf("VerifyUserFromRequest %s -> HTTP %d", req.RemoteAddr, errResp.Code)
622+
w.WriteHeader(errResp.Code)
623+
if err := enc.Encode(errResp); err != nil {
624+
logger.WithError(err).Error("failed to encode JSON response")
625+
}
626+
return
627+
}
628+
629+
f(w, req)
630+
}
631+
632+
return http.HandlerFunc(h)
633+
}
634+
604635
type FederationWakeups struct {
605636
FsAPI *fedInternal.FederationInternalAPI
606637
origins sync.Map

Diff for: internal/gomatrixserverlib/fclient/federationclient.go

+31-30
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ type FederationClient interface {
6464
ctx context.Context, origin, s spec.ServerName, userID string, field string,
6565
) (res RespProfile, err error)
6666

67-
P2PSendTransactionToRelay(ctx context.Context, u spec.UserID, t gomatrixserverlib.Transaction, forwardingServer spec.ServerName) (res EmptyResp, err error)
68-
P2PGetTransactionFromRelay(ctx context.Context, u spec.UserID, prev RelayEntry, relayServer spec.ServerName) (res RespGetRelayTransaction, err error)
67+
DownloadMedia(ctx context.Context, origin, destination spec.ServerName, mediaID string) (res *http.Response, err error)
6968
}
7069

7170
// A FederationClient is a matrix federation client that adds
@@ -142,34 +141,6 @@ func (ac *federationClient) SendTransaction(
142141
return
143142
}
144143

145-
// P2PSendTransactionToRelay sends a transaction for forwarding to the destination.
146-
func (ac *federationClient) P2PSendTransactionToRelay(
147-
ctx context.Context, u spec.UserID, t gomatrixserverlib.Transaction, forwardingServer spec.ServerName,
148-
) (res EmptyResp, err error) {
149-
path := federationPathPrefixV1 + "/send_relay/" +
150-
string(t.TransactionID) + "/" +
151-
url.PathEscape(u.String())
152-
req := NewFederationRequest("PUT", t.Origin, forwardingServer, path)
153-
if err = req.SetContent(t); err != nil {
154-
return
155-
}
156-
err = ac.doRequest(ctx, req, &res)
157-
return
158-
}
159-
160-
// P2PGetTransactionFromRelay requests a transaction from a relay destined for this server.
161-
func (ac *federationClient) P2PGetTransactionFromRelay(
162-
ctx context.Context, u spec.UserID, prev RelayEntry, relayServer spec.ServerName,
163-
) (res RespGetRelayTransaction, err error) {
164-
path := federationPathPrefixV1 + "/relay_txn/" + url.PathEscape(u.String())
165-
req := NewFederationRequest("GET", u.Domain(), relayServer, path)
166-
if err = req.SetContent(prev); err != nil {
167-
return
168-
}
169-
err = ac.doRequest(ctx, req, &res)
170-
return
171-
}
172-
173144
// Creates a version query string with all the specified room versions, typically
174145
// the list of all supported room versions.
175146
// Needed when making a /make_knock or /make_join request.
@@ -736,3 +707,33 @@ func (ac *federationClient) RoomHierarchy(
736707
}
737708
return
738709
}
710+
711+
// DownloadMedia performs an authenticated federation request for a given mediaID.
712+
// The caller is responsible to close the returned response body.
713+
func (ac *federationClient) DownloadMedia(
714+
ctx context.Context, origin, destination spec.ServerName, mediaID string,
715+
) (*http.Response, error) {
716+
var identity *SigningIdentity
717+
for _, id := range ac.identities {
718+
if id.ServerName == origin {
719+
identity = id
720+
break
721+
}
722+
}
723+
if identity == nil {
724+
return nil, fmt.Errorf("no signing identity for server name %q", origin)
725+
}
726+
727+
path := federationPathPrefixV1 + "/media/download/" + url.PathEscape(mediaID)
728+
req := NewFederationRequest("GET", origin, destination, path)
729+
730+
if err := req.Sign(identity.ServerName, identity.KeyID, identity.PrivateKey); err != nil {
731+
return nil, err
732+
}
733+
734+
httpReq, err := req.HTTPRequest()
735+
if err != nil {
736+
return nil, err
737+
}
738+
return ac.DoHTTPRequest(ctx, httpReq)
739+
}

Diff for: internal/gomatrixserverlib/fclient/federationtypes.go

+2
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ type PublicRoom struct {
129129
GuestCanJoin bool `json:"guest_can_join"`
130130
// The URL for the room's avatar, if one is set.
131131
AvatarURL string `json:"avatar_url,omitempty"`
132+
// The join rule for this room
133+
JoinRule string `json:"join_rule,omitempty"`
132134
}
133135

134136
// A RespEventAuth is the content of a response to GET /_matrix/federation/v1/event_auth/{roomID}/{eventID}

Diff for: internal/httputil/httpapi.go

+28-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package httputil
1616

1717
import (
18+
"encoding/json"
1819
"io"
1920
"net/http"
2021
"net/http/httptest"
@@ -41,6 +42,7 @@ type BasicAuth struct {
4142

4243
type AuthAPIOpts struct {
4344
GuestAccessAllowed bool
45+
WithAuth bool
4446
}
4547

4648
// AuthAPIOption is an option to MakeAuthAPI to add additional checks (e.g. guest access) to verify
@@ -54,6 +56,13 @@ func WithAllowGuests() AuthAPIOption {
5456
}
5557
}
5658

59+
// WithAuth is an option to MakeHTTPAPI to add authentication.
60+
func WithAuth() AuthAPIOption {
61+
return func(opts *AuthAPIOpts) {
62+
opts.WithAuth = true
63+
}
64+
}
65+
5766
// MakeAuthAPI turns a util.JSONRequestHandler function into an http.Handler which authenticates the request.
5867
func MakeAuthAPI(
5968
metricsName string, userAPI userapi.QueryAcccessTokenAPI,
@@ -170,10 +179,27 @@ func MakeExternalAPI(metricsName string, f func(*http.Request) util.JSONResponse
170179
return http.HandlerFunc(withSpan)
171180
}
172181

173-
// MakeHTMLAPI adds Span metrics to the HTML Handler function
182+
// MakeHTTPAPI adds Span metrics to the HTML Handler function
174183
// This is used to serve HTML alongside JSON error messages
175-
func MakeHTMLAPI(metricsName string, enableMetrics bool, f func(http.ResponseWriter, *http.Request)) http.Handler {
184+
func MakeHTTPAPI(metricsName string, userAPI userapi.QueryAcccessTokenAPI, enableMetrics bool, f func(http.ResponseWriter, *http.Request), checks ...AuthAPIOption) http.Handler {
176185
withSpan := func(w http.ResponseWriter, req *http.Request) {
186+
// apply additional checks, if any
187+
opts := AuthAPIOpts{}
188+
for _, opt := range checks {
189+
opt(&opts)
190+
}
191+
192+
if opts.WithAuth {
193+
logger := util.GetLogger(req.Context())
194+
_, jsonErr := auth.VerifyUserFromRequest(req, userAPI)
195+
if jsonErr != nil {
196+
w.WriteHeader(jsonErr.Code)
197+
if err := json.NewEncoder(w).Encode(jsonErr.JSON); err != nil {
198+
logger.WithError(err).Error("failed to encode JSON response")
199+
}
200+
return
201+
}
202+
}
177203
f(w, req)
178204
}
179205

Diff for: internal/sqlutil/sqlutil_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -218,5 +218,5 @@ func assertNoError(t *testing.T, err error, msg string) {
218218
if err == nil {
219219
return
220220
}
221-
t.Fatalf(msg)
221+
t.Fatal(msg)
222222
}

Diff for: mediaapi/mediaapi.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
package mediaapi
1616

1717
import (
18-
"github.com/gorilla/mux"
18+
"github.com/neilalexander/harmony/internal/gomatrixserverlib"
1919
"github.com/neilalexander/harmony/internal/gomatrixserverlib/fclient"
20+
"github.com/neilalexander/harmony/internal/httputil"
2021
"github.com/neilalexander/harmony/internal/sqlutil"
2122
"github.com/neilalexander/harmony/mediaapi/routing"
2223
"github.com/neilalexander/harmony/mediaapi/storage"
@@ -27,18 +28,20 @@ import (
2728

2829
// AddPublicRoutes sets up and registers HTTP handlers for the MediaAPI component.
2930
func AddPublicRoutes(
30-
mediaRouter *mux.Router,
31+
routers httputil.Routers,
3132
cm *sqlutil.Connections,
3233
cfg *config.Dendrite,
3334
userAPI userapi.MediaUserAPI,
3435
client *fclient.Client,
36+
fedClient fclient.FederationClient,
37+
keyRing gomatrixserverlib.JSONVerifier,
3538
) {
3639
mediaDB, err := storage.NewMediaAPIDatasource(cm, &cfg.MediaAPI.Database)
3740
if err != nil {
3841
logrus.WithError(err).Panicf("failed to connect to media db")
3942
}
4043

4144
routing.Setup(
42-
mediaRouter, cfg, mediaDB, userAPI, client,
45+
routers, cfg, mediaDB, userAPI, client, fedClient, keyRing,
4346
)
4447
}

0 commit comments

Comments
 (0)