gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 := &gtsmodel.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 = &gtsmodel.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 := &gtsmodel.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 := &gtsmodel.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 := &gtsmodel.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 }