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-05-08 14:25:55 +02:00
2023-01-02 13:10:50 +01:00
package apps
2021-05-08 14:25:55 +02:00
import (
2025-03-10 18:35:08 +01:00
"errors"
2021-05-08 14:25:55 +02:00
"fmt"
"net/http"
2025-03-10 18:35:08 +01:00
"slices"
"strings"
2021-05-08 14:25:55 +02:00
"github.com/gin-gonic/gin"
2023-01-02 13:10:50 +01:00
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
2022-06-08 20:38:03 +02:00
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
2021-05-08 14:25:55 +02:00
)
2022-06-08 20:38:03 +02:00
// these consts are used to ensure users can't spam huge entries into our database
2021-11-22 08:46:19 +01:00
const (
2022-11-05 11:06:50 +01:00
formFieldLen = 1024
formRedirectLen = 2056
2021-11-22 08:46:19 +01:00
)
2021-08-02 19:06:44 +02:00
// AppsPOSTHandler swagger:operation POST /api/v1/apps appCreate
//
// Register a new application on this instance.
//
// The registered application can be used to obtain an application token.
// This can then be used to register a new account, or (through user auth) obtain an access token.
//
2025-03-10 18:35:08 +01:00
// If the application was registered with a Bearer token passed in the Authorization header, the created application will be managed by the authenticated user (must have scope write:applications).
//
// Parameters can also be given in the body of the request, as JSON, if the content-type is set to 'application/json'.
// Parameters can also be given in the body of the request, as XML, if the content-type is set to 'application/xml'.
2021-08-02 19:06:44 +02:00
//
2022-09-28 19:30:40 +02:00
// ---
// tags:
// - apps
2021-08-02 19:06:44 +02:00
//
2022-09-28 19:30:40 +02:00
// consumes:
// - application/json
// - application/xml
// - application/x-www-form-urlencoded
2021-08-02 19:06:44 +02:00
//
2022-09-28 19:30:40 +02:00
// produces:
// - application/json
2021-08-02 19:06:44 +02:00
//
2022-09-28 19:30:40 +02:00
// responses:
// '200':
// description: "The newly-created application."
// schema:
// "$ref": "#/definitions/application"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '404':
// description: not found
// '406':
// description: not acceptable
// '500':
// description: internal server error
2021-05-08 14:25:55 +02:00
func ( m * Module ) AppsPOSTHandler ( c * gin . Context ) {
2025-02-26 13:04:55 +01:00
authed , errWithCode := apiutil . TokenAuth ( c ,
false , false , false , false ,
)
if errWithCode != nil {
apiutil . ErrorHandler ( c , errWithCode , m . processor . InstanceGetV1 )
2021-05-08 14:25:55 +02:00
return
}
2025-03-10 18:35:08 +01:00
if authed . Token != nil {
// If a token has been passed, user
// needs write perm on applications.
if ! slices . ContainsFunc (
strings . Split ( authed . Token . GetScope ( ) , " " ) ,
func ( hasScope string ) bool {
return apiutil . Scope ( hasScope ) . Permits ( apiutil . ScopeWriteApplications )
} ,
) {
const errText = "token has insufficient scope permission"
errWithCode := gtserror . NewErrorForbidden ( errors . New ( errText ) , errText )
apiutil . ErrorHandler ( c , errWithCode , m . processor . InstanceGetV1 )
return
}
}
if authed . Account != nil && authed . Account . IsMoving ( ) {
apiutil . ForbiddenAfterMove ( c )
return
}
2023-01-02 13:10:50 +01:00
if _ , err := apiutil . NegotiateAccept ( c , apiutil . JSONAcceptHeaders ... ) ; err != nil {
2025-03-10 18:35:08 +01:00
errWithCode := gtserror . NewErrorNotAcceptable ( err , err . Error ( ) )
apiutil . ErrorHandler ( c , errWithCode , m . processor . InstanceGetV1 )
2021-12-11 17:50:00 +01:00
return
}
2023-01-02 13:10:50 +01:00
form := & apimodel . ApplicationCreateRequest { }
2021-05-08 14:25:55 +02:00
if err := c . ShouldBind ( form ) ; err != nil {
2025-03-10 18:35:08 +01:00
errWithCode := gtserror . NewErrorBadRequest ( err , err . Error ( ) )
apiutil . ErrorHandler ( c , errWithCode , m . processor . InstanceGetV1 )
2021-05-08 14:25:55 +02:00
return
}
2025-03-10 18:35:08 +01:00
if l := len ( [ ] rune ( form . ClientName ) ) ; l > formFieldLen {
m . fieldTooLong ( c , "client_name" , formFieldLen , l )
2021-05-08 14:25:55 +02:00
return
}
2022-06-08 20:38:03 +02:00
2025-03-10 18:35:08 +01:00
if l := len ( [ ] rune ( form . RedirectURIs ) ) ; l > formRedirectLen {
m . fieldTooLong ( c , "redirect_uris" , formRedirectLen , l )
2021-05-08 14:25:55 +02:00
return
}
2022-06-08 20:38:03 +02:00
2025-03-10 18:35:08 +01:00
if l := len ( [ ] rune ( form . Scopes ) ) ; l > formFieldLen {
m . fieldTooLong ( c , "scopes" , formFieldLen , l )
2021-05-08 14:25:55 +02:00
return
}
2025-03-10 18:35:08 +01:00
if l := len ( [ ] rune ( form . Website ) ) ; l > formFieldLen {
m . fieldTooLong ( c , "website" , formFieldLen , l )
2022-06-08 20:38:03 +02:00
return
}
2025-03-10 18:35:08 +01:00
var managedByUserID string
if authed . User != nil {
managedByUserID = authed . User . ID
}
apiApp , errWithCode := m . processor . Application ( ) . Create ( c . Request . Context ( ) , managedByUserID , form )
2022-06-08 20:38:03 +02:00
if errWithCode != nil {
2023-02-02 14:08:13 +01:00
apiutil . ErrorHandler ( c , errWithCode , m . processor . InstanceGetV1 )
2021-05-08 14:25:55 +02:00
return
}
2023-11-27 15:00:57 +01:00
apiutil . JSON ( c , http . StatusOK , apiApp )
2021-05-08 14:25:55 +02:00
}
2025-03-10 18:35:08 +01:00
func ( m * Module ) fieldTooLong ( c * gin . Context , fieldName string , max int , actual int ) {
errText := fmt . Sprintf (
"%s must be less than %d characters, provided %s was %d characters" ,
fieldName , max , fieldName , actual ,
)
errWithCode := gtserror . NewErrorBadRequest ( errors . New ( errText ) , errText )
apiutil . ErrorHandler ( c , errWithCode , m . processor . InstanceGetV1 )
}