gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

domainblockcreate.go (6327B)


      1 // GoToSocial
      2 // Copyright (C) GoToSocial Authors admin@gotosocial.org
      3 // SPDX-License-Identifier: AGPL-3.0-or-later
      4 //
      5 // This program is free software: you can redistribute it and/or modify
      6 // it under the terms of the GNU Affero General Public License as published by
      7 // the Free Software Foundation, either version 3 of the License, or
      8 // (at your option) any later version.
      9 //
     10 // This program is distributed in the hope that it will be useful,
     11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 // GNU Affero General Public License for more details.
     14 //
     15 // You should have received a copy of the GNU Affero General Public License
     16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18 package admin
     19 
     20 import (
     21 	"errors"
     22 	"fmt"
     23 	"net/http"
     24 	"strconv"
     25 
     26 	"github.com/gin-gonic/gin"
     27 	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
     28 	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
     29 	"github.com/superseriousbusiness/gotosocial/internal/gtserror"
     30 	"github.com/superseriousbusiness/gotosocial/internal/oauth"
     31 )
     32 
     33 // DomainBlocksPOSTHandler swagger:operation POST /api/v1/admin/domain_blocks domainBlockCreate
     34 //
     35 // Create one or more domain blocks, from a string or a file.
     36 //
     37 // You have two options when using this endpoint: either you can set `import` to `true` and
     38 // upload a file containing multiple domain blocks, JSON-formatted, or you can leave import as
     39 // `false`, and just add one domain block.
     40 //
     41 // The format of the json file should be something like: `[{"domain":"example.org"},{"domain":"whatever.com","public_comment":"they smell"}]`
     42 //
     43 //	---
     44 //	tags:
     45 //	- admin
     46 //
     47 //	consumes:
     48 //	- multipart/form-data
     49 //
     50 //	produces:
     51 //	- application/json
     52 //
     53 //	parameters:
     54 //	-
     55 //		name: import
     56 //		in: query
     57 //		description: >-
     58 //			Signal that a list of domain blocks is being imported as a file.
     59 //			If set to `true`, then 'domains' must be present as a JSON-formatted file.
     60 //			If set to `false`, then `domains` will be ignored, and `domain` must be present.
     61 //		type: boolean
     62 //		default: false
     63 //	-
     64 //		name: domains
     65 //		in: formData
     66 //		description: >-
     67 //			JSON-formatted list of domain blocks to import.
     68 //			This is only used if `import` is set to `true`.
     69 //		type: file
     70 //	-
     71 //		name: domain
     72 //		in: formData
     73 //		description: >-
     74 //			Single domain to block.
     75 //			Used only if `import` is not `true`.
     76 //		type: string
     77 //	-
     78 //		name: obfuscate
     79 //		in: formData
     80 //		description: >-
     81 //			Obfuscate the name of the domain when serving it publicly.
     82 //			Eg., `example.org` becomes something like `ex***e.org`.
     83 //			Used only if `import` is not `true`.
     84 //		type: boolean
     85 //	-
     86 //		name: public_comment
     87 //		in: formData
     88 //		description: >-
     89 //			Public comment about this domain block.
     90 //			This will be displayed alongside the domain block if you choose to share blocks.
     91 //			Used only if `import` is not `true`.
     92 //		type: string
     93 //	-
     94 //		name: private_comment
     95 //		in: formData
     96 //		description: >-
     97 //			Private comment about this domain block. Will only be shown to other admins, so this
     98 //			is a useful way of internally keeping track of why a certain domain ended up blocked.
     99 //			Used only if `import` is not `true`.
    100 //		type: string
    101 //
    102 //	security:
    103 //	- OAuth2 Bearer:
    104 //		- admin
    105 //
    106 //	responses:
    107 //		'200':
    108 //			description: >-
    109 //				The newly created domain block, if `import` != `true`.
    110 //				If a list has been imported, then an `array` of newly created domain blocks will be returned instead.
    111 //			schema:
    112 //				"$ref": "#/definitions/domainBlock"
    113 //		'400':
    114 //			description: bad request
    115 //		'401':
    116 //			description: unauthorized
    117 //		'403':
    118 //			description: forbidden
    119 //		'404':
    120 //			description: not found
    121 //		'406':
    122 //			description: not acceptable
    123 //		'500':
    124 //			description: internal server error
    125 func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
    126 	authed, err := oauth.Authed(c, true, true, true, true)
    127 	if err != nil {
    128 		apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
    129 		return
    130 	}
    131 
    132 	if !*authed.User.Admin {
    133 		err := fmt.Errorf("user %s not an admin", authed.User.ID)
    134 		apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
    135 		return
    136 	}
    137 
    138 	if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
    139 		apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
    140 		return
    141 	}
    142 
    143 	imp := false
    144 	importString := c.Query(ImportQueryKey)
    145 	if importString != "" {
    146 		i, err := strconv.ParseBool(importString)
    147 		if err != nil {
    148 			err := fmt.Errorf("error parsing %s: %s", ImportQueryKey, err)
    149 			apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
    150 			return
    151 		}
    152 		imp = i
    153 	}
    154 
    155 	form := &apimodel.DomainBlockCreateRequest{}
    156 	if err := c.ShouldBind(form); err != nil {
    157 		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
    158 		return
    159 	}
    160 
    161 	if err := validateCreateDomainBlock(form, imp); err != nil {
    162 		err := fmt.Errorf("error validating form: %s", err)
    163 		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
    164 		return
    165 	}
    166 
    167 	if imp {
    168 		// we're importing multiple blocks
    169 		domainBlocks, errWithCode := m.processor.Admin().DomainBlocksImport(c.Request.Context(), authed.Account, form.Domains)
    170 		if errWithCode != nil {
    171 			apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
    172 			return
    173 		}
    174 		c.JSON(http.StatusOK, domainBlocks)
    175 		return
    176 	}
    177 
    178 	// we're just creating one block
    179 	domainBlock, errWithCode := m.processor.Admin().DomainBlockCreate(c.Request.Context(), authed.Account, form.Domain, form.Obfuscate, form.PublicComment, form.PrivateComment, "")
    180 	if errWithCode != nil {
    181 		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
    182 		return
    183 	}
    184 	c.JSON(http.StatusOK, domainBlock)
    185 }
    186 
    187 func validateCreateDomainBlock(form *apimodel.DomainBlockCreateRequest, imp bool) error {
    188 	if imp {
    189 		if form.Domains.Size == 0 {
    190 			return errors.New("import was specified but list of domains is empty")
    191 		}
    192 	} else {
    193 		// add some more validation here later if necessary
    194 		if form.Domain == "" {
    195 			return errors.New("empty domain provided")
    196 		}
    197 	}
    198 
    199 	return nil
    200 }