diff options
Diffstat (limited to 'internal/api')
-rw-r--r-- | internal/api/client/auth/auth_test.go | 3 | ||||
-rw-r--r-- | internal/api/client/auth/middleware.go | 3 | ||||
-rw-r--r-- | internal/api/client/auth/token.go | 30 | ||||
-rw-r--r-- | internal/api/client/followrequest/accept.go | 57 | ||||
-rw-r--r-- | internal/api/client/followrequest/deny.go | 27 | ||||
-rw-r--r-- | internal/api/client/followrequest/followrequest.go | 68 | ||||
-rw-r--r-- | internal/api/client/followrequest/get.go | 51 | ||||
-rw-r--r-- | internal/api/client/instance/instance.go | 2 | ||||
-rw-r--r-- | internal/api/client/instance/instanceget.go | 1 | ||||
-rw-r--r-- | internal/api/client/media/media.go | 2 | ||||
-rw-r--r-- | internal/api/client/media/mediacreate.go | 20 | ||||
-rw-r--r-- | internal/api/client/media/mediaget.go | 2 | ||||
-rw-r--r-- | internal/api/model/application.go | 8 | ||||
-rw-r--r-- | internal/api/model/attachment.go | 12 | ||||
-rw-r--r-- | internal/api/model/status.go | 4 | ||||
-rw-r--r-- | internal/api/s2s/user/inboxpost.go | 58 | ||||
-rw-r--r-- | internal/api/s2s/user/user.go | 3 | ||||
-rw-r--r-- | internal/api/s2s/webfinger/webfinger.go | 2 | ||||
-rw-r--r-- | internal/api/security/extraheaders.go | 8 | ||||
-rw-r--r-- | internal/api/security/security.go | 1 |
20 files changed, 338 insertions, 24 deletions
diff --git a/internal/api/client/auth/auth_test.go b/internal/api/client/auth/auth_test.go index 7ec788a0e..48d2a2508 100644 --- a/internal/api/client/auth/auth_test.go +++ b/internal/api/client/auth/auth_test.go @@ -28,6 +28,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/db/pg" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/oauth" "golang.org/x/crypto/bcrypt" @@ -103,7 +104,7 @@ func (suite *AuthTestSuite) SetupTest() { log := logrus.New() log.SetLevel(logrus.TraceLevel) - db, err := db.NewPostgresService(context.Background(), suite.config, log) + db, err := pg.NewPostgresService(context.Background(), suite.config, log) if err != nil { logrus.Panicf("error creating database connection: %s", err) } diff --git a/internal/api/client/auth/middleware.go b/internal/api/client/auth/middleware.go index 8313b7c28..2a63cbdb6 100644 --- a/internal/api/client/auth/middleware.go +++ b/internal/api/client/auth/middleware.go @@ -33,7 +33,7 @@ func (m *Module) OauthTokenMiddleware(c *gin.Context) { l := m.log.WithField("func", "OauthTokenMiddleware") l.Trace("entering OauthTokenMiddleware") - ti, err := m.server.ValidationBearerToken(c.Request) + ti, err := m.server.ValidationBearerToken(c.Copy().Request) if err != nil { l.Tracef("could not validate token: %s", err) return @@ -74,4 +74,5 @@ func (m *Module) OauthTokenMiddleware(c *gin.Context) { c.Set(oauth.SessionAuthorizedApplication, app) l.Tracef("set gin context %s to %+v", oauth.SessionAuthorizedApplication, app) } + c.Next() } diff --git a/internal/api/client/auth/token.go b/internal/api/client/auth/token.go index c531a3009..798a88d19 100644 --- a/internal/api/client/auth/token.go +++ b/internal/api/client/auth/token.go @@ -20,16 +20,46 @@ package auth import ( "net/http" + "net/url" "github.com/gin-gonic/gin" ) +type tokenBody struct { + ClientID *string `form:"client_id" json:"client_id" xml:"client_id"` + ClientSecret *string `form:"client_secret" json:"client_secret" xml:"client_secret"` + Code *string `form:"code" json:"code" xml:"code"` + GrantType *string `form:"grant_type" json:"grant_type" xml:"grant_type"` + RedirectURI *string `form:"redirect_uri" json:"redirect_uri" xml:"redirect_uri"` +} + // TokenPOSTHandler should be served as a POST at https://example.org/oauth/token // The idea here is to serve an oauth access token to a user, which can be used for authorizing against non-public APIs. // See https://docs.joinmastodon.org/methods/apps/oauth/#obtain-a-token func (m *Module) TokenPOSTHandler(c *gin.Context) { l := m.log.WithField("func", "TokenPOSTHandler") l.Trace("entered TokenPOSTHandler") + + form := &tokenBody{} + if err := c.ShouldBind(form); err == nil { + c.Request.Form = url.Values{} + if form.ClientID != nil { + c.Request.Form.Set("client_id", *form.ClientID) + } + if form.ClientSecret != nil { + c.Request.Form.Set("client_secret", *form.ClientSecret) + } + if form.Code != nil { + c.Request.Form.Set("code", *form.Code) + } + if form.GrantType != nil { + c.Request.Form.Set("grant_type", *form.GrantType) + } + if form.RedirectURI != nil { + c.Request.Form.Set("redirect_uri", *form.RedirectURI) + } + } + if err := m.server.HandleTokenRequest(c.Writer, c.Request); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } diff --git a/internal/api/client/followrequest/accept.go b/internal/api/client/followrequest/accept.go new file mode 100644 index 000000000..45dc1a2af --- /dev/null +++ b/internal/api/client/followrequest/accept.go @@ -0,0 +1,57 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +package followrequest + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/oauth" +) + +// FollowRequestAcceptPOSTHandler deals with follow request accepting. It should be served at +// /api/v1/follow_requests/:id/authorize +func (m *Module) FollowRequestAcceptPOSTHandler(c *gin.Context) { + l := m.log.WithField("func", "statusCreatePOSTHandler") + authed, err := oauth.Authed(c, true, true, true, true) + if err != nil { + l.Debugf("couldn't auth: %s", err) + c.JSON(http.StatusForbidden, gin.H{"error": err.Error()}) + return + } + + if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() { + l.Debugf("couldn't auth: %s", err) + c.JSON(http.StatusForbidden, gin.H{"error": "account is disabled, not yet approved, or suspended"}) + return + } + + originAccountID := c.Param(IDKey) + if originAccountID == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "no follow request origin account id provided"}) + return + } + + if errWithCode := m.processor.FollowRequestAccept(authed, originAccountID); errWithCode != nil { + l.Debug(errWithCode.Error()) + c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()}) + return + } + c.Status(http.StatusOK) +} diff --git a/internal/api/client/followrequest/deny.go b/internal/api/client/followrequest/deny.go new file mode 100644 index 000000000..c1a9e4dbf --- /dev/null +++ b/internal/api/client/followrequest/deny.go @@ -0,0 +1,27 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +package followrequest + +import "github.com/gin-gonic/gin" + +// FollowRequestDenyPOSTHandler deals with follow request rejection. It should be served at +// /api/v1/follow_requests/:id/reject +func (m *Module) FollowRequestDenyPOSTHandler(c *gin.Context) { + +} diff --git a/internal/api/client/followrequest/followrequest.go b/internal/api/client/followrequest/followrequest.go new file mode 100644 index 000000000..8be957009 --- /dev/null +++ b/internal/api/client/followrequest/followrequest.go @@ -0,0 +1,68 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +package followrequest + +import ( + "net/http" + + "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" + "github.com/superseriousbusiness/gotosocial/internal/config" + "github.com/superseriousbusiness/gotosocial/internal/message" + "github.com/superseriousbusiness/gotosocial/internal/router" +) + +const ( + // IDKey is for status UUIDs + IDKey = "id" + // BasePath is the base path for serving the follow request API + BasePath = "/api/v1/follow_requests" + // BasePathWithID is just the base path with the ID key in it. + // Use this anywhere you need to know the ID of the follow request being queried. + BasePathWithID = BasePath + "/:" + IDKey + + // AcceptPath is used for accepting follow requests + AcceptPath = BasePathWithID + "/authorize" + // DenyPath is used for denying follow requests + DenyPath = BasePathWithID + "/reject" +) + +// Module implements the ClientAPIModule interface for every related to interacting with follow requests +type Module struct { + config *config.Config + processor message.Processor + log *logrus.Logger +} + +// New returns a new follow request module +func New(config *config.Config, processor message.Processor, log *logrus.Logger) api.ClientModule { + return &Module{ + config: config, + processor: processor, + log: log, + } +} + +// Route attaches all routes from this module to the given router +func (m *Module) Route(r router.Router) error { + r.AttachHandler(http.MethodGet, BasePath, m.FollowRequestGETHandler) + r.AttachHandler(http.MethodPost, AcceptPath, m.FollowRequestAcceptPOSTHandler) + r.AttachHandler(http.MethodPost, DenyPath, m.FollowRequestDenyPOSTHandler) + return nil +} diff --git a/internal/api/client/followrequest/get.go b/internal/api/client/followrequest/get.go new file mode 100644 index 000000000..3f02ee02a --- /dev/null +++ b/internal/api/client/followrequest/get.go @@ -0,0 +1,51 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +package followrequest + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/oauth" +) + +// FollowRequestGETHandler allows clients to get a list of their incoming follow requests. +func (m *Module) FollowRequestGETHandler(c *gin.Context) { + l := m.log.WithField("func", "statusCreatePOSTHandler") + authed, err := oauth.Authed(c, true, true, true, true) + if err != nil { + l.Debugf("couldn't auth: %s", err) + c.JSON(http.StatusForbidden, gin.H{"error": err.Error()}) + return + } + + if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() { + l.Debugf("couldn't auth: %s", err) + c.JSON(http.StatusForbidden, gin.H{"error": "account is disabled, not yet approved, or suspended"}) + return + } + + accts, errWithCode := m.processor.FollowRequestsGet(authed) + if errWithCode != nil { + c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()}) + return + } + + c.JSON(http.StatusOK, accts) +} diff --git a/internal/api/client/instance/instance.go b/internal/api/client/instance/instance.go index ed7c18718..ba54480a5 100644 --- a/internal/api/client/instance/instance.go +++ b/internal/api/client/instance/instance.go @@ -11,7 +11,7 @@ import ( ) const ( - // InstanceInformationPath + // InstanceInformationPath is for serving instance info requests InstanceInformationPath = "api/v1/instance" ) diff --git a/internal/api/client/instance/instanceget.go b/internal/api/client/instance/instanceget.go index f8e82c096..6ae419a83 100644 --- a/internal/api/client/instance/instanceget.go +++ b/internal/api/client/instance/instanceget.go @@ -6,6 +6,7 @@ import ( "github.com/gin-gonic/gin" ) +// InstanceInformationGETHandler is for serving instance information at /api/v1/instance func (m *Module) InstanceInformationGETHandler(c *gin.Context) { l := m.log.WithField("func", "InstanceInformationGETHandler") diff --git a/internal/api/client/media/media.go b/internal/api/client/media/media.go index e45eec7ea..f68a73c2c 100644 --- a/internal/api/client/media/media.go +++ b/internal/api/client/media/media.go @@ -33,8 +33,10 @@ import ( // BasePath is the base API path for making media requests const BasePath = "/api/v1/media" + // IDKey is the key for media attachment IDs const IDKey = "id" + // BasePathWithID corresponds to a media attachment with the given ID const BasePathWithID = BasePath + "/:" + IDKey diff --git a/internal/api/client/media/mediacreate.go b/internal/api/client/media/mediacreate.go index c0b4a80d7..9f4702b6b 100644 --- a/internal/api/client/media/mediacreate.go +++ b/internal/api/client/media/mediacreate.go @@ -35,30 +35,32 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) { authed, err := oauth.Authed(c, true, true, true, true) // posting new media is serious business so we want *everything* if err != nil { l.Debugf("couldn't auth: %s", err) - c.JSON(http.StatusForbidden, gin.H{"error": err.Error()}) + c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) return } // extract the media create form from the request context l.Tracef("parsing request form: %s", c.Request.Form) - var form model.AttachmentRequest + form := &model.AttachmentRequest{} if err := c.ShouldBind(&form); err != nil { - l.Debugf("could not parse form from request: %s", err) - c.JSON(http.StatusBadRequest, gin.H{"error": "missing one or more required form values"}) + l.Debugf("error parsing form: %s", err) + c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("could not parse form: %s", err)}) return } // Give the fields on the request form a first pass to make sure the request is superficially valid. l.Tracef("validating form %+v", form) - if err := validateCreateMedia(&form, m.config.MediaConfig); err != nil { + if err := validateCreateMedia(form, m.config.MediaConfig); err != nil { l.Debugf("error validating form: %s", err) - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()}) return } - mastoAttachment, err := m.processor.MediaCreate(authed, &form) + l.Debug("calling processor media create func") + mastoAttachment, err := m.processor.MediaCreate(authed, form) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + l.Debugf("error creating attachment: %s", err) + c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()}) return } @@ -67,7 +69,7 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) { func validateCreateMedia(form *model.AttachmentRequest, config *config.MediaConfig) error { // check there actually is a file attached and it's not size 0 - if form.File == nil || form.File.Size == 0 { + if form.File == nil { return errors.New("no attachment given") } diff --git a/internal/api/client/media/mediaget.go b/internal/api/client/media/mediaget.go index 31c40a5aa..7acb475ce 100644 --- a/internal/api/client/media/mediaget.go +++ b/internal/api/client/media/mediaget.go @@ -43,7 +43,7 @@ func (m *Module) MediaGETHandler(c *gin.Context) { attachment, errWithCode := m.processor.MediaGet(authed, attachmentID) if errWithCode != nil { - c.JSON(errWithCode.Code(),gin.H{"error": errWithCode.Safe()}) + c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()}) return } diff --git a/internal/api/model/application.go b/internal/api/model/application.go index a796c88ea..fe9fada03 100644 --- a/internal/api/model/application.go +++ b/internal/api/model/application.go @@ -43,13 +43,13 @@ type Application struct { // And here: https://docs.joinmastodon.org/client/token/ type ApplicationCreateRequest struct { // A name for your application - ClientName string `form:"client_name" binding:"required"` + ClientName string `form:"client_name" json:"client_name" xml:"client_name" binding:"required"` // 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" binding:"required"` + RedirectURIs string `form:"redirect_uris" json:"redirect_uris" xml:"redirect_uris" binding:"required"` // Space separated list of scopes. If none is provided, defaults to read. - Scopes string `form:"scopes"` + Scopes string `form:"scopes" json:"scopes" xml:"scopes"` // A URL to the homepage of your app - Website string `form:"website"` + Website string `form:"website" json:"website" xml:"website"` } diff --git a/internal/api/model/attachment.go b/internal/api/model/attachment.go index c5dbb0cba..ed53757eb 100644 --- a/internal/api/model/attachment.go +++ b/internal/api/model/attachment.go @@ -24,15 +24,15 @@ import "mime/multipart" // See: https://docs.joinmastodon.org/methods/statuses/media/ type AttachmentRequest struct { File *multipart.FileHeader `form:"file" binding:"required"` - Description string `form:"description" json:"description" xml:"description"` - Focus string `form:"focus" json:"focus" xml:"focus"` + Description string `form:"description"` + Focus string `form:"focus"` } -// AttachmentRequest represents the form data parameters submitted by a client during a media update/PUT request. +// AttachmentUpdateRequest represents the form data parameters submitted by a client during a media update/PUT request. // See: https://docs.joinmastodon.org/methods/statuses/media/ type AttachmentUpdateRequest struct { - Description *string `form:"description" json:"description" xml:"description"` - Focus *string `form:"focus" json:"focus" xml:"focus"` + Description *string `form:"description" json:"description" xml:"description"` + Focus *string `form:"focus" json:"focus" xml:"focus"` } // Attachment represents the object returned to a client after a successful media upload request. @@ -63,7 +63,7 @@ type Attachment struct { // See https://docs.joinmastodon.org/methods/statuses/media/#focal-points points for more. Meta MediaMeta `json:"meta,omitempty"` // Alternate text that describes what is in the media attachment, to be used for the visually impaired or when media attachments do not load. - Description string `json:"description"` + Description string `json:"description,omitempty"` // A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet. // See https://github.com/woltapp/blurhash Blurhash string `json:"blurhash,omitempty"` diff --git a/internal/api/model/status.go b/internal/api/model/status.go index 54d021e29..2cb22aa0d 100644 --- a/internal/api/model/status.go +++ b/internal/api/model/status.go @@ -119,11 +119,15 @@ const ( VisibilityDirect Visibility = "direct" ) +// AdvancedStatusCreateForm wraps the mastodon status create form along with the GTS advanced +// visibility settings. type AdvancedStatusCreateForm struct { StatusCreateRequest AdvancedVisibilityFlagsForm } +// AdvancedVisibilityFlagsForm allows a few more advanced flags to be set on new statuses, in addition +// to the standard mastodon-compatible ones. type AdvancedVisibilityFlagsForm struct { // The gotosocial visibility model VisibilityAdvanced *string `form:"visibility_advanced"` diff --git a/internal/api/s2s/user/inboxpost.go b/internal/api/s2s/user/inboxpost.go new file mode 100644 index 000000000..60b74ab70 --- /dev/null +++ b/internal/api/s2s/user/inboxpost.go @@ -0,0 +1,58 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +package user + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/message" +) + +// InboxPOSTHandler deals with incoming POST requests to an actor's inbox. +// Eg., POST to https://example.org/users/whatever/inbox. +func (m *Module) InboxPOSTHandler(c *gin.Context) { + l := m.log.WithFields(logrus.Fields{ + "func": "InboxPOSTHandler", + "url": c.Request.RequestURI, + }) + + requestedUsername := c.Param(UsernameKey) + if requestedUsername == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "no username specified in request"}) + return + } + + posted, err := m.processor.InboxPost(c.Request.Context(), c.Writer, c.Request) + if err != nil { + if withCode, ok := err.(message.ErrorWithCode); ok { + l.Debug(withCode.Error()) + c.JSON(withCode.Code(), withCode.Safe()) + return + } + l.Debug(err) + c.JSON(http.StatusBadRequest, gin.H{"error": "unable to process request"}) + return + } + + if !posted { + c.JSON(http.StatusBadRequest, gin.H{"error": "unable to process request"}) + } +} diff --git a/internal/api/s2s/user/user.go b/internal/api/s2s/user/user.go index 693fac7c3..a6116247d 100644 --- a/internal/api/s2s/user/user.go +++ b/internal/api/s2s/user/user.go @@ -38,6 +38,8 @@ const ( // Use this anywhere you need to know the username of the user being queried. // Eg https://example.org/users/:username UsersBasePathWithUsername = UsersBasePath + "/:" + UsernameKey + // UsersInboxPath is for serving POST requests to a user's inbox with the given username key. + UsersInboxPath = UsersBasePathWithUsername + "/" + util.InboxPath ) // ActivityPubAcceptHeaders represents the Accept headers mentioned here: @@ -66,5 +68,6 @@ func New(config *config.Config, processor message.Processor, log *logrus.Logger) // Route satisfies the RESTAPIModule interface func (m *Module) Route(s router.Router) error { s.AttachHandler(http.MethodGet, UsersBasePathWithUsername, m.UsersGETHandler) + s.AttachHandler(http.MethodPost, UsersInboxPath, m.InboxPOSTHandler) return nil } diff --git a/internal/api/s2s/webfinger/webfinger.go b/internal/api/s2s/webfinger/webfinger.go index c11d3fb61..168fe1e76 100644 --- a/internal/api/s2s/webfinger/webfinger.go +++ b/internal/api/s2s/webfinger/webfinger.go @@ -29,7 +29,7 @@ import ( ) const ( - // The base path for serving webfinger lookup requests + // WebfingerBasePath is the base path for serving webfinger lookup requests WebfingerBasePath = ".well-known/webfinger" ) diff --git a/internal/api/security/extraheaders.go b/internal/api/security/extraheaders.go new file mode 100644 index 000000000..dfcddfbe1 --- /dev/null +++ b/internal/api/security/extraheaders.go @@ -0,0 +1,8 @@ +package security + +import "github.com/gin-gonic/gin" + +// ExtraHeaders adds any additional required headers to the response +func (m *Module) ExtraHeaders(c *gin.Context) { + c.Header("Server", "Mastodon") +} diff --git a/internal/api/security/security.go b/internal/api/security/security.go index c80b568b3..eaae8471e 100644 --- a/internal/api/security/security.go +++ b/internal/api/security/security.go @@ -42,5 +42,6 @@ func New(config *config.Config, log *logrus.Logger) api.ClientModule { // Route attaches security middleware to the given router func (m *Module) Route(s router.Router) error { s.AttachMiddleware(m.FlocBlock) + s.AttachMiddleware(m.ExtraHeaders) return nil } |