mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 17:42:25 -06:00 
			
		
		
		
	* move config flag names and usage to config package, rewrite config package to use global Configuration{} struct
Signed-off-by: kim <grufwub@gmail.com>
* improved code comment
Signed-off-by: kim <grufwub@gmail.com>
* linter
Signed-off-by: kim <grufwub@gmail.com>
* fix unmarshaling
Signed-off-by: kim <grufwub@gmail.com>
* remove kim's custom go compiler changes
Signed-off-by: kim <grufwub@gmail.com>
* generate setter and flag-name functions, implement these in codebase
Signed-off-by: kim <grufwub@gmail.com>
* update deps
Signed-off-by: kim <grufwub@gmail.com>
* small change
Signed-off-by: kim <grufwub@gmail.com>
* appease the linter...
Signed-off-by: kim <grufwub@gmail.com>
* move configuration into ConfigState structure, ensure reloading to/from viper settings to keep in sync
Signed-off-by: kim <grufwub@gmail.com>
* lint
Signed-off-by: kim <grufwub@gmail.com>
* update code comments
Signed-off-by: kim <grufwub@gmail.com>
* fix merge issue
Signed-off-by: kim <grufwub@gmail.com>
* fix merge issue
Signed-off-by: kim <grufwub@gmail.com>
* improved version string (removes time + go version)
Signed-off-by: kim <grufwub@gmail.com>
* fix version string build to pass test script + consolidate logic in func
Signed-off-by: kim <grufwub@gmail.com>
* add license text, update config.Defaults comment
Signed-off-by: kim <grufwub@gmail.com>
* add license text to generated config helpers file
Signed-off-by: kim <grufwub@gmail.com>
* defer unlock on config.Set___(), to ensure unlocked on panic
Signed-off-by: kim <grufwub@gmail.com>
* make it more obvious which cmd flags are being attached
Signed-off-by: kim <grufwub@gmail.com>
		
	
			
		
			
				
	
	
		
			280 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
   GoToSocial
 | 
						|
   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
 | 
						|
 | 
						|
   This program is free software: you can redistribute it and/or modify
 | 
						|
   it under the terms of the GNU Affero General Public License as published by
 | 
						|
   the Free Software Foundation, either version 3 of the License, or
 | 
						|
   (at your option) any later version.
 | 
						|
 | 
						|
   This program is distributed in the hope that it will be useful,
 | 
						|
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
   GNU Affero General Public License for more details.
 | 
						|
 | 
						|
   You should have received a copy of the GNU Affero General Public License
 | 
						|
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
*/
 | 
						|
 | 
						|
package bundb
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"crypto/rand"
 | 
						|
	"crypto/rsa"
 | 
						|
	"database/sql"
 | 
						|
	"fmt"
 | 
						|
	"net"
 | 
						|
	"net/mail"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/sirupsen/logrus"
 | 
						|
 | 
						|
	"github.com/superseriousbusiness/gotosocial/internal/ap"
 | 
						|
	"github.com/superseriousbusiness/gotosocial/internal/config"
 | 
						|
	"github.com/superseriousbusiness/gotosocial/internal/db"
 | 
						|
	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
 | 
						|
	"github.com/superseriousbusiness/gotosocial/internal/id"
 | 
						|
	"github.com/superseriousbusiness/gotosocial/internal/uris"
 | 
						|
	"golang.org/x/crypto/bcrypt"
 | 
						|
)
 | 
						|
 | 
						|
type adminDB struct {
 | 
						|
	conn *DBConn
 | 
						|
}
 | 
						|
 | 
						|
func (a *adminDB) IsUsernameAvailable(ctx context.Context, username string) (bool, db.Error) {
 | 
						|
	q := a.conn.
 | 
						|
		NewSelect().
 | 
						|
		Model(>smodel.Account{}).
 | 
						|
		Where("username = ?", username).
 | 
						|
		Where("domain = ?", nil)
 | 
						|
 | 
						|
	return a.conn.NotExists(ctx, q)
 | 
						|
}
 | 
						|
 | 
						|
