Skip to content

Commit

Permalink
matrix-org#3333 used ts parameter for appservices
Browse files Browse the repository at this point in the history
The issue involved ensuring that the ts parameter in Dendrite is correctly handled when the request is from an appservice, and that it is ignored or handled differently when not. The resolution was achieved through the following steps:

Refactoring the Logic:
The logic for processing the ts parameter was refactored into a separate function named HandleEventTimestamp. This function determines whether the request is from an appservice by calling isAppService(req). If it is, the function parses the ts parameter using httputil.ParseTSParam(req). If the request is not from an appservice, the function defaults to using the current time or another appropriate handling.

Updating sendevent.go:
The inline logic in sendevent.go that handled the ts parameter was replaced with a call to the new HandleEventTimestamp function. This ensures that the logic is centralized and can be easily tested and maintained.

Creating Tests:
A new test file, ts_param_test.go, was created in the testing directory. This file includes tests that cover various scenarios:

When the ts parameter is valid and the request is from an appservice.
When the ts parameter is invalid or missing.
When the request is not from an appservice.

Signed off by : `Srinjoy Sen Chowdhury [email protected]`
  • Loading branch information
AllMightLegend committed Aug 16, 2024
1 parent eb6c8af commit 35611c4
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 1 deletion.
50 changes: 49 additions & 1 deletion clientapi/routing/sendevent.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,35 @@ func SendEvent(
}
}

evTime, err := httputil.ParseTSParam(req)

// HandleEventTimestamp processes the ts parameter based on whether the request is from an appservice.
func HandleEventTimestamp(req *http.Request) (time.Time, error) {
if isAppService(req) {
evTime, err := httputil.ParseTSParam(req)
if err != nil {
return time.Time{}, err // Return error for further handling
}
return evTime, nil
}

// If not from an appservice, use the current time or other default handling
return time.Now(), nil
}

// Check if the request is from an appservice
func isAppService(req *http.Request) error {
evTime, err := HandleEventTimestamp(req)
if err != nil {
// Handle error, e.g., return a 400 Bad Request
return httputil.LogThenError(req, err)
}

// Use evTime for the event timestamp
// Proceed with your original logic...

return nil
}

if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
Expand Down Expand Up @@ -291,6 +319,26 @@ func updatePowerLevels(req *http.Request, r map[string]interface{}, roomID strin
return nil
}

//If appservices use a specific access token, you can check the request’s authorization header for this token.
func isAppService(req *http.Request) bool {
// Check if the request contains an access token for appservices
accessToken := req.Header.Get("Authorization")

// This is an example; you need to replace it with your appservice token checking logic
if strings.HasPrefix(accessToken, "Bearer") {
token := strings.TrimPrefix(accessToken, "Bearer ")
return isValidAppServiceToken(token)
}

return false
}

func isValidAppServiceToken(token string) bool {
// Placeholder function: implement logic to validate if the token belongs to an appservice
// For example, you could compare against a list of known appservice tokens
return token == "your_appservice_token"
}

// stateEqual compares the new and the existing state event content. If they are equal, returns a *util.JSONResponse
// with the existing event_id, making this an idempotent request.
func stateEqual(ctx context.Context, rsAPI api.ClientRoomserverAPI, eventType, stateKey, roomID string, newContent map[string]interface{}) *util.JSONResponse {
Expand Down
59 changes: 59 additions & 0 deletions test/ts_param_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// ts_param_test.go
package testing

import (
"net/http"
"net/http/httptest"
"testing"
"time"
"your_project_path/routing" // Adjust this import path
)

func createRequestWithTS(ts string, isAppService bool) *http.Request {
req := httptest.NewRequest("POST", "/_matrix/client/r0/rooms/!roomid:domain/send/m.room.message", nil)
q := req.URL.Query()
if ts != "" {
q.Add("ts", ts)
}
req.URL.RawQuery = q.Encode()

if isAppService {
req.Header.Set("Authorization", "Bearer your_appservice_token")
} else {
req.Header.Set("Authorization", "Bearer regular_user_token")
}
return req
}

func TestHandleEventTimestamp_ValidAppService(t *testing.T) {
req := createRequestWithTS("1657890000000", true)
evTime, err := routing.HandleEventTimestamp(req)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}

expectedTime := time.Unix(1657890000, 0)
if !evTime.Equal(expectedTime) {
t.Errorf("Expected time %v, got %v", expectedTime, evTime)
}
}

func TestHandleEventTimestamp_InvalidTS(t *testing.T) {
req := createRequestWithTS("invalid_ts", true)
_, err := routing.HandleEventTimestamp(req)
if err == nil {
t.Fatal("Expected an error, got none")
}
}

func TestHandleEventTimestamp_NonAppService(t *testing.T) {
req := createRequestWithTS("1657890000000", false)
evTime, err := routing.HandleEventTimestamp(req)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}

if time.Now().Sub(evTime) > time.Second {
t.Errorf("Expected current time, got %v", evTime)
}
}

0 comments on commit 35611c4

Please sign in to comment.