mautrix-go/synapseadmin/register.go

111 lines
3.6 KiB
Go

// Copyright (c) 2023 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package synapseadmin
import (
"context"
"crypto/hmac"
"crypto/sha1"
"encoding/hex"
"fmt"
"net/http"
"maunium.net/go/mautrix"
)
type respGetRegisterNonce struct {
Nonce string `json:"nonce"`
}
// ReqSharedSecretRegister is the request content for Client.SharedSecretRegister.
type ReqSharedSecretRegister struct {
// The username to register. Required.
Username string `json:"username"`
// The new password for the user. Required.
Password string `json:"password"`
// Initial displayname for the user. By default, the server will use the username as the displayname.
Displayname string `json:"displayname,omitempty"`
// The type of user to register. Defaults to empty (normal user).
// Officially allowed values are "support" or "bot".
//
// Currently, the only effect is that synapse skips consent requirements for those two user types,
// other than that the user type does absolutely nothing.
UserType string `json:"user_type,omitempty"`
// Whether the created user should be a server admin.
Admin bool `json:"admin"`
// Disable generating a new device along with the registration.
// If true, the access_token and device_id fields in the response will be empty.
InhibitLogin bool `json:"inhibit_login,omitempty"`
// A single-use nonce from GetRegisterNonce. This is automatically filled by Client.SharedSecretRegister if left empty.
Nonce string `json:"nonce"`
// The checksum for the request. This is automatically generated by Client.SharedSecretRegister using Sign.
SHA1Checksum string `json:"mac"`
}
func (req *ReqSharedSecretRegister) Sign(secret string) string {
signer := hmac.New(sha1.New, []byte(secret))
signer.Write([]byte(req.Nonce))
signer.Write([]byte{0})
signer.Write([]byte(req.Username))
signer.Write([]byte{0})
signer.Write([]byte(req.Password))
signer.Write([]byte{0})
if req.Admin {
signer.Write([]byte("admin"))
} else {
signer.Write([]byte("notadmin"))
}
if req.UserType != "" {
signer.Write([]byte{0})
signer.Write([]byte(req.UserType))
}
return hex.EncodeToString(signer.Sum(nil))
}
// GetRegisterNonce gets a nonce that can be used for SharedSecretRegister.
//
// This does not need to be called manually as SharedSecretRegister will automatically call this if no nonce is provided.
func (cli *Client) GetRegisterNonce(ctx context.Context) (string, error) {
var resp respGetRegisterNonce
_, err := cli.MakeFullRequest(ctx, mautrix.FullRequest{
Method: http.MethodGet,
URL: cli.BuildURL(mautrix.SynapseAdminURLPath{"v1", "register"}),
ResponseJSON: &resp,
})
if err != nil {
return "", err
}
return resp.Nonce, nil
}
// SharedSecretRegister creates a new account using a shared secret.
//
// https://matrix-org.github.io/synapse/latest/admin_api/register_api.html
func (cli *Client) SharedSecretRegister(ctx context.Context, sharedSecret string, req ReqSharedSecretRegister) (*mautrix.RespRegister, error) {
var err error
if req.Nonce == "" {
req.Nonce, err = cli.GetRegisterNonce(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get nonce: %w", err)
}
}
req.SHA1Checksum = req.Sign(sharedSecret)
var resp mautrix.RespRegister
_, err = cli.MakeFullRequest(ctx, mautrix.FullRequest{
Method: http.MethodPost,
URL: cli.BuildURL(mautrix.SynapseAdminURLPath{"v1", "register"}),
RequestJSON: req,
ResponseJSON: &resp,
})
if err != nil {
return nil, err
}
return &resp, nil
}