diff --git a/README.md b/README.md index 75c827c33..2a8c36508 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,10 @@ $ cp dendrite-config.yaml dendrite.yaml # Build and run the server: $ ./bin/dendrite-monolith-server --tls-cert server.crt --tls-key server.key --config dendrite.yaml + +# Create an user account (add -admin for an admin user). +# Specify the localpart only, e.g. 'alice' for '@alice:domain.com' +$ ./bin/create-account --config dendrite.yaml -username alice ``` Then point your favourite Matrix client at `http://localhost:8008` or `https://localhost:8448`. diff --git a/clientapi/routing/admin.go b/clientapi/routing/admin.go new file mode 100644 index 000000000..31e431c78 --- /dev/null +++ b/clientapi/routing/admin.go @@ -0,0 +1,49 @@ +package routing + +import ( + "net/http" + + "github.com/gorilla/mux" + "github.com/matrix-org/dendrite/clientapi/jsonerror" + "github.com/matrix-org/dendrite/internal/httputil" + roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" + userapi "github.com/matrix-org/dendrite/userapi/api" + "github.com/matrix-org/util" +) + +func AdminEvacuateRoom(req *http.Request, device *userapi.Device, rsAPI roomserverAPI.RoomserverInternalAPI) util.JSONResponse { + if device.AccountType != userapi.AccountTypeAdmin { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("This API can only be used by admin users."), + } + } + 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, + JSON: jsonerror.MissingArgument("Expecting room ID."), + } + } + res := &roomserverAPI.PerformAdminEvacuateRoomResponse{} + rsAPI.PerformAdminEvacuateRoom( + req.Context(), + &roomserverAPI.PerformAdminEvacuateRoomRequest{ + RoomID: roomID, + }, + res, + ) + if err := res.Error; err != nil { + return err.JSONResponse() + } + return util.JSONResponse{ + Code: 200, + JSON: map[string]interface{}{ + "affected": res.Affected, + }, + } +} diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index ec90b80db..ba1c76b81 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -122,40 +122,7 @@ func Setup( dendriteAdminRouter.Handle("/admin/evacuateRoom/{roomID}", httputil.MakeAuthAPI("admin_evacuate_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { - if device.AccountType != userapi.AccountTypeAdmin { - return util.JSONResponse{ - Code: http.StatusForbidden, - JSON: jsonerror.Forbidden("This API can only be used by admin users."), - } - } - 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, - JSON: jsonerror.MissingArgument("Expecting room ID."), - } - } - res := &roomserverAPI.PerformAdminEvacuateRoomResponse{} - rsAPI.PerformAdminEvacuateRoom( - req.Context(), - &roomserverAPI.PerformAdminEvacuateRoomRequest{ - RoomID: roomID, - }, - res, - ) - if err := res.Error; err != nil { - return err.JSONResponse() - } - return util.JSONResponse{ - Code: 200, - JSON: map[string]interface{}{ - "affected": res.Affected, - }, - } + return AdminEvacuateRoom(req, device, rsAPI) }), ).Methods(http.MethodGet, http.MethodOptions) diff --git a/federationapi/routing/devices.go b/federationapi/routing/devices.go index 8890eac4b..57286fa90 100644 --- a/federationapi/routing/devices.go +++ b/federationapi/routing/devices.go @@ -20,6 +20,7 @@ import ( keyapi "github.com/matrix-org/dendrite/keyserver/api" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" + "github.com/tidwall/gjson" ) // GetUserDevices for the given user id @@ -69,9 +70,14 @@ func GetUserDevices( continue } + displayName := dev.DisplayName + if displayName == "" { + displayName = gjson.GetBytes(dev.DeviceKeys.KeyJSON, "unsigned.device_display_name").Str + } + device := gomatrixserverlib.RespUserDevice{ DeviceID: dev.DeviceID, - DisplayName: dev.DisplayName, + DisplayName: displayName, Keys: key, } diff --git a/keyserver/internal/internal.go b/keyserver/internal/internal.go index 1677cf8e3..e556f44b0 100644 --- a/keyserver/internal/internal.go +++ b/keyserver/internal/internal.go @@ -632,43 +632,55 @@ func (a *KeyInternalAPI) uploadLocalDeviceKeys(ctx context.Context, req *api.Per } var keysToStore []api.DeviceMessage - // assert that the user ID / device ID are not lying for each key - for _, key := range req.DeviceKeys { - var serverName gomatrixserverlib.ServerName - _, serverName, err = gomatrixserverlib.SplitID('@', key.UserID) - if err != nil { - continue // ignore invalid users - } - if serverName != a.ThisServer { - continue // ignore remote users - } - if len(key.KeyJSON) == 0 { - keysToStore = append(keysToStore, key.WithStreamID(0)) - continue // deleted keys don't need sanity checking - } - // check that the device in question actually exists in the user - // API before we try and store a key for it - if _, ok := existingDeviceMap[key.DeviceID]; !ok { - continue - } - gotUserID := gjson.GetBytes(key.KeyJSON, "user_id").Str - gotDeviceID := gjson.GetBytes(key.KeyJSON, "device_id").Str - if gotUserID == key.UserID && gotDeviceID == key.DeviceID { - keysToStore = append(keysToStore, key.WithStreamID(0)) - continue - } - - res.KeyError(key.UserID, key.DeviceID, &api.KeyError{ - Err: fmt.Sprintf( - "user_id or device_id mismatch: users: %s - %s, devices: %s - %s", - gotUserID, key.UserID, gotDeviceID, key.DeviceID, - ), - }) - } if req.OnlyDisplayNameUpdates { - // add the display name field from keysToStore into existingKeys - keysToStore = appendDisplayNames(existingKeys, keysToStore) + for _, existingKey := range existingKeys { + for _, newKey := range req.DeviceKeys { + switch { + case existingKey.UserID != newKey.UserID: + continue + case existingKey.DeviceID != newKey.DeviceID: + continue + case existingKey.DisplayName != newKey.DisplayName: + existingKey.DisplayName = newKey.DisplayName + } + } + keysToStore = append(keysToStore, existingKey) + } + } else { + // assert that the user ID / device ID are not lying for each key + for _, key := range req.DeviceKeys { + var serverName gomatrixserverlib.ServerName + _, serverName, err = gomatrixserverlib.SplitID('@', key.UserID) + if err != nil { + continue // ignore invalid users + } + if serverName != a.ThisServer { + continue // ignore remote users + } + if len(key.KeyJSON) == 0 { + keysToStore = append(keysToStore, key.WithStreamID(0)) + continue // deleted keys don't need sanity checking + } + // check that the device in question actually exists in the user + // API before we try and store a key for it + if _, ok := existingDeviceMap[key.DeviceID]; !ok { + continue + } + gotUserID := gjson.GetBytes(key.KeyJSON, "user_id").Str + gotDeviceID := gjson.GetBytes(key.KeyJSON, "device_id").Str + if gotUserID == key.UserID && gotDeviceID == key.DeviceID { + keysToStore = append(keysToStore, key.WithStreamID(0)) + continue + } + + res.KeyError(key.UserID, key.DeviceID, &api.KeyError{ + Err: fmt.Sprintf( + "user_id or device_id mismatch: users: %s - %s, devices: %s - %s", + gotUserID, key.UserID, gotDeviceID, key.DeviceID, + ), + }) + } } // store the device keys and emit changes @@ -764,16 +776,3 @@ func emitDeviceKeyChanges(producer KeyChangeProducer, existing, new []api.Device } return producer.ProduceKeyChanges(keysAdded) } - -func appendDisplayNames(existing, new []api.DeviceMessage) []api.DeviceMessage { - for i, existingDevice := range existing { - for _, newDevice := range new { - if existingDevice.DeviceID != newDevice.DeviceID { - continue - } - existingDevice.DisplayName = newDevice.DisplayName - existing[i] = existingDevice - } - } - return existing -} diff --git a/mediaapi/storage/storage_test.go b/mediaapi/storage/storage_test.go index 8d3403045..fa88cd8e7 100644 --- a/mediaapi/storage/storage_test.go +++ b/mediaapi/storage/storage_test.go @@ -123,11 +123,19 @@ func TestThumbnailsStorage(t *testing.T) { t.Fatalf("expected %d stored thumbnail metadata, got %d", len(thumbnails), len(gotMediadatas)) } for i := range gotMediadatas { - if !reflect.DeepEqual(thumbnails[i].MediaMetadata, gotMediadatas[i].MediaMetadata) { - t.Fatalf("expected metadata %+v, got %v", thumbnails[i].MediaMetadata, gotMediadatas[i].MediaMetadata) + // metadata may be returned in a different order than it was stored, perform a search + metaDataMatches := func() bool { + for _, t := range thumbnails { + if reflect.DeepEqual(t.MediaMetadata, gotMediadatas[i].MediaMetadata) && reflect.DeepEqual(t.ThumbnailSize, gotMediadatas[i].ThumbnailSize) { + return true + } + } + return false } - if !reflect.DeepEqual(thumbnails[i].ThumbnailSize, gotMediadatas[i].ThumbnailSize) { - t.Fatalf("expected metadata %+v, got %v", thumbnails[i].ThumbnailSize, gotMediadatas[i].ThumbnailSize) + + if !metaDataMatches() { + t.Fatalf("expected metadata %+v, got %+v", thumbnails[i].MediaMetadata, gotMediadatas[i].MediaMetadata) + } } })