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 }