Skip to content

Commit a545a03

Browse files
committed
Merge branch 'release/0.116.0'
2 parents ddda42b + 21f301f commit a545a03

34 files changed

+3003
-3410
lines changed

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ before_install:
102102
/tmp/varnish/usr/sbin/varnishd -n /tmp -p feature=+esi_disable_xml_check -p vmod_dir=/tmp/varnish/usr/lib/varnish/vmods -F -f $PROJECT_DIR/tests/test_files/varnish.vcl -a *:8080 > /dev/null & export VARNISH_PID=$!
103103
fi
104104
install:
105+
- nvm install 0.12.4
106+
- nvm use 0.12.4
107+
- npm install -g npm@2.10.1
105108
- cd $PROJECT_DIR
106109
- cp website/settings/local-travis.py website/settings/local.py
107110
- cp api/base/settings/local-travis.py api/base/settings/local.py

CHANGELOG

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
Changelog
33
*********
44

5+
0.116.0 (2017-07-05)
6+
====================
7+
- Allow Styling in Admin App Preprint Provider HTML Fields
8+
- OSF side changes for new navbar
9+
- Distinguish Staging Admin from Production Admin
10+
- Create Scope to Get User Email Address
11+
- Cachebust CSS assets
12+
513
0.115.0 (2017-06-21)
614
====================
715

README-docker-compose.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
## Docker Sync
9090

9191
Ubuntu: Skip install of docker-sync, fswatch, and unison. instead...
92-
`cp docker-compose.ubuntu.yml docker-compose.override.yml`
92+
`cp docker-compose.linux.yml docker-compose.override.yml`
9393
Ignore future steps that start, stop, or wait for docker-sync
9494

9595
1. Install Docker Sync 0.3.5
@@ -171,7 +171,7 @@ Ubuntu: Skip install of docker-sync, fswatch, and unison. instead...
171171
- Populate preprint providers:
172172
- After resetting your database or with a new install you will need to populate the table of preprint providers. **You must have run migrations first.**
173173
- `docker-compose run --rm web python -m scripts.update_taxonomies`
174-
- `docker-compose run --rm web python -m scripts.populate_preprint_providers`
174+
- `docker-compose run --rm web python manage.py populate_fake_preprint_providers`
175175
- Populate citation styles
176176
- Needed for api v2 citation style rendering.
177177
- `docker-compose run --rm web python -m scripts.parse_citation_styles`

admin/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ If you are manually testing functionality for permissions views, do not log in t
1414
- Select the already existing OSF User you'd like to make an admin superuser with `user = OSFUser.objects.get(username=<your_user@cos.io>)`
1515
- Set that user to be a superuser and staff with `user.is_superuser = True` and `user.is_staff = True`
1616
- Save your user with `user.save()`
17+
- Commit the changes with `commit()`
1718

1819
3. Log in to the admin
1920
- Visit the admin at `http://localhost:8001/`

admin/preprint_providers/forms.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,17 @@ def clean_advisory_board(self, *args, **kwargs):
3737
return bleach.clean(
3838
self.data.get('advisory_board'),
3939
tags=['a', 'b', 'br', 'div', 'em', 'h2', 'h3', 'li', 'p', 'strong', 'ul'],
40-
attributes=['class', 'href', 'title', 'target'],
40+
attributes=['class', 'style', 'href', 'title', 'target'],
41+
styles=['text-align', 'vertical-align'],
4142
strip=True
4243
)
4344

4445
def clean_description(self, *args, **kwargs):
4546
return bleach.clean(
4647
self.data.get('description'),
4748
tags=['a', 'br', 'em', 'p', 'span', 'strong'],
48-
attributes=['class', 'href', 'title', 'target'],
49+
attributes=['class', 'style', 'href', 'title', 'target'],
50+
styles=['text-align', 'vertical-align'],
4951
strip=True
5052
)
5153

@@ -54,6 +56,6 @@ def clean_footer_links(self, *args, **kwargs):
5456
self.data.get('footer_links'),
5557
tags=['a', 'br', 'div', 'em', 'p', 'span', 'strong'],
5658
attributes=['class', 'style', 'href', 'title', 'target'],
57-
styles=['vertical-align'],
59+
styles=['text-align', 'vertical-align'],
5860
strip=True
5961
)

admin/static/js/pages/prereg-admin-page.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
var $ = require('jquery');
22
require('bootstrap');
33

4-
require('osf-style');
5-
64
var $osf = require('js/osfHelpers');
75
var osfLanguage = require('js/osfLanguage');
86

@@ -14,13 +12,13 @@ $(document).ready(function() {
1412
$drafts.addClass('osf-box');
1513

1614
var $draft = $(this);
17-
$draft.removeClass('osf-box');
15+
$draft.removeClass('osf-box');
1816
$draft.addClass('osf-box-lt');
1917
});
2018

