mirror of
https://github.com/element-hq/dendrite.git
synced 2025-09-13 21:02:25 +03:00
mas: first successful attempt of login with via mas
This commit is contained in:
parent
150be588f5
commit
63a199cec3
31 changed files with 1224 additions and 287 deletions
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/tidwall/gjson"
|
||||
|
||||
capi "github.com/element-hq/dendrite/clientapi/api"
|
||||
"github.com/element-hq/dendrite/clientapi/auth"
|
||||
"github.com/element-hq/dendrite/test"
|
||||
"github.com/element-hq/dendrite/test/testrig"
|
||||
"github.com/element-hq/dendrite/userapi"
|
||||
|
@ -48,7 +49,8 @@ func TestAdminCreateToken(t *testing.T) {
|
|||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
userVerifier := auth.DefaultUserVerifier{UserAPI: userAPI}
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, &userVerifier, caching.DisableMetrics)
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
aliceAdmin: {},
|
||||
bob: {},
|
||||
|
@ -199,7 +201,8 @@ func TestAdminListRegistrationTokens(t *testing.T) {
|
|||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
userVerifier := auth.DefaultUserVerifier{UserAPI: userAPI}
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, &userVerifier, caching.DisableMetrics)
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
aliceAdmin: {},
|
||||
bob: {},
|
||||
|
@ -317,7 +320,8 @@ func TestAdminGetRegistrationToken(t *testing.T) {
|
|||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
userVerifier := auth.DefaultUserVerifier{UserAPI: userAPI}
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, &userVerifier, caching.DisableMetrics)
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
aliceAdmin: {},
|
||||
bob: {},
|
||||
|
@ -418,7 +422,8 @@ func TestAdminDeleteRegistrationToken(t *testing.T) {
|
|||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
userVerifier := auth.DefaultUserVerifier{UserAPI: userAPI}
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, &userVerifier, caching.DisableMetrics)
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
aliceAdmin: {},
|
||||
bob: {},
|
||||
|
@ -512,7 +517,8 @@ func TestAdminUpdateRegistrationToken(t *testing.T) {
|
|||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
userVerifier := auth.DefaultUserVerifier{UserAPI: userAPI}
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, &userVerifier, caching.DisableMetrics)
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
aliceAdmin: {},
|
||||
bob: {},
|
||||
|
@ -697,7 +703,8 @@ func TestAdminResetPassword(t *testing.T) {
|
|||
// Needed for changing the password/login
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
// We mostly need the userAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
userVerifier := auth.DefaultUserVerifier{UserAPI: userAPI}
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, &userVerifier, caching.DisableMetrics)
|
||||
|
||||
// Create the users in the userapi and login
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
|
@ -801,8 +808,9 @@ func TestPurgeRoom(t *testing.T) {
|
|||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
userVerifier := auth.DefaultUserVerifier{UserAPI: userAPI}
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, &userVerifier, caching.DisableMetrics)
|
||||
|
||||
// Create the users in the userapi and login
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
|
@ -872,8 +880,10 @@ func TestAdminEvacuateRoom(t *testing.T) {
|
|||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
userVerifier := auth.DefaultUserVerifier{UserAPI: userAPI}
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, &userVerifier, caching.DisableMetrics)
|
||||
|
||||
// Create the users in the userapi and login
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
|
@ -976,8 +986,10 @@ func TestAdminEvacuateUser(t *testing.T) {
|
|||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
userVerifier := auth.DefaultUserVerifier{UserAPI: userAPI}
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, &userVerifier, caching.DisableMetrics)
|
||||
|
||||
// Create the users in the userapi and login
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
|
@ -1059,8 +1071,10 @@ func TestAdminMarkAsStale(t *testing.T) {
|
|||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
userVerifier := auth.DefaultUserVerifier{UserAPI: userAPI}
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, &userVerifier, caching.DisableMetrics)
|
||||
|
||||
// Create the users in the userapi and login
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
|
@ -1147,8 +1161,10 @@ func TestAdminQueryEventReports(t *testing.T) {
|
|||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
userVerifier := auth.DefaultUserVerifier{UserAPI: userAPI}
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, &userVerifier, caching.DisableMetrics)
|
||||
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
alice: {},
|
||||
|
@ -1376,8 +1392,10 @@ func TestEventReportsGetDelete(t *testing.T) {
|
|||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
userVerifier := auth.DefaultUserVerifier{UserAPI: userAPI}
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, &userVerifier, caching.DisableMetrics)
|
||||
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
alice: {},
|
||||
|
|
|
@ -16,8 +16,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/element-hq/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
||||
// OWASP recommends at least 128 bits of entropy for tokens: https://www.owasp.org/index.php/Insufficient_Session-ID_Length
|
||||
|
@ -37,51 +35,6 @@ type AccountDatabase interface {
|
|||
GetAccountByPassword(ctx context.Context, localpart, password string) (*api.Account, error)
|
||||
}
|
||||
|
||||
// VerifyUserFromRequest authenticates the HTTP request,
|
||||
// on success returns Device of the requester.
|
||||
// Finds local user or an application service user.
|
||||
// Note: For an AS user, AS dummy device is returned.
|
||||
// On failure returns an JSON error response which can be sent to the client.
|
||||
func VerifyUserFromRequest(
|
||||
req *http.Request, userAPI api.QueryAcccessTokenAPI,
|
||||
) (*api.Device, *util.JSONResponse) {
|
||||
// Try to find the Application Service user
|
||||
token, err := ExtractAccessToken(req)
|
||||
if err != nil {
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: spec.MissingToken(err.Error()),
|
||||
}
|
||||
}
|
||||
var res api.QueryAccessTokenResponse
|
||||
err = userAPI.QueryAccessToken(req.Context(), &api.QueryAccessTokenRequest{
|
||||
AccessToken: token,
|
||||
AppServiceUserID: req.URL.Query().Get("user_id"),
|
||||
}, &res)
|
||||
if err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("userAPI.QueryAccessToken failed")
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.InternalServerError{},
|
||||
}
|
||||
}
|
||||
if res.Err != "" {
|
||||
if strings.HasPrefix(strings.ToLower(res.Err), "forbidden:") { // TODO: use actual error and no string comparison
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.Forbidden(res.Err),
|
||||
}
|
||||
}
|
||||
}
|
||||
if res.Device == nil {
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: spec.UnknownToken("Unknown token"),
|
||||
}
|
||||
}
|
||||
return res.Device, nil
|
||||
}
|
||||
|
||||
// GenerateAccessToken creates a new access token. Returns an error if failed to generate
|
||||
// random bytes.
|
||||
func GenerateAccessToken() (string, error) {
|
||||
|
|
|
@ -11,4 +11,5 @@ const (
|
|||
LoginTypeRecaptcha = "m.login.recaptcha"
|
||||
LoginTypeApplicationService = "m.login.application_service"
|
||||
LoginTypeToken = "m.login.token"
|
||||
LoginTypeCrossSigningReset = "org.matrix.cross_signing_reset"
|
||||
)
|
||||
|
|
59
clientapi/auth/default_user_verifier.go
Normal file
59
clientapi/auth/default_user_verifier.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/element-hq/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
||||
// DefaultUserVerifier implements UserVerifier interface
|
||||
type DefaultUserVerifier struct {
|
||||
UserAPI api.QueryAcccessTokenAPI
|
||||
}
|
||||
|
||||
// VerifyUserFromRequest authenticates the HTTP request,
|
||||
// on success returns Device of the requester.
|
||||
// Finds local user or an application service user.
|
||||
// Note: For an AS user, AS dummy device is returned.
|
||||
// On failure returns an JSON error response which can be sent to the client.
|
||||
func (d *DefaultUserVerifier) VerifyUserFromRequest(req *http.Request) (*api.Device, *util.JSONResponse) {
|
||||
util.GetLogger(req.Context()).Debug("Default VerifyUserFromRequest")
|
||||
// Try to find the Application Service user
|
||||
token, err := ExtractAccessToken(req)
|
||||
if err != nil {
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: spec.MissingToken(err.Error()),
|
||||
}
|
||||
}
|
||||
var res api.QueryAccessTokenResponse
|
||||
err = d.UserAPI.QueryAccessToken(req.Context(), &api.QueryAccessTokenRequest{
|
||||
AccessToken: token,
|
||||
AppServiceUserID: req.URL.Query().Get("user_id"),
|
||||
}, &res)
|
||||
if err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("userAPI.QueryAccessToken failed")
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.InternalServerError{},
|
||||
}
|
||||
}
|
||||
if res.Err != "" {
|
||||
if strings.HasPrefix(strings.ToLower(res.Err), "forbidden:") { // TODO: use actual error and no string comparison
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.Forbidden(res.Err),
|
||||
}
|
||||
}
|
||||
}
|
||||
if res.Device == nil {
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: spec.UnknownToken("Unknown token"),
|
||||
}
|
||||
}
|
||||
return res.Device, nil
|
||||
}
|
|
@ -36,7 +36,9 @@ func AddPublicRoutes(
|
|||
fsAPI federationAPI.ClientFederationAPI,
|
||||
userAPI userapi.ClientUserAPI,
|
||||
userDirectoryProvider userapi.QuerySearchProfilesAPI,
|
||||
extRoomsProvider api.ExtraPublicRoomsProvider, enableMetrics bool,
|
||||
extRoomsProvider api.ExtraPublicRoomsProvider,
|
||||
userVerifier httputil.UserVerifier,
|
||||
enableMetrics bool,
|
||||
) {
|
||||
js, natsClient := natsInstance.Prepare(processContext, &cfg.Global.JetStream)
|
||||
|
||||
|
@ -55,6 +57,7 @@ func AddPublicRoutes(
|
|||
cfg, rsAPI, asAPI,
|
||||
userAPI, userDirectoryProvider, federation,
|
||||
syncProducer, transactionsCache, fsAPI,
|
||||
extRoomsProvider, natsClient, enableMetrics,
|
||||
extRoomsProvider, natsClient,
|
||||
userVerifier, enableMetrics,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"golang.org/x/exp/constraints"
|
||||
|
||||
clientapi "github.com/element-hq/dendrite/clientapi/api"
|
||||
clienthttputil "github.com/element-hq/dendrite/clientapi/httputil"
|
||||
"github.com/element-hq/dendrite/internal/httputil"
|
||||
roomserverAPI "github.com/element-hq/dendrite/roomserver/api"
|
||||
"github.com/element-hq/dendrite/setup/config"
|
||||
|
@ -517,6 +518,166 @@ func AdminCheckUsernameAvailable(
|
|||
}
|
||||
}
|
||||
|
||||
func AdminHandleUserDeviceByUserID(
|
||||
req *http.Request,
|
||||
userAPI userapi.ClientUserAPI,
|
||||
) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
userID, ok := vars["userID"]
|
||||
if !ok {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.MissingParam("Expecting user ID."),
|
||||
}
|
||||
}
|
||||
|
||||
logger := util.GetLogger(req.Context())
|
||||
|
||||
switch req.Method {
|
||||
case http.MethodPost:
|
||||
local, domain, err := gomatrixserverlib.SplitID('@', userID)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidParam(userID),
|
||||
}
|
||||
}
|
||||
var payload struct {
|
||||
DeviceID string `json:"device_id"`
|
||||
}
|
||||
if resErr := clienthttputil.UnmarshalJSONRequest(req, &payload); resErr != nil {
|
||||
return *resErr
|
||||
}
|
||||
|
||||
var rs userapi.PerformDeviceCreationResponse
|
||||
if err := userAPI.PerformDeviceCreation(req.Context(), &userapi.PerformDeviceCreationRequest{
|
||||
Localpart: local,
|
||||
ServerName: domain,
|
||||
DeviceID: &payload.DeviceID,
|
||||
IPAddr: "",
|
||||
UserAgent: req.UserAgent(),
|
||||
NoDeviceListUpdate: false,
|
||||
FromRegistration: false,
|
||||
}, &rs); err != nil {
|
||||
logger.WithError(err).Debug("PerformDeviceCreation failed")
|
||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
logger.WithError(err).Debug("PerformDeviceCreation succeeded")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusCreated,
|
||||
JSON: struct{}{},
|
||||
}
|
||||
case http.MethodGet:
|
||||
var res userapi.QueryDevicesResponse
|
||||
if err := userAPI.QueryDevices(req.Context(), &userapi.QueryDevicesRequest{UserID: userID}, &res); err != nil {
|
||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
jsonDevices := make([]deviceJSON, 0, len(res.Devices))
|
||||
for i := range res.Devices {
|
||||
d := &res.Devices[i]
|
||||
jsonDevices = append(jsonDevices, deviceJSON{
|
||||
DeviceID: d.ID,
|
||||
DisplayName: d.DisplayName,
|
||||
LastSeenIP: d.LastSeenIP,
|
||||
LastSeenTS: d.LastSeenTS,
|
||||
})
|
||||
}
|
||||
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: struct {
|
||||
Devices []deviceJSON `json:"devices"`
|
||||
Total int `json:"total"`
|
||||
}{
|
||||
Devices: jsonDevices,
|
||||
Total: len(res.Devices),
|
||||
},
|
||||
}
|
||||
default:
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusMethodNotAllowed,
|
||||
JSON: struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type adminExternalID struct {
|
||||
AuthProvider string `json:"auth_provider"`
|
||||
ExternalID string `json:"external_id"`
|
||||
}
|
||||
|
||||
type adminCreateOrModifyAccountRequest struct {
|
||||
DisplayName string `json:"display_name"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
// TODO: the following fields are not used here, but they are used in Synapse. Probably we should reproduce the logic of the
|
||||
// endpoint fully compatible.
|
||||
// Password string `json:"password"`
|
||||
// LogoutDevices bool `json:"logout_devices"`
|
||||
// Threepids json.RawMessage `json:"threepids"`
|
||||
// ExternalIDs []adminExternalID `json:"external_ids"`
|
||||
// Admin bool `json:"admin"`
|
||||
// Deactivated bool `json:"deactivated"`
|
||||
// Locked bool `json:"locked"`
|
||||
}
|
||||
|
||||
func AdminCreateOrModifyAccount(req *http.Request, userAPI userapi.ClientUserAPI) util.JSONResponse {
|
||||
logger := util.GetLogger(req.Context())
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
userID, ok := vars["userID"]
|
||||
if !ok {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.MissingParam("Expecting user ID."),
|
||||
}
|
||||
}
|
||||
local, domain, err := gomatrixserverlib.SplitID('@', userID)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidParam(userID),
|
||||
}
|
||||
}
|
||||
var r adminCreateOrModifyAccountRequest
|
||||
if resErr := clienthttputil.UnmarshalJSONRequest(req, &r); resErr != nil {
|
||||
logger.Debugf("UnmarshalJSONRequest failed: %+v", *resErr)
|
||||
return *resErr
|
||||
}
|
||||
logger.Debugf("adminCreateOrModifyAccountRequest is: %+v", r)
|
||||
statusCode := http.StatusOK
|
||||
{
|
||||
var res userapi.PerformAccountCreationResponse
|
||||
err = userAPI.PerformAccountCreation(req.Context(), &userapi.PerformAccountCreationRequest{
|
||||
AccountType: userapi.AccountTypeUser,
|
||||
Localpart: local,
|
||||
ServerName: domain,
|
||||
OnConflict: api.ConflictUpdate,
|
||||
AvatarURL: r.AvatarURL,
|
||||
DisplayName: r.DisplayName,
|
||||
}, &res)
|
||||
if err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Debugln("Failed creating account")
|
||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
if res.AccountCreated {
|
||||
statusCode = http.StatusCreated
|
||||
}
|
||||
}
|
||||
|
||||
return util.JSONResponse{
|
||||
Code: statusCode,
|
||||
JSON: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// GetEventReports returns reported events for a given user/room.
|
||||
func GetEventReports(
|
||||
req *http.Request,
|
||||
|
|
|
@ -8,6 +8,8 @@ package routing
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/element-hq/dendrite/clientapi/auth"
|
||||
"github.com/element-hq/dendrite/clientapi/auth/authtypes"
|
||||
|
@ -39,28 +41,83 @@ func UploadCrossSigningDeviceKeys(
|
|||
if sessionID == "" {
|
||||
sessionID = util.RandomString(sessionIDLength)
|
||||
}
|
||||
if uploadReq.Auth.Type != authtypes.LoginTypePassword {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: newUserInteractiveResponse(
|
||||
sessionID,
|
||||
[]authtypes.Flow{
|
||||
{
|
||||
Stages: []authtypes.LoginType{authtypes.LoginTypePassword},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
),
|
||||
|
||||
isCrossSigningSetup := false
|
||||
masterKeyUpdatableWithoutUIA := false
|
||||
{
|
||||
var keysResp api.QueryMasterKeysResponse
|
||||
keyserverAPI.QueryMasterKeys(req.Context(), &api.QueryMasterKeysRequest{UserID: device.UserID}, &keysResp)
|
||||
if err := keysResp.Error; err != nil {
|
||||
return convertKeyError(err)
|
||||
}
|
||||
if k := keysResp.Key; k != nil {
|
||||
isCrossSigningSetup = true
|
||||
if k.UpdatableWithoutUIABeforeMs != nil {
|
||||
masterKeyUpdatableWithoutUIA = time.Now().UnixMilli() < *k.UpdatableWithoutUIABeforeMs
|
||||
}
|
||||
}
|
||||
}
|
||||
typePassword := auth.LoginTypePassword{
|
||||
GetAccountByPassword: accountAPI.QueryAccountByPassword,
|
||||
Config: cfg,
|
||||
|
||||
if isCrossSigningSetup {
|
||||
// With MSC3861, UIA is not possible. Instead, the auth service has to explicitly mark the master key as replaceable.
|
||||
if cfg.MSCs.MSC3861Enabled() {
|
||||
if !masterKeyUpdatableWithoutUIA {
|
||||
url := ""
|
||||
if m := cfg.MSCs.MSC3861; m.AccountManagementURL != "" {
|
||||
url = strings.Join([]string{m.AccountManagementURL, "?action=", authtypes.LoginTypeCrossSigningReset}, "")
|
||||
} else {
|
||||
url = m.Issuer
|
||||
}
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: newUserInteractiveResponse(
|
||||
"dummy",
|
||||
[]authtypes.Flow{
|
||||
{
|
||||
Stages: []authtypes.LoginType{authtypes.LoginTypeCrossSigningReset},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
authtypes.LoginTypeCrossSigningReset: map[string]string{
|
||||
"url": url,
|
||||
},
|
||||
},
|
||||
strings.Join([]string{
|
||||
"To reset your end-to-end encryption cross-signing, identity, you first need to approve it at",
|
||||
url,
|
||||
"and then try again.",
|
||||
}, " "),
|
||||
),
|
||||
}
|
||||
}
|
||||
// XXX: is it necessary?
|
||||
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypeCrossSigningReset)
|
||||
} else {
|
||||
if uploadReq.Auth.Type != authtypes.LoginTypePassword {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: newUserInteractiveResponse(
|
||||
sessionID,
|
||||
[]authtypes.Flow{
|
||||
{
|
||||
Stages: []authtypes.LoginType{authtypes.LoginTypePassword},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
"",
|
||||
),
|
||||
}
|
||||
}
|
||||
typePassword := auth.LoginTypePassword{
|
||||
GetAccountByPassword: accountAPI.QueryAccountByPassword,
|
||||
Config: cfg,
|
||||
}
|
||||
if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil {
|
||||
return *authErr
|
||||
}
|
||||
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword)
|
||||
}
|
||||
}
|
||||
if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil {
|
||||
return *authErr
|
||||
}
|
||||
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword)
|
||||
|
||||
uploadReq.UserID = device.UserID
|
||||
keyserverAPI.PerformUploadDeviceKeys(req.Context(), &uploadReq.PerformUploadDeviceKeysRequest, uploadRes)
|
||||
|
@ -108,7 +165,17 @@ func UploadCrossSigningDeviceSignatures(req *http.Request, keyserverAPI api.Clie
|
|||
keyserverAPI.PerformUploadDeviceSignatures(req.Context(), uploadReq, uploadRes)
|
||||
|
||||
if err := uploadRes.Error; err != nil {
|
||||
switch {
|
||||
return convertKeyError(err)
|
||||
}
|
||||
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
func convertKeyError(err *api.KeyError) util.JSONResponse {
|
||||
switch {
|
||||
case err.IsInvalidSignature:
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
|
@ -130,10 +197,4 @@ func UploadCrossSigningDeviceSignatures(req *http.Request, keyserverAPI api.Clie
|
|||
JSON: spec.Unknown(err.Error()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: struct{}{},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ func Password(
|
|||
},
|
||||
},
|
||||
nil,
|
||||
"",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,6 +234,7 @@ type userInteractiveResponse struct {
|
|||
Completed []authtypes.LoginType `json:"completed"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
Session string `json:"session"`
|
||||
Msg string `json:"msg,omitempty"`
|
||||
}
|
||||
|
||||
// newUserInteractiveResponse will return a struct to be sent back to the client
|
||||
|
@ -242,9 +243,10 @@ func newUserInteractiveResponse(
|
|||
sessionID string,
|
||||
fs []authtypes.Flow,
|
||||
params map[string]interface{},
|
||||
msg string,
|
||||
) userInteractiveResponse {
|
||||
return userInteractiveResponse{
|
||||
fs, sessions.getCompletedStages(sessionID), params, sessionID,
|
||||
fs, sessions.getCompletedStages(sessionID), params, sessionID, msg,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -817,7 +819,7 @@ func checkAndCompleteFlow(
|
|||
return util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: newUserInteractiveResponse(sessionID,
|
||||
cfg.Derived.Registration.Flows, cfg.Derived.Registration.Params),
|
||||
cfg.Derived.Registration.Flows, cfg.Derived.Registration.Params, ""),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,9 @@ func Setup(
|
|||
transactionsCache *transactions.Cache,
|
||||
federationSender federationAPI.ClientFederationAPI,
|
||||
extRoomsProvider api.ExtraPublicRoomsProvider,
|
||||
natsClient *nats.Conn, enableMetrics bool,
|
||||
natsClient *nats.Conn,
|
||||
userVerifier httputil.UserVerifier,
|
||||
enableMetrics bool,
|
||||
) {
|
||||
cfg := &dendriteCfg.ClientAPI
|
||||
mscCfg := &dendriteCfg.MSCs
|
||||
|
@ -171,19 +173,19 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
|
||||
}
|
||||
dendriteAdminRouter.Handle("/admin/registrationTokens/new",
|
||||
httputil.MakeAdminAPI("admin_registration_tokens_new", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAdminAPI("admin_registration_tokens_new", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return AdminCreateNewRegistrationToken(req, cfg, userAPI)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
dendriteAdminRouter.Handle("/admin/registrationTokens",
|
||||
httputil.MakeAdminAPI("admin_list_registration_tokens", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAdminAPI("admin_list_registration_tokens", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return AdminListRegistrationTokens(req, cfg, userAPI)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
dendriteAdminRouter.Handle("/admin/registrationTokens/{token}",
|
||||
httputil.MakeAdminAPI("admin_get_registration_token", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAdminAPI("admin_get_registration_token", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
switch req.Method {
|
||||
case http.MethodGet:
|
||||
return AdminGetRegistrationToken(req, cfg, userAPI)
|
||||
|
@ -202,43 +204,43 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodPut, http.MethodDelete, http.MethodOptions)
|
||||
|
||||
dendriteAdminRouter.Handle("/admin/evacuateRoom/{roomID}",
|
||||
httputil.MakeAdminAPI("admin_evacuate_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAdminAPI("admin_evacuate_room", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return AdminEvacuateRoom(req, rsAPI)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
dendriteAdminRouter.Handle("/admin/evacuateUser/{userID}",
|
||||
httputil.MakeAdminAPI("admin_evacuate_user", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAdminAPI("admin_evacuate_user", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return AdminEvacuateUser(req, rsAPI)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
dendriteAdminRouter.Handle("/admin/purgeRoom/{roomID}",
|
||||
httputil.MakeAdminAPI("admin_purge_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAdminAPI("admin_purge_room", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return AdminPurgeRoom(req, rsAPI)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
dendriteAdminRouter.Handle("/admin/resetPassword/{userID}",
|
||||
httputil.MakeAdminAPI("admin_reset_password", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAdminAPI("admin_reset_password", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return AdminResetPassword(req, cfg, device, userAPI)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
dendriteAdminRouter.Handle("/admin/downloadState/{serverName}/{roomID}",
|
||||
httputil.MakeAdminAPI("admin_download_state", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAdminAPI("admin_download_state", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return AdminDownloadState(req, device, rsAPI)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
dendriteAdminRouter.Handle("/admin/fulltext/reindex",
|
||||
httputil.MakeAdminAPI("admin_fultext_reindex", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAdminAPI("admin_fultext_reindex", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return AdminReindex(req, cfg, device, natsClient)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
dendriteAdminRouter.Handle("/admin/refreshDevices/{userID}",
|
||||
httputil.MakeAdminAPI("admin_refresh_devices", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAdminAPI("admin_refresh_devices", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return AdminMarkAsStale(req, cfg, userAPI)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
@ -252,7 +254,7 @@ func Setup(
|
|||
}
|
||||
|
||||
synapseAdminRouter.Handle("/admin/v1/send_server_notice/{txnID}",
|
||||
httputil.MakeAuthAPI("send_server_notice", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("send_server_notice", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
// not specced, but ensure we're rate limiting requests to this endpoint
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
|
@ -273,7 +275,7 @@ func Setup(
|
|||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
|
||||
synapseAdminRouter.Handle("/admin/v1/send_server_notice",
|
||||
httputil.MakeAuthAPI("send_server_notice", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("send_server_notice", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
// not specced, but ensure we're rate limiting requests to this endpoint
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
|
@ -301,12 +303,12 @@ func Setup(
|
|||
unstableMux := publicAPIMux.PathPrefix("/unstable").Subrouter()
|
||||
|
||||
v3mux.Handle("/createRoom",
|
||||
httputil.MakeAuthAPI("createRoom", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("createRoom", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return CreateRoom(req, device, cfg, userAPI, rsAPI, asAPI)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/join/{roomIDOrAlias}",
|
||||
httputil.MakeAuthAPI(spec.Join, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI(spec.Join, userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -340,11 +342,21 @@ func Setup(
|
|||
httputil.MakeServiceAdminAPI("admin_username_available", m.AdminToken, func(r *http.Request) util.JSONResponse {
|
||||
return AdminCheckUsernameAvailable(r, userAPI, cfg)
|
||||
})).Methods(http.MethodGet)
|
||||
|
||||
synapseAdminRouter.Handle("/admin/v2/users/{userID}",
|
||||
httputil.MakeServiceAdminAPI("admin_provision_user", m.AdminToken, func(r *http.Request) util.JSONResponse {
|
||||
return AdminCreateOrModifyAccount(r, userAPI)
|
||||
})).Methods(http.MethodPut)
|
||||
|
||||
synapseAdminRouter.Handle("/admin/v2/users/{userID}/devices",
|
||||
httputil.MakeServiceAdminAPI("admin_user_devices", m.AdminToken, func(r *http.Request) util.JSONResponse {
|
||||
return AdminHandleUserDeviceByUserID(r, userAPI)
|
||||
})).Methods(http.MethodPost, http.MethodGet)
|
||||
}
|
||||
|
||||
if mscCfg.Enabled("msc2753") {
|
||||
v3mux.Handle("/peek/{roomIDOrAlias}",
|
||||
httputil.MakeAuthAPI(spec.Peek, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI(spec.Peek, userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -359,12 +371,12 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
}
|
||||
v3mux.Handle("/joined_rooms",
|
||||
httputil.MakeAuthAPI("joined_rooms", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("joined_rooms", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return GetJoinedRooms(req, device, rsAPI)
|
||||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/join",
|
||||
httputil.MakeAuthAPI(spec.Join, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI(spec.Join, userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -386,7 +398,7 @@ func Setup(
|
|||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/leave",
|
||||
httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("membership", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -400,7 +412,7 @@ func Setup(
|
|||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/unpeek",
|
||||
httputil.MakeAuthAPI("unpeek", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("unpeek", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -411,7 +423,7 @@ func Setup(
|
|||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/ban",
|
||||
httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("membership", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -420,7 +432,7 @@ func Setup(
|
|||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/invite",
|
||||
httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("membership", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -432,7 +444,7 @@ func Setup(
|
|||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/kick",
|
||||
httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("membership", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -441,7 +453,7 @@ func Setup(
|
|||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/unban",
|
||||
httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("membership", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -450,7 +462,7 @@ func Setup(
|
|||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/send/{eventType}",
|
||||
httputil.MakeAuthAPI("send_message", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("send_message", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -459,7 +471,7 @@ func Setup(
|
|||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/send/{eventType}/{txnID}",
|
||||
httputil.MakeAuthAPI("send_message", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("send_message", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -470,7 +482,7 @@ func Setup(
|
|||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/state", httputil.MakeAuthAPI("room_state", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
v3mux.Handle("/rooms/{roomID}/state", httputil.MakeAuthAPI("room_state", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -478,7 +490,7 @@ func Setup(
|
|||
return OnIncomingStateRequest(req.Context(), device, rsAPI, vars["roomID"])
|
||||
}, httputil.WithAllowGuests())).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/aliases", httputil.MakeAuthAPI("aliases", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
v3mux.Handle("/rooms/{roomID}/aliases", httputil.MakeAuthAPI("aliases", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -486,7 +498,7 @@ func Setup(
|
|||
return GetAliases(req, rsAPI, device, vars["roomID"])
|
||||
})).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/state/{type:[^/]+/?}", httputil.MakeAuthAPI("room_state", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
v3mux.Handle("/rooms/{roomID}/state/{type:[^/]+/?}", httputil.MakeAuthAPI("room_state", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -497,7 +509,7 @@ func Setup(
|
|||
return OnIncomingStateTypeRequest(req.Context(), device, rsAPI, vars["roomID"], eventType, "", eventFormat)
|
||||
}, httputil.WithAllowGuests())).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/state/{type}/{stateKey}", httputil.MakeAuthAPI("room_state", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
v3mux.Handle("/rooms/{roomID}/state/{type}/{stateKey}", httputil.MakeAuthAPI("room_state", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -507,7 +519,7 @@ func Setup(
|
|||
}, httputil.WithAllowGuests())).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/state/{eventType:[^/]+/?}",
|
||||
httputil.MakeAuthAPI("send_message", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("send_message", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -519,7 +531,7 @@ func Setup(
|
|||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/state/{eventType}/{stateKey}",
|
||||
httputil.MakeAuthAPI("send_message", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("send_message", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -533,7 +545,7 @@ func Setup(
|
|||
// TODO: clear based on some criteria
|
||||
roomHierarchyPaginationCache := NewRoomHierarchyPaginationCache()
|
||||
v1mux.Handle("/rooms/{roomID}/hierarchy",
|
||||
httputil.MakeAuthAPI("spaces", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("spaces", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -567,7 +579,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/directory/room/{roomAlias}",
|
||||
httputil.MakeAuthAPI("directory_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("directory_room", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -577,7 +589,7 @@ func Setup(
|
|||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/directory/room/{roomAlias}",
|
||||
httputil.MakeAuthAPI("directory_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("directory_room", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -596,7 +608,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/directory/list/room/{roomID}",
|
||||
httputil.MakeAuthAPI("directory_list", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("directory_list", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -605,7 +617,7 @@ func Setup(
|
|||
}),
|
||||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
v3mux.Handle("/directory/list/appservice/{networkID}/{roomID}",
|
||||
httputil.MakeAuthAPI("directory_list", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("directory_list", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -616,7 +628,7 @@ func Setup(
|
|||
|
||||
// Undocumented endpoint
|
||||
v3mux.Handle("/directory/list/appservice/{networkID}/{roomID}",
|
||||
httputil.MakeAuthAPI("directory_list", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("directory_list", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -632,19 +644,19 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/logout",
|
||||
httputil.MakeAuthAPI("logout", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("logout", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return Logout(req, userAPI, device)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/logout/all",
|
||||
httputil.MakeAuthAPI("logout", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("logout", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return LogoutAll(req, userAPI, device)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/typing/{userID}",
|
||||
httputil.MakeAuthAPI("rooms_typing", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("rooms_typing", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -656,7 +668,7 @@ func Setup(
|
|||
}),
|
||||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/redact/{eventID}",
|
||||
httputil.MakeAuthAPI("rooms_redact", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("rooms_redact", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -665,7 +677,7 @@ func Setup(
|
|||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/redact/{eventID}/{txnId}",
|
||||
httputil.MakeAuthAPI("rooms_redact", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("rooms_redact", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -676,7 +688,7 @@ func Setup(
|
|||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/sendToDevice/{eventType}/{txnID}",
|
||||
httputil.MakeAuthAPI("send_to_device", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("send_to_device", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -690,7 +702,7 @@ func Setup(
|
|||
// rather than r0. It's an exact duplicate of the above handler.
|
||||
// TODO: Remove this if/when sytest is fixed!
|
||||
unstableMux.Handle("/sendToDevice/{eventType}/{txnID}",
|
||||
httputil.MakeAuthAPI("send_to_device", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("send_to_device", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -701,7 +713,7 @@ func Setup(
|
|||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/account/whoami",
|
||||
httputil.MakeAuthAPI("whoami", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("whoami", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -710,7 +722,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/account/password",
|
||||
httputil.MakeAuthAPI("password", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("password", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -719,7 +731,7 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/account/deactivate",
|
||||
httputil.MakeAuthAPI("deactivate", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("deactivate", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -739,7 +751,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/auth/{authType}/fallback/web",
|
||||
httputil.MakeHTTPAPI("auth_fallback", userAPI, enableMetrics, func(w http.ResponseWriter, req *http.Request) {
|
||||
httputil.MakeHTTPAPI("auth_fallback", userVerifier, enableMetrics, func(w http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
AuthFallback(w, req, vars["authType"], cfg)
|
||||
}),
|
||||
|
@ -748,7 +760,7 @@ func Setup(
|
|||
// Push rules
|
||||
|
||||
v3mux.Handle("/pushrules",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidParam("missing trailing slash"),
|
||||
|
@ -757,13 +769,13 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/pushrules/",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return GetAllPushRules(req.Context(), device, userAPI)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/pushrules/",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidParam("scope, kind and rule ID must be specified"),
|
||||
|
@ -772,7 +784,7 @@ func Setup(
|
|||
).Methods(http.MethodPut)
|
||||
|
||||
v3mux.Handle("/pushrules/{scope}/",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -782,7 +794,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/pushrules/{scope}",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidParam("missing trailing slash after scope"),
|
||||
|
@ -791,7 +803,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/pushrules/{scope:[^/]+/?}",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidParam("kind and rule ID must be specified"),
|
||||
|
@ -800,7 +812,7 @@ func Setup(
|
|||
).Methods(http.MethodPut)
|
||||
|
||||
v3mux.Handle("/pushrules/{scope}/{kind}/",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -810,7 +822,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/pushrules/{scope}/{kind}",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidParam("missing trailing slash after kind"),
|
||||
|
@ -819,7 +831,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/pushrules/{scope}/{kind:[^/]+/?}",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidParam("rule ID must be specified"),
|
||||
|
@ -828,7 +840,7 @@ func Setup(
|
|||
).Methods(http.MethodPut)
|
||||
|
||||
v3mux.Handle("/pushrules/{scope}/{kind}/{ruleID}",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -838,7 +850,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/pushrules/{scope}/{kind}/{ruleID}",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -852,7 +864,7 @@ func Setup(
|
|||
).Methods(http.MethodPut)
|
||||
|
||||
v3mux.Handle("/pushrules/{scope}/{kind}/{ruleID}",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -862,7 +874,7 @@ func Setup(
|
|||
).Methods(http.MethodDelete)
|
||||
|
||||
v3mux.Handle("/pushrules/{scope}/{kind}/{ruleID}/{attr}",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -872,7 +884,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/pushrules/{scope}/{kind}/{ruleID}/{attr}",
|
||||
httputil.MakeAuthAPI("push_rules", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("push_rules", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -904,7 +916,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/profile/{userID}/avatar_url",
|
||||
httputil.MakeAuthAPI("profile_avatar_url", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("profile_avatar_url", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -929,7 +941,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/profile/{userID}/displayname",
|
||||
httputil.MakeAuthAPI("profile_displayname", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("profile_displayname", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -946,19 +958,19 @@ func Setup(
|
|||
threePIDClient := base.CreateClient(dendriteCfg, nil) // TODO: Move this somewhere else, e.g. pass in as parameter
|
||||
|
||||
v3mux.Handle("/account/3pid",
|
||||
httputil.MakeAuthAPI("account_3pid", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("account_3pid", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return GetAssociated3PIDs(req, userAPI, device)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/account/3pid",
|
||||
httputil.MakeAuthAPI("account_3pid", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("account_3pid", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return CheckAndSave3PIDAssociation(req, userAPI, device, cfg, threePIDClient)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/account/3pid/delete",
|
||||
httputil.MakeAuthAPI("account_3pid", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("account_3pid", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return Forget3PID(req, userAPI)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
@ -970,7 +982,7 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/voip/turnServer",
|
||||
httputil.MakeAuthAPI("turn_server", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("turn_server", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -979,13 +991,13 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/thirdparty/protocols",
|
||||
httputil.MakeAuthAPI("thirdparty_protocols", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("thirdparty_protocols", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return Protocols(req, asAPI, device, "")
|
||||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/thirdparty/protocol/{protocolID}",
|
||||
httputil.MakeAuthAPI("thirdparty_protocols", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("thirdparty_protocols", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -995,7 +1007,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/thirdparty/user/{protocolID}",
|
||||
httputil.MakeAuthAPI("thirdparty_user", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("thirdparty_user", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1005,13 +1017,13 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/thirdparty/user",
|
||||
httputil.MakeAuthAPI("thirdparty_user", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("thirdparty_user", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return User(req, asAPI, device, "", req.URL.Query())
|
||||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/thirdparty/location/{protocolID}",
|
||||
httputil.MakeAuthAPI("thirdparty_location", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("thirdparty_location", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1021,7 +1033,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/thirdparty/location",
|
||||
httputil.MakeAuthAPI("thirdparty_location", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("thirdparty_location", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return Location(req, asAPI, device, "", req.URL.Query())
|
||||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
@ -1037,7 +1049,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/user/{userID}/account_data/{type}",
|
||||
httputil.MakeAuthAPI("user_account_data", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("user_account_data", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1047,7 +1059,7 @@ func Setup(
|
|||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/user/{userID}/rooms/{roomID}/account_data/{type}",
|
||||
httputil.MakeAuthAPI("user_account_data", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("user_account_data", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1057,7 +1069,7 @@ func Setup(
|
|||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/user/{userID}/account_data/{type}",
|
||||
httputil.MakeAuthAPI("user_account_data", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("user_account_data", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1067,7 +1079,7 @@ func Setup(
|
|||
).Methods(http.MethodGet)
|
||||
|
||||
v3mux.Handle("/user/{userID}/rooms/{roomID}/account_data/{type}",
|
||||
httputil.MakeAuthAPI("user_account_data", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("user_account_data", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1077,7 +1089,7 @@ func Setup(
|
|||
).Methods(http.MethodGet)
|
||||
|
||||
v3mux.Handle("/admin/whois/{userID}",
|
||||
httputil.MakeAuthAPI("admin_whois", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("admin_whois", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1087,7 +1099,7 @@ func Setup(
|
|||
).Methods(http.MethodGet)
|
||||
|
||||
v3mux.Handle("/user/{userID}/openid/request_token",
|
||||
httputil.MakeAuthAPI("openid_request_token", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("openid_request_token", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -1100,7 +1112,7 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/user_directory/search",
|
||||
httputil.MakeAuthAPI("userdirectory_search", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("userdirectory_search", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -1126,7 +1138,7 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/read_markers",
|
||||
httputil.MakeAuthAPI("rooms_read_markers", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("rooms_read_markers", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -1139,7 +1151,7 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/forget",
|
||||
httputil.MakeAuthAPI("rooms_forget", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("rooms_forget", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -1152,7 +1164,7 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/upgrade",
|
||||
httputil.MakeAuthAPI("rooms_upgrade", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("rooms_upgrade", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1162,13 +1174,13 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/devices",
|
||||
httputil.MakeAuthAPI("get_devices", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("get_devices", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return GetDevicesByLocalpart(req, userAPI, device)
|
||||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/devices/{deviceID}",
|
||||
httputil.MakeAuthAPI("get_device", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("get_device", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1178,7 +1190,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/devices/{deviceID}",
|
||||
httputil.MakeAuthAPI("device_data", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("device_data", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1188,7 +1200,7 @@ func Setup(
|
|||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/devices/{deviceID}",
|
||||
httputil.MakeAuthAPI("delete_device", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("delete_device", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1198,25 +1210,25 @@ func Setup(
|
|||
).Methods(http.MethodDelete, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/delete_devices",
|
||||
httputil.MakeAuthAPI("delete_devices", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("delete_devices", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return DeleteDevices(req, userInteractiveAuth, userAPI, device)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/notifications",
|
||||
httputil.MakeAuthAPI("get_notifications", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("get_notifications", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return GetNotifications(req, device, userAPI)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/pushers",
|
||||
httputil.MakeAuthAPI("get_pushers", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("get_pushers", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return GetPushers(req, device, userAPI)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/pushers/set",
|
||||
httputil.MakeAuthAPI("set_pushers", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("set_pushers", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -1226,7 +1238,7 @@ func Setup(
|
|||
|
||||
// Stub implementations for sytest
|
||||
v3mux.Handle("/events",
|
||||
httputil.MakeAuthAPI("events", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("events", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return util.JSONResponse{Code: http.StatusOK, JSON: map[string]interface{}{
|
||||
"chunk": []interface{}{},
|
||||
"start": "",
|
||||
|
@ -1236,7 +1248,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/initialSync",
|
||||
httputil.MakeAuthAPI("initial_sync", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("initial_sync", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return util.JSONResponse{Code: http.StatusOK, JSON: map[string]interface{}{
|
||||
"end": "",
|
||||
}}
|
||||
|
@ -1244,7 +1256,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/user/{userId}/rooms/{roomId}/tags",
|
||||
httputil.MakeAuthAPI("get_tags", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("get_tags", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1254,7 +1266,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/user/{userId}/rooms/{roomId}/tags/{tag}",
|
||||
httputil.MakeAuthAPI("put_tag", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("put_tag", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1264,7 +1276,7 @@ func Setup(
|
|||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/user/{userId}/rooms/{roomId}/tags/{tag}",
|
||||
httputil.MakeAuthAPI("delete_tag", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("delete_tag", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1274,7 +1286,7 @@ func Setup(
|
|||
).Methods(http.MethodDelete, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/capabilities",
|
||||
httputil.MakeAuthAPI("capabilities", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("capabilities", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -1284,7 +1296,7 @@ func Setup(
|
|||
|
||||
// Key Backup Versions (Metadata)
|
||||
|
||||
getBackupKeysVersion := httputil.MakeAuthAPI("get_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
getBackupKeysVersion := httputil.MakeAuthAPI("get_backup_keys_version", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1292,11 +1304,11 @@ func Setup(
|
|||
return KeyBackupVersion(req, userAPI, device, vars["version"])
|
||||
})
|
||||
|
||||
getLatestBackupKeysVersion := httputil.MakeAuthAPI("get_latest_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
getLatestBackupKeysVersion := httputil.MakeAuthAPI("get_latest_backup_keys_version", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return KeyBackupVersion(req, userAPI, device, "")
|
||||
})
|
||||
|
||||
putBackupKeysVersion := httputil.MakeAuthAPI("put_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
putBackupKeysVersion := httputil.MakeAuthAPI("put_backup_keys_version", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1304,7 +1316,7 @@ func Setup(
|
|||
return ModifyKeyBackupVersionAuthData(req, userAPI, device, vars["version"])
|
||||
})
|
||||
|
||||
deleteBackupKeysVersion := httputil.MakeAuthAPI("delete_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
deleteBackupKeysVersion := httputil.MakeAuthAPI("delete_backup_keys_version", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1312,7 +1324,7 @@ func Setup(
|
|||
return DeleteKeyBackupVersion(req, userAPI, device, vars["version"])
|
||||
})
|
||||
|
||||
postNewBackupKeysVersion := httputil.MakeAuthAPI("post_new_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
postNewBackupKeysVersion := httputil.MakeAuthAPI("post_new_backup_keys_version", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return CreateKeyBackupVersion(req, userAPI, device)
|
||||
})
|
||||
|
||||
|
@ -1331,7 +1343,7 @@ func Setup(
|
|||
// Inserting E2E Backup Keys
|
||||
|
||||
// Bulk room and session
|
||||
putBackupKeys := httputil.MakeAuthAPI("put_backup_keys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
putBackupKeys := httputil.MakeAuthAPI("put_backup_keys", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
version := req.URL.Query().Get("version")
|
||||
if version == "" {
|
||||
return util.JSONResponse{
|
||||
|
@ -1348,7 +1360,7 @@ func Setup(
|
|||
})
|
||||
|
||||
// Single room bulk session
|
||||
putBackupKeysRoom := httputil.MakeAuthAPI("put_backup_keys_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
putBackupKeysRoom := httputil.MakeAuthAPI("put_backup_keys_room", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1380,7 +1392,7 @@ func Setup(
|
|||
})
|
||||
|
||||
// Single room, single session
|
||||
putBackupKeysRoomSession := httputil.MakeAuthAPI("put_backup_keys_room_session", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
putBackupKeysRoomSession := httputil.MakeAuthAPI("put_backup_keys_room_session", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1422,11 +1434,11 @@ func Setup(
|
|||
|
||||
// Querying E2E Backup Keys
|
||||
|
||||
getBackupKeys := httputil.MakeAuthAPI("get_backup_keys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
getBackupKeys := httputil.MakeAuthAPI("get_backup_keys", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return GetBackupKeys(req, userAPI, device, req.URL.Query().Get("version"), "", "")
|
||||
})
|
||||
|
||||
getBackupKeysRoom := httputil.MakeAuthAPI("get_backup_keys_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
getBackupKeysRoom := httputil.MakeAuthAPI("get_backup_keys_room", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1434,7 +1446,7 @@ func Setup(
|
|||
return GetBackupKeys(req, userAPI, device, req.URL.Query().Get("version"), vars["roomID"], "")
|
||||
})
|
||||
|
||||
getBackupKeysRoomSession := httputil.MakeAuthAPI("get_backup_keys_room_session", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
getBackupKeysRoomSession := httputil.MakeAuthAPI("get_backup_keys_room_session", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1454,11 +1466,11 @@ func Setup(
|
|||
|
||||
// Cross-signing device keys
|
||||
|
||||
postDeviceSigningKeys := httputil.MakeAuthAPI("post_device_signing_keys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
postDeviceSigningKeys := httputil.MakeAuthAPI("post_device_signing_keys", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return UploadCrossSigningDeviceKeys(req, userInteractiveAuth, userAPI, device, userAPI, cfg)
|
||||
})
|
||||
|
||||
postDeviceSigningSignatures := httputil.MakeAuthAPI("post_device_signing_signatures", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
postDeviceSigningSignatures := httputil.MakeAuthAPI("post_device_signing_signatures", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return UploadCrossSigningDeviceSignatures(req, userAPI, device)
|
||||
}, httputil.WithAllowGuests())
|
||||
|
||||
|
@ -1470,27 +1482,27 @@ func Setup(
|
|||
|
||||
// Supplying a device ID is deprecated.
|
||||
v3mux.Handle("/keys/upload/{deviceID}",
|
||||
httputil.MakeAuthAPI("keys_upload", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("keys_upload", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return UploadKeys(req, userAPI, device)
|
||||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/keys/upload",
|
||||
httputil.MakeAuthAPI("keys_upload", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("keys_upload", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return UploadKeys(req, userAPI, device)
|
||||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/keys/query",
|
||||
httputil.MakeAuthAPI("keys_query", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("keys_query", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return QueryKeys(req, userAPI, device)
|
||||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/keys/claim",
|
||||
httputil.MakeAuthAPI("keys_claim", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("keys_claim", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return ClaimKeys(req, userAPI)
|
||||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomId}/receipt/{receiptType}/{eventId}",
|
||||
httputil.MakeAuthAPI(spec.Join, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI(spec.Join, userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -1503,7 +1515,7 @@ func Setup(
|
|||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/presence/{userId}/status",
|
||||
httputil.MakeAuthAPI("set_presence", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("set_presence", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1512,7 +1524,7 @@ func Setup(
|
|||
}),
|
||||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
v3mux.Handle("/presence/{userId}/status",
|
||||
httputil.MakeAuthAPI("get_presence", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("get_presence", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1522,7 +1534,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/joined_members",
|
||||
httputil.MakeAuthAPI("rooms_members", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("rooms_members", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1532,7 +1544,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/report/{eventID}",
|
||||
httputil.MakeAuthAPI("report_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("report_event", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1542,7 +1554,7 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
synapseAdminRouter.Handle("/admin/v1/event_reports",
|
||||
httputil.MakeAdminAPI("admin_report_events", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAdminAPI("admin_report_events", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
from := parseUint64OrDefault(req.URL.Query().Get("from"), 0)
|
||||
limit := parseUint64OrDefault(req.URL.Query().Get("limit"), 100)
|
||||
dir := req.URL.Query().Get("dir")
|
||||
|
@ -1556,7 +1568,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
synapseAdminRouter.Handle("/admin/v1/event_reports/{reportID}",
|
||||
httputil.MakeAdminAPI("admin_report_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAdminAPI("admin_report_event", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -1566,7 +1578,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
synapseAdminRouter.Handle("/admin/v1/event_reports/{reportID}",
|
||||
httputil.MakeAdminAPI("admin_report_event_delete", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAdminAPI("admin_report_event_delete", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
|
|
@ -58,17 +58,23 @@ func WithAuth() AuthAPIOption {
|
|||
}
|
||||
}
|
||||
|
||||
type UserVerifier interface {
|
||||
// VerifyUserFromRequest authenticates the HTTP request,
|
||||
// on success returns Device of the requester.
|
||||
VerifyUserFromRequest(req *http.Request) (*userapi.Device, *util.JSONResponse)
|
||||
}
|
||||
|
||||
// MakeAuthAPI turns a util.JSONRequestHandler function into an http.Handler which authenticates the request.
|
||||
func MakeAuthAPI(
|
||||
metricsName string, userAPI userapi.QueryAcccessTokenAPI,
|
||||
metricsName string, userVerifier UserVerifier,
|
||||
f func(*http.Request, *userapi.Device) util.JSONResponse,
|
||||
checks ...AuthAPIOption,
|
||||
) http.Handler {
|
||||
h := func(req *http.Request) util.JSONResponse {
|
||||
logger := util.GetLogger(req.Context())
|
||||
device, err := auth.VerifyUserFromRequest(req, userAPI)
|
||||
device, err := userVerifier.VerifyUserFromRequest(req)
|
||||
if err != nil {
|
||||
logger.Debugf("VerifyUserFromRequest %s -> HTTP %d", req.RemoteAddr, err.Code)
|
||||
logger.Debugf("VerifyUserFromRequest %s -> HTTP %d: JSON %+v", req.RemoteAddr, err.Code, err.JSON)
|
||||
return *err
|
||||
}
|
||||
// add the user ID to the logger
|
||||
|
@ -122,11 +128,11 @@ func MakeAuthAPI(
|
|||
// MakeAdminAPI is a wrapper around MakeAuthAPI which enforces that the request can only be
|
||||
// completed by a user that is a server administrator.
|
||||
func MakeAdminAPI(
|
||||
metricsName string, userAPI userapi.QueryAcccessTokenAPI,
|
||||
metricsName string, userVerifier UserVerifier,
|
||||
f func(*http.Request, *userapi.Device) util.JSONResponse,
|
||||
) http.Handler {
|
||||
return MakeAuthAPI(metricsName, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if device.AccountType != userapi.AccountTypeAdmin {
|
||||
return MakeAuthAPI(metricsName, userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if device == nil || device.AccountType != userapi.AccountTypeAdmin {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.Forbidden("This API can only be used by admin users."),
|
||||
|
@ -136,8 +142,8 @@ func MakeAdminAPI(
|
|||
})
|
||||
}
|
||||
|
||||
// MakeServiceAdminAPI is a wrapper around MakeAuthAPI which enforces that the request can only be
|
||||
// completed by a trusted service e.g. Matrix Auth Service.
|
||||
// MakeServiceAdminAPI is a wrapper around MakeExternalAPI which enforces that the request can only be
|
||||
// completed by a trusted service e.g. Matrix Auth Service (MAS).
|
||||
func MakeServiceAdminAPI(
|
||||
metricsName, serviceToken string,
|
||||
f func(*http.Request) util.JSONResponse,
|
||||
|
@ -232,7 +238,7 @@ func MakeExternalAPI(metricsName string, f func(*http.Request) util.JSONResponse
|
|||
|
||||
// MakeHTTPAPI adds Span metrics to the HTML Handler function
|
||||
// This is used to serve HTML alongside JSON error messages
|
||||
func MakeHTTPAPI(metricsName string, userAPI userapi.QueryAcccessTokenAPI, enableMetrics bool, f func(http.ResponseWriter, *http.Request), checks ...AuthAPIOption) http.Handler {
|
||||
func MakeHTTPAPI(metricsName string, userVerifier UserVerifier, enableMetrics bool, f func(http.ResponseWriter, *http.Request), checks ...AuthAPIOption) http.Handler {
|
||||
withSpan := func(w http.ResponseWriter, req *http.Request) {
|
||||
if req.Method == http.MethodOptions {
|
||||
util.SetCORSHeaders(w)
|
||||
|
@ -252,7 +258,7 @@ func MakeHTTPAPI(metricsName string, userAPI userapi.QueryAcccessTokenAPI, enabl
|
|||
|
||||
if opts.WithAuth {
|
||||
logger := util.GetLogger(req.Context())
|
||||
_, jsonErr := auth.VerifyUserFromRequest(req, userAPI)
|
||||
_, jsonErr := userVerifier.VerifyUserFromRequest(req)
|
||||
if jsonErr != nil {
|
||||
w.WriteHeader(jsonErr.Code)
|
||||
if err := json.NewEncoder(w).Encode(jsonErr.JSON); err != nil {
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"github.com/element-hq/dendrite/mediaapi/routing"
|
||||
"github.com/element-hq/dendrite/mediaapi/storage"
|
||||
"github.com/element-hq/dendrite/setup/config"
|
||||
userapi "github.com/element-hq/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -23,10 +22,10 @@ func AddPublicRoutes(
|
|||
routers httputil.Routers,
|
||||
cm *sqlutil.Connections,
|
||||
cfg *config.Dendrite,
|
||||
userAPI userapi.MediaUserAPI,
|
||||
client *fclient.Client,
|
||||
fedClient fclient.FederationClient,
|
||||
keyRing gomatrixserverlib.JSONVerifier,
|
||||
userVerifier httputil.UserVerifier,
|
||||
) {
|
||||
mediaDB, err := storage.NewMediaAPIDatasource(cm, &cfg.MediaAPI.Database)
|
||||
if err != nil {
|
||||
|
@ -34,6 +33,6 @@ func AddPublicRoutes(
|
|||
}
|
||||
|
||||
routing.Setup(
|
||||
routers, cfg, mediaDB, userAPI, client, fedClient, keyRing,
|
||||
routers, cfg, mediaDB, client, fedClient, keyRing, userVerifier,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -42,10 +42,10 @@ func Setup(
|
|||
routers httputil.Routers,
|
||||
cfg *config.Dendrite,
|
||||
db storage.Database,
|
||||
userAPI userapi.MediaUserAPI,
|
||||
client *fclient.Client,
|
||||
federationClient fclient.FederationClient,
|
||||
keyRing gomatrixserverlib.JSONVerifier,
|
||||
userVerifier httputil.UserVerifier,
|
||||
) {
|
||||
rateLimits := httputil.NewRateLimits(&cfg.ClientAPI.RateLimiting)
|
||||
|
||||
|
@ -58,7 +58,7 @@ func Setup(
|
|||
}
|
||||
|
||||
uploadHandler := httputil.MakeAuthAPI(
|
||||
"upload", userAPI,
|
||||
"upload", userVerifier,
|
||||
func(req *http.Request, dev *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, dev); r != nil {
|
||||
return *r
|
||||
|
@ -67,7 +67,7 @@ func Setup(
|
|||
},
|
||||
)
|
||||
|
||||
configHandler := httputil.MakeAuthAPI("config", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
configHandler := httputil.MakeAuthAPI("config", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
}
|
||||
|
@ -97,13 +97,13 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
// v1 client endpoints requiring auth
|
||||
downloadHandlerAuthed := httputil.MakeHTTPAPI("download", userAPI, cfg.Global.Metrics.Enabled, makeDownloadAPI("download_authed_client", &cfg.MediaAPI, rateLimits, db, client, federationClient, activeRemoteRequests, activeThumbnailGeneration, false), httputil.WithAuth())
|
||||
downloadHandlerAuthed := httputil.MakeHTTPAPI("download", userVerifier, cfg.Global.Metrics.Enabled, makeDownloadAPI("download_authed_client", &cfg.MediaAPI, rateLimits, db, client, federationClient, activeRemoteRequests, activeThumbnailGeneration, false), httputil.WithAuth())
|
||||
v1mux.Handle("/config", configHandler).Methods(http.MethodGet, http.MethodOptions)
|
||||
v1mux.Handle("/download/{serverName}/{mediaId}", downloadHandlerAuthed).Methods(http.MethodGet, http.MethodOptions)
|
||||
v1mux.Handle("/download/{serverName}/{mediaId}/{downloadName}", downloadHandlerAuthed).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v1mux.Handle("/thumbnail/{serverName}/{mediaId}",
|
||||
httputil.MakeHTTPAPI("thumbnail", userAPI, cfg.Global.Metrics.Enabled, makeDownloadAPI("thumbnail_authed_client", &cfg.MediaAPI, rateLimits, db, client, federationClient, activeRemoteRequests, activeThumbnailGeneration, false), httputil.WithAuth()),
|
||||
httputil.MakeHTTPAPI("thumbnail", userVerifier, cfg.Global.Metrics.Enabled, makeDownloadAPI("thumbnail_authed_client", &cfg.MediaAPI, rateLimits, db, client, federationClient, activeRemoteRequests, activeThumbnailGeneration, false), httputil.WithAuth()),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
// same, but for federation
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
package config
|
||||
|
||||
import "slices"
|
||||
|
||||
type MSCs struct {
|
||||
Matrix *Global `yaml:"-"`
|
||||
|
||||
// The MSCs to enable. Supported MSCs include:
|
||||
// 'msc3861': Delegate auth to an OIDC provider. This line MUST always go first if the msc is used https://github.com/matrix-org/matrix-spec-proposals/pull/3861
|
||||
// 'msc2444': Peeking over federation - https://github.com/matrix-org/matrix-doc/pull/2444
|
||||
// 'msc2753': Peeking via /sync - https://github.com/matrix-org/matrix-doc/pull/2753
|
||||
// 'msc2836': Threading - https://github.com/matrix-org/matrix-doc/pull/2836
|
||||
// 'msc3861': Delegate auth to an OIDC provider https://github.com/matrix-org/matrix-spec-proposals/pull/3861
|
||||
MSCs []string `yaml:"mscs"`
|
||||
|
||||
// MSC3861 contains config related to the experimental feature MSC3861. It takes effect only if 'msc3861' is included in 'MSCs' array
|
||||
MSC3861 *MSC3861 `yaml:"msc3861,omitempty"`
|
||||
|
||||
Database DatabaseOptions `yaml:"database,omitempty"`
|
||||
|
@ -42,6 +45,10 @@ func (c *MSCs) Verify(configErrs *ConfigErrors) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *MSCs) MSC3861Enabled() bool {
|
||||
return slices.Contains(c.MSCs, "msc3861") && c.MSC3861 != nil && c.MSC3861.Enabled
|
||||
}
|
||||
|
||||
type MSC3861 struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Issuer string `yaml:"issuer"`
|
||||
|
|
|
@ -7,9 +7,12 @@
|
|||
package setup
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
appserviceAPI "github.com/element-hq/dendrite/appservice/api"
|
||||
"github.com/element-hq/dendrite/clientapi"
|
||||
"github.com/element-hq/dendrite/clientapi/api"
|
||||
"github.com/element-hq/dendrite/clientapi/auth"
|
||||
"github.com/element-hq/dendrite/federationapi"
|
||||
federationAPI "github.com/element-hq/dendrite/federationapi/api"
|
||||
"github.com/element-hq/dendrite/internal/caching"
|
||||
|
@ -27,6 +30,7 @@ import (
|
|||
userapi "github.com/element-hq/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
||||
// Monolith represents an instantiation of all dependencies required to build
|
||||
|
@ -46,6 +50,8 @@ type Monolith struct {
|
|||
// Optional
|
||||
ExtPublicRoomsProvider api.ExtraPublicRoomsProvider
|
||||
ExtUserDirectoryProvider userapi.QuerySearchProfilesAPI
|
||||
|
||||
UserVerifierProvider *UserVerifierProvider
|
||||
}
|
||||
|
||||
// AddAllPublicRoutes attaches all public paths to the given router
|
||||
|
@ -58,6 +64,10 @@ func (m *Monolith) AddAllPublicRoutes(
|
|||
caches *caching.Caches,
|
||||
enableMetrics bool,
|
||||
) {
|
||||
if m.UserVerifierProvider == nil {
|
||||
m.UserVerifierProvider = NewUserVerifierProvider(&auth.DefaultUserVerifier{UserAPI: m.UserAPI})
|
||||
}
|
||||
|
||||
userDirectoryProvider := m.ExtUserDirectoryProvider
|
||||
if userDirectoryProvider == nil {
|
||||
userDirectoryProvider = m.UserAPI
|
||||
|
@ -65,15 +75,29 @@ func (m *Monolith) AddAllPublicRoutes(
|
|||
clientapi.AddPublicRoutes(
|
||||
processCtx, routers, cfg, natsInstance, m.FedClient, m.RoomserverAPI, m.AppserviceAPI, transactions.New(),
|
||||
m.FederationAPI, m.UserAPI, userDirectoryProvider,
|
||||
m.ExtPublicRoomsProvider, enableMetrics,
|
||||
m.ExtPublicRoomsProvider, m.UserVerifierProvider, enableMetrics,
|
||||
)
|
||||
federationapi.AddPublicRoutes(
|
||||
processCtx, routers, cfg, natsInstance, m.UserAPI, m.FedClient, m.KeyRing, m.RoomserverAPI, m.FederationAPI, enableMetrics,
|
||||
)
|
||||
mediaapi.AddPublicRoutes(routers, cm, cfg, m.UserAPI, m.Client, m.FedClient, m.KeyRing)
|
||||
syncapi.AddPublicRoutes(processCtx, routers, cfg, cm, natsInstance, m.UserAPI, m.RoomserverAPI, caches, enableMetrics)
|
||||
mediaapi.AddPublicRoutes(routers, cm, cfg, m.Client, m.FedClient, m.KeyRing, m.UserVerifierProvider)
|
||||
syncapi.AddPublicRoutes(processCtx, routers, cfg, cm, natsInstance, m.UserAPI, m.RoomserverAPI, caches, m.UserVerifierProvider, enableMetrics)
|
||||
|
||||
if m.RelayAPI != nil {
|
||||
relayapi.AddPublicRoutes(routers, cfg, m.KeyRing, m.RelayAPI)
|
||||
}
|
||||
}
|
||||
|
||||
type UserVerifierProvider struct {
|
||||
UserVerifier httputil.UserVerifier
|
||||
}
|
||||
|
||||
func (u *UserVerifierProvider) VerifyUserFromRequest(req *http.Request) (*userapi.Device, *util.JSONResponse) {
|
||||
return u.UserVerifier.VerifyUserFromRequest(req)
|
||||
}
|
||||
|
||||
func NewUserVerifierProvider(userVerifier httputil.UserVerifier) *UserVerifierProvider {
|
||||
return &UserVerifierProvider{
|
||||
UserVerifier: userVerifier,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ func toClientResponse(ctx context.Context, res *MSC2836EventRelationshipsRespons
|
|||
// Enable this MSC
|
||||
func Enable(
|
||||
cfg *config.Dendrite, cm *sqlutil.Connections, routers httputil.Routers, rsAPI roomserver.RoomserverInternalAPI, fsAPI fs.FederationInternalAPI,
|
||||
userAPI userapi.UserInternalAPI, keyRing gomatrixserverlib.JSONVerifier,
|
||||
userVerifier httputil.UserVerifier, keyRing gomatrixserverlib.JSONVerifier,
|
||||
) error {
|
||||
db, err := NewDatabase(cm, &cfg.MSCs.Database)
|
||||
if err != nil {
|
||||
|
@ -124,7 +124,7 @@ func Enable(
|
|||
})
|
||||
|
||||
routers.Client.Handle("/unstable/event_relationships",
|
||||
httputil.MakeAuthAPI("eventRelationships", userAPI, eventRelationshipHandler(db, rsAPI, fsAPI)),
|
||||
httputil.MakeAuthAPI("eventRelationships", userVerifier, eventRelationshipHandler(db, rsAPI, fsAPI)),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
routers.Federation.Handle("/unstable/event_relationships", httputil.MakeExternalAPI(
|
||||
|
|
17
setup/mscs/msc3861/msc3861.go
Normal file
17
setup/mscs/msc3861/msc3861.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package msc3861
|
||||
|
||||
import (
|
||||
"github.com/element-hq/dendrite/setup"
|
||||
)
|
||||
|
||||
func Enable(m *setup.Monolith) error {
|
||||
userVerifier, err := newMSC3861UserVerifier(
|
||||
m.UserAPI, m.Config.Global.ServerName,
|
||||
m.Config.MSCs.MSC3861, !m.Config.ClientAPI.GuestsDisabled,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.UserVerifierProvider.UserVerifier = userVerifier
|
||||
return nil
|
||||
}
|
444
setup/mscs/msc3861/msc3861_user_verifier.go
Normal file
444
setup/mscs/msc3861/msc3861_user_verifier.go
Normal file
|
@ -0,0 +1,444 @@
|
|||
package msc3861
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/element-hq/dendrite/clientapi/auth"
|
||||
"github.com/element-hq/dendrite/setup/config"
|
||||
"github.com/element-hq/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
||||
const externalAuthProvider string = "oauth-delegated"
|
||||
|
||||
// Scopes as defined by MSC2967
|
||||
// https://github.com/matrix-org/matrix-spec-proposals/pull/2967
|
||||
const (
|
||||
scopeMatrixAPI string = "urn:matrix:org.matrix.msc2967.client:api:*"
|
||||
scopeMatrixGuest string = "urn:matrix:org.matrix.msc2967.client:api:guest"
|
||||
scopeMatrixDevicePrefix string = "urn:matrix:org.matrix.msc2967.client:device:"
|
||||
)
|
||||
|
||||
type errCode string
|
||||
|
||||
const (
|
||||
codeIntrospectionNot2xx errCode = "introspectionIsNot2xx"
|
||||
codeInvalidClientToken errCode = "invalidClientToken"
|
||||
codeAuthError errCode = "authError"
|
||||
codeMxidError errCode = "mxidError"
|
||||
codeOpenidConfigEndpointNon2xx errCode = "openidConfigEndpointNon2xx"
|
||||
codeOpenidConfigDecodingFailed errCode = "openidConfigDecodingFailed"
|
||||
)
|
||||
|
||||
// MSC3861UserVerifier implements UserVerifier interface
|
||||
type MSC3861UserVerifier struct {
|
||||
userAPI api.UserInternalAPI
|
||||
serverName spec.ServerName
|
||||
cfg *config.MSC3861
|
||||
httpClient *http.Client
|
||||
openIdConfig *OpenIDConfiguration
|
||||
allowGuest bool
|
||||
}
|
||||
|
||||
func newMSC3861UserVerifier(
|
||||
userAPI api.UserInternalAPI,
|
||||
serverName spec.ServerName,
|
||||
cfg *config.MSC3861,
|
||||
allowGuest bool,
|
||||
) (*MSC3861UserVerifier, error) {
|
||||
openIdConfig, err := fetchOpenIDConfiguration(&http.Client{}, cfg.Issuer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &MSC3861UserVerifier{
|
||||
userAPI: userAPI,
|
||||
serverName: serverName,
|
||||
cfg: cfg,
|
||||
openIdConfig: openIdConfig,
|
||||
allowGuest: allowGuest,
|
||||
httpClient: http.DefaultClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type mscError struct {
|
||||
Code errCode
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (r *mscError) Error() string {
|
||||
return fmt.Sprintf("%s: %s", r.Code, r.Msg)
|
||||
}
|
||||
|
||||
// VerifyUserFromRequest authenticates the HTTP request, on success returns Device of the requester.
|
||||
func (m *MSC3861UserVerifier) VerifyUserFromRequest(req *http.Request) (*api.Device, *util.JSONResponse) {
|
||||
util.GetLogger(req.Context()).Debug("MSC3861.VerifyUserFromRequest")
|
||||
// Try to find the Application Service user
|
||||
token, err := auth.ExtractAccessToken(req)
|
||||
if err != nil {
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: spec.MissingToken(err.Error()),
|
||||
}
|
||||
}
|
||||
// TODO: try to get appservice user first. See https://github.com/element-hq/synapse/blob/develop/synapse/api/auth/msc3861_delegated.py#L273
|
||||
userData, err := m.getUserByAccessToken(req.Context(), token)
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case (*mscError):
|
||||
switch e.Code {
|
||||
case codeIntrospectionNot2xx, codeOpenidConfigDecodingFailed, codeOpenidConfigEndpointNon2xx:
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusServiceUnavailable,
|
||||
JSON: spec.Unknown(e.Error()),
|
||||
}
|
||||
case codeInvalidClientToken:
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.Forbidden(e.Error()),
|
||||
}
|
||||
case codeAuthError:
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.Unknown(e.Error()),
|
||||
}
|
||||
case codeMxidError:
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.Unknown(e.Error()),
|
||||
}
|
||||
default:
|
||||
r := util.ErrorResponse(err)
|
||||
return nil, &r
|
||||
}
|
||||
default:
|
||||
r := util.ErrorResponse(err)
|
||||
return nil, &r
|
||||
}
|
||||
}
|
||||
|
||||
// Do not record requests from MAS using the virtual `__oidc_admin` user.
|
||||
if token != m.cfg.AdminToken {
|
||||
// TODO: not sure which exact data we should record here. See the link for reference
|
||||
// https://github.com/element-hq/synapse/blob/develop/synapse/api/auth/base.py#L365
|
||||
}
|
||||
|
||||
if !m.allowGuest && userData.IsGuest {
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: spec.Forbidden(strings.Join([]string{"Insufficient scope: ", scopeMatrixAPI}, "")),
|
||||
}
|
||||
}
|
||||
|
||||
return userData.Device, nil
|
||||
}
|
||||
|
||||
type requester struct {
|
||||
Device *api.Device
|
||||
UserID *spec.UserID
|
||||
Scope []string
|
||||
IsGuest bool
|
||||
}
|
||||
|
||||
func (m *MSC3861UserVerifier) getUserByAccessToken(ctx context.Context, token string) (*requester, error) {
|
||||
var userID *spec.UserID
|
||||
logger := util.GetLogger(ctx)
|
||||
|
||||
if adminToken := m.cfg.AdminToken; adminToken != "" && token == adminToken {
|
||||
// XXX: This is a temporary solution so that the admin API can be called by
|
||||
// the OIDC provider. This will be removed once we have OIDC client
|
||||
// credentials grant support in matrix-authentication-service.
|
||||
logger.Info("Admin token used")
|
||||
// XXX: that user doesn't exist and won't be provisioned.
|
||||
adminUser, err := createUserID("__oidc_admin", m.serverName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &requester{
|
||||
UserID: adminUser,
|
||||
Scope: []string{"urn:synapse:admin:*"},
|
||||
Device: &api.Device{UserID: adminUser.Local(), AccountType: api.AccountTypeAdmin},
|
||||
}, nil
|
||||
}
|
||||
|
||||
introspectionResult, err := m.introspectToken(ctx, token)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("MSC3861UserVerifier:introspectToken")
|
||||
return nil, err
|
||||
}
|
||||
logger.Debugf("Introspection result: %+v", *introspectionResult)
|
||||
|
||||
if !introspectionResult.Active {
|
||||
return nil, &mscError{Code: codeInvalidClientToken, Msg: "Token is not active"}
|
||||
}
|
||||
|
||||
scopes := introspectionResult.Scopes()
|
||||
hasUserScope, hasGuestScope := slices.Contains(scopes, scopeMatrixAPI), slices.Contains(scopes, scopeMatrixGuest)
|
||||
if !hasUserScope && !hasGuestScope {
|
||||
return nil, &mscError{Code: codeInvalidClientToken, Msg: "No scope in token granting user rights"}
|
||||
}
|
||||
|
||||
sub := introspectionResult.Sub
|
||||
if sub == "" {
|
||||
return nil, &mscError{Code: codeInvalidClientToken, Msg: "Invalid sub claim in the introspection result"}
|
||||
}
|
||||
|
||||
localpart := ""
|
||||
{
|
||||
var rs api.QueryLocalpartExternalIDResponse
|
||||
if err = m.userAPI.QueryExternalUserIDByLocalpartAndProvider(ctx, &api.QueryLocalpartExternalIDRequest{
|
||||
ExternalID: sub,
|
||||
AuthProvider: externalAuthProvider,
|
||||
}, &rs); err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
if l := rs.LocalpartExternalID; l != nil {
|
||||
localpart = l.Localpart
|
||||
}
|
||||
}
|
||||
|
||||
if localpart == "" {
|
||||
// If we could not find a user via the external_id, it either does not exist,
|
||||
// or the external_id was never recorded
|
||||
username := introspectionResult.Username
|
||||
if username == "" {
|
||||
return nil, &mscError{Code: codeAuthError, Msg: "Invalid username claim in the introspection result"}
|
||||
}
|
||||
userID, err = createUserID(username, m.serverName)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("getUserByAccessToken:createUserID")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// First try to find a user from the username claim
|
||||
var account *api.Account
|
||||
{
|
||||
var rs api.QueryAccountByLocalpartResponse
|
||||
err := m.userAPI.QueryAccountByLocalpart(ctx, &api.QueryAccountByLocalpartRequest{Localpart: userID.Local(), ServerName: userID.Domain()}, &rs)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
logger.WithError(err).Error("QueryAccountByLocalpart")
|
||||
return nil, err
|
||||
}
|
||||
account = rs.Account
|
||||
}
|
||||
|
||||
if account == nil {
|
||||
// If the user does not exist, we should create it on the fly
|
||||
var rs api.PerformAccountCreationResponse
|
||||
if err = m.userAPI.PerformAccountCreation(ctx, &api.PerformAccountCreationRequest{
|
||||
AccountType: api.AccountTypeUser,
|
||||
Localpart: userID.Local(),
|
||||
ServerName: userID.Domain(),
|
||||
}, &rs); err != nil {
|
||||
logger.WithError(err).Error("PerformAccountCreation")
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := m.userAPI.PerformLocalpartExternalUserIDCreation(ctx, &api.PerformLocalpartExternalUserIDCreationRequest{
|
||||
Localpart: userID.Local(),
|
||||
ExternalID: sub,
|
||||
AuthProvider: externalAuthProvider,
|
||||
}); err != nil {
|
||||
logger.WithError(err).Error("PerformLocalpartExternalUserIDCreation")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localpart = userID.Local()
|
||||
}
|
||||
|
||||
if userID == nil {
|
||||
userID, err = createUserID(localpart, m.serverName)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("getUserByAccessToken:createUserID")
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
deviceIDs := make([]string, 0, 1)
|
||||
for i := range scopes {
|
||||
if s := scopes[i]; strings.HasPrefix(s, scopeMatrixDevicePrefix) {
|
||||
deviceIDs = append(deviceIDs, s[len(scopeMatrixDevicePrefix):])
|
||||
}
|
||||
}
|
||||
|
||||
if len(deviceIDs) != 1 {
|
||||
logger.Errorf("Invalid device IDs in scope: %+v", deviceIDs)
|
||||
return nil, &mscError{Code: codeAuthError, Msg: "Invalid device IDs in scope"}
|
||||
}
|
||||
|
||||
var device *api.Device
|
||||
|
||||
deviceID := deviceIDs[0]
|
||||
if len(deviceID) > 255 || len(deviceID) < 1 {
|
||||
return nil, &mscError{
|
||||
Code: codeAuthError,
|
||||
Msg: strings.Join([]string{"Invalid device ID in scope: ", deviceID}, ""),
|
||||
}
|
||||
}
|
||||
logger.Debugf("deviceID is: %s", deviceID)
|
||||
logger.Debugf("scope is: %+v", scopes)
|
||||
|
||||
userDeviceExists := false
|
||||
{
|
||||
var rs api.QueryDevicesResponse
|
||||
err := m.userAPI.QueryDevices(ctx, &api.QueryDevicesRequest{UserID: userID.String()}, &rs)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range rs.Devices {
|
||||
if d := &rs.Devices[i]; d.ID == deviceID {
|
||||
userDeviceExists = true
|
||||
device = d
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.Debugf("userDeviceExists is: %t", userDeviceExists)
|
||||
if !userDeviceExists {
|
||||
var rs api.PerformDeviceCreationResponse
|
||||
deviceDisplayName := "OIDC-native client"
|
||||
if err := m.userAPI.PerformDeviceCreation(ctx, &api.PerformDeviceCreationRequest{
|
||||
Localpart: localpart,
|
||||
ServerName: m.serverName,
|
||||
AccessToken: token,
|
||||
DeviceID: &deviceID,
|
||||
DeviceDisplayName: &deviceDisplayName,
|
||||
// TODO: Cannot add IPAddr and Useragent values here. Should we care about it here?
|
||||
}, &rs); err != nil {
|
||||
logger.WithError(err).Error("PerformDeviceCreation")
|
||||
return nil, err
|
||||
}
|
||||
device = rs.Device
|
||||
logger.Debugf("PerformDeviceCreationResponse is: %+v", rs)
|
||||
}
|
||||
|
||||
|
||||
|
||||
return &requester{
|
||||
Device: device,
|
||||
UserID: userID,
|
||||
Scope: scopes,
|
||||
IsGuest: hasGuestScope && !hasUserScope,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createUserID(local string, serverName spec.ServerName) (*spec.UserID, error) {
|
||||
userID, err := spec.NewUserID(strings.Join([]string{"@", local, ":", string(serverName)}, ""), false)
|
||||
if err != nil {
|
||||
return nil, &mscError{Code: codeMxidError, Msg: err.Error()}
|
||||
}
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
func (m *MSC3861UserVerifier) introspectToken(ctx context.Context, token string) (*introspectionResponse, error) {
|
||||
formBody := url.Values{"token": []string{token}}
|
||||
encoded := formBody.Encode()
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, m.openIdConfig.IntrospectionEndpoint, strings.NewReader(encoded))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.SetBasicAuth(m.cfg.ClientID, m.cfg.ClientSecret)
|
||||
|
||||
resp, err := m.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body := resp.Body
|
||||
defer resp.Body.Close()
|
||||
|
||||
if c := resp.StatusCode; c < 200 || c >= 300 {
|
||||
return nil, errors.New(strings.Join([]string{"The introspection endpoint returned a '", resp.Status ,"' response"}, ""))
|
||||
}
|
||||
var ir introspectionResponse
|
||||
if err := json.NewDecoder(body).Decode(&ir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ir, nil
|
||||
}
|
||||
|
||||
type OpenIDConfiguration struct {
|
||||
Issuer string `json:"issuer"`
|
||||
AuthorizationEndpoint string `json:"authorization_endpoint"`
|
||||
TokenEndpoint string `json:"token_endpoint"`
|
||||
JWKsURI string `json:"jwks_uri"`
|
||||
RegistrationEndpoint string `json:"registration_endpoint"`
|
||||
ScopesSupported []string `json:"scopes_supported"`
|
||||
ResponseTypesSupported []string `json:"response_types_supported"`
|
||||
ResponseModesSupported []string `json:"response_modes_supported"`
|
||||
GrantTypesSupported []string `json:"grant_types_supported"`
|
||||
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
|
||||
TokenEndpointAuthSigningAlgCaluesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported"`
|
||||
RevocationEnpoint string `json:"revocation_endpoint"`
|
||||
RevocationEndpointAuthMethodsSupported []string `json:"revocation_endpoint_auth_methods_supported"`
|
||||
RevocationEndpointAuthSigningAlgValues []string `json:"revocation_endpoint_auth_signing_alg_values_supported"`
|
||||
IntrospectionEndpoint string `json:"introspection_endpoint"`
|
||||
IntrospectionEndpointAuthMethodsSupported []string `json:"introspection_endpoint_auth_methods_supported"`
|
||||
IntrospectionEndpointAuthSigningAlgValues []string `json:"introspection_endpoint_auth_signing_alg_values_supported"`
|
||||
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
|
||||
UserinfoEndpoint string `json:"userinfo_endpoint"`
|
||||
SubjectTypesSupported []string `json:"subject_types_supported"`
|
||||
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
|
||||
UserinfoSigningAlgValuesSupported []string `json:"userinfo_signing_alg_values_supported"`
|
||||
DisplayValuesSupported []string `json:"display_values_supported"`
|
||||
ClaimTypesSupported []string `json:"claim_types_supported"`
|
||||
ClaimsSupported []string `json:"claims_supported"`
|
||||
ClaimsParameterSupported bool `json:"claims_parameter_supported"`
|
||||
RequestParameterSupported bool `json:"request_parameter_supported"`
|
||||
RequestURIParameterSupported bool `json:"request_uri_parameter_supported"`
|
||||
PromptValuesSupported []string `json:"prompt_values_supported"`
|
||||
DeviceAuthorizaEndpoint string `json:"device_authorization_endpoint"`
|
||||
AccountManagementURI string `json:"account_management_uri"`
|
||||
AccountManagementActionsSupported []string `json:"account_management_actions_supported"`
|
||||
}
|
||||
|
||||
func fetchOpenIDConfiguration(httpClient *http.Client, authHostURL string) (*
|
||||
OpenIDConfiguration, error) {
|
||||
u, err := url.Parse(authHostURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u = u.JoinPath(".well-known/openid-configuration")
|
||||
resp, err := httpClient.Get(u.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, &mscError{Code: codeOpenidConfigEndpointNon2xx, Msg: ".well-known/openid-configuration endpoint returned non-200 response"}
|
||||
}
|
||||
var oic OpenIDConfiguration
|
||||
if err := json.NewDecoder(resp.Body).Decode(&oic); err != nil {
|
||||
return nil, &mscError{Code: codeOpenidConfigDecodingFailed, Msg: err.Error()}
|
||||
}
|
||||
return &oic, nil
|
||||
}
|
||||
|
||||
// introspectionResponse as described in the RFC https://datatracker.ietf.org/doc/html/rfc7662#section-2.2
|
||||
type introspectionResponse struct {
|
||||
Active bool `json:"active"` // required
|
||||
Scope string `json:"scope"` // optional
|
||||
Username string `json:"username"` // optional
|
||||
TokenType string `json:"token_type"` // optional
|
||||
Exp *int64 `json:"exp"` // optional
|
||||
Iat *int64 `json:"iat"` // optional
|
||||
Nfb *int64 `json:"nfb"` // optional
|
||||
Sub string `json:"sub"` // optional
|
||||
Jti string `json:"jti"` // optional
|
||||
Aud string `json:"aud"` // optional
|
||||
Iss string `json:"iss"` // optional
|
||||
}
|
||||
|
||||
func (i *introspectionResponse) Scopes() []string {
|
||||
return strings.Split(i.Scope, " ")
|
||||
}
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/element-hq/dendrite/setup"
|
||||
"github.com/element-hq/dendrite/setup/config"
|
||||
"github.com/element-hq/dendrite/setup/mscs/msc2836"
|
||||
"github.com/element-hq/dendrite/setup/mscs/msc3861"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
@ -34,10 +35,11 @@ func Enable(cfg *config.Dendrite, cm *sqlutil.Connections, routers httputil.Rout
|
|||
func EnableMSC(cfg *config.Dendrite, cm *sqlutil.Connections, routers httputil.Routers, monolith *setup.Monolith, msc string, caches *caching.Caches) error {
|
||||
switch msc {
|
||||
case "msc2836":
|
||||
return msc2836.Enable(cfg, cm, routers, monolith.RoomserverAPI, monolith.FederationAPI, monolith.UserAPI, monolith.KeyRing)
|
||||
return msc2836.Enable(cfg, cm, routers, monolith.RoomserverAPI, monolith.FederationAPI, monolith.UserVerifierProvider, monolith.KeyRing)
|
||||
case "msc2444": // enabled inside federationapi
|
||||
case "msc2753": // enabled inside clientapi
|
||||
case "msc3861": // enabled inside clientapi
|
||||
case "msc3861":
|
||||
return msc3861.Enable(monolith)
|
||||
default:
|
||||
logrus.Warnf("EnableMSC: unknown MSC '%s', this MSC is either not supported or is natively supported by Dendrite", msc)
|
||||
}
|
||||
|
|
|
@ -36,16 +36,17 @@ func Setup(
|
|||
lazyLoadCache caching.LazyLoadCache,
|
||||
fts fulltext.Indexer,
|
||||
rateLimits *httputil.RateLimits,
|
||||
userVerifier httputil.UserVerifier,
|
||||
) {
|
||||
v1unstablemux := csMux.PathPrefix("/{apiversion:(?:v1|unstable)}/").Subrouter()
|
||||
v3mux := csMux.PathPrefix("/{apiversion:(?:r0|v3)}/").Subrouter()
|
||||
|
||||
// TODO: Add AS support for all handlers below.
|
||||
v3mux.Handle("/sync", httputil.MakeAuthAPI("sync", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
v3mux.Handle("/sync", httputil.MakeAuthAPI("sync", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return srp.OnIncomingSyncRequest(req, device)
|
||||
}, httputil.WithAllowGuests())).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/messages", httputil.MakeAuthAPI("room_messages", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
v3mux.Handle("/rooms/{roomID}/messages", httputil.MakeAuthAPI("room_messages", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
// not specced, but ensure we're rate limiting requests to this endpoint
|
||||
if r := rateLimits.Limit(req, device); r != nil {
|
||||
return *r
|
||||
|
@ -58,7 +59,7 @@ func Setup(
|
|||
}, httputil.WithAllowGuests())).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/event/{eventID}",
|
||||
httputil.MakeAuthAPI("rooms_get_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("rooms_get_event", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -68,7 +69,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/user/{userId}/filter",
|
||||
httputil.MakeAuthAPI("put_filter", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("put_filter", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -78,7 +79,7 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/user/{userId}/filter/{filterId}",
|
||||
httputil.MakeAuthAPI("get_filter", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("get_filter", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -87,12 +88,12 @@ func Setup(
|
|||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/keys/changes", httputil.MakeAuthAPI("keys_changes", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
v3mux.Handle("/keys/changes", httputil.MakeAuthAPI("keys_changes", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return srp.OnIncomingKeyChangeRequest(req, device)
|
||||
}, httputil.WithAllowGuests())).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomId}/context/{eventId}",
|
||||
httputil.MakeAuthAPI("context", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("context", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -108,7 +109,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v1unstablemux.Handle("/rooms/{roomId}/relations/{eventId}",
|
||||
httputil.MakeAuthAPI("relations", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("relations", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -122,7 +123,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v1unstablemux.Handle("/rooms/{roomId}/relations/{eventId}/{relType}",
|
||||
httputil.MakeAuthAPI("relation_type", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("relation_type", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -136,7 +137,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v1unstablemux.Handle("/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}",
|
||||
httputil.MakeAuthAPI("relation_type_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("relation_type_event", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -150,7 +151,7 @@ func Setup(
|
|||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/search",
|
||||
httputil.MakeAuthAPI("search", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("search", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
if !cfg.Fulltext.Enabled {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusNotImplemented,
|
||||
|
@ -173,7 +174,7 @@ func Setup(
|
|||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/members",
|
||||
httputil.MakeAuthAPI("rooms_members", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
httputil.MakeAuthAPI("rooms_members", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
|
|
@ -42,6 +42,7 @@ func AddPublicRoutes(
|
|||
userAPI userapi.SyncUserAPI,
|
||||
rsAPI api.SyncRoomserverAPI,
|
||||
caches caching.LazyLoadCache,
|
||||
userVerifier httputil.UserVerifier,
|
||||
enableMetrics bool,
|
||||
) {
|
||||
js, natsClient := natsInstance.Prepare(processContext, &dendriteCfg.Global.JetStream)
|
||||
|
@ -149,5 +150,6 @@ func AddPublicRoutes(
|
|||
routers.Client, requestPool, syncDB, userAPI,
|
||||
rsAPI, &dendriteCfg.SyncAPI, caches, fts,
|
||||
rateLimits,
|
||||
userVerifier,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ type UserInternalAPI interface {
|
|||
|
||||
QuerySearchProfilesAPI // used by p2p demos
|
||||
QueryAccountByLocalpart(ctx context.Context, req *QueryAccountByLocalpartRequest, res *QueryAccountByLocalpartResponse) (err error)
|
||||
QueryExternalUserIDByLocalpartAndProvider(ctx context.Context, req *QueryLocalpartExternalIDRequest, res *QueryLocalpartExternalIDResponse) (err error)
|
||||
PerformLocalpartExternalUserIDCreation(ctx context.Context, req *PerformLocalpartExternalUserIDCreationRequest) (err error)
|
||||
}
|
||||
|
||||
// api functions required by the appservice api
|
||||
|
@ -129,6 +131,7 @@ type QuerySearchProfilesAPI interface {
|
|||
QuerySearchProfiles(ctx context.Context, req *QuerySearchProfilesRequest, res *QuerySearchProfilesResponse) error
|
||||
}
|
||||
|
||||
// FIXME: typo in Acccess
|
||||
// common function for creating authenticated endpoints (used in client/media/sync api)
|
||||
type QueryAcccessTokenAPI interface {
|
||||
QueryAccessToken(ctx context.Context, req *QueryAccessTokenRequest, res *QueryAccessTokenResponse) error
|
||||
|
@ -316,6 +319,9 @@ type PerformAccountCreationRequest struct {
|
|||
Localpart string // Required: The localpart for this account. Ignored if account type is guest.
|
||||
ServerName spec.ServerName // optional: if not specified, default server name used instead
|
||||
|
||||
DisplayName string // optional: this is populated only by MAS. In the legacy flow it's not used
|
||||
AvatarURL string // optional: this is populated only by MAS. In the legacy flow it's not used
|
||||
|
||||
AppServiceID string // optional: the application service ID (not user ID) creating this account, if any.
|
||||
Password string // optional: if missing then this account will be a passwordless account
|
||||
OnConflict Conflict
|
||||
|
@ -653,10 +659,26 @@ type QueryAccountByLocalpartResponse struct {
|
|||
Account *Account
|
||||
}
|
||||
|
||||
type QueryLocalpartExternalIDRequest struct {
|
||||
ExternalID string
|
||||
AuthProvider string
|
||||
}
|
||||
|
||||
type QueryLocalpartExternalIDResponse struct {
|
||||
LocalpartExternalID *LocalpartExternalID
|
||||
}
|
||||
|
||||
type PerformLocalpartExternalUserIDCreationRequest struct {
|
||||
Localpart string
|
||||
ExternalID string
|
||||
AuthProvider string
|
||||
}
|
||||
|
||||
// API functions required by the clientapi
|
||||
type ClientKeyAPI interface {
|
||||
UploadDeviceKeysAPI
|
||||
QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse)
|
||||
QueryMasterKeys(ctx context.Context, req *QueryMasterKeysRequest, res *QueryMasterKeysResponse)
|
||||
PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse) error
|
||||
|
||||
PerformUploadDeviceSignatures(ctx context.Context, req *PerformUploadDeviceSignaturesRequest, res *PerformUploadDeviceSignaturesResponse)
|
||||
|
@ -918,6 +940,16 @@ type QueryKeysResponse struct {
|
|||
Error *KeyError
|
||||
}
|
||||
|
||||
type QueryMasterKeysRequest struct {
|
||||
UserID string
|
||||
}
|
||||
|
||||
type QueryMasterKeysResponse struct {
|
||||
Key *types.CrossSigningKey
|
||||
// Set if there was a fatal error processing this query
|
||||
Error *KeyError
|
||||
}
|
||||
|
||||
type QueryKeyChangesRequest struct {
|
||||
// The offset of the last received key event, or sarama.OffsetOldest if this is from the beginning
|
||||
Offset int64
|
||||
|
|
|
@ -114,7 +114,9 @@ func (a *UserInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.
|
|||
|
||||
byPurpose[fclient.CrossSigningKeyPurposeMaster] = req.MasterKey
|
||||
for _, key := range req.MasterKey.Keys { // iterates once, see sanityCheckKey
|
||||
toStore[fclient.CrossSigningKeyPurposeMaster] = key
|
||||
toStore[fclient.CrossSigningKeyPurposeMaster] = types.CrossSigningKey{
|
||||
KeyData: key,
|
||||
}
|
||||
}
|
||||
hasMasterKey = true
|
||||
}
|
||||
|
@ -130,7 +132,9 @@ func (a *UserInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.
|
|||
|
||||
byPurpose[fclient.CrossSigningKeyPurposeSelfSigning] = req.SelfSigningKey
|
||||
for _, key := range req.SelfSigningKey.Keys { // iterates once, see sanityCheckKey
|
||||
toStore[fclient.CrossSigningKeyPurposeSelfSigning] = key
|
||||
toStore[fclient.CrossSigningKeyPurposeSelfSigning] = types.CrossSigningKey{
|
||||
KeyData: key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,7 +149,9 @@ func (a *UserInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.
|
|||
|
||||
byPurpose[fclient.CrossSigningKeyPurposeUserSigning] = req.UserSigningKey
|
||||
for _, key := range req.UserSigningKey.Keys { // iterates once, see sanityCheckKey
|
||||
toStore[fclient.CrossSigningKeyPurposeUserSigning] = key
|
||||
toStore[fclient.CrossSigningKeyPurposeUserSigning] = types.CrossSigningKey{
|
||||
KeyData: key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,7 +204,7 @@ func (a *UserInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.
|
|||
changed = true
|
||||
break
|
||||
}
|
||||
if !bytes.Equal(old, new) {
|
||||
if !bytes.Equal(old.KeyData, new.KeyData) {
|
||||
// One of the existing keys for a purpose we already knew about has
|
||||
// changed.
|
||||
changed = true
|
||||
|
@ -210,7 +216,7 @@ func (a *UserInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.
|
|||
}
|
||||
|
||||
// Store the keys.
|
||||
if err := a.KeyDatabase.StoreCrossSigningKeysForUser(ctx, req.UserID, toStore); err != nil {
|
||||
if err := a.KeyDatabase.StoreCrossSigningKeysForUser(ctx, req.UserID, toStore, nil); err != nil {
|
||||
res.Error = &api.KeyError{
|
||||
Err: fmt.Sprintf("a.DB.StoreCrossSigningKeysForUser: %s", err),
|
||||
}
|
||||
|
|
|
@ -234,6 +234,19 @@ func (a *UserInternalAPI) PerformMarkAsStaleIfNeeded(ctx context.Context, req *a
|
|||
return a.Updater.ManualUpdate(ctx, req.Domain, req.UserID)
|
||||
}
|
||||
|
||||
func (a *UserInternalAPI) QueryMasterKeys(ctx context.Context, req *api.QueryMasterKeysRequest, res *api.QueryMasterKeysResponse) {
|
||||
crossSigningKeyMap, err := a.KeyDatabase.CrossSigningKeysDataForUserAndKeyType(ctx, req.UserID, fclient.CrossSigningKeyPurposeMaster)
|
||||
if err != nil {
|
||||
res.Error = &api.KeyError{
|
||||
Err: fmt.Sprintf("failed to query user cross signing master keys: %s", err),
|
||||
}
|
||||
return
|
||||
}
|
||||
if key, ok := crossSigningKeyMap[fclient.CrossSigningKeyPurposeMaster]; ok {
|
||||
res.Key = &key
|
||||
}
|
||||
}
|
||||
|
||||
// nolint:gocyclo
|
||||
func (a *UserInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse) {
|
||||
var respMu sync.Mutex
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
|
@ -247,10 +248,17 @@ func (a *UserInternalAPI) PerformAccountCreation(ctx context.Context, req *api.P
|
|||
return nil
|
||||
}
|
||||
|
||||
if _, _, err = a.DB.SetDisplayName(ctx, req.Localpart, serverName, req.Localpart); err != nil {
|
||||
displayName := cmp.Or(req.DisplayName, req.Localpart)
|
||||
if _, _, err = a.DB.SetDisplayName(ctx, req.Localpart, serverName, displayName); err != nil {
|
||||
return fmt.Errorf("a.DB.SetDisplayName: %w", err)
|
||||
}
|
||||
|
||||
if req.AvatarURL != "" {
|
||||
if _, _, err := a.DB.SetAvatarURL(ctx, req.Localpart, serverName, req.AvatarURL); err != nil {
|
||||
return fmt.Errorf("a.DB.SetAvatarURL: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
postRegisterJoinRooms(a.Config, acc, a.RSAPI)
|
||||
|
||||
res.AccountCreated = true
|
||||
|
@ -594,6 +602,15 @@ func (a *UserInternalAPI) QueryAccountByLocalpart(ctx context.Context, req *api.
|
|||
return
|
||||
}
|
||||
|
||||
func (a *UserInternalAPI) PerformLocalpartExternalUserIDCreation(ctx context.Context, req *api.PerformLocalpartExternalUserIDCreationRequest) (err error) {
|
||||
return a.DB.CreateLocalpartExternalID(ctx, req.Localpart, req.ExternalID, req.AuthProvider)
|
||||
}
|
||||
|
||||
func (a *UserInternalAPI) QueryExternalUserIDByLocalpartAndProvider(ctx context.Context, req *api.QueryLocalpartExternalIDRequest, res *api.QueryLocalpartExternalIDResponse) (err error) {
|
||||
res.LocalpartExternalID, err = a.DB.GetLocalpartForExternalID(ctx, req.ExternalID, req.AuthProvider)
|
||||
return
|
||||
}
|
||||
|
||||
// Return the appservice 'device' or nil if the token is not an appservice. Returns an error if there was a problem
|
||||
// creating a 'device'.
|
||||
func (a *UserInternalAPI) queryAppServiceToken(ctx context.Context, token, appServiceUserID string) (*api.Device, error) {
|
||||
|
|
|
@ -226,9 +226,10 @@ type KeyDatabase interface {
|
|||
|
||||
CrossSigningKeysForUser(ctx context.Context, userID string) (map[fclient.CrossSigningKeyPurpose]fclient.CrossSigningKey, error)
|
||||
CrossSigningKeysDataForUser(ctx context.Context, userID string) (types.CrossSigningKeyMap, error)
|
||||
CrossSigningKeysDataForUserAndKeyType(ctx context.Context, userID string, keyType fclient.CrossSigningKeyPurpose) (types.CrossSigningKeyMap, error)
|
||||
CrossSigningSigsForTarget(ctx context.Context, originUserID, targetUserID string, targetKeyID gomatrixserverlib.KeyID) (types.CrossSigningSigMap, error)
|
||||
|
||||
StoreCrossSigningKeysForUser(ctx context.Context, userID string, keyMap types.CrossSigningKeyMap) error
|
||||
StoreCrossSigningKeysForUser(ctx context.Context, userID string, keyMap types.CrossSigningKeyMap, updatableWithoutUIABeforeMs *int64) error
|
||||
StoreCrossSigningSigsForTarget(ctx context.Context, originUserID string, originKeyID gomatrixserverlib.KeyID, targetUserID string, targetKeyID gomatrixserverlib.KeyID, signature spec.Base64Bytes) error
|
||||
|
||||
DeleteStaleDeviceLists(
|
||||
|
|
|
@ -24,23 +24,29 @@ CREATE TABLE IF NOT EXISTS keyserver_cross_signing_keys (
|
|||
user_id TEXT NOT NULL,
|
||||
key_type SMALLINT NOT NULL,
|
||||
key_data TEXT NOT NULL,
|
||||
updatable_without_uia_before_ms BIGINT DEFAULT NULL,
|
||||
PRIMARY KEY (user_id, key_type)
|
||||
);
|
||||
`
|
||||
|
||||
const selectCrossSigningKeysForUserSQL = "" +
|
||||
"SELECT key_type, key_data FROM keyserver_cross_signing_keys" +
|
||||
"SELECT key_type, key_data, updatable_without_uia_before_ms FROM keyserver_cross_signing_keys" +
|
||||
" WHERE user_id = $1"
|
||||
|
||||
const selectCrossSigningKeysForUserAndKeyTypeSQL = "" +
|
||||
"SELECT key_type, key_data, updatable_without_uia_before_ms FROM keyserver_cross_signing_keys" +
|
||||
" WHERE user_id = $1 AND key_type = $2"
|
||||
|
||||
const upsertCrossSigningKeysForUserSQL = "" +
|
||||
"INSERT INTO keyserver_cross_signing_keys (user_id, key_type, key_data)" +
|
||||
" VALUES($1, $2, $3)" +
|
||||
" ON CONFLICT (user_id, key_type) DO UPDATE SET key_data = $3"
|
||||
|
||||
type crossSigningKeysStatements struct {
|
||||
db *sql.DB
|
||||
selectCrossSigningKeysForUserStmt *sql.Stmt
|
||||
upsertCrossSigningKeysForUserStmt *sql.Stmt
|
||||
db *sql.DB
|
||||
selectCrossSigningKeysForUserStmt *sql.Stmt
|
||||
selectCrossSigningKeysForUserAndKeyTypeStmt *sql.Stmt
|
||||
upsertCrossSigningKeysForUserStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func NewPostgresCrossSigningKeysTable(db *sql.DB) (tables.CrossSigningKeys, error) {
|
||||
|
@ -53,6 +59,7 @@ func NewPostgresCrossSigningKeysTable(db *sql.DB) (tables.CrossSigningKeys, erro
|
|||
}
|
||||
return s, sqlutil.StatementList{
|
||||
{&s.selectCrossSigningKeysForUserStmt, selectCrossSigningKeysForUserSQL},
|
||||
{&s.selectCrossSigningKeysForUserAndKeyTypeStmt, selectCrossSigningKeysForUserAndKeyTypeSQL},
|
||||
{&s.upsertCrossSigningKeysForUserStmt, upsertCrossSigningKeysForUserSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
@ -69,27 +76,64 @@ func (s *crossSigningKeysStatements) SelectCrossSigningKeysForUser(
|
|||
for rows.Next() {
|
||||
var keyTypeInt int16
|
||||
var keyData spec.Base64Bytes
|
||||
if err = rows.Scan(&keyTypeInt, &keyData); err != nil {
|
||||
var updatableWithoutUIABeforeMs *int64
|
||||
if err = rows.Scan(&keyTypeInt, &keyData, &updatableWithoutUIABeforeMs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyType, ok := types.KeyTypeIntToPurpose[keyTypeInt]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown key purpose int %d", keyTypeInt)
|
||||
}
|
||||
r[keyType] = keyData
|
||||
r[keyType] = types.CrossSigningKey{
|
||||
UpdatableWithoutUIABeforeMs: updatableWithoutUIABeforeMs,
|
||||
KeyData: keyData,
|
||||
}
|
||||
}
|
||||
err = rows.Err()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *crossSigningKeysStatements) SelectCrossSigningKeysForUserAndKeyType(
|
||||
ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose,
|
||||
) (r types.CrossSigningKeyMap, err error) {
|
||||
keyTypeInt, ok := types.KeyTypePurposeToInt[keyType]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown key purpose %q", keyType)
|
||||
}
|
||||
rows, err := sqlutil.TxStmt(txn, s.selectCrossSigningKeysForUserAndKeyTypeStmt).QueryContext(ctx, userID, keyTypeInt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "SelectCrossSigningKeysForUserAndKeyType: rows.close() failed")
|
||||
r = types.CrossSigningKeyMap{}
|
||||
for rows.Next() {
|
||||
var keyTypeInt int16
|
||||
var keyData spec.Base64Bytes
|
||||
var updatableWithoutUIABeforeMs *int64
|
||||
if err = rows.Scan(&keyTypeInt, &keyData, &updatableWithoutUIABeforeMs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyType, ok := types.KeyTypeIntToPurpose[keyTypeInt]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown key purpose int %d", keyTypeInt)
|
||||
}
|
||||
r[keyType] = types.CrossSigningKey{
|
||||
UpdatableWithoutUIABeforeMs: updatableWithoutUIABeforeMs,
|
||||
KeyData: keyData,
|
||||
}
|
||||
}
|
||||
err = rows.Err()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *crossSigningKeysStatements) UpsertCrossSigningKeysForUser(
|
||||
ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose, keyData spec.Base64Bytes,
|
||||
ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose, keyData spec.Base64Bytes, updatableWithoutUIABeforeMs *int64,
|
||||
) error {
|
||||
keyTypeInt, ok := types.KeyTypePurposeToInt[keyType]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown key purpose %q", keyType)
|
||||
}
|
||||
if _, err := sqlutil.TxStmt(txn, s.upsertCrossSigningKeysForUserStmt).ExecContext(ctx, userID, keyTypeInt, keyData); err != nil {
|
||||
if _, err := sqlutil.TxStmt(txn, s.upsertCrossSigningKeysForUserStmt).ExecContext(ctx, userID, keyTypeInt, keyData, updatableWithoutUIABeforeMs); err != nil {
|
||||
return fmt.Errorf("s.upsertCrossSigningKeysForUserStmt: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -1101,12 +1101,12 @@ func (d *KeyDatabase) CrossSigningKeysForUser(ctx context.Context, userID string
|
|||
}
|
||||
results := map[fclient.CrossSigningKeyPurpose]fclient.CrossSigningKey{}
|
||||
for purpose, key := range keyMap {
|
||||
keyID := gomatrixserverlib.KeyID("ed25519:" + key.Encode())
|
||||
keyID := gomatrixserverlib.KeyID("ed25519:" + key.KeyData.Encode())
|
||||
result := fclient.CrossSigningKey{
|
||||
UserID: userID,
|
||||
Usage: []fclient.CrossSigningKeyPurpose{purpose},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{
|
||||
keyID: key,
|
||||
keyID: key.KeyData,
|
||||
},
|
||||
}
|
||||
sigMap, err := d.CrossSigningSigsTable.SelectCrossSigningSigsForTarget(ctx, nil, userID, userID, keyID)
|
||||
|
@ -1137,16 +1137,21 @@ func (d *KeyDatabase) CrossSigningKeysDataForUser(ctx context.Context, userID st
|
|||
return d.CrossSigningKeysTable.SelectCrossSigningKeysForUser(ctx, nil, userID)
|
||||
}
|
||||
|
||||
// CrossSigningKeysForUserAndKeyType returns the latest known cross-signing keys for a user and key type, if any.
|
||||
func (d *KeyDatabase) CrossSigningKeysDataForUserAndKeyType(ctx context.Context, userID string, keyType fclient.CrossSigningKeyPurpose) (types.CrossSigningKeyMap, error) {
|
||||
return d.CrossSigningKeysTable.SelectCrossSigningKeysForUserAndKeyType(ctx, nil, userID, keyType)
|
||||
}
|
||||
|
||||
// CrossSigningSigsForTarget returns the signatures for a given user's key ID, if any.
|
||||
func (d *KeyDatabase) CrossSigningSigsForTarget(ctx context.Context, originUserID, targetUserID string, targetKeyID gomatrixserverlib.KeyID) (types.CrossSigningSigMap, error) {
|
||||
return d.CrossSigningSigsTable.SelectCrossSigningSigsForTarget(ctx, nil, originUserID, targetUserID, targetKeyID)
|
||||
}
|
||||
|
||||
// StoreCrossSigningKeysForUser stores the latest known cross-signing keys for a user.
|
||||
func (d *KeyDatabase) StoreCrossSigningKeysForUser(ctx context.Context, userID string, keyMap types.CrossSigningKeyMap) error {
|
||||
func (d *KeyDatabase) StoreCrossSigningKeysForUser(ctx context.Context, userID string, keyMap types.CrossSigningKeyMap, updatableWithoutUIABeforeMs *int64) error {
|
||||
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||
for keyType, keyData := range keyMap {
|
||||
if err := d.CrossSigningKeysTable.UpsertCrossSigningKeysForUser(ctx, txn, userID, keyType, keyData); err != nil {
|
||||
for keyType, key := range keyMap {
|
||||
if err := d.CrossSigningKeysTable.UpsertCrossSigningKeysForUser(ctx, txn, userID, keyType, key.KeyData, key.UpdatableWithoutUIABeforeMs); err != nil {
|
||||
return fmt.Errorf("d.CrossSigningKeysTable.InsertCrossSigningKeysForUser: %w", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,22 +24,28 @@ CREATE TABLE IF NOT EXISTS keyserver_cross_signing_keys (
|
|||
user_id TEXT NOT NULL,
|
||||
key_type INTEGER NOT NULL,
|
||||
key_data TEXT NOT NULL,
|
||||
updatable_without_uia_before_ms BIGINT DEFAULT NULL,
|
||||
PRIMARY KEY (user_id, key_type)
|
||||
);
|
||||
`
|
||||
|
||||
const selectCrossSigningKeysForUserSQL = "" +
|
||||
"SELECT key_type, key_data FROM keyserver_cross_signing_keys" +
|
||||
"SELECT key_type, key_data, updatable_without_uia_before_ms FROM keyserver_cross_signing_keys" +
|
||||
" WHERE user_id = $1"
|
||||
|
||||
const selectCrossSigningKeysForUserAndKeyTypeSQL = "" +
|
||||
"SELECT key_type, key_data, updatable_without_uia_before_ms FROM keyserver_cross_signing_keys" +
|
||||
" WHERE user_id = $1 AND key_type = $2"
|
||||
|
||||
const upsertCrossSigningKeysForUserSQL = "" +
|
||||
"INSERT OR REPLACE INTO keyserver_cross_signing_keys (user_id, key_type, key_data)" +
|
||||
" VALUES($1, $2, $3)"
|
||||
"INSERT OR REPLACE INTO keyserver_cross_signing_keys (user_id, key_type, key_data, updatable_without_uia_before_ms)" +
|
||||
" VALUES($1, $2, $3, $4)"
|
||||
|
||||
type crossSigningKeysStatements struct {
|
||||
db *sql.DB
|
||||
selectCrossSigningKeysForUserStmt *sql.Stmt
|
||||
upsertCrossSigningKeysForUserStmt *sql.Stmt
|
||||
db *sql.DB
|
||||
selectCrossSigningKeysForUserStmt *sql.Stmt
|
||||
selectCrossSigningKeysForUserAndKeyTypeStmt *sql.Stmt
|
||||
upsertCrossSigningKeysForUserStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func NewSqliteCrossSigningKeysTable(db *sql.DB) (tables.CrossSigningKeys, error) {
|
||||
|
@ -52,6 +58,7 @@ func NewSqliteCrossSigningKeysTable(db *sql.DB) (tables.CrossSigningKeys, error)
|
|||
}
|
||||
return s, sqlutil.StatementList{
|
||||
{&s.selectCrossSigningKeysForUserStmt, selectCrossSigningKeysForUserSQL},
|
||||
{&s.selectCrossSigningKeysForUserAndKeyTypeStmt, selectCrossSigningKeysForUserAndKeyTypeSQL},
|
||||
{&s.upsertCrossSigningKeysForUserStmt, upsertCrossSigningKeysForUserSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
@ -68,27 +75,64 @@ func (s *crossSigningKeysStatements) SelectCrossSigningKeysForUser(
|
|||
for rows.Next() {
|
||||
var keyTypeInt int16
|
||||
var keyData spec.Base64Bytes
|
||||
if err = rows.Scan(&keyTypeInt, &keyData); err != nil {
|
||||
var updatableWithoutUiaBeforeMs *int64
|
||||
if err = rows.Scan(&keyTypeInt, &keyData, &updatableWithoutUiaBeforeMs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyType, ok := types.KeyTypeIntToPurpose[keyTypeInt]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown key purpose int %d", keyTypeInt)
|
||||
}
|
||||
r[keyType] = keyData
|
||||
r[keyType] = types.CrossSigningKey{
|
||||
UpdatableWithoutUIABeforeMs: updatableWithoutUiaBeforeMs,
|
||||
KeyData: keyData,
|
||||
}
|
||||
}
|
||||
err = rows.Err()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *crossSigningKeysStatements) SelectCrossSigningKeysForUserAndKeyType(
|
||||
ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose,
|
||||
) (r types.CrossSigningKeyMap, err error) {
|
||||
keyTypeInt, ok := types.KeyTypePurposeToInt[keyType]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown key purpose %q", keyType)
|
||||
}
|
||||
rows, err := sqlutil.TxStmt(txn, s.selectCrossSigningKeysForUserAndKeyTypeStmt).QueryContext(ctx, userID, keyTypeInt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "SelectCrossSigningKeysForUserAndKeyType: rows.close() failed")
|
||||
r = types.CrossSigningKeyMap{}
|
||||
for rows.Next() {
|
||||
var keyTypeInt int16
|
||||
var keyData spec.Base64Bytes
|
||||
var updatableWithoutUIABeforeMs *int64
|
||||
if err = rows.Scan(&keyTypeInt, &keyData, &updatableWithoutUIABeforeMs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyType, ok := types.KeyTypeIntToPurpose[keyTypeInt]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown key purpose int %d", keyTypeInt)
|
||||
}
|
||||
r[keyType] = types.CrossSigningKey{
|
||||
UpdatableWithoutUIABeforeMs: updatableWithoutUIABeforeMs,
|
||||
KeyData: keyData,
|
||||
}
|
||||
}
|
||||
err = rows.Err()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *crossSigningKeysStatements) UpsertCrossSigningKeysForUser(
|
||||
ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose, keyData spec.Base64Bytes,
|
||||
ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose, keyData spec.Base64Bytes, updatableWithoutUIABeforeMs *int64,
|
||||
) error {
|
||||
keyTypeInt, ok := types.KeyTypePurposeToInt[keyType]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown key purpose %q", keyType)
|
||||
}
|
||||
if _, err := sqlutil.TxStmt(txn, s.upsertCrossSigningKeysForUserStmt).ExecContext(ctx, userID, keyTypeInt, keyData); err != nil {
|
||||
if _, err := sqlutil.TxStmt(txn, s.upsertCrossSigningKeysForUserStmt).ExecContext(ctx, userID, keyTypeInt, keyData, updatableWithoutUIABeforeMs); err != nil {
|
||||
return fmt.Errorf("s.upsertCrossSigningKeysForUserStmt: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -133,10 +133,6 @@ type LocalpartExternalIDsTable interface {
|
|||
Delete(ctx context.Context, txn *sql.Tx, externalID, authProvider string) error
|
||||
}
|
||||
|
||||
type UIAuthSessionsTable interface {
|
||||
SelectByID(ctx context.Context, txn *sql.Tx, sessionID int) (*api.UIAuthSession, error)
|
||||
}
|
||||
|
||||
type NotificationFilter uint32
|
||||
|
||||
const (
|
||||
|
@ -202,7 +198,8 @@ type StaleDeviceLists interface {
|
|||
|
||||
type CrossSigningKeys interface {
|
||||
SelectCrossSigningKeysForUser(ctx context.Context, txn *sql.Tx, userID string) (r types.CrossSigningKeyMap, err error)
|
||||
UpsertCrossSigningKeysForUser(ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose, keyData spec.Base64Bytes) error
|
||||
SelectCrossSigningKeysForUserAndKeyType(ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose) (r types.CrossSigningKeyMap, err error)
|
||||
UpsertCrossSigningKeysForUser(ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose, keyData spec.Base64Bytes, updatableWithoutUIABeforeMs *int64) error
|
||||
}
|
||||
|
||||
type CrossSigningSigs interface {
|
||||
|
|
|
@ -37,8 +37,13 @@ var KeyTypeIntToPurpose = map[int16]fclient.CrossSigningKeyPurpose{
|
|||
3: fclient.CrossSigningKeyPurposeUserSigning,
|
||||
}
|
||||
|
||||
type CrossSigningKey struct {
|
||||
UpdatableWithoutUIABeforeMs *int64
|
||||
KeyData spec.Base64Bytes
|
||||
}
|
||||
|
||||
// Map of purpose -> public key
|
||||
type CrossSigningKeyMap map[fclient.CrossSigningKeyPurpose]spec.Base64Bytes
|
||||
type CrossSigningKeyMap map[fclient.CrossSigningKeyPurpose]CrossSigningKey
|
||||
|
||||
// Map of user ID -> key ID -> signature
|
||||
type CrossSigningSigMap map[string]map[gomatrixserverlib.KeyID]spec.Base64Bytes
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue