mirror of
https://github.com/hoernschen/dendrite.git
synced 2024-12-27 07:28:27 +00:00
Third Party Proxy Support, 3PL endpoints authed
This commit is contained in:
parent
2bd33d4168
commit
2e6eff2012
15 changed files with 394 additions and 109 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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{},
|
||||
|
|
|
@ -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}",
|
||||
|
|
|
@ -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}
|
||||
|
||||
|
|
Loading…
Reference in a new issue