From e8165db50f7423a941147b45a819a220781ea850 Mon Sep 17 00:00:00 2001 From: nxshock Date: Wed, 31 Jul 2019 20:01:20 +0500 Subject: [PATCH] Implement ListRoom GET/PUT methods --- STATUS.md | 2 ++ internal/backend.go | 3 ++ internal/backends/memory/backend.go | 13 +++++++ internal/backends/memory/backend_test.go | 20 +++++++++++ internal/backends/memory/rooms.go | 9 ++++- internal/backends/memory/user.go | 32 ++++++++++++----- internal/backends/memory/user_test.go | 21 +++++++++++ internal/handlers.go | 45 ++++++++++++++++++++++++ internal/models/createroom/request.go | 2 +- internal/models/listroom/listroom.go | 13 +++++++ internal/server.go | 7 ++-- 11 files changed, 152 insertions(+), 15 deletions(-) create mode 100644 internal/models/listroom/listroom.go diff --git a/STATUS.md b/STATUS.md index 20ccb0d..10e484a 100644 --- a/STATUS.md +++ b/STATUS.md @@ -11,4 +11,6 @@ Implemented from [Client-Server API](https://matrix.org/docs/spec/client_server/ - [x] [5.7.1 GET /_matrix/client/r0/account/whoami](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-account-whoami) - [x] [6.1 GET /_matrix/client/r0/capabilities](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-capabilities) - [x] [10.4.1 GET /_matrix/client/r0/joined_rooms](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-joined-rooms) +- [x] [10.5.1 GET /_matrix/client/r0/directory/list/room/{roomId}](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-directory-list-room-roomid) +- [x] [10.5.2 PUT /_matrix/client/r0/directory/list/room/{roomId}](https://matrix.org/docs/spec/client_server/latest#put-matrix-client-r0-directory-list-room-roomid) - [x] [13.10.1.1 GET /_matrix/client/r0/devices](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-devices) diff --git a/internal/backend.go b/internal/backend.go index 843c336..2783828 100644 --- a/internal/backend.go +++ b/internal/backend.go @@ -12,6 +12,7 @@ type Backend interface { Register(username, password, device string) (user User, token string, err *models.ApiError) Login(username, password, device string) (token string, err *models.ApiError) GetUserByToken(token string) (user User) + GetRoomByID(id string) Room Sync(token string, request sync.SyncRequest) (response *sync.SyncReply, err *models.ApiError) } @@ -23,6 +24,7 @@ type Room interface { Name() string Topic() string Events() []rooms.Event + Visibility() createroom.VisibilityType } type User interface { @@ -36,6 +38,7 @@ type User interface { JoinedRooms() []Room ChangePassword(newPassword string) Devices() []devices.Device + SetRoomVisibility(Room, createroom.VisibilityType) *models.ApiError Logout(token string) LogoutAll() } diff --git a/internal/backends/memory/backend.go b/internal/backends/memory/backend.go index 229a72b..73a4427 100644 --- a/internal/backends/memory/backend.go +++ b/internal/backends/memory/backend.go @@ -90,3 +90,16 @@ func (backend *Backend) GetUserByToken(token string) internal.User { return nil } + +func (backend *Backend) GetRoomByID(id string) internal.Room { + backend.mutex.Lock() + defer backend.mutex.Unlock() + + for roomID, room := range backend.rooms { + if roomID == id { + return room + } + } + + return nil +} diff --git a/internal/backends/memory/backend_test.go b/internal/backends/memory/backend_test.go index d900a4c..2def696 100644 --- a/internal/backends/memory/backend_test.go +++ b/internal/backends/memory/backend_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "github.com/nxshock/signaller/internal/models/createroom" ) func TestRegisterUser(t *testing.T) { @@ -89,3 +91,21 @@ func TestLogout(t *testing.T) { assert.Nil(t, backend.GetUserByToken(token)) } + +func TestGetRoomByID(t *testing.T) { + backend := NewBackend("localhost") + + user, token, err := backend.Register("user", "", "") + assert.Nil(t, err) + assert.NotNil(t, user) + assert.NotEmpty(t, token) + + request := createroom.Request{ + RoomAliasName: "room1", + Name: "room1"} + + room, err := user.CreateRoom(request) + assert.Nil(t, err) + assert.NotNil(t, room) + assert.Equal(t, room.ID(), backend.GetRoomByID(room.ID()).ID()) +} diff --git a/internal/backends/memory/rooms.go b/internal/backends/memory/rooms.go index 6e1c5be..8291e20 100644 --- a/internal/backends/memory/rooms.go +++ b/internal/backends/memory/rooms.go @@ -10,7 +10,7 @@ import ( type Room struct { id string - Visibility createroom.VisibilityType + visibility createroom.VisibilityType aliasName string name string topic string @@ -72,6 +72,13 @@ func (room *Room) Events() []rooms.Event { return result } +func (room *Room) Visibility() createroom.VisibilityType { + room.mutex.RLock() + defer room.mutex.RUnlock() + + return room.visibility +} + func (room *Room) Creator() internal.User { room.mutex.RLock() defer room.mutex.RUnlock() diff --git a/internal/backends/memory/user.go b/internal/backends/memory/user.go index 39932eb..3f09b7a 100644 --- a/internal/backends/memory/user.go +++ b/internal/backends/memory/user.go @@ -83,21 +83,22 @@ func (user *User) CreateRoom(request createroom.Request) (internal.Room, *models } room := &Room{ - id: newToken(groupIDSize), - aliasName: request.RoomAliasName, - name: request.Name, - topic: request.Topic, - events: events, - creator: user, - joined: []internal.User{user}, - server: user.backend} + id: newToken(groupIDSize), + aliasName: request.RoomAliasName, + name: request.Name, + topic: request.Topic, + events: events, + creator: user, + joined: []internal.User{user}, + visibility: request.Visibility, + server: user.backend} for i, _ := range room.events { room.events[i].Room = room //v.Room = room } - user.backend.rooms[room.id] = room + user.backend.rooms[room.ID()] = room return room, nil } @@ -193,6 +194,19 @@ func (user *User) Devices() []devices.Device { return result } +func (user *User) SetRoomVisibility(room internal.Room, visibilityType createroom.VisibilityType) *models.ApiError { + if user.ID() != room.Creator().ID() { + return internal.NewError(models.M_FORBIDDEN, "only room owner can change visibility") // TODO: room administrators can use this method too + } + + room.(*Room).mutex.Lock() + defer room.(*Room).mutex.Unlock() + + room.(*Room).visibility = visibilityType + + return nil +} + func (user *User) ChangePassword(newPassword string) { user.mutex.Lock() defer user.mutex.Unlock() diff --git a/internal/backends/memory/user_test.go b/internal/backends/memory/user_test.go index c5f8793..992dd64 100644 --- a/internal/backends/memory/user_test.go +++ b/internal/backends/memory/user_test.go @@ -143,3 +143,24 @@ func TestDevices(t *testing.T) { assert.Len(t, devices, 1) assert.Equal(t, expectedDeviceID, devices[0].DeviceID) } + +func TestSetRoomVisibility(t *testing.T) { + backend := NewBackend("localhost") + + user, _, err := backend.Register("user1", "", "") + assert.Nil(t, err) + + request := createroom.Request{ + RoomAliasName: "room1", + Name: "room1", + Visibility: createroom.VisibilityTypePrivate} + + room, err := user.CreateRoom(request) + assert.Nil(t, err) + assert.NotNil(t, room) + assert.Equal(t, createroom.VisibilityTypePrivate, room.Visibility()) + + err = user.SetRoomVisibility(room, createroom.VisibilityTypePublic) + assert.Nil(t, err) + assert.Equal(t, createroom.VisibilityTypePublic, room.Visibility()) +} diff --git a/internal/handlers.go b/internal/handlers.go index 4d3ceef..b8098fd 100644 --- a/internal/handlers.go +++ b/internal/handlers.go @@ -8,11 +8,14 @@ import ( "strconv" "strings" + "github.com/gorilla/mux" + "github.com/nxshock/signaller/internal/models/common" "github.com/nxshock/signaller/internal/models" "github.com/nxshock/signaller/internal/models/capabilities" "github.com/nxshock/signaller/internal/models/joinedrooms" + "github.com/nxshock/signaller/internal/models/listroom" login "github.com/nxshock/signaller/internal/models/login" "github.com/nxshock/signaller/internal/models/password" register "github.com/nxshock/signaller/internal/models/register" @@ -290,6 +293,48 @@ func DevicesHandler(w http.ResponseWriter, r *http.Request) { } +// https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-directory-list-room-roomid +// https://matrix.org/docs/spec/client_server/latest#put-matrix-client-r0-directory-list-room-roomid +func listRoomHandler(w http.ResponseWriter, r *http.Request) { + room := currServer.Backend.GetRoomByID(mux.Vars(r)["roomID"]) // TODO: can ["roomID"] throw panic? + if room == nil { + errorResponse(w, models.M_NOT_FOUND, http.StatusBadRequest, "room not found") + return + } + + switch r.Method { + case http.MethodGet: + response := listroom.Response{ + Visibility: room.Visibility()} + + sendJsonResponse(w, http.StatusOK, response) + case http.MethodPut: + token := getTokenFromResponse(r) + if token == "" { + errorResponse(w, models.M_FORBIDDEN, http.StatusForbidden, "") + return + } + + user := currServer.Backend.GetUserByToken(token) + if user == nil { + errorResponse(w, models.M_UNKNOWN_TOKEN, http.StatusBadRequest, "") + return + } + + var request listroom.Request + err := getRequest(r, &request) + if err != nil { + errorResponse(w, models.M_BAD_JSON, http.StatusBadRequest, err.Error()) + return + } + user.SetRoomVisibility(room, request.Visibility) + + sendJsonResponse(w, http.StatusOK, struct{}{}) + default: + errorResponse(w, models.M_UNKNOWN, http.StatusBadRequest, "wrong method: "+r.Method) + } +} + func sendJsonResponse(w http.ResponseWriter, httpStatus int, data interface{}) error { b, err := json.Marshal(data) if err != nil { diff --git a/internal/models/createroom/request.go b/internal/models/createroom/request.go index fecd38b..c9e45bd 100644 --- a/internal/models/createroom/request.go +++ b/internal/models/createroom/request.go @@ -9,7 +9,7 @@ type VisibilityType string const ( VisibilityTypePrivate VisibilityType = "private" - VisibilityTypePublic = "public" + VisibilityTypePublic VisibilityType = "public" ) type Preset string diff --git a/internal/models/listroom/listroom.go b/internal/models/listroom/listroom.go new file mode 100644 index 0000000..010a407 --- /dev/null +++ b/internal/models/listroom/listroom.go @@ -0,0 +1,13 @@ +package listroom + +import ( + "github.com/nxshock/signaller/internal/models/createroom" +) + +type Request struct { + Visibility createroom.VisibilityType `json:"visibility"` // The new visibility setting for the room. Defaults to 'public'. One of: ["private", "public"] +} + +type Response struct { + Visibility createroom.VisibilityType `json:"visibility"` // The visibility of the room in the directory. One of: ["private", "public"] +} diff --git a/internal/server.go b/internal/server.go index bb4a827..1367950 100644 --- a/internal/server.go +++ b/internal/server.go @@ -1,14 +1,12 @@ package internal import ( + "errors" "net/http" + "strconv" "github.com/gorilla/mux" - "errors" - - "strconv" - "github.com/nxshock/signaller/internal/models/capabilities" ) @@ -38,6 +36,7 @@ func NewServer(port int) (*Server, error) { router.HandleFunc("/_matrix/client/r0/sync", SyncHandler) router.HandleFunc("/_matrix/client/r0/capabilities", CapabilitiesHandler) router.HandleFunc("/_matrix/client/r0/devices", DevicesHandler) + router.HandleFunc("/_matrix/client/r0/directory/list/room/{roomID}", listRoomHandler) router.HandleFunc("/", RootHandler) if port <= 0 || port > 65535 {