diff --git a/go.mod b/go.mod index 17cb2889b..64c68ba11 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/tidwall/buntdb v1.2.0 // indirect github.com/tidwall/pretty v1.1.0 // indirect github.com/urfave/cli/v2 v2.3.0 - golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect + golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.3.0 ) diff --git a/go.sum b/go.sum index bafd27c22..b73d02c47 100644 --- a/go.sum +++ b/go.sum @@ -240,8 +240,9 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= diff --git a/internal/api/server.go b/internal/api/server.go index ed622210b..8af9e75fa 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -35,6 +35,10 @@ type Server interface { Stop() } +type AddsRoutes interface { + AddRoutes(s Server) error +} + type server struct { APIGroup *gin.RouterGroup logger *logrus.Logger diff --git a/internal/db/db.go b/internal/db/db.go index 03e30b41b..4ea4e1af6 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -25,7 +25,6 @@ "github.com/go-fed/activity/pub" "github.com/gotosocial/gotosocial/internal/config" - "github.com/gotosocial/oauth2/v4" "github.com/sirupsen/logrus" ) @@ -40,11 +39,6 @@ type DB interface { */ pub.Database - /* - OAUTH2 DATABASE FUNCTIONS - */ - TokenStore() oauth2.TokenStore - /* ANY ADDITIONAL DESIRED FUNCTIONS */ diff --git a/internal/db/postgres.go b/internal/db/postgres.go index 96452d5ae..dae6b11e2 100644 --- a/internal/db/postgres.go +++ b/internal/db/postgres.go @@ -315,8 +315,9 @@ func (ps *postgresService) Stop(ctx context.Context) error { func (ps *postgresService) CreateSchema(ctx context.Context) error { models := []interface{}{ - (*gtsmodel.GTSAccount)(nil), - (*gtsmodel.GTSStatus)(nil), + (*gtsmodel.Account)(nil), + (*gtsmodel.Status)(nil), + (*gtsmodel.User)(nil), } ps.log.Info("creating db schema") diff --git a/internal/email/email.go b/internal/email/email.go new file mode 100644 index 000000000..d70f6c5b5 --- /dev/null +++ b/internal/email/email.go @@ -0,0 +1,20 @@ +/* + GoToSocial + Copyright (C) 2021 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 . +*/ + +// package email provides a service for interacting with an SMTP server +package email diff --git a/internal/gtsmodel/account.go b/internal/gtsmodel/account.go index 7cd942ee8..84ba027b2 100644 --- a/internal/gtsmodel/account.go +++ b/internal/gtsmodel/account.go @@ -26,10 +26,10 @@ "time" ) -// GTSAccount represents a GoToSocial user account -type GTSAccount struct { - GTSAvatar - GTSHeader +// Account represents a GoToSocial user account +type Account struct { + Avatar + Header URI string URL string ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull"` @@ -66,7 +66,7 @@ type GTSAccount struct { SuspensionOrigin int } -type GTSAvatar struct { +type Avatar struct { AvatarFileName string AvatarContentType string AvatarFileSize int @@ -75,7 +75,7 @@ type GTSAvatar struct { AvatarStorageSchemaVersion int } -type GTSHeader struct { +type Header struct { HeaderFileName string HeaderContentType string HeaderFileSize int diff --git a/internal/gtsmodel/status.go b/internal/gtsmodel/status.go index cba8a6ba8..39c450934 100644 --- a/internal/gtsmodel/status.go +++ b/internal/gtsmodel/status.go @@ -20,7 +20,7 @@ import "time" -type GTSStatus struct { +type Status struct { ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull"` URI string URL string diff --git a/internal/gtsmodel/user.go b/internal/gtsmodel/user.go new file mode 100644 index 000000000..577590ddf --- /dev/null +++ b/internal/gtsmodel/user.go @@ -0,0 +1,65 @@ +/* + GoToSocial + Copyright (C) 2021 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 . +*/ + +package gtsmodel + +import ( + "net" + "time" +) + +type User struct { + ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull"` + Email string `pg:",notnull"` + CreatedAt time.Time `pg:"type:timestamp,notnull"` + UpdatedAt time.Time `pg:"type:timestamp,notnull"` + EncryptedPassword string `pg:",notnull"` + ResetPasswordToken string + ResetPasswordSentAt time.Time `pg:"type:timestamp"` + SignInCount int + CurrentSignInAt time.Time `pg:"type:timestamp"` + LastSignInAt time.Time `pg:"type:timestamp"` + CurrentSignInIP net.IP + LastSignInIP net.IP + Admin bool + ConfirmationToken string + ConfirmedAt time.Time `pg:"type:timestamp"` + ConfirmationSentAt time.Time `pg:"type:timestamp"` + UnconfirmedEmail string + Locale string + EncryptedOTPSecret string + EncryptedOTPSecretIv string + EncryptedOTPSecretSalt string + ConsumedTimestamp int + OTPRequiredForLogin bool + LastEmailedAt time.Time `pg:"type:timestamp"` + OTPBackupCodes []string + FilteredLanguages []string + AccountID string `pg:",notnull"` + Disabled bool + Moderator bool + InviteID string + RememberToken string + ChosenLanguages []string + CreatedByApplicationID string + Approved bool + SignInToken string + SignInTokenSentAt time.Time `pg:"type:timestamp"` + WebauthnID string + SignUpIP net.IP +} diff --git a/internal/oauth/oauth.go b/internal/oauth/oauth.go index d79db95ed..050c23dab 100644 --- a/internal/oauth/oauth.go +++ b/internal/oauth/oauth.go @@ -19,19 +19,25 @@ package oauth import ( + "github.com/go-pg/pg/v10" + "github.com/gotosocial/gotosocial/internal/api" + "github.com/gotosocial/gotosocial/internal/gtsmodel" "github.com/gotosocial/oauth2/v4" "github.com/gotosocial/oauth2/v4/errors" "github.com/gotosocial/oauth2/v4/manage" "github.com/gotosocial/oauth2/v4/server" "github.com/sirupsen/logrus" + "golang.org/x/crypto/bcrypt" ) type API struct { manager *manage.Manager server *server.Server + conn *pg.DB + log *logrus.Logger } -func New(ts oauth2.TokenStore, cs oauth2.ClientStore, log *logrus.Logger) *API { +func New(ts oauth2.TokenStore, cs oauth2.ClientStore, conn *pg.DB, log *logrus.Logger) *API { manager := manage.NewDefaultManager() manager.MapTokenStorage(ts) manager.MapClientStorage(cs) @@ -49,5 +55,41 @@ func New(ts oauth2.TokenStore, cs oauth2.ClientStore, log *logrus.Logger) *API { return &API{ manager: manager, server: srv, + conn: conn, + log: log, } } + +func (a *API) AddRoutes(s api.Server) error { + return nil +} + +func incorrectPassword() (string, error) { + return "", errors.New("password/email combination was incorrect") +} + +func (a *API) PasswordAuthorizationHandler(email string, password string) (userid string, err error) { + // first we select the user from the database based on email address, bail if no user found for that email + gtsUser := >smodel.User{} + if err := a.conn.Model(gtsUser).Where("email = ?", email).Select(); err != nil { + a.log.Debugf("user %s was not retrievable from db during oauth authorization attempt: %s", email, err) + return incorrectPassword() + } + + // make sure a password is actually set and bail if not + if gtsUser.EncryptedPassword == "" { + a.log.Warnf("encrypted password for user %s was empty for some reason", gtsUser.Email) + return incorrectPassword() + } + + // compare the provided password with the encrypted one from the db, bail if they don't match + if err := bcrypt.CompareHashAndPassword([]byte(gtsUser.EncryptedPassword), []byte(password)); err != nil { + a.log.Debugf("password hash didn't match for user %s during login attempt: %s", gtsUser.Email, err) + return incorrectPassword() + } + + // If we've made it this far the email/password is correct so we need the oauth client-id of the user + // This is, conveniently, the same as the user ID, so we can just return it. + userid = gtsUser.ID + return +} diff --git a/internal/oauth/pgclientstore.go b/internal/oauth/pgclientstore.go index dda5fb3d6..22bb54f55 100644 --- a/internal/oauth/pgclientstore.go +++ b/internal/oauth/pgclientstore.go @@ -37,9 +37,9 @@ func NewPGClientStore(conn *pg.DB) oauth2.ClientStore { return pts } -func (pcs *pgClientStore) GetByID(ctx context.Context, id string) (oauth2.ClientInfo, error) { +func (pcs *pgClientStore) GetByID(ctx context.Context, clientID string) (oauth2.ClientInfo, error) { poc := &oauthClient{ - ID: id, + ID: clientID, } if err := pcs.conn.WithContext(ctx).Model(poc).Where("id = ?", poc.ID).Select(); err != nil { return nil, err diff --git a/internal/oauth/pgclientstore_test.go b/internal/oauth/pgclientstore_test.go index 3f8de064d..d6a76981f 100644 --- a/internal/oauth/pgclientstore_test.go +++ b/internal/oauth/pgclientstore_test.go @@ -13,10 +13,10 @@ type PgClientStoreTestSuite struct { suite.Suite - conn *pg.DB - testClientID string - testClientSecret string - testClientDomain string + conn *pg.DB + testClientID string + testClientSecret string + testClientDomain string testClientUserID string }