| 
									
										
										
										
											2023-03-12 16:00:57 +01:00
										 |  |  | // GoToSocial | 
					
						
							|  |  |  | // Copyright (C) GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | // SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // 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/>. | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | package bundb | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"crypto/rand" | 
					
						
							|  |  |  | 	"crypto/rsa" | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net/mail" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-11 11:45:53 +02:00
										 |  |  | 	"github.com/google/uuid" | 
					
						
							| 
									
										
										
										
											2021-08-31 15:59:12 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/ap" | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | 
					
						
							|  |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/db" | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 
					
						
							|  |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/id" | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/log" | 
					
						
							| 
									
										
										
										
											2022-12-08 17:35:14 +00:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/state" | 
					
						
							| 
									
										
										
										
											2021-12-20 15:19:53 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/uris" | 
					
						
							| 
									
										
										
										
											2023-08-07 19:38:11 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/util" | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 	"github.com/uptrace/bun" | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	"golang.org/x/crypto/bcrypt" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | // generate RSA keys of this length | 
					
						
							|  |  |  | const rsaKeyBits = 2048 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | type adminDB struct { | 
					
						
							| 
									
										
										
										
											2024-02-07 14:43:27 +00:00
										 |  |  | 	db    *bun.DB | 
					
						
							| 
									
										
										
										
											2022-12-08 17:35:14 +00:00
										 |  |  | 	state *state.State | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-25 09:34:05 +01:00
										 |  |  | func (a *adminDB) IsUsernameAvailable(ctx context.Context, username string) (bool, error) { | 
					
						
							|  |  |  | 	q := a.db. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		NewSelect(). | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 		TableExpr("? AS ?", bun.Ident("accounts"), bun.Ident("account")). | 
					
						
							|  |  |  | 		Column("account.id"). | 
					
						
							|  |  |  | 		Where("? = ?", bun.Ident("account.username"), username). | 
					
						
							|  |  |  | 		Where("? IS NULL", bun.Ident("account.domain")) | 
					
						
							| 
									
										
										
										
											2024-02-07 14:43:27 +00:00
										 |  |  | 	return notExists(ctx, q) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-25 09:34:05 +01:00
										 |  |  | func (a *adminDB) IsEmailAvailable(ctx context.Context, email string) (bool, error) { | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	// parse the domain from the email | 
					
						
							|  |  |  | 	m, err := mail.ParseAddress(email) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		return false, fmt.Errorf("error parsing email address %s: %s", email, err) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	domain := strings.Split(m.Address, "@")[1] // domain will always be the second part after @ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// check if the email domain is blocked | 
					
						
							| 
									
										
										
										
											2023-07-25 09:34:05 +01:00
										 |  |  | 	emailDomainBlockedQ := a.db. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		NewSelect(). | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 		TableExpr("? AS ?", bun.Ident("email_domain_blocks"), bun.Ident("email_domain_block")). | 
					
						
							|  |  |  | 		Column("email_domain_block.id"). | 
					
						
							|  |  |  | 		Where("? = ?", bun.Ident("email_domain_block.domain"), domain) | 
					
						
							| 
									
										
										
										
											2024-02-07 14:43:27 +00:00
										 |  |  | 	emailDomainBlocked, err := exists(ctx, emailDomainBlockedQ) | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return false, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if emailDomainBlocked { | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		return false, fmt.Errorf("email domain %s is blocked", domain) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// check if this email is associated with a user already | 
					
						
							| 
									
										
										
										
											2023-07-25 09:34:05 +01:00
										 |  |  | 	q := a.db. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		NewSelect(). | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 		TableExpr("? AS ?", bun.Ident("users"), bun.Ident("user")). | 
					
						
							|  |  |  | 		Column("user.id"). | 
					
						
							|  |  |  | 		Where("? = ?", bun.Ident("user.email"), email). | 
					
						
							|  |  |  | 		WhereOr("? = ?", bun.Ident("user.unconfirmed_email"), email) | 
					
						
							| 
									
										
										
										
											2024-02-07 14:43:27 +00:00
										 |  |  | 	return notExists(ctx, q) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-25 09:34:05 +01:00
										 |  |  | func (a *adminDB) NewSignup(ctx context.Context, newSignup gtsmodel.NewSignup) (*gtsmodel.User, error) { | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 	// If something went wrong previously while doing a new | 
					
						
							|  |  |  | 	// sign up with this username, we might already have an | 
					
						
							|  |  |  | 	// account, so check first. | 
					
						
							|  |  |  | 	account, err := a.state.DB.GetAccountByUsernameDomain(ctx, newSignup.Username, "") | 
					
						
							|  |  |  | 	if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							|  |  |  | 		// Real error occurred. | 
					
						
							|  |  |  | 		err := gtserror.Newf("error checking for existing account: %w", err) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 	// If we didn't yet have an account | 
					
						
							|  |  |  | 	// with this username, create one now. | 
					
						
							|  |  |  | 	if account == nil { | 
					
						
							|  |  |  | 		uris := uris.GenerateURIsForAccount(newSignup.Username) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		accountID, err := id.NewRandomULID() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			err := gtserror.Newf("error creating new account id: %w", err) | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-01-31 16:03:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 		privKey, err := rsa.GenerateKey(rand.Reader, rsaKeyBits) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 			err := gtserror.Newf("error creating new rsa private key: %w", err) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 		account = >smodel.Account{ | 
					
						
							| 
									
										
										
										
											2021-12-20 15:19:53 +01:00
										 |  |  | 			ID:                    accountID, | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 			Username:              newSignup.Username, | 
					
						
							|  |  |  | 			DisplayName:           newSignup.Username, | 
					
						
							|  |  |  | 			URI:                   uris.UserURI, | 
					
						
							|  |  |  | 			URL:                   uris.UserURL, | 
					
						
							|  |  |  | 			InboxURI:              uris.InboxURI, | 
					
						
							|  |  |  | 			OutboxURI:             uris.OutboxURI, | 
					
						
							|  |  |  | 			FollowingURI:          uris.FollowingURI, | 
					
						
							|  |  |  | 			FollowersURI:          uris.FollowersURI, | 
					
						
							|  |  |  | 			FeaturedCollectionURI: uris.FeaturedCollectionURI, | 
					
						
							| 
									
										
										
										
											2021-08-31 15:59:12 +02:00
										 |  |  | 			ActorType:             ap.ActorPerson, | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 			PrivateKey:            privKey, | 
					
						
							|  |  |  | 			PublicKey:             &privKey.PublicKey, | 
					
						
							|  |  |  | 			PublicKeyURI:          uris.PublicKeyURI, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-01-31 16:03:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 		// Insert the new account! | 
					
						
							|  |  |  | 		if err := a.state.DB.PutAccount(ctx, account); err != nil { | 
					
						
							| 
									
										
										
										
											2022-11-15 18:45:15 +00:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-06-11 11:54:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Insert basic settings for new account. | 
					
						
							|  |  |  | 		account.Settings = >smodel.AccountSettings{ | 
					
						
							|  |  |  | 			AccountID: accountID, | 
					
						
							|  |  |  | 			Privacy:   gtsmodel.VisibilityDefault, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := a.state.DB.PutAccountSettings(ctx, account.Settings); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Stub empty stats for new account. | 
					
						
							|  |  |  | 		if err := a.state.DB.StubAccountStats(ctx, account); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 	// Created or already had an account. | 
					
						
							|  |  |  | 	// Ensure user not already created. | 
					
						
							|  |  |  | 	user, err := a.state.DB.GetUserByAccountID(ctx, account.ID) | 
					
						
							|  |  |  | 	if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							|  |  |  | 		// Real error occurred. | 
					
						
							|  |  |  | 		err := gtserror.Newf("error checking for existing user: %w", err) | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		// Pin account to (new) | 
					
						
							|  |  |  | 		// user before returning. | 
					
						
							|  |  |  | 		user.Account = account | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 	if user != nil { | 
					
						
							|  |  |  | 		// Already had a user for this | 
					
						
							|  |  |  | 		// account, just return that. | 
					
						
							|  |  |  | 		return user, nil | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 	// Had no user for this account, time to create one! | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	newUserID, err := id.NewRandomULID() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 		err := gtserror.Newf("error creating new user id: %w", err) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 	encryptedPassword, err := bcrypt.GenerateFromPassword( | 
					
						
							|  |  |  | 		[]byte(newSignup.Password), | 
					
						
							|  |  |  | 		bcrypt.DefaultCost, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		err := gtserror.Newf("error hashing password: %w", err) | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	user = >smodel.User{ | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		ID:                     newUserID, | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 		AccountID:              account.ID, | 
					
						
							|  |  |  | 		Account:                account, | 
					
						
							|  |  |  | 		EncryptedPassword:      string(encryptedPassword), | 
					
						
							|  |  |  | 		SignUpIP:               newSignup.SignUpIP.To4(), | 
					
						
							| 
									
										
										
										
											2024-04-11 11:45:53 +02:00
										 |  |  | 		Reason:                 newSignup.Reason, | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 		Locale:                 newSignup.Locale, | 
					
						
							|  |  |  | 		UnconfirmedEmail:       newSignup.Email, | 
					
						
							|  |  |  | 		CreatedByApplicationID: newSignup.AppID, | 
					
						
							|  |  |  | 		ExternalID:             newSignup.ExternalID, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if newSignup.EmailVerified { | 
					
						
							|  |  |  | 		// Mark given email as confirmed. | 
					
						
							|  |  |  | 		user.ConfirmedAt = time.Now() | 
					
						
							|  |  |  | 		user.Email = newSignup.Email | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 	if newSignup.Admin { | 
					
						
							|  |  |  | 		// Make new user mod + admin. | 
					
						
							| 
									
										
										
										
											2023-08-07 19:38:11 +02:00
										 |  |  | 		user.Moderator = util.Ptr(true) | 
					
						
							|  |  |  | 		user.Admin = util.Ptr(true) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 	if newSignup.PreApproved { | 
					
						
							|  |  |  | 		// Mark new user as approved. | 
					
						
							| 
									
										
										
										
											2023-08-07 19:38:11 +02:00
										 |  |  | 		user.Approved = util.Ptr(true) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 	// Insert the user! | 
					
						
							|  |  |  | 	if err := a.state.DB.PutUser(ctx, user); err != nil { | 
					
						
							|  |  |  | 		err := gtserror.Newf("db error inserting user: %w", err) | 
					
						
							| 
									
										
										
										
											2022-11-15 18:45:15 +00:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-23 12:33:17 +02:00
										 |  |  | 	return user, nil | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-25 09:34:05 +01:00
										 |  |  | func (a *adminDB) CreateInstanceAccount(ctx context.Context) error { | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	username := config.GetHost() | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-25 09:34:05 +01:00
										 |  |  | 	q := a.db. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		NewSelect(). | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 		TableExpr("? AS ?", bun.Ident("accounts"), bun.Ident("account")). | 
					
						
							|  |  |  | 		Column("account.id"). | 
					
						
							|  |  |  | 		Where("? = ?", bun.Ident("account.username"), username). | 
					
						
							| 
									
										
										
										
											2023-07-05 12:34:37 +02:00
										 |  |  | 		Where("? IS NULL", bun.Ident("account.domain")) | 
					
						
							| 
									
										
										
										
											2021-11-17 13:40:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-07 14:43:27 +00:00
										 |  |  | 	exists, err := exists(ctx, q) | 
					
						
							| 
									
										
										
										
											2021-11-17 13:40:09 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if exists { | 
					
						
							| 
									
										
										
										
											2023-02-17 12:02:29 +01:00
										 |  |  | 		log.Infof(ctx, "instance account %s already exists", username) | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 	key, err := rsa.GenerateKey(rand.Reader, rsaKeyBits) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-02-17 12:02:29 +01:00
										 |  |  | 		log.Errorf(ctx, "error creating new rsa key: %s", err) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	aID, err := id.NewRandomULID() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-20 15:19:53 +01:00
										 |  |  | 	newAccountURIs := uris.GenerateURIsForAccount(username) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	acct := >smodel.Account{ | 
					
						
							|  |  |  | 		ID:                    aID, | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 		Username:              username, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		DisplayName:           username, | 
					
						
							|  |  |  | 		URL:                   newAccountURIs.UserURL, | 
					
						
							|  |  |  | 		PrivateKey:            key, | 
					
						
							|  |  |  | 		PublicKey:             &key.PublicKey, | 
					
						
							|  |  |  | 		PublicKeyURI:          newAccountURIs.PublicKeyURI, | 
					
						
							| 
									
										
										
										
											2021-08-31 15:59:12 +02:00
										 |  |  | 		ActorType:             ap.ActorPerson, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		URI:                   newAccountURIs.UserURI, | 
					
						
							|  |  |  | 		InboxURI:              newAccountURIs.InboxURI, | 
					
						
							|  |  |  | 		OutboxURI:             newAccountURIs.OutboxURI, | 
					
						
							|  |  |  | 		FollowersURI:          newAccountURIs.FollowersURI, | 
					
						
							|  |  |  | 		FollowingURI:          newAccountURIs.FollowingURI, | 
					
						
							| 
									
										
										
										
											2023-03-01 18:52:44 +01:00
										 |  |  | 		FeaturedCollectionURI: newAccountURIs.FeaturedCollectionURI, | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-15 18:45:15 +00:00
										 |  |  | 	// insert the new account! | 
					
						
							| 
									
										
										
										
											2022-12-08 17:35:14 +00:00
										 |  |  | 	if err := a.state.DB.PutAccount(ctx, acct); err != nil { | 
					
						
							| 
									
										
										
										
											2022-11-15 18:45:15 +00:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-17 12:02:29 +01:00
										 |  |  | 	log.Infof(ctx, "instance account %s CREATED with id %s", username, acct.ID) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-25 09:34:05 +01:00
										 |  |  | func (a *adminDB) CreateInstanceInstance(ctx context.Context) error { | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 	protocol := config.GetProtocol() | 
					
						
							|  |  |  | 	host := config.GetHost() | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// check if instance entry already exists | 
					
						
							| 
									
										
										
										
											2023-07-25 09:34:05 +01:00
										 |  |  | 	q := a.db. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		NewSelect(). | 
					
						
							| 
									
										
										
										
											2022-10-08 13:50:48 +02:00
										 |  |  | 		Column("instance.id"). | 
					
						
							|  |  |  | 		TableExpr("? AS ?", bun.Ident("instances"), bun.Ident("instance")). | 
					
						
							|  |  |  | 		Where("? = ?", bun.Ident("instance.domain"), host) | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-07 14:43:27 +00:00
										 |  |  | 	exists, err := exists(ctx, q) | 
					
						
							| 
									
										
										
										
											2021-08-26 17:22:41 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if exists { | 
					
						
							| 
									
										
										
										
											2023-02-17 12:02:29 +01:00
										 |  |  | 		log.Infof(ctx, "instance entry already exists") | 
					
						
							| 
									
										
										
										
											2021-08-26 17:46:24 +02:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	iID, err := id.NewRandomULID() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	i := >smodel.Instance{ | 
					
						
							|  |  |  | 		ID:     iID, | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | 		Domain: host, | 
					
						
							|  |  |  | 		Title:  host, | 
					
						
							|  |  |  | 		URI:    fmt.Sprintf("%s://%s", protocol, host), | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-25 09:34:05 +01:00
										 |  |  | 	insertQ := a.db. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		NewInsert(). | 
					
						
							|  |  |  | 		Model(i) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-26 17:22:41 +02:00
										 |  |  | 	_, err = insertQ.Exec(ctx) | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-08-17 17:26:21 +01:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-17 12:02:29 +01:00
										 |  |  | 	log.Infof(ctx, "created instance instance %s with id %s", host, i.ID) | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-09-04 15:55:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-11 11:45:53 +02:00
										 |  |  | func (a *adminDB) CreateInstanceApplication(ctx context.Context) error { | 
					
						
							|  |  |  | 	// Check if instance application already exists. | 
					
						
							|  |  |  | 	// Instance application client_id always = the | 
					
						
							|  |  |  | 	// instance account's ID so this is an easy check. | 
					
						
							|  |  |  | 	instanceAcct, err := a.state.DB.GetInstanceAccount(ctx, "") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	exists, err := exists( | 
					
						
							|  |  |  | 		ctx, | 
					
						
							|  |  |  | 		a.db. | 
					
						
							|  |  |  | 			NewSelect(). | 
					
						
							|  |  |  | 			Column("application.id"). | 
					
						
							|  |  |  | 			TableExpr("? AS ?", bun.Ident("applications"), bun.Ident("application")). | 
					
						
							|  |  |  | 			Where("? = ?", bun.Ident("application.client_id"), instanceAcct.ID), | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if exists { | 
					
						
							|  |  |  | 		log.Infof(ctx, "instance application already exists") | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Generate new IDs for this | 
					
						
							|  |  |  | 	// application and its client. | 
					
						
							|  |  |  | 	protocol := config.GetProtocol() | 
					
						
							|  |  |  | 	host := config.GetHost() | 
					
						
							|  |  |  | 	url := protocol + "://" + host | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	clientID := instanceAcct.ID | 
					
						
							|  |  |  | 	clientSecret := uuid.NewString() | 
					
						
							|  |  |  | 	appID, err := id.NewRandomULID() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Generate the application | 
					
						
							|  |  |  | 	// to put in the database. | 
					
						
							|  |  |  | 	app := >smodel.Application{ | 
					
						
							|  |  |  | 		ID:           appID, | 
					
						
							|  |  |  | 		Name:         host + " instance application", | 
					
						
							|  |  |  | 		Website:      url, | 
					
						
							|  |  |  | 		RedirectURI:  url, | 
					
						
							|  |  |  | 		ClientID:     clientID, | 
					
						
							|  |  |  | 		ClientSecret: clientSecret, | 
					
						
							|  |  |  | 		Scopes:       "write:accounts", | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Store it. | 
					
						
							|  |  |  | 	if err := a.state.DB.PutApplication(ctx, app); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Model an oauth client | 
					
						
							|  |  |  | 	// from the application. | 
					
						
							|  |  |  | 	oc := >smodel.Client{ | 
					
						
							|  |  |  | 		ID:     clientID, | 
					
						
							|  |  |  | 		Secret: clientSecret, | 
					
						
							|  |  |  | 		Domain: url, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Store it. | 
					
						
							| 
									
										
										
										
											2024-04-15 14:22:21 +01:00
										 |  |  | 	return a.state.DB.PutClient(ctx, oc) | 
					
						
							| 
									
										
										
										
											2024-04-11 11:45:53 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (a *adminDB) GetInstanceApplication(ctx context.Context) (*gtsmodel.Application, error) { | 
					
						
							|  |  |  | 	// Instance app clientID == instanceAcct.ID, | 
					
						
							|  |  |  | 	// so get the instance account first. | 
					
						
							|  |  |  | 	instanceAcct, err := a.state.DB.GetInstanceAccount(ctx, "") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	app := new(gtsmodel.Application) | 
					
						
							|  |  |  | 	if err := a.db. | 
					
						
							|  |  |  | 		NewSelect(). | 
					
						
							|  |  |  | 		Model(app). | 
					
						
							|  |  |  | 		Where("? = ?", bun.Ident("application.client_id"), instanceAcct.ID). | 
					
						
							|  |  |  | 		Scan(ctx); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return app, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (a *adminDB) CountApprovedSignupsSince(ctx context.Context, since time.Time) (int, error) { | 
					
						
							|  |  |  | 	return a.db. | 
					
						
							|  |  |  | 		NewSelect(). | 
					
						
							|  |  |  | 		TableExpr("? AS ?", bun.Ident("users"), bun.Ident("user")). | 
					
						
							|  |  |  | 		Where("? > ?", bun.Ident("user.created_at"), since). | 
					
						
							|  |  |  | 		Where("? = ?", bun.Ident("user.approved"), true). | 
					
						
							|  |  |  | 		Count(ctx) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (a *adminDB) CountUnhandledSignups(ctx context.Context) (int, error) { | 
					
						
							|  |  |  | 	return a.db. | 
					
						
							|  |  |  | 		NewSelect(). | 
					
						
							|  |  |  | 		TableExpr("? AS ?", bun.Ident("users"), bun.Ident("user")). | 
					
						
							|  |  |  | 		// Approved is false by default. | 
					
						
							|  |  |  | 		// Explicitly rejected sign-ups end up elsewhere. | 
					
						
							|  |  |  | 		Where("? = ?", bun.Ident("user.approved"), false). | 
					
						
							|  |  |  | 		Count(ctx) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-04 15:55:17 +02:00
										 |  |  | /* | 
					
						
							|  |  |  | 	ACTION FUNCS | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (a *adminDB) GetAdminAction(ctx context.Context, id string) (*gtsmodel.AdminAction, error) { | 
					
						
							|  |  |  | 	action := new(gtsmodel.AdminAction) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := a.db. | 
					
						
							|  |  |  | 		NewSelect(). | 
					
						
							|  |  |  | 		Model(action). | 
					
						
							|  |  |  | 		Scan(ctx); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return action, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (a *adminDB) GetAdminActions(ctx context.Context) ([]*gtsmodel.AdminAction, error) { | 
					
						
							|  |  |  | 	actions := make([]*gtsmodel.AdminAction, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := a.db. | 
					
						
							|  |  |  | 		NewSelect(). | 
					
						
							|  |  |  | 		Model(&actions). | 
					
						
							|  |  |  | 		Scan(ctx); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return actions, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (a *adminDB) PutAdminAction(ctx context.Context, action *gtsmodel.AdminAction) error { | 
					
						
							|  |  |  | 	_, err := a.db. | 
					
						
							|  |  |  | 		NewInsert(). | 
					
						
							|  |  |  | 		Model(action). | 
					
						
							|  |  |  | 		Exec(ctx) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (a *adminDB) UpdateAdminAction(ctx context.Context, action *gtsmodel.AdminAction, columns ...string) error { | 
					
						
							|  |  |  | 	// Update the action's last-updated | 
					
						
							|  |  |  | 	action.UpdatedAt = time.Now() | 
					
						
							|  |  |  | 	if len(columns) != 0 { | 
					
						
							|  |  |  | 		columns = append(columns, "updated_at") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_, err := a.db. | 
					
						
							|  |  |  | 		NewUpdate(). | 
					
						
							|  |  |  | 		Model(action). | 
					
						
							|  |  |  | 		Where("? = ?", bun.Ident("admin_action.id"), action.ID). | 
					
						
							|  |  |  | 		Column(columns...). | 
					
						
							|  |  |  | 		Exec(ctx) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (a *adminDB) DeleteAdminAction(ctx context.Context, id string) error { | 
					
						
							|  |  |  | 	_, err := a.db. | 
					
						
							|  |  |  | 		NewDelete(). | 
					
						
							|  |  |  | 		TableExpr("? AS ?", bun.Ident("admin_actions"), bun.Ident("admin_action")). | 
					
						
							|  |  |  | 		Where("? = ?", bun.Ident("admin_action"), id). | 
					
						
							|  |  |  | 		Exec(ctx) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } |