Third Party Proxy Support, 3PL endpoints authed

This commit is contained in:
Andrew Morgan 2018-07-17 11:58:26 +01:00 committed by user
parent 2bd33d4168
commit 2e6eff2012
15 changed files with 394 additions and 109 deletions

View file

@ -23,12 +23,11 @@ import (
"errors"
"net/http"
"github.com/matrix-org/dendrite/appservice/types"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"github.com/matrix-org/dendrite/clientapi/auth/storage/accounts"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/dendrite/appservice/types"
commonHTTP "github.com/matrix-org/dendrite/common/http"
"github.com/matrix-org/gomatrixserverlib"
opentracing "github.com/opentracing/opentracing-go"
)
@ -75,7 +74,7 @@ type GetProtocolDefinitionRequest struct {
// GetProtocolDefinitionResponse is a response providing a protocol definition
// for the given protocol ID
type GetProtocolDefinitionResponse struct {
ProtocolDefinition string `json:"protocol_definition"`
ProtocolDefinition gomatrixserverlib.RawJSON `json:"protocol_definition"`
}
// GetAllProtocolDefinitionsRequest is a request to the appservice component
@ -89,6 +88,22 @@ type GetAllProtocolDefinitionsResponse struct {
Protocols types.ThirdPartyProtocols `json:"protocols"`
}
// ThirdPartyProxyRequest is a request from a client to a third party lookup
// endpoint on an application service. The job of the homeserver is simply to
// proxy the request, thus the same request/response format can be use for each
// third party lookup endpoint.
type ThirdPartyProxyRequest struct {
ProtocolID string `json:"protocol_id"`
Path string `json:"path"`
Content gomatrixserverlib.RawJSON `json:"content"`
}
// ThirdPartyProxyResponse is a response from an application service to a client
// about a third party lookup request.
type ThirdPartyProxyResponse struct {
Content gomatrixserverlib.RawJSON `json:"content"`
}
// AppServiceQueryAPI is used to query user and room alias data from application
// services
type AppServiceQueryAPI interface {
@ -116,6 +131,11 @@ type AppServiceQueryAPI interface {
req *GetAllProtocolDefinitionsRequest,
response *GetAllProtocolDefinitionsResponse,
) error
ThirdPartyProxy(
ctx context.Context,
req *ThirdPartyProxyRequest,
response *ThirdPartyProxyResponse,
) error
}
// RoomAliasExistsPath is the HTTP path for the RoomAliasExists API
@ -130,6 +150,9 @@ const GetAllProtocolDefinitionsPath = "/api/appservice/GetAllProtocolDefinitions
// AppServiceUserIDExistsPath is the HTTP path for the UserIDExists API
const AppServiceUserIDExistsPath = "/api/appservice/UserIDExists"
// ThirdPartyProxyPath is the HTTP path for the ThirdPartyProxy API
const ThirdPartyProxyPath = "/api/appservice/ThirdPartyProxy"
// httpAppServiceQueryAPI contains the URL to an appservice query API and a
// reference to a httpClient used to reach it
type httpAppServiceQueryAPI struct {
@ -244,3 +267,16 @@ func RetreiveUserProfile(
// profile should not be nil at this point
return profile, nil
}
// ThirdPartyProxy implements AppServiceQueryAPI
func (h *httpAppServiceQueryAPI) ThirdPartyProxy(
ctx context.Context,
request *ThirdPartyProxyRequest,
response *ThirdPartyProxyResponse,
) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "appserviceThirdPartyProxy")
defer span.Finish()
apiURL := h.appserviceURL + ThirdPartyProxyPath
return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
}

View file