2119
$('.prereg-draft-save').click(function(e) {
2220

23-
var $draftElement = $(this).closest('.prereg-draft') ;
21+
var $draftElement = $(this).closest('.prereg-draft') ;
2422
var $form = $draftElement.find('.prereg-draft-form');
2523
var data = {};
2624
$.each($form.serializeArray(), function(_, item) {

api/base/serializers.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,49 @@ def to_internal_value(self, data):
106106
return self.field.to_internal_value(data)
107107

108108

109+
class EmailScopeRequired(ser.Field):
110+
"""
111+
Skip field if USER_EMAIL scope is not present.
112+
SKip field if requesting user != serialized user.
113+
"""
114+
115+
def __init__(self, field, **kwargs):
116+
super(EmailScopeRequired, self).__init__(**kwargs)
117+
self.field = field
118+
self.required = field.required
119+
self.read_only = field.read_only
120+
121+
def get_attribute(self, instance):
122+
request = self.context.get('request')
123+
view = self.context.get('view')
124+
if view and request and request.auth:
125+
scopes = request.auth.attributes['accessTokenScope']
126+
if 'osf.users.user_email' in scopes and request.user == view.get_user():
127+
return self.field.get_attribute(instance)
128+
raise SkipField
129+
130+
def bind(self, field_name, parent):
131+
super(EmailScopeRequired, self).bind(field_name, parent)
132+
self.field.bind(field_name, self)
133+
134+
def to_internal_value(self, data):
135+
return self.field.to_internal_value(data)
136+
137+
def to_representation(self, value):
138+
if getattr(self.field.root, 'child', None):
139+
self.field.parent = self.field.root.child
140+
else:
141+
self.field.parent = self.field.root
142+
return self.field.to_representation(value)
143+
144+
def to_esi_representation(self, value, envelope='data'):
145+
if getattr(self.field.root, 'child', None):
146+
self.field.parent = self.field.root.child
147+
else:
148+
self.field.parent = self.field.root
149+
return self.field.to_esi_representation(value, envelope)
150+
151+
109152
class HideIfRegistration(ser.Field):
110153
"""
111154
If node is a registration, this field will return None.

api/users/serializers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from api.base.serializers import (
1010
JSONAPISerializer, LinksField, RelationshipField, DevOnly, IDField, TypeField, ListDictField,
11-
DateByVersion,
11+
DateByVersion, EmailScopeRequired,
1212
)
1313
from api.base.utils import absolute_reverse, get_user_auth
1414

@@ -34,6 +34,7 @@ class UserSerializer(JSONAPISerializer):
3434
timezone = HideIfDisabled(ser.CharField(required=False, help_text="User's timezone, e.g. 'Etc/UTC"))
3535
locale = HideIfDisabled(ser.CharField(required=False, help_text="User's locale, e.g. 'en_US'"))
3636
social = ListDictField(required=False)
37+
email = EmailScopeRequired(ser.CharField(source='username', read_only=True))
3738

3839
links = HideIfDisabled(LinksField(
3940
{

api_tests/base/test_auth.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,3 +245,29 @@ def test_full_write_scope_can_read_guid_view_and_user_cannot_view_project(self,
245245
assert_equal(res.location, redirect_url)
246246
redirect_res = res.follow(auth='some_valid_token', auth_type='jwt', expect_errors=True)
247247
assert_equal(redirect_res.status_code, 403)
248+
249+
@mock.patch('framework.auth.cas.CasClient.profile')
250+
def test_user_email_scope_can_read_email(self, mock_user_info):
251+
mock_user_info.return_value = self._scoped_response(['osf.users.user_email', 'osf.users.profile_read'])
252+
url = api_v2_url('users/me/', base_route='/', base_prefix='v2/')
253+
res = self.app.get(url, auth='some_valid_token', auth_type='jwt', expect_errors=True)
254+
assert_equal(res.status_code, 200)
255+
assert_equal(res.json['data']['attributes']['email'], self.user.username)
256+
257+
@mock.patch('framework.auth.cas.CasClient.profile')
258+
def test_non_user_email_scope_cannot_read_email(self, mock_user_info):
259+
mock_user_info.return_value = self._scoped_response(['osf.users.profile_read'])
260+
url = api_v2_url('users/me/', base_route='/', base_prefix='v2/')
261+
res = self.app.get(url, auth='some_valid_token', auth_type='jwt', expect_errors=True)
262+
assert_equal(res.status_code, 200)
263+
assert_not_in('email', res.json['data']['attributes'])
264+
assert_not_in(self.user.username, res.json)
265+
266+
@mock.patch('framework.auth.cas.CasClient.profile')
267+
def test_user_email_scope_cannot_read_other_email(self, mock_user_info):
268+
mock_user_info.return_value = self._scoped_response(['osf.users.user_email', 'osf.users.profile_read'])
269+
url = api_v2_url('users/{}/'.format(self.user2._id), base_route='/', base_prefix='v2/')
270+
res = self.app.get(url, auth='some_valid_token', auth_type='jwt', expect_errors=True)
271+
assert_equal(res.status_code, 200)
272+
assert_not_in('email', res.json['data']['attributes'])
273+
assert_not_in(self.user2.username, res.json)

0 commit comments

Comments
 (0)