2022-04-29 09:10:08 +01:00
package routing
import (
2023-01-19 20:02:32 +00:00
"context"
2022-08-12 12:00:07 +01:00
"encoding/json"
2023-04-28 16:46:01 +01:00
"errors"
2022-09-30 09:32:31 +01:00
"fmt"
2022-04-29 09:10:08 +01:00
"net/http"
2023-06-22 17:37:21 +01:00
"regexp"
"strconv"
2022-09-27 17:06:49 +01:00
"time"
2022-04-29 09:10:08 +01:00
"github.com/gorilla/mux"
2022-12-22 10:54:03 +00:00
"github.com/matrix-org/dendrite/internal"
2023-04-28 16:46:01 +01:00
"github.com/matrix-org/dendrite/internal/eventutil"
2022-09-30 09:32:31 +01:00
"github.com/matrix-org/gomatrixserverlib"
2023-04-19 15:50:33 +01:00
"github.com/matrix-org/gomatrixserverlib/spec"
2022-09-30 09:32:31 +01:00
"github.com/matrix-org/util"
"github.com/nats-io/nats.go"
"github.com/sirupsen/logrus"
2023-06-22 17:37:21 +01:00
"golang.org/x/exp/constraints"
2022-09-30 09:32:31 +01:00
2023-06-22 17:37:21 +01:00
clientapi "github.com/matrix-org/dendrite/clientapi/api"
2022-04-29 09:10:08 +01:00
"github.com/matrix-org/dendrite/internal/httputil"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
2022-08-12 12:00:07 +01:00
"github.com/matrix-org/dendrite/setup/config"
2022-09-27 17:06:49 +01:00
"github.com/matrix-org/dendrite/setup/jetstream"
2023-02-20 13:58:03 +00:00
"github.com/matrix-org/dendrite/userapi/api"
2023-06-22 17:37:21 +01:00
userapi "github.com/matrix-org/dendrite/userapi/api"
2022-04-29 09:10:08 +01:00
)
2023-06-22 17:37:21 +01:00
var validRegistrationTokenRegex = regexp . MustCompile ( "^[[:ascii:][:digit:]_]*$" )
func AdminCreateNewRegistrationToken ( req * http . Request , cfg * config . ClientAPI , userAPI userapi . ClientUserAPI ) util . JSONResponse {
if ! cfg . RegistrationRequiresToken {
return util . JSONResponse {
Code : http . StatusForbidden ,
JSON : spec . Forbidden ( "Registration via tokens is not enabled on this homeserver" ) ,
}
}
request := struct {
Token string ` json:"token" `
UsesAllowed * int32 ` json:"uses_allowed,omitempty" `
ExpiryTime * int64 ` json:"expiry_time,omitempty" `
Length int32 ` json:"length" `
} { }
if err := json . NewDecoder ( req . Body ) . Decode ( & request ) ; err != nil {
return util . JSONResponse {
Code : http . StatusBadRequest ,
JSON : spec . BadJSON ( fmt . Sprintf ( "Failed to decode request body: %s" , err ) ) ,
}
}
token := request . Token
usesAllowed := request . UsesAllowed
expiryTime := request . ExpiryTime
length := request . Length
if len ( token ) == 0 {
if length == 0 {
// length not provided in request. Assign default value of 16.
length = 16
}
// token not present in request body. Hence, generate a random token.
if length <= 0 || length > 64 {
return util . JSONResponse {
Code : http . StatusBadRequest ,
JSON : spec . BadJSON ( "length must be greater than zero and not greater than 64" ) ,
}
}
token = util . RandomString ( int ( length ) )
}
if len ( token ) > 64 {
//Token present in request body, but is too long.
return util . JSONResponse {
Code : http . StatusBadRequest ,
JSON : spec . BadJSON ( "token must not be longer than 64" ) ,
}
}
isTokenValid := validRegistrationTokenRegex . Match ( [ ] byte ( token ) )
if ! isTokenValid {
return util . JSONResponse {
Code : http . StatusBadRequest ,
JSON : spec . BadJSON ( "token must consist only of characters matched by the regex [A-Za-z0-9-_]" ) ,
}
}
// At this point, we have a valid token, either through request body or through random generation.
if usesAllowed != nil && * usesAllowed < 0 {
return util . JSONResponse {
Code : http . StatusBadRequest ,
JSON : spec . BadJSON ( "uses_allowed must be a non-negative integer or null" ) ,
}
}
if expiryTime != nil && spec . Timestamp ( * expiryTime ) . Time ( ) . Before ( time . Now ( ) ) {
return util . JSONResponse {
Code : http . StatusBadRequest ,
JSON : spec . BadJSON ( "expiry_time must not be in the past" ) ,
}
}
pending := int32 ( 0 )
completed := int32 ( 0 )
// If usesAllowed or expiryTime is 0, it means they are not present in the request. NULL (indicating unlimited uses / no expiration will be persisted in DB)
registrationToken := & clientapi . RegistrationToken {
Token : & token ,
UsesAllowed : usesAllowed ,
Pending : & pending ,
Completed : & completed ,
ExpiryTime : expiryTime ,
}
created , err := userAPI . PerformAdminCreateRegistrationToken ( req . Context ( ) , registrationToken )
if ! created {
return util . JSONResponse {
Code : http . StatusConflict ,
JSON : map [ string ] string {
"error" : fmt . Sprintf ( "token: %s already exists" , token ) ,
} ,
}
}
if err != nil {
return util . JSONResponse {
Code : http . StatusInternalServerError ,
JSON : err ,
}
}
return util . JSONResponse {
Code : 200 ,
JSON : map [ string ] interface { } {
"token" : token ,
"uses_allowed" : getReturnValue ( usesAllowed ) ,
"pending" : pending ,
"completed" : completed ,
"expiry_time" : getReturnValue ( expiryTime ) ,
} ,
}
}
func getReturnValue [ t constraints . Integer ] ( in * t ) any {
if in == nil {
return nil
}
return * in
}
func AdminListRegistrationTokens ( req * http . Request , cfg * config . ClientAPI , userAPI userapi . ClientUserAPI ) util . JSONResponse {
queryParams := req . URL . Query ( )
returnAll := true
valid := true
validQuery , ok := queryParams [ "valid" ]
if ok {
returnAll = false
validValue , err := strconv . ParseBool ( validQuery [ 0 ] )
if err != nil {
return util . JSONResponse {
Code : http . StatusBadRequest ,
JSON : spec . BadJSON ( "invalid 'valid' query parameter" ) ,
}
}
valid = validValue
}
tokens , err := userAPI . PerformAdminListRegistrationTokens ( req . Context ( ) , returnAll , valid )
if err != nil {
return util . JSONResponse {
Code : http . StatusInternalServerError ,
JSON : spec . ErrorUnknown ,
}
}
return util . JSONResponse {
Code : 200 ,
JSON : map [ string ] interface { } {
"registration_tokens" : tokens ,
} ,
}
}
func AdminGetRegistrationToken ( req * http . Request , cfg * config . ClientAPI , userAPI userapi . ClientUserAPI ) util . JSONResponse {
vars , err := httputil . URLDecodeMapValues ( mux . Vars ( req ) )
if err != nil {
return util . ErrorResponse ( err )
}
tokenText := vars [ "token" ]
token , err := userAPI . PerformAdminGetRegistrationToken ( req . Context ( ) , tokenText )
if err != nil {
return util . JSONResponse {
Code : http . StatusNotFound ,
JSON : spec . NotFound ( fmt . Sprintf ( "token: %s not found" , tokenText ) ) ,
}
}
return util . JSONResponse {
Code : 200 ,
JSON : token ,
}
}
func AdminDeleteRegistrationToken ( req * http . Request , cfg * config . ClientAPI , userAPI userapi . ClientUserAPI ) util . JSONResponse {
vars , err := httputil . URLDecodeMapValues ( mux . Vars ( req ) )
if err != nil {
return util . ErrorResponse ( err )
}
tokenText := vars [ "token" ]
err = userAPI . PerformAdminDeleteRegistrationToken ( req . Context ( ) , tokenText )
if err != nil {
return util . JSONResponse {
Code : http . StatusInternalServerError ,
JSON : err ,
}
}
return util . JSONResponse {
Code : 200 ,
JSON : map [ string ] interface { } { } ,
}
}
func AdminUpdateRegistrationToken ( req * http . Request , cfg * config . ClientAPI , userAPI userapi . ClientUserAPI ) util . JSONResponse {
vars , err := httputil . URLDecodeMapValues ( mux . Vars ( req ) )
if err != nil {
return util . ErrorResponse ( err )
}
tokenText := vars [ "token" ]
request := make ( map [ string ] * int64 )
if err = json . NewDecoder ( req . Body ) . Decode ( & request ) ; err != nil {
return util . JSONResponse {
Code : http . StatusBadRequest ,
JSON : spec . BadJSON ( fmt . Sprintf ( "Failed to decode request body: %s" , err ) ) ,
}
}
newAttributes := make ( map [ string ] interface { } )
usesAllowed , ok := request [ "uses_allowed" ]
if ok {
// Only add usesAllowed to newAtrributes if it is present and valid
if usesAllowed != nil && * usesAllowed < 0 {
return util . JSONResponse {
Code : http . StatusBadRequest ,
JSON : spec . BadJSON ( "uses_allowed must be a non-negative integer or null" ) ,
}
}
newAttributes [ "usesAllowed" ] = usesAllowed
}
expiryTime , ok := request [ "expiry_time" ]
if ok {
// Only add expiryTime to newAtrributes if it is present and valid
if expiryTime != nil && spec . Timestamp ( * expiryTime ) . Time ( ) . Before ( time . Now ( ) ) {
return util . JSONResponse {
Code : http . StatusBadRequest ,
JSON : spec . BadJSON ( "expiry_time must not be in the past" ) ,
}
}
newAttributes [ "expiryTime" ] = expiryTime
}
if len ( newAttributes ) == 0 {
// No attributes to update. Return existing token
return AdminGetRegistrationToken ( req , cfg , userAPI )
}
updatedToken , err := userAPI . PerformAdminUpdateRegistrationToken ( req . Context ( ) , tokenText , newAttributes )
if err != nil {
return util . JSONResponse {
Code : http . StatusNotFound ,
JSON : spec . NotFound ( fmt . Sprintf ( "token: %s not found" , tokenText ) ) ,
}
}
return util . JSONResponse {
Code : 200 ,
JSON : * updatedToken ,
}
}
2023-03-27 14:39:33 +01:00
func AdminEvacuateRoom ( req * http . Request , rsAPI roomserverAPI . ClientRoomserverAPI ) util . JSONResponse {
2022-04-29 09:10:08 +01:00
vars , err := httputil . URLDecodeMapValues ( mux . Vars ( req ) )
if err != nil {
return util . ErrorResponse ( err )
}
2023-04-28 16:46:01 +01:00
affected , err := rsAPI . PerformAdminEvacuateRoom ( req . Context ( ) , vars [ "roomID" ] )
2023-05-17 01:33:27 +01:00
switch err . ( type ) {
2023-04-28 16:46:01 +01:00
case nil :
case eventutil . ErrRoomNoExists :
return util . JSONResponse {
Code : http . StatusNotFound ,
2023-05-09 23:46:49 +01:00
JSON : spec . NotFound ( err . Error ( ) ) ,
2023-04-28 16:46:01 +01:00
}
default :
logrus . WithError ( err ) . WithField ( "roomID" , vars [ "roomID" ] ) . Error ( "Failed to evacuate room" )
2022-08-11 15:29:33 +01:00
return util . ErrorResponse ( err )
}
2022-04-29 09:10:08 +01:00
return util . JSONResponse {
Code : 200 ,
JSON : map [ string ] interface { } {
2023-04-28 16:46:01 +01:00
"affected" : affected ,
2022-04-29 09:10:08 +01:00
} ,
}
}
2022-06-29 15:29:39 +01:00
2023-04-28 16:46:01 +01:00
func AdminEvacuateUser ( req * http . Request , rsAPI roomserverAPI . ClientRoomserverAPI ) util . JSONResponse {
2022-06-29 15:29:39 +01:00
vars , err := httputil . URLDecodeMapValues ( mux . Vars ( req ) )
if err != nil {
return util . ErrorResponse ( err )
}
2023-03-27 14:39:33 +01:00
2023-04-28 16:46:01 +01:00
affected , err := rsAPI . PerformAdminEvacuateUser ( req . Context ( ) , vars [ "userID" ] )
2022-08-12 12:00:07 +01:00
if err != nil {
2023-04-28 16:46:01 +01:00
logrus . WithError ( err ) . WithField ( "userID" , vars [ "userID" ] ) . Error ( "Failed to evacuate user" )
2022-08-12 12:00:07 +01:00
return util . MessageResponse ( http . StatusBadRequest , err . Error ( ) )
}
2023-04-28 16:46:01 +01:00
2022-06-29 15:29:39 +01:00
return util . JSONResponse {
Code : 200 ,
JSON : map [ string ] interface { } {
2023-04-28 16:46:01 +01:00
"affected" : affected ,
2022-06-29 15:29:39 +01:00
} ,
}
}
2022-08-12 12:00:07 +01:00
2023-04-28 16:46:01 +01:00
func AdminPurgeRoom ( req * http . Request , rsAPI roomserverAPI . ClientRoomserverAPI ) util . JSONResponse {
2023-01-19 20:02:32 +00:00
vars , err := httputil . URLDecodeMapValues ( mux . Vars ( req ) )
if err != nil {
return util . ErrorResponse ( err )
}
2023-03-27 14:39:33 +01:00
2023-04-28 16:46:01 +01:00
if err = rsAPI . PerformAdminPurgeRoom ( context . Background ( ) , vars [ "roomID" ] ) ; err != nil {
2023-01-19 20:02:32 +00:00
return util . ErrorResponse ( err )
}
2023-04-28 16:46:01 +01:00
2023-01-19 20:02:32 +00:00
return util . JSONResponse {
Code : 200 ,
2023-04-28 16:46:01 +01:00
JSON : struct { } { } ,
2023-01-19 20:02:32 +00:00
}
}
2023-02-20 13:58:03 +00:00
func AdminResetPassword ( req * http . Request , cfg * config . ClientAPI , device * api . Device , userAPI api . ClientUserAPI ) util . JSONResponse {
2022-12-22 10:54:03 +00:00
if req . Body == nil {
return util . JSONResponse {
Code : http . StatusBadRequest ,
2023-05-09 23:46:49 +01:00
JSON : spec . Unknown ( "Missing request body" ) ,
2022-12-22 10:54:03 +00:00
}
}
2022-08-12 12:00:07 +01:00
vars , err := httputil . URLDecodeMapValues ( mux . Vars ( req ) )
if err != nil {
return util . ErrorResponse ( err )
}
2022-12-22 10:54:03 +00:00
var localpart string
userID := vars [ "userID" ]
localpart , serverName , err := cfg . Matrix . SplitLocalID ( '@' , userID )
if err != nil {
2022-08-12 12:00:07 +01:00
return util . JSONResponse {
Code : http . StatusBadRequest ,
2023-05-09 23:46:49 +01:00
JSON : spec . InvalidParam ( err . Error ( ) ) ,
2022-12-22 10:54:03 +00:00
}
}
2023-02-20 13:58:03 +00:00
accAvailableResp := & api . QueryAccountAvailabilityResponse { }
if err = userAPI . QueryAccountAvailability ( req . Context ( ) , & api . QueryAccountAvailabilityRequest {
2022-12-22 10:54:03 +00:00
Localpart : localpart ,
ServerName : serverName ,
} , accAvailableResp ) ; err != nil {
return util . JSONResponse {
Code : http . StatusInternalServerError ,
2023-05-17 01:33:27 +01:00
JSON : spec . InternalServerError { } ,
2022-08-12 12:00:07 +01:00
}
}
2022-12-22 10:54:03 +00:00
if accAvailableResp . Available {
return util . JSONResponse {
Code : http . StatusNotFound ,
2023-05-09 23:46:49 +01:00
JSON : spec . Unknown ( "User does not exist" ) ,
2022-12-22 10:54:03 +00:00
}
2022-11-11 16:41:37 +00:00
}
2022-08-12 12:00:07 +01:00
request := struct {
2023-05-30 09:02:53 +01:00
Password string ` json:"password" `
LogoutDevices bool ` json:"logout_devices" `
2022-08-12 12:00:07 +01:00
} { }
2022-12-23 13:11:11 +00:00
if err = json . NewDecoder ( req . Body ) . Decode ( & request ) ; err != nil {
2022-08-12 12:00:07 +01:00
return util . JSONResponse {
Code : http . StatusBadRequest ,
2023-05-09 23:46:49 +01:00
JSON : spec . Unknown ( "Failed to decode request body: " + err . Error ( ) ) ,
2022-08-12 12:00:07 +01:00
}
}
if request . Password == "" {
return util . JSONResponse {
Code : http . StatusBadRequest ,
2023-05-09 23:46:49 +01:00
JSON : spec . MissingParam ( "Expecting non-empty password." ) ,
2022-08-12 12:00:07 +01:00
}
}
2022-12-22 10:54:03 +00:00
2022-12-23 13:11:11 +00:00
if err = internal . ValidatePassword ( request . Password ) ; err != nil {
return * internal . PasswordResponse ( err )
2022-12-22 10:54:03 +00:00
}
2023-02-20 13:58:03 +00:00
updateReq := & api . PerformPasswordUpdateRequest {
2022-08-12 12:00:07 +01:00
Localpart : localpart ,
2022-11-11 16:41:37 +00:00
ServerName : serverName ,
2022-08-12 12:00:07 +01:00
Password : request . Password ,
2023-05-30 09:02:53 +01:00
LogoutDevices : request . LogoutDevices ,
2022-08-12 12:00:07 +01:00
}
2023-02-20 13:58:03 +00:00
updateRes := & api . PerformPasswordUpdateResponse { }
2022-08-12 12:00:07 +01:00
if err := userAPI . PerformPasswordUpdate ( req . Context ( ) , updateReq , updateRes ) ; err != nil {
return util . JSONResponse {
Code : http . StatusBadRequest ,
2023-05-09 23:46:49 +01:00
JSON : spec . Unknown ( "Failed to perform password update: " + err . Error ( ) ) ,
2022-08-12 12:00:07 +01:00
}
}
return util . JSONResponse {
Code : http . StatusOK ,
JSON : struct {
Updated bool ` json:"password_updated" `
} {
Updated : updateRes . PasswordUpdated ,
} ,
}
}
2022-09-27 17:06:49 +01:00
2023-02-20 13:58:03 +00:00
func AdminReindex ( req * http . Request , cfg * config . ClientAPI , device * api . Device , natsClient * nats . Conn ) util . JSONResponse {
2022-09-27 17:06:49 +01:00
_ , err := natsClient . RequestMsg ( nats . NewMsg ( cfg . Matrix . JetStream . Prefixed ( jetstream . InputFulltextReindex ) ) , time . Second * 10 )
if err != nil {
logrus . WithError ( err ) . Error ( "failed to publish nats message" )
2023-05-17 01:33:27 +01:00
return util . JSONResponse {
Code : http . StatusInternalServerError ,
JSON : spec . InternalServerError { } ,
}
2022-09-27 17:06:49 +01:00
}
return util . JSONResponse {
Code : http . StatusOK ,
JSON : struct { } { } ,
}
}
2022-09-30 09:32:31 +01:00
func AdminMarkAsStale ( req * http . Request , cfg * config . ClientAPI , keyAPI api . ClientKeyAPI ) util . JSONResponse {
vars , err := httputil . URLDecodeMapValues ( mux . Vars ( req ) )
if err != nil {
return util . ErrorResponse ( err )
}
userID := vars [ "userID" ]
_ , domain , err := gomatrixserverlib . SplitID ( '@' , userID )
if err != nil {
return util . MessageResponse ( http . StatusBadRequest , err . Error ( ) )
}
2022-10-26 12:59:19 +01:00
if cfg . Matrix . IsLocalServerName ( domain ) {
2022-09-30 09:32:31 +01:00
return util . JSONResponse {
Code : http . StatusBadRequest ,
2023-05-09 23:46:49 +01:00
JSON : spec . InvalidParam ( "Can not mark local device list as stale" ) ,
2022-09-30 09:32:31 +01:00
}
}
err = keyAPI . PerformMarkAsStaleIfNeeded ( req . Context ( ) , & api . PerformMarkAsStaleRequest {
UserID : userID ,
Domain : domain ,
} , & struct { } { } )
if err != nil {
return util . JSONResponse {
Code : http . StatusInternalServerError ,
2023-05-09 23:46:49 +01:00
JSON : spec . Unknown ( fmt . Sprintf ( "Failed to mark device list as stale: %s" , err ) ) ,
2022-09-30 09:32:31 +01:00
}
}
return util . JSONResponse {
Code : http . StatusOK ,
JSON : struct { } { } ,
}
}
2022-10-31 09:13:28 +00:00
2023-04-28 16:46:01 +01:00
func AdminDownloadState ( req * http . Request , device * api . Device , rsAPI roomserverAPI . ClientRoomserverAPI ) util . JSONResponse {
2022-10-31 09:13:28 +00:00
vars , err := httputil . URLDecodeMapValues ( mux . Vars ( req ) )
if err != nil {
return util . ErrorResponse ( err )
}
roomID , ok := vars [ "roomID" ]
if ! ok {
return util . JSONResponse {
Code : http . StatusBadRequest ,
2023-05-09 23:46:49 +01:00
JSON : spec . MissingParam ( "Expecting room ID." ) ,
2022-10-31 09:13:28 +00:00
}
}
serverName , ok := vars [ "serverName" ]
if ! ok {
return util . JSONResponse {
Code : http . StatusBadRequest ,
2023-05-09 23:46:49 +01:00
JSON : spec . MissingParam ( "Expecting remote server name." ) ,
2022-10-31 09:13:28 +00:00
}
}
2023-04-28 16:46:01 +01:00
if err = rsAPI . PerformAdminDownloadState ( req . Context ( ) , roomID , device . UserID , spec . ServerName ( serverName ) ) ; err != nil {
2023-05-17 01:33:27 +01:00
if errors . Is ( err , eventutil . ErrRoomNoExists { } ) {
2023-04-28 16:46:01 +01:00
return util . JSONResponse {
Code : 200 ,
2023-05-17 01:33:27 +01:00
JSON : spec . NotFound ( err . Error ( ) ) ,
2023-04-28 16:46:01 +01:00
}
}
logrus . WithError ( err ) . WithFields ( logrus . Fields {
"userID" : device . UserID ,
"serverName" : serverName ,
"roomID" : roomID ,
} ) . Error ( "failed to download state" )
return util . ErrorResponse ( err )
2022-10-31 09:13:28 +00:00
}
return util . JSONResponse {
Code : 200 ,
2023-04-28 16:46:01 +01:00
JSON : struct { } { } ,
2022-10-31 09:13:28 +00:00
}
}
2024-03-22 21:32:30 +00:00
// GetEventReports returns reported events for a given user/room.
func GetEventReports (
req * http . Request ,
rsAPI roomserverAPI . ClientRoomserverAPI ,
from , limit uint64 ,
backwards bool ,
userID , roomID string ,
) util . JSONResponse {
eventReports , count , err := rsAPI . QueryAdminEventReports ( req . Context ( ) , from , limit , backwards , userID , roomID )
if err != nil {
logrus . WithError ( err ) . Error ( "failed to query event reports" )
return util . JSONResponse {
Code : http . StatusInternalServerError ,
JSON : spec . InternalServerError { } ,
}
}
resp := map [ string ] any {
"event_reports" : eventReports ,
"total" : count ,
}
// Add a next_token if there are still reports
if int64 ( from + limit ) < count {
resp [ "next_token" ] = int ( from ) + len ( eventReports )
}
return util . JSONResponse {
Code : http . StatusOK ,
JSON : resp ,
}
}
func parseUint64OrDefault ( input string , defaultValue uint64 ) uint64 {
v , err := strconv . ParseUint ( input , 10 , 64 )
if err != nil {
return defaultValue
}
return v
}