admin.go (8738B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package bundb 19 20 import ( 21 "context" 22 "crypto/rand" 23 "crypto/rsa" 24 "fmt" 25 "net" 26 "net/mail" 27 "strings" 28 "time" 29 30 "github.com/superseriousbusiness/gotosocial/internal/ap" 31 "github.com/superseriousbusiness/gotosocial/internal/config" 32 "github.com/superseriousbusiness/gotosocial/internal/db" 33 "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 34 "github.com/superseriousbusiness/gotosocial/internal/id" 35 "github.com/superseriousbusiness/gotosocial/internal/log" 36 "github.com/superseriousbusiness/gotosocial/internal/state" 37 "github.com/superseriousbusiness/gotosocial/internal/uris" 38 "github.com/uptrace/bun" 39 "golang.org/x/crypto/bcrypt" 40 ) 41 42 // generate RSA keys of this length 43 const rsaKeyBits = 2048 44 45 type adminDB struct { 46 conn *DBConn 47 state *state.State 48 } 49 50 func (a *adminDB) IsUsernameAvailable(ctx context.Context, username string) (bool, db.Error) { 51 q := a.conn. 52 NewSelect(). 53 TableExpr("? AS ?", bun.Ident("accounts"), bun.Ident("account")). 54 Column("account.id"). 55 Where("? = ?", bun.Ident("account.username"), username). 56 Where("? IS NULL", bun.Ident("account.domain")) 57 return a.conn.NotExists(ctx, q) 58 } 59 60 func (a *adminDB) IsEmailAvailable(ctx context.Context, email string) (bool, db.Error) { 61 // parse the domain from the email 62 m, err := mail.ParseAddress(email) 63 if err != nil { 64 return false, fmt.Errorf("error parsing email address %s: %s", email, err) 65 } 66 domain := strings.Split(m.Address, "@")[1] // domain will always be the second part after @ 67 68 // check if the email domain is blocked 69 emailDomainBlockedQ := a.conn. 70 NewSelect(). 71 TableExpr("? AS ?", bun.Ident("email_domain_blocks"), bun.Ident("email_domain_block")). 72 Column("email_domain_block.id"). 73 Where("? = ?", bun.Ident("email_domain_block.domain"), domain) 74 emailDomainBlocked, err := a.conn.Exists(ctx, emailDomainBlockedQ) 75 if err != nil { 76 return false, err 77 } 78 if emailDomainBlocked { 79 return false, fmt.Errorf("email domain %s is blocked", domain) 80 } 81 82 // check if this email is associated with a user already 83 q := a.conn. 84 NewSelect(). 85 TableExpr("? AS ?", bun.Ident("users"), bun.Ident("user")). 86 Column("user.id"). 87 Where("? = ?", bun.Ident("user.email"), email). 88 WhereOr("? = ?", bun.Ident("user.unconfirmed_email"), email) 89 return a.conn.NotExists(ctx, q) 90 } 91 92 func (a *adminDB) NewSignup(ctx context.Context, username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string, emailVerified bool, externalID string, admin bool) (*gtsmodel.User, db.Error) { 93 key, err := rsa.GenerateKey(rand.Reader, rsaKeyBits) 94 if err != nil { 95 log.Errorf(ctx, "error creating new rsa key: %s", err) 96 return nil, err 97 } 98 99 // if something went wrong while creating a user, we might already have an account, so check here first... 100 acct := >smodel.Account{} 101 if err := a.conn. 102 NewSelect(). 103 Model(acct). 104 Where("? = ?", bun.Ident("account.username"), username). 105 WhereGroup(" AND ", whereEmptyOrNull("account.domain")). 106 Scan(ctx); err != nil { 107 err = a.conn.ProcessError(err) 108 if err != db.ErrNoEntries { 109 log.Errorf(ctx, "error checking for existing account: %s", err) 110 return nil, err 111 } 112 113 // if we have db.ErrNoEntries, we just don't have an 114 // account yet so create one before we proceed 115 accountURIs := uris.GenerateURIsForAccount(username) 116 accountID, err := id.NewRandomULID() 117 if err != nil { 118 return nil, err 119 } 120 121 acct = >smodel.Account{ 122 ID: accountID, 123 Username: username, 124 DisplayName: username, 125 Reason: reason, 126 Privacy: gtsmodel.VisibilityDefault, 127 URL: accountURIs.UserURL, 128 PrivateKey: key, 129 PublicKey: &key.PublicKey, 130 PublicKeyURI: accountURIs.PublicKeyURI, 131 ActorType: ap.ActorPerson, 132 URI: accountURIs.UserURI, 133 InboxURI: accountURIs.InboxURI, 134 OutboxURI: accountURIs.OutboxURI, 135 FollowersURI: accountURIs.FollowersURI, 136 FollowingURI: accountURIs.FollowingURI, 137 FeaturedCollectionURI: accountURIs.FeaturedCollectionURI, 138 } 139 140 // insert the new account! 141 if err := a.state.DB.PutAccount(ctx, acct); err != nil { 142 return nil, err 143 } 144 } 145 146 // we either created or already had an account by now, 147 // so proceed with creating a user for that account 148 149 pw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) 150 if err != nil { 151 return nil, fmt.Errorf("error hashing password: %s", err) 152 } 153 154 newUserID, err := id.NewRandomULID() 155 if err != nil { 156 return nil, err 157 } 158 159 // if we don't require moderator approval, just pre-approve the user 160 approved := !requireApproval 161 u := >smodel.User{ 162 ID: newUserID, 163 AccountID: acct.ID, 164 Account: acct, 165 EncryptedPassword: string(pw), 166 SignUpIP: signUpIP.To4(), 167 Locale: locale, 168 UnconfirmedEmail: email, 169 CreatedByApplicationID: appID, 170 Approved: &approved, 171 ExternalID: externalID, 172 } 173 174 if emailVerified { 175 u.ConfirmedAt = time.Now() 176 u.Email = email 177 } 178 179 if admin { 180 admin := true 181 moderator := true 182 u.Admin = &admin 183 u.Moderator = &moderator 184 } 185 186 // insert the user! 187 if err := a.state.DB.PutUser(ctx, u); err != nil { 188 return nil, err 189 } 190 191 return u, nil 192 } 193 194 func (a *adminDB) CreateInstanceAccount(ctx context.Context) db.Error { 195 username := config.GetHost() 196 197 q := a.conn. 198 NewSelect(). 199 TableExpr("? AS ?", bun.Ident("accounts"), bun.Ident("account")). 200 Column("account.id"). 201 Where("? = ?", bun.Ident("account.username"), username). 202 WhereGroup(" AND ", whereEmptyOrNull("account.domain")) 203 204 exists, err := a.conn.Exists(ctx, q) 205 if err != nil { 206 return err 207 } 208 if exists { 209 log.Infof(ctx, "instance account %s already exists", username) 210 return nil 211 } 212 213 key, err := rsa.GenerateKey(rand.Reader, rsaKeyBits) 214 if err != nil { 215 log.Errorf(ctx, "error creating new rsa key: %s", err) 216 return err 217 } 218 219 aID, err := id.NewRandomULID() 220 if err != nil { 221 return err 222 } 223 224 newAccountURIs := uris.GenerateURIsForAccount(username) 225 acct := >smodel.Account{ 226 ID: aID, 227 Username: username, 228 DisplayName: username, 229 URL: newAccountURIs.UserURL, 230 PrivateKey: key, 231 PublicKey: &key.PublicKey, 232 PublicKeyURI: newAccountURIs.PublicKeyURI, 233 ActorType: ap.ActorPerson, 234 URI: newAccountURIs.UserURI, 235 InboxURI: newAccountURIs.InboxURI, 236 OutboxURI: newAccountURIs.OutboxURI, 237 FollowersURI: newAccountURIs.FollowersURI, 238 FollowingURI: newAccountURIs.FollowingURI, 239 FeaturedCollectionURI: newAccountURIs.FeaturedCollectionURI, 240 } 241 242 // insert the new account! 243 if err := a.state.DB.PutAccount(ctx, acct); err != nil { 244 return err 245 } 246 247 log.Infof(ctx, "instance account %s CREATED with id %s", username, acct.ID) 248 return nil 249 } 250 251 func (a *adminDB) CreateInstanceInstance(ctx context.Context) db.Error { 252 protocol := config.GetProtocol() 253 host := config.GetHost() 254 255 // check if instance entry already exists 256 q := a.conn. 257 NewSelect(). 258 Column("instance.id"). 259 TableExpr("? AS ?", bun.Ident("instances"), bun.Ident("instance")). 260 Where("? = ?", bun.Ident("instance.domain"), host) 261 262 exists, err := a.conn.Exists(ctx, q) 263 if err != nil { 264 return err 265 } 266 if exists { 267 log.Infof(ctx, "instance entry already exists") 268 return nil 269 } 270 271 iID, err := id.NewRandomULID() 272 if err != nil { 273 return err 274 } 275 276 i := >smodel.Instance{ 277 ID: iID, 278 Domain: host, 279 Title: host, 280 URI: fmt.Sprintf("%s://%s", protocol, host), 281 } 282 283 insertQ := a.conn. 284 NewInsert(). 285 Model(i) 286 287 _, err = insertQ.Exec(ctx) 288 if err != nil { 289 return a.conn.ProcessError(err) 290 } 291 292 log.Infof(ctx, "created instance instance %s with id %s", host, i.ID) 293 return nil 294 }