@ -133,7 +133,10 @@ func setupWorkers(
workerStates []types.ApplicationServiceWorkerState,
) {
// Clear all old protocol definitions on startup
appserviceDB.ClearProtocolDefinitions(context.TODO())
err := appserviceDB.ClearProtocolDefinitions(context.TODO())
if err != nil {
log.WithError(err).Fatalf("unable to clear appservice protocol definitions from db")
}
// Create a worker that handles transmitting events to a single homeserver
for _, workerState := range workerStates {

View file

@ -17,23 +17,31 @@
package query
import (
"bytes"
"context"
"database/sql"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"sync"
"time"
"github.com/matrix-org/dendrite/appservice/api"
"github.com/matrix-org/dendrite/appservice/storage"
"github.com/matrix-org/dendrite/common"
"github.com/matrix-org/dendrite/common/config"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"
opentracing "github.com/opentracing/opentracing-go"
log "github.com/sirupsen/logrus"
)
const roomAliasExistsPath = "/rooms/"
const userIDExistsPath = "/users/"
const (
roomAliasExistsPath = "/rooms/"
userIDExistsPath = "/users/"
)
// AppServiceQueryAPI is an implementation of api.AppServiceQueryAPI
type AppServiceQueryAPI struct {
@ -42,44 +50,6 @@ type AppServiceQueryAPI struct {
Db *storage.Database
}
// GetProtocolDefinition queries the database for the protocol definition of a
// protocol with given ID
func (a *AppServiceQueryAPI) GetProtocolDefinition(
ctx context.Context,
request *api.GetProtocolDefinitionRequest,
response *api.GetProtocolDefinitionResponse,
) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "ApplicationServiceGetProtocolDefinition")
defer span.Finish()
protocolDefinition, err := a.Db.GetProtocolDefinition(ctx, request.ProtocolID)
if err != nil {
return err
}
response.ProtocolDefinition = protocolDefinition
return nil
}
// GetAllProtocolDefinitions queries the database for all known protocol
// definitions and their IDs
func (a *AppServiceQueryAPI) GetAllProtocolDefinitions(
ctx context.Context,
request *api.GetAllProtocolDefinitionsRequest,
response *api.GetAllProtocolDefinitionsResponse,
) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "ApplicationServiceGetAllProtocolDefinitions")
defer span.Finish()
protocolDefinitions, err := a.Db.GetAllProtocolDefinitions(ctx)
if err != nil {
return err
}
response.Protocols = protocolDefinitions
return nil
}
// RoomAliasExists performs a request to '/room/{roomAlias}' on all known
// handling application services until one admits to owning the room
func (a *AppServiceQueryAPI) RoomAliasExists(
@ -100,6 +70,10 @@ func (a *AppServiceQueryAPI) RoomAliasExists(
if appservice.URL != "" && appservice.IsInterestedInRoomAlias(request.Alias) {
// The full path to the rooms API, includes hs token
URL, err := url.Parse(appservice.URL + roomAliasExistsPath)
if err != nil {
return err
}
URL.Path += request.Alias
apiURL := URL.String() + "?access_token=" + appservice.HSToken
@ -110,15 +84,14 @@ func (a *AppServiceQueryAPI) RoomAliasExists(
return err
}
req = req.WithContext(ctx)
resp, err := a.HTTPClient.Do(req)
if resp != nil {
defer func() {
err = resp.Body.Close()
if err != nil {
log.WithFields(log.Fields{
"appservice_id": appservice.ID,
"status_code": resp.StatusCode,
"appservice": appservice.ID,
"status_code": resp.StatusCode,
}).WithError(err).Error("Unable to close application service response body")
}
}()
@ -137,8 +110,8 @@ func (a *AppServiceQueryAPI) RoomAliasExists(
default:
// Application service reported an error. Warn
log.WithFields(log.Fields{
"appservice_id": appservice.ID,
"status_code": resp.StatusCode,
"appservice": appservice.ID,
"status_code": resp.StatusCode,
}).Warn("Application service responded with non-OK status code")
}
}
@ -177,34 +150,30 @@ func (a *AppServiceQueryAPI) UserIDExists(
if err != nil {
return err
}
resp, err := a.HTTPClient.Do(req.WithContext(ctx))
req = req.WithContext(ctx)
// Make a request to the application service
resp, err := a.HTTPClient.Do(req)
if resp != nil {
defer func() {
err = resp.Body.Close()
if err != nil {
log.WithFields(log.Fields{
"appservice_id": appservice.ID,
"status_code": resp.StatusCode,
}).Error("Unable to close application service response body")
}
}()
defer resp.Body.Close() // nolint: errcheck
}
if err != nil {
log.WithFields(log.Fields{
"appservice_id": appservice.ID,
"appservice": appservice.ID,
}).WithError(err).Error("issue querying user ID on application service")
return err
}
if resp.StatusCode == http.StatusOK {
// StatusOK received from appservice. User ID exists
response.UserIDExists = true
return nil
}
// Log non OK
// Log if return code is not OK
log.WithFields(log.Fields{
"appservice_id": appservice.ID,
"status_code": resp.StatusCode,
"appservice": appservice.ID,
"status_code": resp.StatusCode,
}).Warn("application service responded with non-OK status code")
}
}
@ -213,6 +182,180 @@ func (a *AppServiceQueryAPI) UserIDExists(
return nil
}
// GetProtocolDefinition queries the database for the protocol definition of a
// protocol with given ID
func (a *AppServiceQueryAPI) GetProtocolDefinition(
ctx context.Context,
request *api.GetProtocolDefinitionRequest,
response *api.GetProtocolDefinitionResponse,
) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "ApplicationServiceGetProtocolDefinition")
defer span.Finish()
protocolDefinition, err := a.Db.GetProtocolDefinition(ctx, request.ProtocolID)
if err != nil && err != sql.ErrNoRows {
return err
}
response.ProtocolDefinition = gomatrixserverlib.RawJSON(protocolDefinition)
return nil
}
// GetAllProtocolDefinitions queries the database for all known protocol
// definitions and their IDs
func (a *AppServiceQueryAPI) GetAllProtocolDefinitions(
ctx context.Context,
request *api.GetAllProtocolDefinitionsRequest,
response *api.GetAllProtocolDefinitionsResponse,
) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "ApplicationServiceGetAllProtocolDefinitions")
defer span.Finish()
protocolDefinitions, err := a.Db.GetAllProtocolDefinitions(ctx)
if err != nil {
return err
}
response.Protocols = protocolDefinitions
return nil
}
// ThirdPartyProxy will proxy a request for third party lookup information from
// a client to a connected application service. If a protocol ID is specified,
// the application service that handles it will solely be contacted, otherwise
// all application services will be contacted and their responses concatenated
func (a *AppServiceQueryAPI) ThirdPartyProxy(
ctx context.Context,
request *api.ThirdPartyProxyRequest,
response *api.ThirdPartyProxyResponse,
) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "ApplicationServiceGetAllProtocolDefinitions")
defer span.Finish()
// Create an HTTP client if one does not already exist
if a.HTTPClient == nil {
a.HTTPClient = makeHTTPClient()
}
appservicesToContact := make([]config.ApplicationService, 0, len(a.Cfg.Derived.ApplicationServices))
if request.ProtocolID == "" {
// If no protocol ID was specified, send the request to all known application
// services in parallel and stitch together the results
appservicesToContact = a.Cfg.Derived.ApplicationServices
} else {
// Otherwise simply proxy the request to the application services handling
// this protocol and return the result
for _, as := range a.Cfg.Derived.ApplicationServices {
for _, protocol := range as.Protocols {
if request.ProtocolID == protocol {
appservicesToContact = append(appservicesToContact, as)
break
}
}
}
}
// Contact each application service in parallel, wait for all to finish before continuing
var wg sync.WaitGroup
var err error
responseSlice := make([]interface{}, 0)
for _, appservice := range appservicesToContact {
// Increment goroutine waitgroup counter
wg.Add(1)
go func(ctx context.Context,
h *http.Client,
req *api.ThirdPartyProxyRequest,
as config.ApplicationService,
r *[]interface{},
) {
// Decrement waitgroup counter once the request has finished
defer wg.Done()
// Contact the application service, return nil or error if something went wrong
contactApplicationService(ctx, h, req, as, r)
}(ctx, a.HTTPClient, request, appservice, &responseSlice)
}
// Wait for all requests to finish
wg.Wait()
// Convert the slice of responses back to JSON
response.Content, err = json.Marshal(responseSlice)
return err
}
// contactApplicationService proxies a third party lookup request to an
// application service
func contactApplicationService(
ctx context.Context,
httpClient *http.Client,
request *api.ThirdPartyProxyRequest,
as config.ApplicationService,
responseSlice *[]interface{},
) {
// Build the request with body and parameters to the application service
if as.URL == "" {
return
}
// Send a request to each application service. If one responds that it has
// created the room, immediately return.
requestURL := as.URL + request.Path
req, err := http.NewRequest(http.MethodGet, requestURL, bytes.NewReader(request.Content))
if err != nil {
log.WithFields(log.Fields{
"appservice": as.ID,
}).WithError(err).Error("problem building proxy request to application service")
return
}
req = req.WithContext(ctx)
// Make a request to the application service
resp, err := httpClient.Do(req)
if resp != nil {
defer resp.Body.Close() // nolint: errcheck
}
if err != nil {
log.WithFields(log.Fields{
"appservice": as.ID,
}).WithError(err).Warn("unable to proxy request to application service")
return
}
if resp.StatusCode != http.StatusOK {
log.WithFields(log.Fields{
"appservice": as.ID,
"status_code": resp.StatusCode,
}).Warn("non-OK response from application server while proxying request")
return
}
// Unmarshal response body into a generic slice and append to the slice of
// existing responses, to eventually be returned to the client
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.WithFields(log.Fields{
"appservice": as.ID,
}).WithError(err).Warn("unable to read response from application server while proxying request")
return
}
// Temporary slice to unmarshal into
querySlice := make([]interface{}, 0)
err = json.Unmarshal(body, &querySlice)
if err != nil {
log.WithFields(log.Fields{
"appservice": as.ID,
}).WithError(err).Warn("unable to unmarshal response from application server while proxying request")
return
}
// Append to existing responses
fmt.Println("Adding", querySlice)
*responseSlice = append(*responseSlice, querySlice...)
}
// makeHTTPClient creates an HTTP client with certain options that will be used for all query requests to application services
func makeHTTPClient() *http.Client {
return &http.Client{
@ -279,4 +422,21 @@ func (a *AppServiceQueryAPI) SetupHTTP(servMux *http.ServeMux) {
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
servMux.Handle(
api.ThirdPartyProxyPath,
common.MakeInternalAPI("appserviceThirdPartyProxy", func(req *http.Request) util.JSONResponse {
var request api.ThirdPartyProxyRequest
var response api.ThirdPartyProxyResponse
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.ErrorResponse(err)
}
if err := a.ThirdPartyProxy(req.Context(), &request, &response); err != nil {
if err == sql.ErrNoRows {
return util.JSONResponse{Code: http.StatusNotFound, JSON: &response}
}
return util.ErrorResponse(err)
}
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
}

View file

@ -27,7 +27,12 @@ import (
"github.com/matrix-org/util"
)
const pathPrefixApp = "/_matrix/app/r0"
const (
// PathPrefixApp for current stable application services API version
PathPrefixApp = "/_matrix/app/r0"
// PathPrefixAppUnstable is the unstable application services API version
PathPrefixAppUnstable = "/_matrix/app/unstable"
)
// Setup registers HTTP handlers with the given ServeMux. It also supplies the given http.Client
// to clients which need to make outbound HTTP requests.
@ -38,7 +43,7 @@ func Setup(
federation *gomatrixserverlib.FederationClient, // nolint: unparam
transactionsCache *transactions.Cache, // nolint: unparam
) {
appMux := apiMux.PathPrefix(pathPrefixApp).Subrouter()
appMux := apiMux.PathPrefix(PathPrefixApp).Subrouter()
appMux.Handle("/alias",
common.MakeExternalAPI("alias", func(req *http.Request) util.JSONResponse {

View file

@ -139,7 +139,7 @@ func (d *Database) StoreProtocolDefinition(
return d.thirdparty.insertProtocolDefinition(ctx, protocolID, protocolDefinition)
}
// ClearProtocolDefinition clears all protocol definition entries in the
// ClearProtocolDefinitions clears all protocol definition entries in the
// database. This is done on each startup to wipe old protocol definitions from
// previous application services.
func (d *Database) ClearProtocolDefinitions(

View file

@ -26,8 +26,10 @@ import (
const thirdPartySchema = `
-- Stores protocol definitions for clients to later request
CREATE TABLE IF NOT EXISTS appservice_third_party_protocol_def (
-- The ID of the procotol
-- The ID of the protocol
protocol_id TEXT NOT NULL PRIMARY KEY,
-- The URL of the application service handling this protocol
appservice_url TEXT NOT NULL,
-- The JSON-encoded protocol definition
protocol_definition TEXT NOT NULL,
UNIQUE(protocol_id)
@ -106,6 +108,7 @@ func (s *thirdPartyStatements) selectAllProtocolDefinitions(
ctx context.Context,
) (protocols types.ThirdPartyProtocols, err error) {
protocolDefinitionRows, err := s.selectAllProtocolDefinitionsStmt.QueryContext(ctx)
protocols = make(types.ThirdPartyProtocols)
if err != nil && err != sql.ErrNoRows {
return
}
@ -116,12 +119,14 @@ func (s *thirdPartyStatements) selectAllProtocolDefinitions(
}
}()
// Extract protocol information from each row
for protocolDefinitionRows.Next() {
var protocolID, protocolDefinition string
if err = protocolDefinitionRows.Scan(&protocolID, &protocolDefinition); err != nil {
return nil, err
}
// Store each protocol in a map indexed by protocol ID
protocols[protocolID] = gomatrixserverlib.RawJSON(protocolDefinition)
}
@ -129,7 +134,7 @@ func (s *thirdPartyStatements) selectAllProtocolDefinitions(
}
// insertProtocolDefinition inserts a protocol ID along with its definition in
// order for clients to later retreive it from the client-server API.
// order for clients to later retrieve it from the client-server API.
func (s *thirdPartyStatements) insertProtocolDefinition(
ctx context.Context,
protocolID, protocolDefinition string,

View file

@ -79,7 +79,13 @@ func ThirdPartyWorker(
}
// Cache protocol definition for clients to request later
storeProtocolDefinition(ctx, db, appservice, protocolID, protocolDefinition)
err = storeProtocolDefinition(ctx, db, protocolID, protocolDefinition)
if err != nil {
log.WithFields(log.Fields{
"appservice": appservice.ID,
"definition": protocolDefinition,
}).WithError(err).Fatalf("unable to store appservice protocol definition in db")
}
}
}
@ -144,7 +150,6 @@ func retreiveProtocolInformation(
func storeProtocolDefinition(
ctx context.Context,
db *storage.Database,
appservice config.ApplicationService,
protocolID, protocolDefinition string,
) error {
return db.StoreProtocolDefinition(ctx, protocolID, protocolDefinition)

View file

@ -158,6 +158,7 @@ func verifyAccessToken(req *http.Request, deviceDB DeviceDatabase) (device *auth
}
return
}
fmt.Println("Got token:", token)
device, err = deviceDB.GetDeviceByAccessToken(req.Context(), token)
if err != nil {
if err == sql.ErrNoRows {

View file

@ -35,9 +35,14 @@ import (
"github.com/matrix-org/util"
)
const pathPrefixV1 = "/_matrix/client/api/v1"
const pathPrefixR0 = "/_matrix/client/r0"
const pathPrefixUnstable = "/_matrix/client/unstable"
const (
// PathPrefixClientAPI is the deprecated client server API version
PathPrefixClientAPI = "/_matrix/client/api/v1"
// PathPrefixClient is the current stable client server API version
PathPrefixClient = "/_matrix/client/r0"
// PathPrefixClientUnstable is the unstable client server API version
PathPrefixClientUnstable = "/_matrix/client/unstable"
)
// Setup registers HTTP handlers with the given ServeMux. It also supplies the given http.Client
// to clients which need to make outbound HTTP requests.
@ -56,7 +61,6 @@ func Setup(
typingProducer *producers.TypingServerProducer,
transactionsCache *transactions.Cache,
) {
apiMux.Handle("/_matrix/client/versions",
common.MakeExternalAPI("versions", func(req *http.Request) util.JSONResponse {
return util.JSONResponse{
@ -73,9 +77,9 @@ func Setup(
}),
).Methods(http.MethodGet, http.MethodOptions)
r0mux := apiMux.PathPrefix(pathPrefixR0).Subrouter()
v1mux := apiMux.PathPrefix(pathPrefixV1).Subrouter()
unstableMux := apiMux.PathPrefix(pathPrefixUnstable).Subrouter()
r0mux := apiMux.PathPrefix(PathPrefixClient).Subrouter()
v1mux := apiMux.PathPrefix(PathPrefixClientAPI).Subrouter()
unstableMux := apiMux.PathPrefix(PathPrefixClientUnstable).Subrouter()
authData := auth.Data{accountDB, deviceDB, cfg.Derived.ApplicationServices}
@ -313,16 +317,6 @@ func Setup(
}),
).Methods(http.MethodGet, http.MethodOptions)
unstableMux.Handle("/thirdparty/protocols",
common.MakeExternalAPI("thirdparty_protocols", func(req *http.Request) util.JSONResponse {
// TODO: Return the third party protcols
return util.JSONResponse{
Code: http.StatusOK,
JSON: struct{}{},
}
}),
).Methods(http.MethodGet, http.MethodOptions)
r0mux.Handle("/rooms/{roomID}/initialSync",
common.MakeExternalAPI("rooms_initial_sync", func(req *http.Request) util.JSONResponse {
// TODO: Allow people to peek into rooms.
@ -390,18 +384,44 @@ func Setup(
// Third party lookups
r0mux.Handle("/thirdparty/protocol/{protocolID}",
common.MakeExternalAPI("get_protocols", func(req *http.Request) util.JSONResponse {
common.MakeAuthAPI("third_party_protocols", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
vars := mux.Vars(req)
return GetThirdPartyProtocol(req, asAPI, vars["protocolID"])
}),
).Methods(http.MethodGet, http.MethodOptions)
r0mux.Handle("/thirdparty/protocols",
common.MakeExternalAPI("get_protocols", func(req *http.Request) util.JSONResponse {
common.MakeAuthAPI("third_party_protocol", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
return GetThirdPartyProtocols(req, asAPI)
}),
).Methods(http.MethodGet, http.MethodOptions)
r0mux.Handle("/thirdparty/location/{protocolID}",
common.MakeAuthAPI("third_party_protocol", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
vars := mux.Vars(req)
return ThirdPartyProxy(req, asAPI, vars["protocolID"])
}),
).Methods(http.MethodGet, http.MethodOptions)
r0mux.Handle("/thirdparty/user/{protocolID}",
common.MakeAuthAPI("third_party_protocol", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
vars := mux.Vars(req)
return ThirdPartyProxy(req, asAPI, vars["protocolID"])
}),
).Methods(http.MethodGet, http.MethodOptions)
r0mux.Handle("/thirdparty/location",
common.MakeAuthAPI("third_party_protocol", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
return ThirdPartyProxy(req, asAPI, "")
}),
).Methods(http.MethodGet, http.MethodOptions)
r0mux.Handle("/thirdparty/user",
common.MakeAuthAPI("third_party_protocol", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
return ThirdPartyProxy(req, asAPI, "")
}),
).Methods(http.MethodGet, http.MethodOptions)
// Stub implementations for sytest
r0mux.Handle("/events",
common.MakeExternalAPI("events", func(req *http.Request) util.JSONResponse {

View file

@ -15,11 +15,14 @@
package routing
import (
"encoding/json"
"io/ioutil"
"net/http"
"strings"
appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
appserviceRouting "github.com/matrix-org/dendrite/appservice/routing"
"github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/util"
)
@ -39,6 +42,14 @@ func GetThirdPartyProtocol(
return httputil.LogThenError(req, err)
}
// Account for unknown protocol/empty definition
if len(queryRes.ProtocolDefinition) == 0 {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: jsonerror.NotFound("unknown protocol"),
}
}
return util.JSONResponse{
Code: http.StatusOK,
JSON: queryRes.ProtocolDefinition,
@ -60,15 +71,44 @@ func GetThirdPartyProtocols(
// TODO: Check what we get if no protocols defined by anyone
// Marshal protocols to JSON
protocolJSON, err := json.Marshal(queryRes.Protocols)
// Return protocol IDs along with definitions
return util.JSONResponse{
Code: http.StatusOK,
JSON: queryRes.Protocols,
}
}
// ThirdPartyProxy proxies a third party lookup request to the handler
// application service
func ThirdPartyProxy(
req *http.Request,
asAPI appserviceAPI.AppServiceQueryAPI,
protocolID string,
) util.JSONResponse {
// Read the request body
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return httputil.LogThenError(req, err)
}
// Return protocol IDs along with definitions
// Rewrite the path from a client URL to an application service URL
requestPath := strings.Replace(req.URL.String(), PathPrefixClient, appserviceRouting.PathPrefixAppUnstable, 1)
// Proxy the location lookup to the appservices component, which will send it
// off to an application service
queryReq := appserviceAPI.ThirdPartyProxyRequest{
ProtocolID: protocolID,
Path: requestPath,
Content: body,
}
var queryRes appserviceAPI.ThirdPartyProxyResponse
if err := asAPI.ThirdPartyProxy(req.Context(), &queryReq, &queryRes); err != nil {
return httputil.LogThenError(req, err)
}
// Return response to the client
return util.JSONResponse{
Code: http.StatusOK,
JSON: protocolJSON,
JSON: queryRes.Content,
}
}

View file

@ -231,8 +231,8 @@ func checkErrors(config *Dendrite) (err error) {
// Namespace-related checks
for key, namespaceSlice := range appservice.NamespaceMap {
for _, namespace := range namespaceSlice {
if err := validateNamespace(&appservice, key, &namespace, groupIDRegexp); err != nil {
return err
if err = validateNamespace(&appservice, key, &namespace, groupIDRegexp); err != nil {
return
}
}
}
@ -240,7 +240,7 @@ func checkErrors(config *Dendrite) (err error) {
// Check if the url has trailing /'s. If so, remove them
appservice.URL = strings.TrimRight(appservice.URL, "/")
if err = duplicationCheck(appservice, &idMap, &tokenMap, &protocolMap); err != nil {
return err
return
}
// TODO: Remove once rate_limited is implemented

View file

@ -30,8 +30,10 @@ import (
)
const (
pathPrefixV2Keys = "/_matrix/key/v2"
pathPrefixV1Federation = "/_matrix/federation/v1"
// PathPrefixKeys is the current Key API version
PathPrefixKeys = "/_matrix/key/v2"
// PathPrefixFederation is the current Federation API version
PathPrefixFederation = "/_matrix/federation/v1"
)
// Setup registers HTTP handlers with the given ServeMux.
@ -47,8 +49,8 @@ func Setup(
accountDB *accounts.Database,
deviceDB *devices.Database,
) {
v2keysmux := apiMux.PathPrefix(pathPrefixV2Keys).Subrouter()
v1fedmux := apiMux.PathPrefix(pathPrefixV1Federation).Subrouter()
v2keysmux := apiMux.PathPrefix(PathPrefixKeys).Subrouter()
v1fedmux := apiMux.PathPrefix(PathPrefixFederation).Subrouter()
localKeys := common.MakeExternalAPI("localkeys", func(req *http.Request) util.JSONResponse {
return LocalKeys(cfg)

View file

@ -31,7 +31,10 @@ import (
"github.com/prometheus/client_golang/prometheus"
)
const pathPrefixR0 = "/_matrix/media/v1"
const (
// PathPrefixMedia is the current Media API version
PathPrefixMedia = "/_matrix/media/v1"
)
// Setup registers the media API HTTP handlers
func Setup(
@ -41,7 +44,7 @@ func Setup(
deviceDB *devices.Database,
client *gomatrixserverlib.Client,
) {
r0mux := apiMux.PathPrefix(pathPrefixR0).Subrouter()
r0mux := apiMux.PathPrefix(PathPrefixMedia).Subrouter()
activeThumbnailGeneration := &types.ActiveThumbnailGeneration{
PathToResult: map[string]*types.ThumbnailGenerationResult{},

View file

@ -27,12 +27,14 @@ import (
"github.com/matrix-org/util"
)
const pathPrefixR0 = "/_matrix/client/r0"
const (
// PathPrefixClientPublicRooms is the current Client-Server Public Rooms API version
PathPrefixClientPublicRooms = "/_matrix/client/r0"
)
// Setup configures the given mux with publicroomsapi server listeners
func Setup(apiMux *mux.Router, deviceDB *devices.Database, publicRoomsDB *storage.PublicRoomsServerDatabase) {
r0mux := apiMux.PathPrefix(pathPrefixR0).Subrouter()
r0mux := apiMux.PathPrefix(PathPrefixClientPublicRooms).Subrouter()
authData := auth.Data{nil, deviceDB, nil}
r0mux.Handle("/directory/list/room/{roomID}",

View file

@ -27,11 +27,14 @@ import (
"github.com/matrix-org/util"
)
const pathPrefixR0 = "/_matrix/client/r0"
const (
// PathPrefixClientSync is the current Client-Server Sync API version
PathPrefixClientSync = "/_matrix/client/r0"
)
// Setup configures the given mux with sync-server listeners
func Setup(apiMux *mux.Router, srp *sync.RequestPool, syncDB *storage.SyncServerDatabase, deviceDB *devices.Database) {
r0mux := apiMux.PathPrefix(pathPrefixR0).Subrouter()
r0mux := apiMux.PathPrefix(PathPrefixClientSync).Subrouter()
authData := auth.Data{nil, deviceDB, nil}