Skip to content

Commit

Permalink
Merge 8ddb979 into 8ff8112
Browse files Browse the repository at this point in the history
  • Loading branch information
teleyinex committed Jan 20, 2017
2 parents 8ff8112 + 8ddb979 commit 6db9793
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 23 deletions.
44 changes: 44 additions & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1063,3 +1063,47 @@ It returns a JSON object with the following information:
u'flash': u'You reset your password successfully!',
u'next': u'/'
}
Account reset API Key
~~~~~~~~~~~~~~~~~~~~~~
**Endpoint: /account/<user>/resetapikey**
*Allowed methods*: **GET/POST**
**GET**
It returns a JSON object with the following information:
* **csrf**: The CSRF token for validating the post.
**Example output**
.. code-block:: python
{
"form":
{
"csrf": "token",
}
}
**POST**
To send a valid POST request you need to pass the *csrf token* in the headers. Use
the following header: "X-CSRFToken".
It returns a JSON object with the following information:
* **flash**: A success message, or error indicating if the request was succesful.
* **status**: A status message, indicating if something went wrong.
* **next**: Suggested URL to redirect the user.
**Example output**
.. code-block:: python
{
u'status': u'success',
u'flash': u'New API-KEY generated',
u'next': u'/account/<user>'
}
3 changes: 2 additions & 1 deletion pybossa/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,8 @@ def _api_authentication():
# Handle forms
request.body = request.form
if (request.method == 'POST' and
request.headers['Content-Type'] == 'application/json'):
request.headers['Content-Type'] == 'application/json' and
request.data):
request.body = get_json_multidict(request)

@app.context_processor
Expand Down
5 changes: 3 additions & 2 deletions pybossa/forms/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ def __init__(self, message=None):
self.message = message

def __call__(self, form, field):
if any(c in field.data for c in self.not_valid_chars):
raise ValidationError(self.message)
if field.data:
if any(c in field.data for c in self.not_valid_chars):
raise ValidationError(self.message)


class CommaSeparatedIntegers(object):
Expand Down
28 changes: 17 additions & 11 deletions pybossa/view/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@

import pybossa.model as model
from flask.ext.babel import gettext
from flask_wtf.csrf import generate_csrf
from flask import jsonify
from pybossa.core import signer, uploader, sentinel, newsletter
from pybossa.util import Pagination, handle_content_type
from pybossa.util import get_user_signup_method
Expand Down Expand Up @@ -654,7 +656,7 @@ def forgot_password():
return handle_content_type(data)


@blueprint.route('/<name>/resetapikey', methods=['POST'])
@blueprint.route('/<name>/resetapikey', methods=['GET', 'POST'])
@login_required
def reset_api_key(name):
"""
Expand All @@ -663,13 +665,17 @@ def reset_api_key(name):
Returns a Jinja2 template.
"""
user = user_repo.get_by_name(name)
if not user:
return abort(404)
ensure_authorized_to('update', user)
user.api_key = model.make_uuid()
user_repo.update(user)
cached_users.delete_user_summary(user.name)
msg = gettext('New API-KEY generated')
flash(msg, 'success')
return redirect(url_for('account.profile', name=name))
if request.method == 'POST':
user = user_repo.get_by_name(name)
if not user:
return abort(404)
ensure_authorized_to('update', user)
user.api_key = model.make_uuid()
user_repo.update(user)
cached_users.delete_user_summary(user.name)
msg = gettext('New API-KEY generated')
flash(msg, 'success')
return redirect_content_type(url_for('account.profile', name=name))
else:
csrf = dict(form=dict(csrf=generate_csrf()))
return jsonify(csrf)
13 changes: 9 additions & 4 deletions test/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,15 @@ def app_get_json(self, url, follow_redirects=False, headers=None):
return self.app.get(url, follow_redirects=follow_redirects,
headers=headers, content_type='application/json')

