summaryrefslogtreecommitdiff
path: root/internal/processing/account/create.go
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2024-04-11 11:45:53 +0200
committerLibravatar GitHub <noreply@github.com>2024-04-11 11:45:53 +0200
commit9fb8a78f91adffd5f4d28df1270e407c25a7a16e (patch)
treed68200744e28d07e75a52bb0c9f6593c86a38a91 /internal/processing/account/create.go
parent[performance] massively improved ActivityPub delivery worker efficiency (#2812) (diff)
downloadgotosocial-9fb8a78f91adffd5f4d28df1270e407c25a7a16e.tar.xz
[feature] New user sign-up via web page (#2796)
* [feature] User sign-up form and admin notifs * add chosen + filtered languages to migration * remove stray comment * chosen languages schmosen schmanguages * proper error on local account missing
Diffstat (limited to 'internal/processing/account/create.go')
-rw-r--r--internal/processing/account/create.go99
1 files changed, 78 insertions, 21 deletions
diff --git a/internal/processing/account/create.go b/internal/processing/account/create.go
index 1925feb63..12b2d5e57 100644
--- a/internal/processing/account/create.go
+++ b/internal/processing/account/create.go
@@ -20,6 +20,7 @@ package account
import (
"context"
"fmt"
+ "time"
"github.com/superseriousbusiness/gotosocial/internal/ap"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
@@ -32,15 +33,48 @@ import (
)
// Create processes the given form for creating a new account,
-// returning an oauth token for that account if successful.
+// returning a new user (with attached account) if successful.
//
-// Precondition: the form's fields should have already been validated and normalized by the caller.
+// App should be the app used to create the account.
+// If nil, the instance app will be used.
+//
+// Precondition: the form's fields should have already been
+// validated and normalized by the caller.
func (p *Processor) Create(
ctx context.Context,
- appToken oauth2.TokenInfo,
app *gtsmodel.Application,
form *apimodel.AccountCreateRequest,
-) (*apimodel.Token, gtserror.WithCode) {
+) (*gtsmodel.User, gtserror.WithCode) {
+ const (
+ usersPerDay = 10
+ regBacklog = 20
+ )
+
+ // Ensure no more than usersPerDay
+ // have registered in the last 24h.
+ newUsersCount, err := p.state.DB.CountApprovedSignupsSince(ctx, time.Now().Add(-24*time.Hour))
+ if err != nil {
+ err := fmt.Errorf("db error counting new users: %w", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ if newUsersCount >= usersPerDay {
+ err := fmt.Errorf("this instance has hit its limit of new sign-ups for today; you can try again tomorrow")
+ return nil, gtserror.NewErrorUnprocessableEntity(err, err.Error())
+ }
+
+ // Ensure the new users backlog isn't full.
+ backlogLen, err := p.state.DB.CountUnhandledSignups(ctx)
+ if err != nil {
+ err := fmt.Errorf("db error counting registration backlog length: %w", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ if backlogLen >= regBacklog {
+ err := fmt.Errorf("this instance's sign-up backlog is currently full; you must wait until pending sign-ups are handled by the admin(s)")
+ return nil, gtserror.NewErrorUnprocessableEntity(err, err.Error())
+ }
+
emailAvailable, err := p.state.DB.IsEmailAvailable(ctx, form.Email)
if err != nil {
err := fmt.Errorf("db error checking email availability: %w", err)
@@ -67,38 +101,61 @@ func (p *Processor) Create(
reason = form.Reason
}
+ // Use instance app if no app provided.
+ if app == nil {
+ app, err = p.state.DB.GetInstanceApplication(ctx)
+ if err != nil {
+ err := fmt.Errorf("db error getting instance app: %w", err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ }
+
user, err := p.state.DB.NewSignup(ctx, gtsmodel.NewSignup{
- Username: form.Username,
- Email: form.Email,
- Password: form.Password,
- Reason: text.SanitizeToPlaintext(reason),
- PreApproved: !config.GetAccountsApprovalRequired(), // Mark as approved if no approval required.
- SignUpIP: form.IP,
- Locale: form.Locale,
- AppID: app.ID,
+ Username: form.Username,
+ Email: form.Email,
+ Password: form.Password,
+ Reason: text.SanitizeToPlaintext(reason),
+ SignUpIP: form.IP,
+ Locale: form.Locale,
+ AppID: app.ID,
})
if err != nil {
err := fmt.Errorf("db error creating new signup: %w", err)
return nil, gtserror.NewErrorInternalError(err)
}
- // Generate access token *before* doing side effects; we
- // don't want to process side effects if something borks.
- accessToken, err := p.oauthServer.GenerateUserAccessToken(ctx, appToken, app.ClientSecret, user.ID)
- if err != nil {
- err := fmt.Errorf("error creating new access token for user %s: %w", user.ID, err)
- return nil, gtserror.NewErrorInternalError(err)
- }
-
// There are side effects for creating a new account
// (confirmation emails etc), perform these async.
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{
APObjectType: ap.ObjectProfile,
APActivityType: ap.ActivityCreate,
- GTSModel: user.Account,
+ GTSModel: user,
OriginAccount: user.Account,
})
+ return user, nil
+}
+
+// TokenForNewUser generates an OAuth Bearer token
+// for a new user (with account) created by Create().
+func (p *Processor) TokenForNewUser(
+ ctx context.Context,
+ appToken oauth2.TokenInfo,
+ app *gtsmodel.Application,
+ user *gtsmodel.User,
+) (*apimodel.Token, gtserror.WithCode) {
+ // Generate access token.
+ accessToken, err := p.oauthServer.GenerateUserAccessToken(
+ ctx,
+ appToken,
+ app.ClientSecret,
+ user.ID,
+ )
+ if err != nil {
+ err := fmt.Errorf("error creating new access token for user %s: %w", user.ID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
return &apimodel.Token{
AccessToken: accessToken.GetAccess(),
TokenType: "Bearer",