135 lines
3.1 KiB
Go
135 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/hmac"
|
|
"crypto/sha1"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
)
|
|
|
|
// regEndpoint is the endpoint for shared secret registration
|
|
const regEndpoint = "/_synapse/admin/v1/register"
|
|
|
|
// RegistrationReq represents the JSON body of a registration request
|
|
type RegistrationReq struct {
|
|
Nonce string `json:"nonce"`
|
|
Username string `json:"username"`
|
|
DisplayName string `json:"displayname"`
|
|
Password string `json:"password"`
|
|
Admin bool `json:"admin"`
|
|
MAC string `json:"mac"`
|
|
}
|
|
|
|
// registerUser registers the given user with the matrix server
|
|
func registerUser(username, displayName, password string, u url.URL) error {
|
|
u.Path = regEndpoint
|
|
req, err := generateRegRequest(username, displayName, password, u)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
data, err := json.Marshal(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Info("Issuing registration request",
|
|
slog.String("username", req.Username),
|
|
slog.String("nonce", req.Nonce),
|
|
slog.String("mac", req.MAC),
|
|
)
|
|
|
|
res, err := http.Post(u.String(), "application/json", bytes.NewReader(data))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != 200 {
|
|
var errRes struct {
|
|
Message string `json:"message"`
|
|
ErrCode string `json:"errcode"`
|
|
Error string `json:"error"`
|
|
}
|
|
|
|
err = json.NewDecoder(res.Body).Decode(&errRes)
|
|
if err != nil || (errRes.Message == "" && errRes.Error == "") {
|
|
return errors.New("http: " + res.Status)
|
|
} else if errRes.Error != "" {
|
|
return errors.New(u.Host + ": " + errRes.ErrCode + ": " + errRes.Error)
|
|
} else {
|
|
return errors.New(u.Host + ": " + errRes.Message)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// generateRegRequest generates the JSON struct that will serve as the body of a use registration request
|
|
func generateRegRequest(username, displayName, password string, u url.URL) (*RegistrationReq, error) {
|
|
secret := os.Getenv("SHARED_SECRET")
|
|
|
|
nonce, err := getNonce(u)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
mac, err := calculateMAC(secret, username, password, nonce)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &RegistrationReq{
|
|
nonce,
|
|
username,
|
|
displayName,
|
|
password,
|
|
false,
|
|
mac,
|
|
}, nil
|
|
}
|
|
|
|
// getNonce requests and returns a nonce from the matrix server
|
|
func getNonce(u url.URL) (string, error) {
|
|
u.Path = regEndpoint
|
|
res, err := http.Get(u.String())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != 200 {
|
|
return "", errors.New("http: " + res.Status)
|
|
}
|
|
|
|
var resp struct {
|
|
Nonce string `json:"nonce"`
|
|
}
|
|
err = json.NewDecoder(res.Body).Decode(&resp)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return resp.Nonce, nil
|
|
}
|
|
|
|
// calculateMAC calculates a returns an HMAC-SHA1 for a new non-admin user
|
|
func calculateMAC(secret, username, password, nonce string) (string, error) {
|
|
h := hmac.New(sha1.New, []byte(secret))
|
|
b := &bytes.Buffer{}
|
|
b.WriteString(nonce)
|
|
b.WriteByte(0)
|
|
b.WriteString(username)
|
|
b.WriteByte(0)
|
|
b.WriteString(password)
|
|
b.WriteByte(0)
|
|
b.WriteString("notadmin")
|
|
b.WriteTo(h)
|
|
return hex.EncodeToString(h.Sum(nil)), nil
|
|
} |