Skip to content

Commit

Permalink
Merge branch 'main' into rei/json_ct_on_login
Browse files Browse the repository at this point in the history
  • Loading branch information
kegsay authored Oct 9, 2023
2 parents 81e0040 + 9e57f77 commit 4e966c9
Show file tree
Hide file tree
Showing 123 changed files with 2,869 additions and 2,859 deletions.
File renamed without changes.
18 changes: 0 additions & 18 deletions internal/b/blueprints.go → b/blueprints.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,24 +97,6 @@ type Event struct {
Sender string
StateKey *string
Content map[string]interface{}

/* The following fields are ignored in blueprints as clients are unable to set them.
* They are used with federation.Server.
*/

Unsigned map[string]interface{}

// The events needed to authenticate this event.
// This can be either []EventReference for room v1/v2, or []string for room v3 onwards.
// If it is left at nil, MustCreateEvent will populate it automatically based on the room state.
AuthEvents interface{}

// The prev events of the event if we want to override or falsify them.
// If it is left at nil, MustCreateEvent will populate it automatically based on the forward extremities.
PrevEvents interface{}

// If this is a redaction, the event that it redacts
Redacts string
}

func MustValidate(bp Blueprint) Blueprint {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
163 changes: 163 additions & 0 deletions client/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package client

import (
"crypto/hmac"
"crypto/sha1"
"encoding/hex"
"io"

"github.com/tidwall/gjson"
)

const (
SharedSecret = "complement"
)

type LoginOpt func(map[string]interface{})

func WithDeviceID(deviceID string) LoginOpt {
return func(loginBody map[string]interface{}) {
loginBody["device_id"] = deviceID
}
}

// LoginUser will log in to a homeserver and create a new device on an existing user.
func (c *CSAPI) LoginUser(t TestLike, localpart, password string, opts ...LoginOpt) (userID, accessToken, deviceID string) {
t.Helper()
reqBody := map[string]interface{}{
"identifier": map[string]interface{}{
"type": "m.id.user",
"user": localpart,
},
"password": password,
"type": "m.login.password",
}

for _, opt := range opts {
opt(reqBody)
}

res := c.MustDo(t, "POST", []string{"_matrix", "client", "v3", "login"}, WithJSONBody(t, reqBody))

body, err := io.ReadAll(res.Body)
if err != nil {
t.Fatalf("unable to read response body: %v", err)
}

userID = GetJSONFieldStr(t, body, "user_id")
accessToken = GetJSONFieldStr(t, body, "access_token")
deviceID = GetJSONFieldStr(t, body, "device_id")
return userID, accessToken, deviceID
}

// LoginUserWithRefreshToken will log in to a homeserver, with refresh token enabled,
// and create a new device on an existing user.
func (c *CSAPI) LoginUserWithRefreshToken(t TestLike, localpart, password string) (userID, accessToken, refreshToken, deviceID string, expiresInMs int64) {
t.Helper()
reqBody := map[string]interface{}{
"identifier": map[string]interface{}{
"type": "m.id.user",
"user": localpart,
},
"password": password,
"type": "m.login.password",
"refresh_token": true,
}
res := c.MustDo(t, "POST", []string{"_matrix", "client", "v3", "login"}, WithJSONBody(t, reqBody))

body, err := io.ReadAll(res.Body)
if err != nil {
t.Fatalf("unable to read response body: %v", err)
}

userID = GetJSONFieldStr(t, body, "user_id")
accessToken = GetJSONFieldStr(t, body, "access_token")
deviceID = GetJSONFieldStr(t, body, "device_id")
refreshToken = GetJSONFieldStr(t, body, "refresh_token")
expiresInMs = gjson.GetBytes(body, "expires_in_ms").Int()
return userID, accessToken, refreshToken, deviceID, expiresInMs
}

// RefreshToken will consume a refresh token and return a new access token and refresh token.
func (c *CSAPI) ConsumeRefreshToken(t TestLike, refreshToken string) (newAccessToken, newRefreshToken string, expiresInMs int64) {
t.Helper()
reqBody := map[string]interface{}{
"refresh_token": refreshToken,
}
res := c.MustDo(t, "POST", []string{"_matrix", "client", "v3", "refresh"}, WithJSONBody(t, reqBody))

body, err := io.ReadAll(res.Body)
if err != nil {
t.Fatalf("unable to read response body: %v", err)
}

newAccessToken = GetJSONFieldStr(t, body, "access_token")
newRefreshToken = GetJSONFieldStr(t, body, "refresh_token")
expiresInMs = gjson.GetBytes(body, "expires_in_ms").Int()
return newAccessToken, newRefreshToken, expiresInMs
}

// RegisterUser will register the user with given parameters and
// return user ID, access token and device ID. It fails the test on network error.
func (c *CSAPI) RegisterUser(t TestLike, localpart, password string) (userID, accessToken, deviceID string) {
t.Helper()
reqBody := map[string]interface{}{
"auth": map[string]string{
"type": "m.login.dummy",
},
"username": localpart,
"password": password,
}
res := c.MustDo(t, "POST", []string{"_matrix", "client", "v3", "register"}, WithJSONBody(t, reqBody))

body, err := io.ReadAll(res.Body)
if err != nil {
t.Fatalf("unable to read response body: %v", err)
}

userID = GetJSONFieldStr(t, body, "user_id")
accessToken = GetJSONFieldStr(t, body, "access_token")
deviceID = GetJSONFieldStr(t, body, "device_id")
return userID, accessToken, deviceID
}

// RegisterSharedSecret registers a new account with a shared secret via HMAC
// See https://github.com/matrix-org/synapse/blob/e550ab17adc8dd3c48daf7fedcd09418a73f524b/synapse/_scripts/register_new_matrix_user.py#L40
func (c *CSAPI) RegisterSharedSecret(t TestLike, user, pass string, isAdmin bool) (userID, accessToken, deviceID string) {
resp := c.Do(t, "GET", []string{"_synapse", "admin", "v1", "register"})
if resp.StatusCode != 200 {
t.Skipf("Homeserver image does not support shared secret registration, /_synapse/admin/v1/register returned HTTP %d", resp.StatusCode)
return
}
body := ParseJSON(t, resp)
nonce := gjson.GetBytes(body, "nonce")
if !nonce.Exists() {
t.Fatalf("Malformed shared secret GET response: %s", string(body))
}
mac := hmac.New(sha1.New, []byte(SharedSecret))
mac.Write([]byte(nonce.Str))
mac.Write([]byte("\x00"))
mac.Write([]byte(user))
mac.Write([]byte("\x00"))
mac.Write([]byte(pass))
mac.Write([]byte("\x00"))
if isAdmin {
mac.Write([]byte("admin"))
} else {
mac.Write([]byte("notadmin"))
}
sig := mac.Sum(nil)
reqBody := map[string]interface{}{
"nonce": nonce.Str,
"username": user,
"password": pass,
"mac": hex.EncodeToString(sig),
"admin": isAdmin,
}
resp = c.MustDo(t, "POST", []string{"_synapse", "admin", "v1", "register"}, WithJSONBody(t, reqBody))
body = ParseJSON(t, resp)
userID = GetJSONFieldStr(t, body, "user_id")
accessToken = GetJSONFieldStr(t, body, "access_token")
deviceID = GetJSONFieldStr(t, body, "device_id")
return userID, accessToken, deviceID
}
Loading

0 comments on commit 4e966c9

Please sign in to comment.