Skip to content
This repository was archived by the owner on Mar 15, 2018. It is now read-only.

Commit 0746728

Browse files
author
Andy McKay
committed
add in permissions api (bug 868252)
1 parent 66b8f18 commit 0746728

File tree

5 files changed

+117
-27
lines changed

5 files changed

+117
-27
lines changed

docs/api/topics/accounts.rst

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ User accounts on the Firefox Marketplace.
99
Account
1010
=======
1111

12-
The account API, makes use of the term `mine`. This is an explicit variable to
12+
.. note:: Requires authentication.
13+
14+
The account API, makes use of the term ``mine``. This is an explicit variable to
1315
lookup the logged in user account id.
1416

1517
.. http:get:: /api/v1/account/settings/mine/
1618
1719
Returns data on the currently logged in user.
1820

19-
.. note:: Requires authentication.
20-
2121
**Response**
2222

2323
.. code-block:: json
@@ -27,9 +27,6 @@ lookup the logged in user account id.
2727
"display_name": "Nice person",
2828
}
2929
30-
The same information is also accessible at the canoncial `resource_uri`
31-
`/api/v1/account/settings/1/`.
32-
3330
To update account information:
3431

3532
.. http:patch:: /api/v1/account/settings/mine/
@@ -53,8 +50,6 @@ Fields that can be updated:
5350
Returns a list of the installed apps for the currently logged in user. This
5451
ignores any reviewer or developer installed apps.
5552

56-
.. note:: Requires authentication.
57-
5853
**Request**
5954

6055
The standard :ref:`list-query-params-label`.
@@ -65,6 +60,32 @@ Fields that can be updated:
6560
:param objects: A :ref:`listing <objects-response-label>` of :ref:`apps <app-response-label>`.
6661
:status 200: sucessfully completed.
6762

63+
.. _permission-get-label:
64+
65+
.. http:get:: /api/v1/account/permissions/mine/
66+
67+
Returns a mapping of the permissions for the currently logged in user.
68+
69+
**Response**
70+
71+
.. code-block:: json
72+
73+
{
74+
"permissions": {
75+
"admin": false,
76+
"developer": false,
77+
"localizer": false,
78+
"lookup": true,
79+
"reviewer": false
80+
},
81+
"resource_uri": "/api/v1/account/permissions/1/"
82+
}
83+
84+
:param permissions: permissions and properties for the user account. It
85+
contains boolean values which describe whether the user has the
86+
permission described by the key of the field.
87+
:status 200: sucessfully completed.
88+
6889
Feedback
6990
========
7091

docs/api/topics/authentication.rst

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ stored as a cookie.
3333
3434
**Response**
3535

36-
:param error: any error that occurred.
37-
:param token: a shared secret to be used on later requests. It should be
36+
:param string error: any error that occurred.
37+
:param string token: a shared secret to be used on later requests. It should be
3838
sent with authorized requests as a query string parameter named
3939
``_user``.
40-
:param settings: user account settings.
40+
:param object permissions: :ref:`user permissions <permission-get-label>`.
41+
:param object settings: user account settings.
4142

4243
Example:
4344

@@ -50,6 +51,13 @@ stored as a cookie.
5051
"display_name": "fred foobar",
5152
"email": "ffoob@example.com",
5253
"region": "appistan"
54+
},
55+
"permissions": {
56+
"reviewer": false,
57+
"admin": false,
58+
"localizer": false,
59+
"lookup": true,
60+
"developer": true
5361
}
5462
}
5563

mkt/account/api.py

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
from django_browserid import get_audience
88
from tastypie import fields, http
99
from tastypie.authorization import Authorization
10+
from tastypie.bundle import Bundle
1011
from tastypie.exceptions import ImmediateHttpResponse
1112
from tastypie.throttle import CacheThrottle
1213

