diff --git a/clientapi/routing/createroom.go b/clientapi/routing/createroom.go index 8d9a235c9..cf2c6633f 100644 --- a/clientapi/routing/createroom.go +++ b/clientapi/routing/createroom.go @@ -48,7 +48,7 @@ type createRoomRequest struct { RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"` PowerLevelContentOverride json.RawMessage `json:"power_level_content_override"` IsDirect bool `json:"is_direct"` - SenderID string `json:"sender_id"` + CryptoID string `json:"cryptoid"` } func (r createRoomRequest) Validate() *util.JSONResponse { @@ -225,7 +225,7 @@ func makeCreateRoomEvents( PrivateKey: privateKey, EventTime: evTime, - SenderID: createRequest.SenderID, + SenderID: createRequest.CryptoID, } createEvents, err := rsAPI.PerformCreateRoomCryptoIDs(ctx, *userID, *roomID, &req) diff --git a/clientapi/routing/joinroom.go b/clientapi/routing/joinroom.go index 5768d14c5..4b82c7f0f 100644 --- a/clientapi/routing/joinroom.go +++ b/clientapi/routing/joinroom.go @@ -27,6 +27,7 @@ import ( "github.com/matrix-org/gomatrix" "github.com/matrix-org/gomatrixserverlib/spec" "github.com/matrix-org/util" + "github.com/sirupsen/logrus" ) func JoinRoomByIDOrAlias( @@ -183,6 +184,17 @@ func JoinRoomByIDOrAliasCryptoIDs( // event content. _ = httputil.UnmarshalJSONRequest(req, &joinReq.Content) + if senderid, ok := joinReq.Content["cryptoid"]; ok { + logrus.Errorf("CryptoID: %s", senderid.(string)) + joinReq.SenderID = spec.SenderID(senderid.(string)) + } else { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: spec.Unknown("Missing cryptoid in request body"), + } + } + delete(joinReq.Content, "cryptoid") + // Work out our localpart for the client profile request. // Request our profile content to populate the request content with. diff --git a/clientapi/routing/membership.go b/clientapi/routing/membership.go index 63f820cd8..02c4460be 100644 --- a/clientapi/routing/membership.go +++ b/clientapi/routing/membership.go @@ -40,10 +40,15 @@ import ( "github.com/matrix-org/util" ) +type membershipCryptoIDsResponse struct { + PDU json.RawMessage `json:"pdu"` +} + func SendBan( req *http.Request, profileAPI userapi.ClientUserAPI, device *userapi.Device, roomID string, cfg *config.ClientAPI, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, + cryptoIDs bool, ) util.JSONResponse { body, evTime, reqErr := extractRequestData(req) if reqErr != nil { @@ -96,13 +101,14 @@ func SendBan( } } - return sendMembership(req.Context(), profileAPI, device, roomID, spec.Ban, body.Reason, cfg, body.UserID, evTime, rsAPI, asAPI) + return sendMembership(req.Context(), profileAPI, device, roomID, spec.Ban, body.Reason, cfg, body.UserID, evTime, rsAPI, asAPI, cryptoIDs) } func sendMembership(ctx context.Context, profileAPI userapi.ClientUserAPI, device *userapi.Device, roomID, membership, reason string, cfg *config.ClientAPI, targetUserID string, evTime time.Time, - rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI) util.JSONResponse { + rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, cryptoIDs bool) util.JSONResponse { + // TODO: cryptoIDs - what about when we don't know the senderID for a user? event, err := buildMembershipEvent( ctx, targetUserID, reason, profileAPI, device, membership, roomID, false, cfg, evTime, rsAPI, asAPI, @@ -115,27 +121,33 @@ func sendMembership(ctx context.Context, profileAPI userapi.ClientUserAPI, devic } } - serverName := device.UserDomain() - if err = roomserverAPI.SendEvents( - ctx, rsAPI, - roomserverAPI.KindNew, - []*types.HeaderedEvent{event}, - device.UserDomain(), - serverName, - serverName, - nil, - false, - ); err != nil { - util.GetLogger(ctx).WithError(err).Error("SendEvents failed") - return util.JSONResponse{ - Code: http.StatusInternalServerError, - JSON: spec.InternalServerError{}, + var json interface{} + if !cryptoIDs { + serverName := device.UserDomain() + if err = roomserverAPI.SendEvents( + ctx, rsAPI, + roomserverAPI.KindNew, + []*types.HeaderedEvent{event}, + device.UserDomain(), + serverName, + serverName, + nil, + false, + ); err != nil { + util.GetLogger(ctx).WithError(err).Error("SendEvents failed") + return util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: spec.InternalServerError{}, + } } + json = struct{}{} + } else { + json = membershipCryptoIDsResponse{PDU: event.JSON()} } return util.JSONResponse{ Code: http.StatusOK, - JSON: struct{}{}, + JSON: json, } } @@ -143,6 +155,7 @@ func SendKick( req *http.Request, profileAPI userapi.ClientUserAPI, device *userapi.Device, roomID string, cfg *config.ClientAPI, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, + cryptoIDs bool, ) util.JSONResponse { body, evTime, reqErr := extractRequestData(req) if reqErr != nil { @@ -217,13 +230,14 @@ func SendKick( } } // TODO: should we be using SendLeave instead? - return sendMembership(req.Context(), profileAPI, device, roomID, spec.Leave, body.Reason, cfg, body.UserID, evTime, rsAPI, asAPI) + return sendMembership(req.Context(), profileAPI, device, roomID, spec.Leave, body.Reason, cfg, body.UserID, evTime, rsAPI, asAPI, cryptoIDs) } func SendUnban( req *http.Request, profileAPI userapi.ClientUserAPI, device *userapi.Device, roomID string, cfg *config.ClientAPI, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceInternalAPI, + cryptoIDs bool, ) util.JSONResponse { body, evTime, reqErr := extractRequestData(req) if reqErr != nil { @@ -273,7 +287,7 @@ func SendUnban( } } // TODO: should we be using SendLeave instead? - return sendMembership(req.Context(), profileAPI, device, roomID, spec.Leave, body.Reason, cfg, body.UserID, evTime, rsAPI, asAPI) + return sendMembership(req.Context(), profileAPI, device, roomID, spec.Leave, body.Reason, cfg, body.UserID, evTime, rsAPI, asAPI, cryptoIDs) } func SendInvite( diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index 6b51bb10a..d46c69fb8 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -490,14 +490,22 @@ func Setup( ) }), ).Methods(http.MethodPost, http.MethodOptions) - // TODO: update for cryptoIDs v3mux.Handle("/rooms/{roomID}/ban", httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) } - return SendBan(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI) + return SendBan(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI, false) + }), + ).Methods(http.MethodPost, http.MethodOptions) + unstableMux.Handle("/org.matrix.msc4080/rooms/{roomID}/ban", + httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { + vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return SendBan(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI, true) }), ).Methods(http.MethodPost, http.MethodOptions) v3mux.Handle("/rooms/{roomID}/invite", @@ -525,24 +533,40 @@ func Setup( return SendInvite(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI, true) }), ).Methods(http.MethodPost, http.MethodOptions) - // TODO: update for cryptoIDs v3mux.Handle("/rooms/{roomID}/kick", httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) } - return SendKick(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI) + return SendKick(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI, false) + }), + ).Methods(http.MethodPost, http.MethodOptions) + unstableMux.Handle("/org.matrix.msc4080/rooms/{roomID}/kick", + httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { + vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return SendKick(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI, true) }), ).Methods(http.MethodPost, http.MethodOptions) - // TODO: update for cryptoIDs v3mux.Handle("/rooms/{roomID}/unban", httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) } - return SendUnban(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI) + return SendUnban(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI, false) + }), + ).Methods(http.MethodPost, http.MethodOptions) + unstableMux.Handle("/org.matrix.msc4080/rooms/{roomID}/unban", + httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { + vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return SendUnban(req, userAPI, device, vars["roomID"], cfg, rsAPI, asAPI, true) }), ).Methods(http.MethodPost, http.MethodOptions) v3mux.Handle("/rooms/{roomID}/send/{eventType}", diff --git a/roomserver/api/perform.go b/roomserver/api/perform.go index 8c72e6910..973b6649d 100644 --- a/roomserver/api/perform.go +++ b/roomserver/api/perform.go @@ -40,6 +40,7 @@ type PerformJoinRequest struct { Content map[string]interface{} `json:"content"` ServerNames []spec.ServerName `json:"server_names"` Unsigned map[string]interface{} `json:"unsigned"` + SenderID spec.SenderID } type PerformJoinRequestCryptoIDs struct { diff --git a/roomserver/internal/api.go b/roomserver/internal/api.go index 7a4e5e9ad..15d137ec4 100644 --- a/roomserver/internal/api.go +++ b/roomserver/internal/api.go @@ -3,6 +3,7 @@ package internal import ( "context" "crypto/ed25519" + "fmt" "github.com/getsentry/sentry-go" "github.com/matrix-org/gomatrixserverlib" @@ -328,7 +329,7 @@ func (r *RoomserverInternalAPI) SigningIdentityFor(ctx context.Context, roomID s roomVersion = roomInfo.RoomVersion } } - if roomVersion == gomatrixserverlib.RoomVersionPseudoIDs || roomVersion == gomatrixserverlib.RoomVersionCryptoIDs { + if roomVersion == gomatrixserverlib.RoomVersionPseudoIDs { privKey, err := r.GetOrCreateUserRoomPrivateKey(ctx, senderID, roomID) if err != nil { return fclient.SigningIdentity{}, err @@ -339,6 +340,19 @@ func (r *RoomserverInternalAPI) SigningIdentityFor(ctx context.Context, roomID s ServerName: spec.ServerName(spec.SenderIDFromPseudoIDKey(privKey)), }, nil } + if roomVersion == gomatrixserverlib.RoomVersionCryptoIDs { + sender, err := r.QuerySenderIDForUser(ctx, roomID, senderID) + if err != nil { + return fclient.SigningIdentity{}, err + } else if sender == nil { + return fclient.SigningIdentity{}, fmt.Errorf("no sender ID for %s in %s", senderID.String(), roomID.String()) + } + return fclient.SigningIdentity{ + PrivateKey: nil, + KeyID: "ed25519:1", + ServerName: spec.ServerName(*sender), + }, nil + } identity, err := r.Cfg.Global.SigningIdentityFor(senderID.Domain()) if err != nil { return fclient.SigningIdentity{}, err diff --git a/roomserver/internal/perform/perform_join.go b/roomserver/internal/perform/perform_join.go index 4f58fb184..1dee53af5 100644 --- a/roomserver/internal/perform/perform_join.go +++ b/roomserver/internal/perform/perform_join.go @@ -571,6 +571,8 @@ func (r *Joiner) performJoinRoomByIDCryptoIDs( return nil, "", "", "", rsAPI.ErrInvalidID{Err: fmt.Errorf("user ID %q is invalid: %w", req.UserID, err)} } + //TODO: CryptoIDs - what is provided & calculated senderIDs don't match? + // Look up the room NID for the supplied room ID. var senderID spec.SenderID checkInvitePending := false @@ -583,13 +585,7 @@ func (r *Joiner) performJoinRoomByIDCryptoIDs( checkInvitePending = true } if senderIDPtr == nil { - // create user room key if needed - key, keyErr := r.RSAPI.GetOrCreateUserRoomPrivateKey(ctx, *userID, *roomID) - if keyErr != nil { - util.GetLogger(ctx).WithError(keyErr).Error("GetOrCreateUserRoomPrivateKey failed") - return nil, "", "", "", fmt.Errorf("GetOrCreateUserRoomPrivateKey failed: %w", keyErr) - } - senderID = spec.SenderIDFromPseudoIDKey(key) + senderID = req.SenderID } else { senderID = *senderIDPtr }