From 1b118841211da90381dd950cafa13ead78b7f589 Mon Sep 17 00:00:00 2001 From: tsmethurst Date: Thu, 18 Mar 2021 23:27:43 +0100 Subject: auth flow working for code --- pkg/mastotypes/oauth.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 pkg/mastotypes/oauth.go (limited to 'pkg') diff --git a/pkg/mastotypes/oauth.go b/pkg/mastotypes/oauth.go new file mode 100644 index 000000000..1b45b38e0 --- /dev/null +++ b/pkg/mastotypes/oauth.go @@ -0,0 +1,37 @@ +/* + 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 mastotypes + +// OAuthAuthorize represents a request sent to https://example.org/oauth/authorize +// See here: https://docs.joinmastodon.org/methods/apps/oauth/ +type OAuthAuthorize struct { + // Forces the user to re-login, which is necessary for authorizing with multiple accounts from the same instance. + ForceLogin string `form:"force_login,omitempty"` + // Should be set equal to `code`. + ResponseType string `form:"response_type"` + // Client ID, obtained during app registration. + ClientID string `form:"client_id"` + // Set a URI to redirect the user to. + // If this parameter is set to urn:ietf:wg:oauth:2.0:oob then the authorization code will be shown instead. + // Must match one of the redirect URIs declared during app registration. + RedirectURI string `form:"redirect_uri"` + // List of requested OAuth scopes, separated by spaces (or by pluses, if using query parameters). + // Must be a subset of scopes declared during app registration. If not provided, defaults to read. + Scope string `form:"scope,omitempty"` +} -- cgit v1.3 From 95faebe60dfcb1ba62a81932e14e876ea9993cd7 Mon Sep 17 00:00:00 2001 From: tsmethurst Date: Thu, 18 Mar 2021 23:54:07 +0100 Subject: extend application for use in oauth --- internal/gtsmodel/application.go | 34 ++++++++++++++++++++++++++++++++++ pkg/mastotypes/application.go | 30 ++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 internal/gtsmodel/application.go (limited to 'pkg') diff --git a/internal/gtsmodel/application.go b/internal/gtsmodel/application.go new file mode 100644 index 000000000..f8c36ca25 --- /dev/null +++ b/internal/gtsmodel/application.go @@ -0,0 +1,34 @@ +/* + 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 + +type Application struct { + ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull"` + Name string `json:"name"` + // The website associated with your application (url) + Website string `json:"website,omitempty"` + // Where the user should be redirected after authorization. + RedirectURI string `json:"redirect_uri"` + // ClientID to use when obtaining an oauth token for this application (ie., in client_id parameter of https://docs.joinmastodon.org/methods/apps/) + ClientID string `json:"client_id"` + // Client secret to use when obtaining an auth token for this application (ie., in client_secret parameter of https://docs.joinmastodon.org/methods/apps/) + ClientSecret string `json:"client_secret"` + // Used for Push Streaming API. Returned with POST /api/v1/apps. Equivalent to https://docs.joinmastodon.org/entities/pushsubscription/#server_key + VapidKey string `json:"vapid_key"` +} diff --git a/pkg/mastotypes/application.go b/pkg/mastotypes/application.go index d2f894306..88128f71d 100644 --- a/pkg/mastotypes/application.go +++ b/pkg/mastotypes/application.go @@ -18,12 +18,38 @@ package mastotypes -// Application represents a mastodon-api Application, as defined here: https://docs.joinmastodon.org/entities/application/ +// Application represents a mastodon-api Application, as defined here: https://docs.joinmastodon.org/entities/application/. +// Primarily, application is used for allowing apps like Tusky etc to connect to Mastodon on behalf of a user. +// See https://docs.joinmastodon.org/methods/apps/ type Application struct { + // The application ID in the db + ID string `json:"id,omitempty"` // The name of your application. Name string `json:"name"` // The website associated with your application (url) - Website string `json:"website"` + Website string `json:"website,omitempty"` + // Where the user should be redirected after authorization. + RedirectURI string `json:"redirect_uri,omitempty"` + // ClientID to use when obtaining an oauth token for this application (ie., in client_id parameter of https://docs.joinmastodon.org/methods/apps/) + ClientID string `json:"client_id,omitempty"` + // Client secret to use when obtaining an auth token for this application (ie., in client_secret parameter of https://docs.joinmastodon.org/methods/apps/) + ClientSecret string `json:"client_secret,omitempty"` // Used for Push Streaming API. Returned with POST /api/v1/apps. Equivalent to https://docs.joinmastodon.org/entities/pushsubscription/#server_key VapidKey string `json:"vapid_key"` } + +// ApplicationPOSTRequest represents a POST request to https://example.org/api/v1/apps. +// See here: https://docs.joinmastodon.org/methods/apps/ +// And here: https://docs.joinmastodon.org/client/token/ +type ApplicationPOSTRequest struct { + // A name for your application + ClientName string `form:"client_name"` + // Where the user should be redirected after authorization. + // To display the authorization code to the user instead of redirecting + // to a web page, use urn:ietf:wg:oauth:2.0:oob in this parameter. + RedirectURIs string `form:"redirect_uris"` + // Space separated list of scopes. If none is provided, defaults to read. + Scopes string `form:"scopes"` + // A URL to the homepage of your app + Website string `form:"website"` +} -- cgit v1.3 From 81760963b04501e237a2822570fb580104aacd0a Mon Sep 17 00:00:00 2001 From: tsmethurst Date: Sat, 20 Mar 2021 19:04:27 +0100 Subject: formatting,comments --- internal/config/config.go | 4 +- internal/gtsmodel/account.go | 158 +++++++++++++++++++++++++++++++------------ internal/gtsmodel/status.go | 8 +-- internal/gtsmodel/user.go | 121 ++++++++++++++++++++++++--------- pkg/mastotypes/oauth.go | 8 +-- 5 files changed, 212 insertions(+), 87 deletions(-) (limited to 'pkg') diff --git a/internal/config/config.go b/internal/config/config.go index 8e2656e3f..ce194cd52 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -48,7 +48,7 @@ func Default() *Config { // TODO: find a way of doing this without code repetition, because having to // repeat all values here and elsewhere is annoying and gonna be prone to mistakes. return &Config{ - DBConfig: &DBConfig{}, + DBConfig: &DBConfig{}, TemplateConfig: &TemplateConfig{}, } } @@ -56,7 +56,7 @@ func Default() *Config { // Empty just returns an empty config func Empty() *Config { return &Config{ - DBConfig: &DBConfig{}, + DBConfig: &DBConfig{}, TemplateConfig: &TemplateConfig{}, } } diff --git a/internal/gtsmodel/account.go b/internal/gtsmodel/account.go index 84ba027b2..7bc8118a3 100644 --- a/internal/gtsmodel/account.go +++ b/internal/gtsmodel/account.go @@ -26,60 +26,130 @@ import ( "time" ) -// Account represents a GoToSocial user account +// Account represents either a local or a remote fediverse account, gotosocial or otherwise (mastodon, pleroma, etc) type Account struct { + /* + BASIC INFO + */ + + // id of this account in the local database; the end-user will never need to know this, it's strictly internal + ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"` + // Username of the account, should just be a string of [a-z0-9_]. Can be added to domain to create the full username in the form ``[username]@[domain]`` eg., ``user_96@example.org`` + Username string `pg:",notnull,unique:userdomain"` // username and domain should be unique *with* each other + // Domain of the account, will be empty if this is a local account, otherwise something like ``example.org`` or ``mastodon.social``. Should be unique with username. + Domain string `pg:",unique:userdomain"` // username and domain + + /* + ACCOUNT METADATA + */ + + // Avatar image for this account Avatar + // Header image for this account Header - URI string - URL string - ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull"` - Username string - Domain string - Secret string - PrivateKey string - PublicKey string - RemoteURL string - CreatedAt time.Time `pg:"type:timestamp,notnull"` - UpdatedAt time.Time `pg:"type:timestamp,notnull"` - Note string - DisplayName string + // DisplayName for this account. Can be empty, then just the Username will be used for display purposes. + DisplayName string + // a key/value map of fields that this account has added to their profile + Fields map[string]string + // A note that this account has on their profile (ie., the account's bio/description of themselves) + Note string + // Is this a memorial account, ie., has the user passed away? + Memorial bool + // This account has moved this account id in the database + MovedToAccountID int + // When was this account created? + CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` + // When was this account last updated? + UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` + // When should this account function until SubscriptionExpiresAt time.Time `pg:"type:timestamp"` - Locked bool - LastWebfingeredAt time.Time `pg:"type:timestamp"` - InboxURL string - OutboxURL string - SharedInboxURL string - FollowersURL string - Protocol int - Memorial bool - MovedToAccountID int - FeaturedCollectionURL string - Fields map[string]string - ActorType string - Discoverable bool - AlsoKnownAs string - SilencedAt time.Time `pg:"type:timestamp"` - SuspendedAt time.Time `pg:"type:timestamp"` - TrustLevel int - HideCollections bool - SensitizedAt time.Time `pg:"type:timestamp"` - SuspensionOrigin int + + /* + PRIVACY SETTINGS + */ + + // Does this account need an approval for new followers? + Locked bool + // Should this account be shown in the instance's profile directory? + Discoverable bool + + /* + ACTIVITYPUB THINGS + */ + + // What is the activitypub URI for this account discovered by webfinger? + URI string `pg:",unique"` + // At which URL can we see the user account in a web browser? + URL string `pg:",unique"` + // RemoteURL where this account is located. Will be empty if this is a local account. + RemoteURL string `pg:",unique"` + // Last time this account was located using the webfinger API. + LastWebfingeredAt time.Time `pg:"type:timestamp"` + // Address of this account's activitypub inbox, for sending activity to + InboxURL string `pg:",unique"` + // Address of this account's activitypub outbox + OutboxURL string `pg:",unique"` + // Don't support shared inbox right now so this is just a stub for a future implementation + SharedInboxURL string `pg:",unique"` + // URL for getting the followers list of this account + FollowersURL string `pg:",unique"` + // URL for getting the featured collection list of this account + FeaturedCollectionURL string `pg:",unique"` + // What type of activitypub actor is this account? + ActorType string + // This account is associated with x account id + AlsoKnownAs string + + /* + CRYPTO FIELDS + */ + + Secret string + // Privatekey for validating activitypub requests, will obviously only be defined for local accounts + PrivateKey string + // Publickey for encoding activitypub requests, will be defined for both local and remote accounts + PublicKey string + + /* + ADMIN FIELDS + */ + + // When was this account set to have all its media shown as sensitive? + SensitizedAt time.Time `pg:"type:timestamp"` + // When was this account silenced (eg., statuses only visible to followers, not public)? + SilencedAt time.Time `pg:"type:timestamp"` + // When was this account suspended (eg., don't allow it to log in/post, don't accept media/posts from this account) + SuspendedAt time.Time `pg:"type:timestamp"` + // How much do we trust this account 🤔 + TrustLevel int + // Should we hide this account's collections? + HideCollections bool + // id of the user that suspended this account through an admin action + SuspensionOrigin int } +// Avatar represents the avatar for the account for display purposes type Avatar struct { - AvatarFileName string - AvatarContentType string - AvatarFileSize int - AvatarUpdatedAt *time.Time `pg:"type:timestamp"` - AvatarRemoteURL *url.URL `pg:"type:text"` + // File name of the avatar on local storage + AvatarFileName string + // Gif? png? jpeg? + AvatarContentType string + AvatarFileSize int + AvatarUpdatedAt *time.Time `pg:"type:timestamp"` + // Where can we retrieve the avatar? + AvatarRemoteURL *url.URL `pg:"type:text"` AvatarStorageSchemaVersion int } +// Header represents the header of the account for display purposes type Header struct { - HeaderFileName string - HeaderContentType string - HeaderFileSize int - HeaderUpdatedAt *time.Time `pg:"type:timestamp"` - HeaderRemoteURL *url.URL `pg:"type:text"` + // File name of the header on local storage + HeaderFileName string + // Gif? png? jpeg? + HeaderContentType string + HeaderFileSize int + HeaderUpdatedAt *time.Time `pg:"type:timestamp"` + // Where can we retrieve the header? + HeaderRemoteURL *url.URL `pg:"type:text"` HeaderStorageSchemaVersion int } diff --git a/internal/gtsmodel/status.go b/internal/gtsmodel/status.go index 39c450934..22e88c08e 100644 --- a/internal/gtsmodel/status.go +++ b/internal/gtsmodel/status.go @@ -22,11 +22,11 @@ import "time" type Status struct { ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull"` - URI string - URL string + URI string `pg:",unique"` + URL string `pg:",unique"` Content string - CreatedAt time.Time `pg:"type:timestamp,notnull"` - UpdatedAt time.Time `pg:"type:timestamp,notnull"` + CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` + UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` Local bool AccountID string InReplyToID string diff --git a/internal/gtsmodel/user.go b/internal/gtsmodel/user.go index 577590ddf..c105899b8 100644 --- a/internal/gtsmodel/user.go +++ b/internal/gtsmodel/user.go @@ -23,43 +23,98 @@ import ( "time" ) +// User represents an actual human user of gotosocial. Note, this is a LOCAL gotosocial user, not a remote account. +// To cross reference this local user with their account (which can be local or remote), use the AccountID field. 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 + /* + BASIC INFO + */ + + // id of this user in the local database; the end-user will never need to know this, it's strictly internal + ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"` + // confirmed email address for this user, this should be unique -- only one email address registered per instance, multiple users per email are not supported + Email string `pg:",notnull,unique"` + // The id of the local gtsmodel.Account entry for this user, if it exists (unconfirmed users don't have an account yet) + AccountID string `pg:"default:'',notnull,unique"` + // The encrypted password of this user, generated using https://pkg.go.dev/golang.org/x/crypto/bcrypt#GenerateFromPassword. A salt is included so we're safe against 🌈 tables + EncryptedPassword string `pg:",notnull"` + + /* + USER METADATA + */ + + // When was this user created? + CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` + // From what IP was this user created? + SignUpIP net.IP + // When was this user updated (eg., password changed, email address changed)? + UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` + // When did this user sign in for their current session? + CurrentSignInAt time.Time `pg:"type:timestamp"` + // What's the most recent IP of this user + CurrentSignInIP net.IP + // When did this user last sign in? + LastSignInAt time.Time `pg:"type:timestamp"` + // What's the previous IP of this user? + LastSignInIP net.IP + // How many times has this user signed in? + SignInCount int + // id of the user who invited this user (who let this guy in?) + InviteID string + // What languages does this user want to see? + ChosenLanguages []string + // What languages does this user not want to see? + FilteredLanguages []string + // In what timezone/locale is this user located? + Locale string + // Which application id created this user? See gtsmodel.Application + CreatedByApplicationID string + // When did we last contact this user + LastEmailedAt time.Time `pg:"type:timestamp"` + + /* + USER CONFIRMATION + */ + + // What confirmation token did we send this user/what are we expecting back? + ConfirmationToken string + // When did the user confirm their email address + ConfirmedAt time.Time `pg:"type:timestamp"` + // When did we send email confirmation to this user? + ConfirmationSentAt time.Time `pg:"type:timestamp"` + // Email address that hasn't yet been confirmed + UnconfirmedEmail string + + /* + ACL FLAGS + */ + + // Is this user a moderator? + Moderator bool + // Is this user an admin? + Admin bool + // Is this user disabled from posting? + Disabled bool + // Has this user been approved by a moderator? + Approved bool + + /* + USER SECURITY + */ + + // The generated token that the user can use to reset their password + ResetPasswordToken string + // When did we email the user their reset-password email? + ResetPasswordSentAt time.Time `pg:"type:timestamp"` + 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 + ConsumedTimestamp int + RememberToken string + SignInToken string + SignInTokenSentAt time.Time `pg:"type:timestamp"` + WebauthnID string } diff --git a/pkg/mastotypes/oauth.go b/pkg/mastotypes/oauth.go index 1b45b38e0..d93ea079f 100644 --- a/pkg/mastotypes/oauth.go +++ b/pkg/mastotypes/oauth.go @@ -22,16 +22,16 @@ package mastotypes // See here: https://docs.joinmastodon.org/methods/apps/oauth/ type OAuthAuthorize struct { // Forces the user to re-login, which is necessary for authorizing with multiple accounts from the same instance. - ForceLogin string `form:"force_login,omitempty"` + ForceLogin string `form:"force_login,omitempty"` // Should be set equal to `code`. ResponseType string `form:"response_type"` // Client ID, obtained during app registration. - ClientID string `form:"client_id"` + ClientID string `form:"client_id"` // Set a URI to redirect the user to. // If this parameter is set to urn:ietf:wg:oauth:2.0:oob then the authorization code will be shown instead. // Must match one of the redirect URIs declared during app registration. - RedirectURI string `form:"redirect_uri"` + RedirectURI string `form:"redirect_uri"` // List of requested OAuth scopes, separated by spaces (or by pluses, if using query parameters). // Must be a subset of scopes declared during app registration. If not provided, defaults to read. - Scope string `form:"scope,omitempty"` + Scope string `form:"scope,omitempty"` } -- cgit v1.3