summaryrefslogtreecommitdiff
path: root/internal/api/client/admin/domainpermissionsubscriptionupdate.go
blob: de73c4d3ea0aff2cfe9f844aa438c8b4f165b531 (plain)
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
245
246
247
248
249
250
251
252
253
254
// 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/gtsmodel"
	"github.com/superseriousbusiness/gotosocial/internal/oauth"
	"github.com/superseriousbusiness/gotosocial/internal/util"
)

// DomainPermissionSubscriptionPATCHHandler swagger:operation PATCH /api/v1/admin/domain_permission_subscriptions/${id} domainPermissionSubscriptionUpdate
//
// Update a domain permission subscription with the given parameters.
//
//	---
//	tags:
//	- admin
//
//	consumes:
//	- multipart/form-data
//	- application/json
//
//	produces:
//	- application/json
//
//	parameters:
//	-
//		name: id
//		required: true
//		in: path
//		description: ID of the domain permission subscription.
//		type: string
//	-
//		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
//	-
//		name: title
//		in: formData
//		description: Optional title for this subscription.
//		type: string
//	-
//		name: uri
//		in: formData
//		description: URI to call in order to fetch the permissions list.
//		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: content_type
//		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 updated 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) DomainPermissionSubscriptionPATCHHandler(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
	}

	id, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
	if errWithCode != nil {
		apiutil.ErrorHandler(c, errWithCode, 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
	}

	// Normalize priority if set.
	var priority *uint8
	if form.Priority != nil {
		prioInt := *form.Priority
		if prioInt < 0 || prioInt > 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
		}

		priority = util.Ptr(uint8(prioInt)) // #nosec G115 -- Just validated.
	}

	// Validate URI if set.
	var uriStr *string
	if form.URI != nil {
		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 = util.Ptr(uri.String())
	}

	// Validate content type if set.
	var contentType *gtsmodel.DomainPermSubContentType
	if form.ContentType != nil {
		ct, errWithCode := parseDomainPermSubContentType(*form.ContentType)
		if errWithCode != nil {
			apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
			return
		}

		contentType = &ct
	}

	// Make sure at least one field is set,
	// otherwise we're trying to update nothing.
	if priority == nil &&
		form.Title == nil &&
		uriStr == nil &&
		contentType == nil &&
		form.AsDraft == nil &&
		form.AdoptOrphans == nil &&
		form.FetchUsername == nil &&
		form.FetchPassword == nil {
		const errText = "no updateable fields set on request"
		errWithCode := gtserror.NewErrorBadRequest(errors.New(errText), errText)
		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
		return
	}

	permSub, errWithCode := m.processor.Admin().DomainPermissionSubscriptionUpdate(
		c.Request.Context(),
		id,
		priority,
		form.Title,
		uriStr,
		contentType,
		form.AsDraft,
		form.AdoptOrphans,
		form.FetchUsername,
		form.FetchPassword,
	)
	if errWithCode != nil {
		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
		return
	}

	apiutil.JSON(c, http.StatusOK, permSub)
}