-
Notifications
You must be signed in to change notification settings - Fork 1
/
adminapi.go
132 lines (118 loc) · 3.57 KB
/
adminapi.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package main
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/url"
"regexp"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/id"
"maunium.net/go/mautrix/synapseadmin"
"maunium.net/go/mautrix/util"
)
type BeeperCheckUsernameResponse struct {
Available bool `json:"available"`
Error string `json:"error"`
}
const useSynapseAPI = true
var validUsernameRegex = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{1,28}bot$`)
func IsValidBotUsername(username string) bool {
return validUsernameRegex.MatchString(username)
}
func IsUsernameAvailable(ctx context.Context, username string) (bool, error) {
if cfg.BeeperAPIURL != "" {
resp, err := cli.Client.Get(cfg.BeeperAPIURL + "/check-username/" + url.PathEscape(username))
if err != nil {
return false, fmt.Errorf("failed to send request to api server: %w", err)
}
var respData BeeperCheckUsernameResponse
err = json.NewDecoder(resp.Body).Decode(&respData)
if err != nil {
return false, fmt.Errorf("failed to decode response from api server: %w", err)
}
return respData.Available, nil
} else {
var err error
if useSynapseAPI {
_, err = synadm.UsernameAvailable(ctx, username)
} else {
_, err = cli.RegisterAvailable(username)
}
if errors.Is(err, mautrix.MUserInUse) || errors.Is(err, mautrix.MExclusive) {
return false, nil
} else if err != nil {
return false, err
} else {
return true, nil
}
}
}
func Login(ctx context.Context, userID id.UserID, password string) (*mautrix.RespLogin, error) {
loginClient, _ := mautrix.NewClient(cfg.HomeserverURL, "", "")
identifier := mautrix.UserIdentifier{
Type: mautrix.IdentifierTypeUser,
User: userID.String(),
}
const deviceDisplayName = "botbot"
if cfg.LoginJWTKey != "" {
return loginClient.Login(&mautrix.ReqLogin{
Type: mautrix.AuthTypeSynapseJWT,
Identifier: identifier,
Token: createLoginToken(userID),
InitialDeviceDisplayName: deviceDisplayName,
})
} else {
return loginClient.Login(&mautrix.ReqLogin{
Type: mautrix.AuthTypePassword,
Identifier: identifier,
Password: password,
InitialDeviceDisplayName: deviceDisplayName,
})
}
}
func RegisterUser(ctx context.Context, username string) (string, error) {
password := util.RandomString(72)
if cfg.BeeperAPIURL != "" {
return password, registerUserBeeper(ctx, username, password)
} else if cfg.RegisterSecret != "" {
return password, registerUserSynapse(ctx, username, password)
} else {
return "", fmt.Errorf("no way to register users configured")
}
}
func registerUserSynapse(ctx context.Context, username, password string) error {
_, err := synadm.SharedSecretRegister(ctx, cfg.RegisterSecret, synapseadmin.ReqSharedSecretRegister{
Username: username,
Password: password,
UserType: "bot",
Admin: false,
InhibitLogin: true,
})
return err
}
type reqBeeperRegister struct {
Username string `json:"username"`
Password string `json:"password"`
}
func registerUserBeeper(ctx context.Context, username, password string) error {
var body bytes.Buffer
err := json.NewEncoder(&body).Encode(&reqBeeperRegister{
Username: username,
Password: password,
})
if err != nil {
return fmt.Errorf("failed to encode request body: %w", err)
}
resp, err := cli.Client.Post(cfg.BeeperAPIURL+"/admin/bot/"+url.PathEscape(username), "application/json", &body)
if err != nil {
return fmt.Errorf("failed to send request to api server: %w", err)
}
if resp.StatusCode == 200 || resp.StatusCode == 201 {
return nil
}
respBody, _ := io.ReadAll(resp.Body)
return fmt.Errorf("failed to register: %s", respBody)
}