def app_post_json(self, url, data, follow_redirects=False, headers=None):
return self.app.post(url, data=json.dumps(data),
follow_redirects=follow_redirects,
headers=headers, content_type='application/json')
def app_post_json(self, url, data=None, follow_redirects=False, headers=None):
if data:
return self.app.post(url, data=json.dumps(data),
follow_redirects=follow_redirects,
headers=headers, content_type='application/json')
else:
return self.app.post(url,
follow_redirects=follow_redirects,
headers=headers, content_type='application/json')

def tearDown(self):
with self.flask_app.app_context():
Expand Down
62 changes: 57 additions & 5 deletions test/test_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,18 +260,20 @@ def test_register_errors_get(self):
def test_register_wrong_content_type(self):
"""Test WEB Register JSON wrong content type."""
with patch.dict(self.flask_app.config, {'WTF_CSRF_ENABLED': True}):
url = '/account/register'
csrf = self.get_csrf(url)
userdict = {'fullname': 'a', 'name': 'name',
'email_addr': None, 'password': 'p'}

res = self.app.post('/account/register', data=userdict,
content_type='application/json')
content_type='application/json',
headers={'X-CSRFToken': csrf})
print res.data
errors = json.loads(res.data)
err_msg = "The browser (or proxy) sent a request that this server could not understand."
assert errors.get('description') == err_msg, err_msg
err_msg = "Error code should be 400"
assert errors.get('code') == 400, err_msg
assert res.status_code == 400, err_msg
assert errors.get('status') == ERROR, errors
assert errors.get('form').get('name') is None, errors
assert len(errors.get('form').get('errors').get('email_addr')) > 0, errors


@with_context
Expand Down Expand Up @@ -3923,6 +3925,56 @@ def test_57_reset_api_key(self):
res = self.app.post(url)
assert res.status_code == 404, res.status_code

@with_context
def test_57_reset_api_key_json(self):
"""Test WEB reset api key JSON works"""
url = "/account/johndoe/update"
# Anonymous user
res = self.app_get_json(url, follow_redirects=True)
err_msg = "Anonymous user should be redirected for authentication"
assert "Please sign in to access this page" in res.data, err_msg
res = self.app_post_json(url, data=dict(foo=1), follow_redirects=True)
assert "Please sign in to access this page" in res.data, res.data
# Authenticated user
self.register()
user = db.session.query(User).get(1)
url = "/account/%s/update" % user.name
api_key = user.api_key
res = self.app_get_json(url, follow_redirects=True)
err_msg = "Authenticated user should get access to reset api key page"
assert res.status_code == 200, err_msg
data = json.loads(res.data)
assert data.get('form').get('name') == user.name, (err_msg, data)

with patch.dict(self.flask_app.config, {'WTF_CSRF_ENABLED': True}):
url = "/account/%s/resetapikey" % user.name
csrf = self.get_csrf(url)
headers = {'X-CSRFToken': csrf}
res = self.app_post_json(url,
follow_redirects=True, headers=headers)
err_msg = "Authenticated user should be able to reset his api key"
assert res.status_code == 200, err_msg
data = json.loads(res.data)
assert data.get('status') == SUCCESS, err_msg
assert data.get('next') == "/account/%s/" % user.name, (err_msg, data)
user = db.session.query(User).get(1)
err_msg = "New generated API key should be different from old one"
assert api_key != user.api_key, (err_msg, data)
self.signout()

self.register(fullname="new", name="new")
res = self.app_post_json(url, headers=headers)
assert res.status_code == 403, res.status_code
data = json.loads(res.data)
assert data.get('code') == 403, data

url = "/account/fake/resetapikey"
res = self.app_post_json(url, headers=headers)
assert res.status_code == 404, res.status_code
data = json.loads(res.data)
assert data.get('code') == 404, data


@with_context
@patch('pybossa.cache.site_stats.get_locs', return_value=[{'latitude': 0, 'longitude': 0}])
def test_58_global_stats(self, mock1):
Expand Down

0 comments on commit 6db9793

Please sign in to comment.