1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
|
// 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/>.
package admin
import (
"errors"
"fmt"
"net/http"
"net/url"
"github.com/gin-gonic/gin"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
// DomainPermissionSubscriptionPOSTHandler swagger:operation POST /api/v1/admin/domain_permission_subscriptions domainPermissionSubscriptionCreate
//
// Create a domain permission subscription with the given parameters.
//
// ---
// tags:
// - admin
//
// consumes:
// - multipart/form-data
// - application/json
//
// produces:
// - application/json
//
// parameters:
// -
// name: priority
// in: formData
// description: >-
// Priority of this subscription compared to others of the same permission type.
// 0-255 (higher = higher priority). Higher priority subscriptions will overwrite
// permissions generated by lower priority subscriptions. When two subscriptions
// have the same `priority` value, priority is indeterminate, so it's recommended
// to always set this value manually.
// type: number
// minimum: 0
// maximum: 255
// default: 0
// -
// name: title
// in: formData
// description: Optional title for this subscription.
// type: string
// -
// name: permission_type
// required: true
// in: formData
// description: >-
// Type of permissions to create by parsing the targeted file/list.
// One of "allow" or "block".
// type: string
// -
// name: as_draft
// in: formData
// description: >-
// If true, domain permissions arising from this subscription will be
// created as drafts that must be approved by a moderator to take effect.
// If false, domain permissions from this subscription will come into force immediately.
// Defaults to "true".
// type: boolean
// default: true
// -
// name: adopt_orphans
// in: formData
// description: >-
// If true, this domain permission subscription will "adopt" domain permissions
// which already exist on the instance, and which meet the following conditions:
// 1) they have no subscription ID (ie., they're "orphaned") and 2) they are present
// in the subscribed list. Such orphaned domain permissions will be given this
// subscription's subscription ID value and be managed by this subscription.
// type: boolean
// default: false
// -
// name: uri
// required: true
// in: formData
// description: URI to call in order to fetch the permissions list.
// type: string
// -
// name: content_type
// required: true
// in: formData
// description: >-
// MIME content type to use when parsing the permissions list.
// One of "text/plain", "text/csv", and "application/json".
// type: string
// -
// name: fetch_username
// in: formData
// description: >-
// Optional basic auth username to provide when fetching given uri.
// If set, will be transmitted along with `fetch_password` when doing the fetch.
// type: string
// -
// name: fetch_password
// in: formData
// description: >-
// Optional basic auth password to provide when fetching given uri.
// If set, will be transmitted along with `fetch_username` when doing the fetch.
// type: string
//
// security:
// - OAuth2 Bearer:
// - admin
//
// responses:
// '200':
// description: The newly created domain permission subscription.
// schema:
// "$ref": "#/definitions/domainPermissionSubscription"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '406':
// description: not acceptable
// '409':
// description: conflict
// '500':
// description: internal server error
func (m *Module) DomainPermissionSubscriptionPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return
}
// Parse + validate form.
form := new(apimodel.DomainPermissionSubscriptionRequest)
if err := c.ShouldBind(form); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
return
}
// Check priority.
// Default to 0.
priority := util.PtrOrZero(form.Priority)
if priority < 0 || priority > 255 {
const errText = "priority must be a number in the range 0 to 255"
errWithCode := gtserror.NewErrorBadRequest(errors.New(errText), errText)
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
// Ensure URI is set.
if form.URI == nil {
const errText = "uri must be set"
errWithCode := gtserror.NewErrorBadRequest(errors.New(errText), errText)
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
// Ensure URI is parseable.
uri, err := url.Parse(*form.URI)
if err != nil {
err := fmt.Errorf("invalid uri provided: %w", err)
errWithCode := gtserror.NewErrorBadRequest(err, err.Error())
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
// Normalize URI by converting back to string.
uriStr := uri.String()
// Content type must be set.
contentTypeStr := util.PtrOrZero(form.ContentType)
contentType, errWithCode := parseDomainPermSubContentType(contentTypeStr)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
// Permission type must be set.
permTypeStr := util.PtrOrZero(form.PermissionType)
permType, errWithCode := parseDomainPermissionType(permTypeStr)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
// Default `as_draft` to true.
asDraft := util.PtrOrValue(form.AsDraft, true)
permSub, errWithCode := m.processor.Admin().DomainPermissionSubscriptionCreate(
c.Request.Context(),
authed.Account,
uint8(priority), // #nosec G115 -- Validated above.
util.PtrOrZero(form.Title), // Optional.
uriStr,
contentType,
permType,
asDraft,
util.PtrOrZero(form.FetchUsername), // Optional.
util.PtrOrZero(form.FetchPassword), // Optional.
)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
apiutil.JSON(c, http.StatusOK, permSub)
}
|