14+
from access import acl
1315
from amo.utils import send_mail_jinja
1416
from mkt.api.authentication import (OAuthAuthentication,
1517
OptionalOAuthAuthentication,
@@ -27,7 +29,20 @@
2729
from .forms import FeedbackForm, LoginForm
2830

2931

30-
class AccountResource(CORSResource, MarketplaceModelResource):
32+
class Mine(object):
33+
34+
def obj_get(self, request=None, **kwargs):
35+
if kwargs.get('pk') == 'mine':
36+
kwargs['pk'] = request.amo_user.pk
37+
38+
# TODO: put in acl checks for admins to get other users information.
39+
obj = super(Mine, self).obj_get(request=request, **kwargs)
40+
if not OwnerAuthorization().is_authorized(request, object=obj):
41+
raise ImmediateHttpResponse(response=http.HttpForbidden())
42+
return obj
43+
44+
45+
class AccountResource(Mine, CORSResource, MarketplaceModelResource):
3146

3247
class Meta(MarketplaceModelResource.Meta):
3348
authentication = (SharedSecretAuthentication(), OAuthAuthentication())
@@ -38,15 +53,28 @@ class Meta(MarketplaceModelResource.Meta):
3853
queryset = UserProfile.objects.filter()
3954
resource_name = 'settings'
4055

41-
def obj_get(self, request=None, **kwargs):
42-
if kwargs.get('pk') == 'mine':
43-
kwargs['pk'] = request.amo_user.pk
4456

45-
# TODO: put in acl checks for admins to get other users information.
46-
obj = super(AccountResource, self).obj_get(request=request, **kwargs)
47-
if not OwnerAuthorization().is_authorized(request, object=obj):
48-
raise ImmediateHttpResponse(response=http.HttpForbidden())
49-
return obj
57+
class PermissionResource(Mine, CORSResource, MarketplaceModelResource):
58+
59+
class Meta(MarketplaceModelResource.Meta):
60+
authentication = (SharedSecretAuthentication(), OAuthAuthentication())
61+
authorization = OwnerAuthorization()
62+
detail_allowed_methods = ['get']
63+
list_allowed_methods = []
64+
fields = ['resource_uri']
65+
queryset = UserProfile.objects.filter()
66+
resource_name = 'permissions'
67+
68+
def dehydrate(self, bundle):
69+
permissions = {
70+
'reviewer': acl.check_reviewer(bundle.request),
71+
'admin': acl.action_allowed(bundle.request, 'Admin', '%'),
72+
'localizer': acl.action_allowed(bundle.request, 'Localizers', '%'),
73+
'lookup': acl.action_allowed(bundle.request, 'AccountLookup', '%'),
74+
'developer': bundle.request.amo_user.is_app_developer
75+
}
76+
bundle.data['permissions'] = permissions
77+
return bundle
5078

5179

5280
class InstalledResource(AppResource):
@@ -65,6 +93,7 @@ def obj_get_list(self, request=None, **kwargs):
6593

6694

6795
class LoginResource(CORSResource, MarketplaceResource):
96+
6897
class Meta(MarketplaceResource.Meta):
6998
resource_name = 'login'
7099
always_return_data = True
@@ -97,15 +126,18 @@ def post_list(self, request, **kwargs):
97126

98127
res = browserid_login(request, browserid_audience=audience)
99128
if res.status_code == 200:
100-
return self.create_response(request, {
129+
request.amo_user = request.user.get_profile()
130+
result = {
101131
'error': None,
102132
'token': self.get_token(request.user.email),
103133
'settings': {
104-
'display_name': (UserProfile.objects
105-
.get(user=request.user).display_name),
134+
'display_name': request.amo_user.display_name,
106135
'email': request.user.email,
107136
}
108-
})
137+
}
138+
result.update(PermissionResource()
139+
.dehydrate(Bundle(request=request)).data)
140+
return self.create_response(request, result)
109141
return res
110142

111143

mkt/account/tests/test_api.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
from django.core import mail
77

88
from mock import patch
9-
from nose.tools import eq_
9+
from nose.tools import eq_, ok_
1010

1111
from amo.tests import TestCase
1212
from mkt.account.api import FeedbackResource
13-
from mkt.api.base import list_url, get_url
13+
from mkt.api.base import get_url, list_url
1414
from mkt.api.tests.test_oauth import BaseOAuth, get_absolute_url
1515
from mkt.api.tests.test_throttle import ThrottleTests
1616
from mkt.constants.apps import INSTALL_TYPE_REVIEWER
@@ -27,6 +27,32 @@ def _test_bad_api_potato_data(self, response, data=None):
2727
assert '__all__' in data['error_message']
2828

2929

30+
class TestPermission(BaseOAuth):
31+
fixtures = fixture('user_2519')
32+
33+
def setUp(self):
34+
super(TestPermission, self).setUp(api_name='account')
35+
self.get_url = get_url('permissions', '2519')
36+
self.user = UserProfile.objects.get(pk=2519)
37+
38+
def test_verbs(self):
39+
self._allowed_verbs(self.get_url, ('get'))
40+
41+
def test_no_permissions(self):
42+
res = self.client.get(self.get_url)
43+
eq_(res.status_code, 200, res.content)
44+
self.assertSetEqual(
45+
['reviewer', 'admin', 'localizer', 'lookup', 'developer'],
46+
res.json['permissions'].keys()
47+
)
48+
ok_(not all(res.json['permissions'].values()))
49+
50+
def test_some_permission(self):
51+
self.grant_permission(self.user, 'Localizers:%')
52+
res = self.client.get(self.get_url)
53+
ok_(res.json['permissions']['localizer'])
54+
55+
3056
class TestAccount(BaseOAuth):
3157
fixtures = fixture('user_2519', 'user_10482', 'webapp_337141')
3258

@@ -154,6 +180,7 @@ def test_login_success(self, http_request):
154180
'cvan@mozilla.com,95c9063d9f249aacfe5697fc83192ed6480c01463e2a80b3'
155181
'5af5ecaef11754700f4be33818d0e83a0cfc2cab365d60ba53b3c2b9f8f6589d1'
156182
'c43e9bbb876eef0,000000')
183+
eq_(data['permissions']['localizer'], False)
157184

158185
@patch('requests.post')
159186
def test_login_failure(self, http_request):

mkt/account/urls.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
from amo.decorators import login_required
77
from lib.misc.urlconf_decorator import decorate
88
from mkt.account.api import (AccountResource, FeedbackResource,
9-
InstalledResource, LoginResource)
9+
InstalledResource, LoginResource,
10+
PermissionResource)
1011
from . import views
1112

1213
settings_patterns = patterns('',
@@ -53,6 +54,7 @@
5354
account.register(FeedbackResource())
5455
account.register(InstalledResource())
5556
account.register(LoginResource())
57+
account.register(PermissionResource())
5658

5759
api_patterns = patterns('',
5860
url('^', include(account.urls)),

0 commit comments

Comments
 (0)