mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-01-22 21:38:11 -05:00
OAuth2: Expose /api/friend for mod tasks
Note: While I'd have preferred to do a "nice" endpoint like what I did with user friends, there's been enough delay in exposing /api/friend functionality to OAuth2 consumers that I decided it would be best to take the less clean solution.
This commit is contained in:
@@ -725,6 +725,32 @@ class ApiController(RedditController):
|
||||
'moderator_invite',
|
||||
)
|
||||
|
||||
# Changes to this dict should also update docstrings for
|
||||
# POST_friend and POST_unfriend
|
||||
api_friend_scope_map = {
|
||||
'moderator': {"modothers"},
|
||||
'moderator_invite': {"modothers"},
|
||||
'contributor': {"modcontributors"},
|
||||
'banned': {"modcontributors"},
|
||||
'wikibanned': {"modcontributors", "modwiki"},
|
||||
'wikicontributor': {"modcontributors", "modwiki"},
|
||||
'friend': None, # Handled with API v1 endpoint
|
||||
'enemy': {"privatemessages"}, # Only valid for POST_unfriend
|
||||
}
|
||||
|
||||
def check_api_friend_oauth_scope(self, type_):
|
||||
if c.oauth_user:
|
||||
needed_scopes = self.api_friend_scope_map[type_]
|
||||
if needed_scopes is None:
|
||||
# OAuth2 access not allowed for this friend rel type
|
||||
# via /api/friend
|
||||
self._auth_error(400, "invalid_request")
|
||||
if not c.oauth_scope.has_access(c.site.name, needed_scopes):
|
||||
# Token does not have the necessary scope to complete
|
||||
# this request.
|
||||
self._auth_error(403, "insufficient_scope")
|
||||
|
||||
@allow_oauth2_access
|
||||
@noresponse(VUser(),
|
||||
VModhash(),
|
||||
nuser = VExistingUname('name'),
|
||||
@@ -732,15 +758,33 @@ class ApiController(RedditController):
|
||||
container = nop('container'),
|
||||
type = VOneOf('type', ('friend', 'enemy') +
|
||||
_sr_friend_types))
|
||||
@api_doc(api_section.users)
|
||||
@api_doc(api_section.users, uses_site=True)
|
||||
def POST_unfriend(self, nuser, iuser, container, type):
|
||||
"""Remove a relationship between a user and another user or subreddit
|
||||
|
||||
The user can either be passed in by name (nuser)
|
||||
or by [fullname](#fullnames) (iuser). If type is friend or enemy,
|
||||
'container' MUST be the current user's fullname;
|
||||
for other types, the subreddit must be set
|
||||
via URL (e.g., /r/funny/api/unfriend)
|
||||
|
||||
OAuth2 use requires appropriate scope based
|
||||
on the 'type' of the relationship:
|
||||
|
||||
* moderator: `modothers`
|
||||
* moderator_invite: `modothers`
|
||||
* contributor: `modcontributors`
|
||||
* banned: `modcontributors`
|
||||
* wikibanned: `modcontributors` and `modwiki`
|
||||
* wikicontributor: `modcontributors` and `modwiki`
|
||||
* friend: Use [/api/v1/me/friends/{username}](#DELETE_api_v1_me_friends_{username})
|
||||
* enemy: `privatemessages`
|
||||
|
||||
Complement to [POST_friend](#POST_api_friend)
|
||||
|
||||
"""
|
||||
Handles removal of a friend (a user-user relation) or removal
|
||||
of a user's privileges from a subreddit (a user-subreddit
|
||||
relation). The user can either be passed in by name (nuser)
|
||||
or by fullname (iuser). If type is friend or enemy, 'container'
|
||||
will be the current user, otherwise the subreddit must be set.
|
||||
"""
|
||||
self.check_api_friend_oauth_scope(type)
|
||||
|
||||
if type in self._sr_friend_types:
|
||||
if isinstance(c.site, FakeSubreddit):
|
||||
abort(403, 'forbidden')
|
||||
@@ -834,6 +878,7 @@ class ApiController(RedditController):
|
||||
editor = row.find('.permissions').data('PermissionEditor')
|
||||
editor.onCommit(update)
|
||||
|
||||
@allow_oauth2_access
|
||||
@validatedForm(VUser(),
|
||||
VModhash(),
|
||||
friend = VExistingUname('name'),
|
||||
@@ -845,14 +890,29 @@ class ApiController(RedditController):
|
||||
ban_message = VMarkdownLength('ban_message', max_length=1000,
|
||||
empty_error=None),
|
||||
)
|
||||
@api_doc(api_section.users)
|
||||
@api_doc(api_section.users, uses_site=True)
|
||||
def POST_friend(self, form, jquery, friend,
|
||||
container, type, type_and_permissions, note, duration,
|
||||
ban_message):
|
||||
"""Create a relationship between a user and another user or subreddit
|
||||
|
||||
OAuth2 use requires appropriate scope based
|
||||
on the 'type' of the relationship:
|
||||
|
||||
* moderator: Use "moderator_invite"
|
||||
* moderator_invite: `modothers`
|
||||
* contributor: `modcontributors`
|
||||
* banned: `modcontributors`
|
||||
* wikibanned: `modcontributors` and `modwiki`
|
||||
* wikicontributor: `modcontributors` and `modwiki`
|
||||
* friend: Use [/api/v1/me/friends/{username}](#PUT_api_v1_me_friends_{username})
|
||||
* enemy: Use [/api/block](#POST_api_block)
|
||||
|
||||
Complement to [POST_unfriend](#POST_api_unfriend)
|
||||
|
||||
"""
|
||||
Complement to POST_unfriend: handles friending as well as
|
||||
privilege changes on subreddits.
|
||||
"""
|
||||
self.check_api_friend_oauth_scope(type)
|
||||
|
||||
if type in self._sr_friend_types:
|
||||
if isinstance(c.site, FakeSubreddit):
|
||||
abort(403, 'forbidden')
|
||||
|
||||
@@ -143,6 +143,14 @@ class OAuth2Scope:
|
||||
"name": _("My Identity"),
|
||||
"description": _("Access my reddit username and signup date."),
|
||||
},
|
||||
"modcontributors": {
|
||||
"id": "modcontributors",
|
||||
"name": _("Approve submitters and ban users"),
|
||||
"description": _(
|
||||
"Add/remove users to approved submitter lists and "
|
||||
"ban/unban users from subreddits I moderate."
|
||||
),
|
||||
},
|
||||
"modflair": {
|
||||
"id": "modflair",
|
||||
"name": _("Moderate Flair"),
|
||||
@@ -169,6 +177,13 @@ class OAuth2Scope:
|
||||
"description": _(
|
||||
"Access the moderation log in subreddits I moderate."),
|
||||
},
|
||||
"modothers": {
|
||||
"id": "modothers",
|
||||
"name": _("Invite or remove other moderators"),
|
||||
"description": _(
|
||||
"Invite or remove other moderators from subreddits I moderate."
|
||||
),
|
||||
},
|
||||
"modtraffic": {
|
||||
"id": "modtraffic",
|
||||
"name": _("Subreddit Traffic"),
|
||||
|
||||
Reference in New Issue
Block a user