func (a *adminDB) IsEmailAvailable(ctx context.Context, email string) (bool, db.Error) {
 | 
						|
	// parse the domain from the email
 | 
						|
	m, err := mail.ParseAddress(email)
 | 
						|
	if err != nil {
 | 
						|
		return false, fmt.Errorf("error parsing email address %s: %s", email, err)
 | 
						|
	}
 | 
						|
	domain := strings.Split(m.Address, "@")[1] // domain will always be the second part after @
 | 
						|
 | 
						|
	// check if the email domain is blocked
 | 
						|
	if err := a.conn.
 | 
						|
		NewSelect().
 | 
						|
		Model(>smodel.EmailDomainBlock{}).
 | 
						|
		Where("domain = ?", domain).
 | 
						|
		Scan(ctx); err == nil {
 | 
						|
		// fail because we found something
 | 
						|
		return false, fmt.Errorf("email domain %s is blocked", domain)
 | 
						|
	} else if err != sql.ErrNoRows {
 | 
						|
		return false, a.conn.ProcessError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	// check if this email is associated with a user already
 | 
						|
	q := a.conn.
 | 
						|
		NewSelect().
 | 
						|
		Model(>smodel.User{}).
 | 
						|
		Where("email = ?", email).
 | 
						|
		WhereOr("unconfirmed_email = ?", email)
 | 
						|
 | 
						|
	return a.conn.NotExists(ctx, q)
 | 
						|
}
 | 
						|
 | 
						|
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, admin bool) (*gtsmodel.User, db.Error) {
 | 
						|
	key, err := rsa.GenerateKey(rand.Reader, 2048)
 | 
						|
	if err != nil {
 | 
						|
		logrus.Errorf("error creating new rsa key: %s", err)
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// if something went wrong while creating a user, we might already have an account, so check here first...
 | 
						|
	acct := >smodel.Account{}
 | 
						|
	q := a.conn.NewSelect().
 | 
						|
		Model(acct).
 | 
						|
		Where("username = ?", username).
 | 
						|
		WhereGroup(" AND ", whereEmptyOrNull("domain"))
 | 
						|
 | 
						|
	if err := q.Scan(ctx); err != nil {
 | 
						|
		// we just don't have an account yet so create one before we proceed
 | 
						|
		accountURIs := uris.GenerateURIsForAccount(username)
 | 
						|
		accountID, err := id.NewRandomULID()
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		acct = >smodel.Account{
 | 
						|
			ID:                    accountID,
 | 
						|
			Username:              username,
 | 
						|
			DisplayName:           username,
 | 
						|
			Reason:                reason,
 | 
						|
			Privacy:               gtsmodel.VisibilityDefault,
 | 
						|
			URL:                   accountURIs.UserURL,
 | 
						|
			PrivateKey:            key,
 | 
						|
			PublicKey:             &key.PublicKey,
 | 
						|
			PublicKeyURI:          accountURIs.PublicKeyURI,
 | 
						|
			ActorType:             ap.ActorPerson,
 | 
						|
			URI:                   accountURIs.UserURI,
 | 
						|
			InboxURI:              accountURIs.InboxURI,
 | 
						|
			OutboxURI:             accountURIs.OutboxURI,
 | 
						|
			FollowersURI:          accountURIs.FollowersURI,
 | 
						|
			FollowingURI:          accountURIs.FollowingURI,
 | 
						|
			FeaturedCollectionURI: accountURIs.CollectionURI,
 | 
						|
		}
 | 
						|
 | 
						|
		if _, err = a.conn.
 | 
						|
			NewInsert().
 | 
						|
			Model(acct).
 | 
						|
			Exec(ctx); err != nil {
 | 
						|
			return nil, a.conn.ProcessError(err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	pw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("error hashing password: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	newUserID, err := id.NewRandomULID()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	u := >smodel.User{
 | 
						|
		ID:                     newUserID,
 | 
						|
		AccountID:              acct.ID,
 | 
						|
		Account:                acct,
 | 
						|
		EncryptedPassword:      string(pw),
 | 
						|
		SignUpIP:               signUpIP.To4(),
 | 
						|
		Locale:                 locale,
 | 
						|
		UnconfirmedEmail:       email,
 | 
						|
		CreatedByApplicationID: appID,
 | 
						|
		Approved:               !requireApproval, // if we don't require moderator approval, just pre-approve the user
 | 
						|
	}
 | 
						|
 | 
						|
	if emailVerified {
 | 
						|
		u.ConfirmedAt = time.Now()
 | 
						|
		u.Email = email
 | 
						|
		u.UnconfirmedEmail = ""
 | 
						|
	}
 | 
						|
 | 
						|
	if admin {
 | 
						|
		u.Admin = true
 | 
						|
		u.Moderator = true
 | 
						|
	}
 | 
						|
 | 
						|
	if _, err = a.conn.
 | 
						|
		NewInsert().
 | 
						|
		Model(u).
 | 
						|
		Exec(ctx); err != nil {
 | 
						|
		return nil, a.conn.ProcessError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	return u, nil
 | 
						|
}
 | 
						|
 | 
						|
func (a *adminDB) CreateInstanceAccount(ctx context.Context) db.Error {
 | 
						|
	username := config.GetHost()
 | 
						|
 | 
						|
	q := a.conn.
 | 
						|
		NewSelect().
 | 
						|
		Model(>smodel.Account{}).
 | 
						|
		Where("username = ?", username).
 | 
						|
		WhereGroup(" AND ", whereEmptyOrNull("domain"))
 | 
						|
 | 
						|
	exists, err := a.conn.Exists(ctx, q)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if exists {
 | 
						|
		logrus.Infof("instance account %s already exists", username)
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	key, err := rsa.GenerateKey(rand.Reader, 2048)
 | 
						|
	if err != nil {
 | 
						|
		logrus.Errorf("error creating new rsa key: %s", err)
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	aID, err := id.NewRandomULID()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	newAccountURIs := uris.GenerateURIsForAccount(username)
 | 
						|
	acct := >smodel.Account{
 | 
						|
		ID:                    aID,
 | 
						|
		Username:              username,
 | 
						|
		DisplayName:           username,
 | 
						|
		URL:                   newAccountURIs.UserURL,
 | 
						|
		PrivateKey:            key,
 | 
						|
		PublicKey:             &key.PublicKey,
 | 
						|
		PublicKeyURI:          newAccountURIs.PublicKeyURI,
 | 
						|
		ActorType:             ap.ActorPerson,
 | 
						|
		URI:                   newAccountURIs.UserURI,
 | 
						|
		InboxURI:              newAccountURIs.InboxURI,
 | 
						|
		OutboxURI:             newAccountURIs.OutboxURI,
 | 
						|
		FollowersURI:          newAccountURIs.FollowersURI,
 | 
						|
		FollowingURI:          newAccountURIs.FollowingURI,
 | 
						|
		FeaturedCollectionURI: newAccountURIs.CollectionURI,
 | 
						|
	}
 | 
						|
 | 
						|
	insertQ := a.conn.
 | 
						|
		NewInsert().
 | 
						|
		Model(acct)
 | 
						|
 | 
						|
	if _, err := insertQ.Exec(ctx); err != nil {
 | 
						|
		return a.conn.ProcessError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	logrus.Infof("instance account %s CREATED with id %s", username, acct.ID)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (a *adminDB) CreateInstanceInstance(ctx context.Context) db.Error {
 | 
						|
	protocol := config.GetProtocol()
 | 
						|
	host := config.GetHost()
 | 
						|
 | 
						|
	// check if instance entry already exists
 | 
						|
	q := a.conn.
 | 
						|
		NewSelect().
 | 
						|
		Model(>smodel.Instance{}).
 | 
						|
		Where("domain = ?", host)
 | 
						|
 | 
						|
	exists, err := a.conn.Exists(ctx, q)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if exists {
 | 
						|
		logrus.Infof("instance entry already exists")
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	iID, err := id.NewRandomULID()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	i := >smodel.Instance{
 | 
						|
		ID:     iID,
 | 
						|
		Domain: host,
 | 
						|
		Title:  host,
 | 
						|
		URI:    fmt.Sprintf("%s://%s", protocol, host),
 | 
						|
	}
 | 
						|
 | 
						|
	insertQ := a.conn.
 | 
						|
		NewInsert().
 | 
						|
		Model(i)
 | 
						|
 | 
						|
	_, err = insertQ.Exec(ctx)
 | 
						|
	if err != nil {
 | 
						|
		return a.conn.ProcessError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	logrus.Infof("created instance instance %s with id %s", host, i.ID)
 | 
						|
	return nil
 | 
						|
}
 |