Fixed updating username in settings view

The timestamps used by the 'last viewed' property of the video progress
feature were converted to strings when sending to the frontend, but never
changed back to timestamps when PUTting via the SDK. I solved it by not
PUTing the user at all, but using PATCH to set the username instead.
This commit is contained in:
Sybren A. Stüvel 2019-05-29 18:37:01 +02:00
parent 23f8c1a446
commit 0f0a4be412
5 changed files with 95 additions and 4 deletions

View File

@ -61,6 +61,9 @@ def _update_search_user_changed_role(sender, user: dict):
def setup_app(app, api_prefix):
from pillar.api import service
from . import patch
patch.setup_app(app, url_prefix=api_prefix)
app.on_pre_GET_users += hooks.check_user_access
app.on_post_GET_users += hooks.post_GET_user

45
pillar/api/users/patch.py Normal file
View File

@ -0,0 +1,45 @@
"""User patching support."""
import logging
import bson
from flask import Blueprint
import werkzeug.exceptions as wz_exceptions
from pillar import current_app
from pillar.auth import current_user
from pillar.api.utils import authorization, jsonify, remove_private_keys
from pillar.api import patch_handler
log = logging.getLogger(__name__)
patch_api_blueprint = Blueprint('users.patch', __name__)
class UserPatchHandler(patch_handler.AbstractPatchHandler):
item_name = 'user'
@authorization.require_login()
def patch_set_username(self, user_id: bson.ObjectId, patch: dict):
"""Updates a user's username."""
if user_id != current_user.user_id:
log.info('User %s tried to change username of user %s',
current_user.user_id, user_id)
raise wz_exceptions.Forbidden('You may only change your own username')
new_username = patch['username']
log.info('User %s uses PATCH to set username to %r', current_user.user_id, new_username)
users_coll = current_app.db('users')
db_user = users_coll.find_one({'_id': user_id})
db_user['username'] = new_username
# Save via Eve to check the schema and trigger update hooks.
response, _, _, status = current_app.put_internal(
'users', remove_private_keys(db_user), _id=user_id)
return jsonify(response), status
def setup_app(app, url_prefix):
UserPatchHandler(patch_api_blueprint)
app.register_api_blueprint(patch_api_blueprint, url_prefix=url_prefix)

View File

@ -11,6 +11,8 @@ import flask_login
import jinja2.filters
import jinja2.utils
import werkzeug.exceptions as wz_exceptions
from werkzeug.local import LocalProxy
import pillarsdk
import pillar.api.utils
@ -225,6 +227,8 @@ def user_to_dict(user: auth.UserClass) -> dict:
def do_json(some_object) -> str:
if isinstance(some_object, LocalProxy):
return do_json(some_object._get_current_object())
if isinstance(some_object, pillarsdk.Resource):
some_object = some_object.to_dict()
if isinstance(some_object, auth.UserClass):

View File

@ -7,6 +7,7 @@ from flask_login import login_required, current_user
from werkzeug.exceptions import abort
from pillar import current_app
from pillar.auth import current_user
from pillar.web import system_util
from pillar.web.users import forms
from pillarsdk import User, exceptions as sdk_exceptions
@ -29,11 +30,12 @@ def profile():
if form.validate_on_submit():
try:
user.username = form.username.data
user.update(api=api)
response = user.set_username(form.username.data, api=api)
log.info('updated username of %s: %s', current_user, response)
flash("Profile updated", 'success')
except sdk_exceptions.ResourceInvalid as e:
message = json.loads(e.content)
except sdk_exceptions.ResourceInvalid as ex:
log.warning('unable to set username %s to %r: %s', current_user, form.username.data, ex)
message = json.loads(ex.content)
flash(message)
blender_id_endpoint = current_app.config['BLENDER_ID_ENDPOINT']

View File

@ -0,0 +1,37 @@
import flask
import flask_login
from pillar.tests import AbstractPillarTest
class UsernameTest(AbstractPillarTest):
def setUp(self, **kwargs) -> None:
super().setUp(**kwargs)
self.user_id = self.create_user()
def test_update_via_web(self) -> None:
from pillar.auth import current_user
import pillar.web.settings.routes
with self.app.app_context():
url = flask.url_for('settings.profile')
with self.app.test_request_context(
path=url,
data={'username': 'je.moeder'},
method='POST',
):
self.login_api_as(self.user_id)
flask_login.login_user(current_user)
pillar.web.settings.routes.profile()
db_user = self.fetch_user_from_db(self.user_id)
self.assertEqual('je.moeder', db_user['username'])
def test_update_via_patch(self) -> None:
self.create_valid_auth_token(self.user_id, 'user-token')
self.patch(f'/api/users/{self.user_id}',
json={'op': 'set-username', 'username': 'je.moeder'},
auth_token='user-token')
db_user = self.fetch_user_from_db(self.user_id)
self.assertEqual('je.moeder', db_user['username'])