From 180a0081007532ed7f6adad8fe3f2cde97031a2f Mon Sep 17 00:00:00 2001 From: Elrond Date: Sun, 5 May 2013 16:45:37 +0200 Subject: [PATCH 1/5] piwigo: Remove possibly_add_cookie. This one was a fake thing to make clients happy. Real sessions coming sonn. --- mediagoblin/plugins/piwigo/views.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/mediagoblin/plugins/piwigo/views.py b/mediagoblin/plugins/piwigo/views.py index bd3f9320..e064b418 100644 --- a/mediagoblin/plugins/piwigo/views.py +++ b/mediagoblin/plugins/piwigo/views.py @@ -133,19 +133,6 @@ def pwg_images_addChunk(request): return True -def possibly_add_cookie(request, response): - # TODO: We should only add a *real* cookie, if - # authenticated. And if there is no cookie already. - if True: - response.set_cookie( - 'pwg_id', - "some_fake_for_now", - path=request.environ['SCRIPT_NAME'], - domain=mg_globals.app_config.get('csrf_cookie_domain'), - secure=(request.scheme.lower() == 'https'), - httponly=True) - - @csrf_exempt def ws_php(request): if request.method not in ("GET", "POST"): @@ -165,6 +152,4 @@ def ws_php(request): response = response_xml(result) - possibly_add_cookie(request, response) - return response From c1df8d19630b1e60598db1bd93171926234b633b Mon Sep 17 00:00:00 2001 From: Elrond Date: Mon, 25 Mar 2013 15:34:21 +0100 Subject: [PATCH 2/5] piwigo: Add .images.add including form handling. To make things a bit easier, switch to WTForms for validating the received data. --- mediagoblin/plugins/piwigo/forms.py | 16 ++++++++++++++++ mediagoblin/plugins/piwigo/tools.py | 15 ++++++++++++++- mediagoblin/plugins/piwigo/views.py | 13 +++++++++++-- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/mediagoblin/plugins/piwigo/forms.py b/mediagoblin/plugins/piwigo/forms.py index 5bb12e62..18cbd5c5 100644 --- a/mediagoblin/plugins/piwigo/forms.py +++ b/mediagoblin/plugins/piwigo/forms.py @@ -26,3 +26,19 @@ class AddSimpleForm(wtforms.Form): # tags = wtforms.FieldList(wtforms.TextField()) category = wtforms.IntegerField() level = wtforms.IntegerField() + + +_md5_validator = wtforms.validators.Regexp(r"^[0-9a-fA-F]{32}$") + + +class AddForm(wtforms.Form): + original_sum = wtforms.TextField(None, + [_md5_validator, + wtforms.validators.Required()]) + thumbnail_sum = wtforms.TextField(None, + [wtforms.validators.Optional(False), + _md5_validator]) + file_sum = wtforms.TextField(None, [_md5_validator]) + name = wtforms.TextField() + date_creation = wtforms.TextField() + categories = wtforms.TextField() diff --git a/mediagoblin/plugins/piwigo/tools.py b/mediagoblin/plugins/piwigo/tools.py index 4d2e985a..cd466367 100644 --- a/mediagoblin/plugins/piwigo/tools.py +++ b/mediagoblin/plugins/piwigo/tools.py @@ -18,7 +18,7 @@ import logging import six import lxml.etree as ET -from werkzeug.exceptions import MethodNotAllowed +from werkzeug.exceptions import MethodNotAllowed, BadRequest from mediagoblin.tools.response import Response @@ -106,3 +106,16 @@ class CmdTable(object): _log.warn("Method %s only allowed for POST", cmd_name) raise MethodNotAllowed() return func + + +def check_form(form): + if not form.validate(): + _log.error("form validation failed for form %r", form) + for f in form: + if len(f.error): + _log.error("Errors for %s: %r", f.name, f.errors) + raise BadRequest() + dump = [] + for f in form: + dump.append("%s=%r" % (f.name, f.data)) + _log.debug("form: %s", " ".join(dump)) diff --git a/mediagoblin/plugins/piwigo/views.py b/mediagoblin/plugins/piwigo/views.py index e064b418..837d8eca 100644 --- a/mediagoblin/plugins/piwigo/views.py +++ b/mediagoblin/plugins/piwigo/views.py @@ -23,8 +23,8 @@ from werkzeug.wrappers import BaseResponse from mediagoblin import mg_globals from mediagoblin.meddleware.csrf import csrf_exempt from mediagoblin.submit.lib import check_file_field -from .tools import CmdTable, PwgNamedArray, response_xml -from .forms import AddSimpleForm +from .tools import CmdTable, PwgNamedArray, response_xml, check_form +from .forms import AddSimpleForm, AddForm _log = logging.getLogger(__name__) @@ -133,6 +133,15 @@ def pwg_images_addChunk(request): return True +@CmdTable("pwg.images.add", True) +def pwg_images_add(request): + _log.info("add: %r", request.form) + form = AddForm(request.form) + check_form(form) + + return {'image_id': 123456, 'url': ''} + + @csrf_exempt def ws_php(request): if request.method not in ("GET", "POST"): From 7fb419ddd2bd1770d62fffadc674c53b670cba81 Mon Sep 17 00:00:00 2001 From: Elrond Date: Fri, 29 Mar 2013 14:49:13 +0100 Subject: [PATCH 3/5] Create new session system for piwigo plugin. Using the brand new itsdangerous sessions to power the sessions for piwigo. The real point is: Clients want to have the session in a "pwg_id" cookie and don't accept any other cookie name. --- mediagoblin/plugins/piwigo/__init__.py | 5 +++++ mediagoblin/plugins/piwigo/tools.py | 31 ++++++++++++++++++++++++++ mediagoblin/plugins/piwigo/views.py | 26 +++++++++++++++------ 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/mediagoblin/plugins/piwigo/__init__.py b/mediagoblin/plugins/piwigo/__init__.py index 73326e9e..c4da708a 100644 --- a/mediagoblin/plugins/piwigo/__init__.py +++ b/mediagoblin/plugins/piwigo/__init__.py @@ -17,6 +17,8 @@ import logging from mediagoblin.tools import pluginapi +from mediagoblin.tools.session import SessionManager +from .tools import PWGSession _log = logging.getLogger(__name__) @@ -32,6 +34,9 @@ def setup_plugin(): pluginapi.register_routes(routes) + PWGSession.session_manager = SessionManager("pwg_id", "plugins.piwigo") + + hooks = { 'setup': setup_plugin } diff --git a/mediagoblin/plugins/piwigo/tools.py b/mediagoblin/plugins/piwigo/tools.py index cd466367..400be615 100644 --- a/mediagoblin/plugins/piwigo/tools.py +++ b/mediagoblin/plugins/piwigo/tools.py @@ -20,6 +20,7 @@ import six import lxml.etree as ET from werkzeug.exceptions import MethodNotAllowed, BadRequest +from mediagoblin.tools.request import setup_user_in_request from mediagoblin.tools.response import Response @@ -119,3 +120,33 @@ def check_form(form): for f in form: dump.append("%s=%r" % (f.name, f.data)) _log.debug("form: %s", " ".join(dump)) + + +class PWGSession(object): + session_manager = None + + def __init__(self, request): + self.request = request + self.in_pwg_session = False + + def __enter__(self): + # Backup old state + self.old_session = self.request.session + self.old_user = self.request.user + # Load piwigo session into state + self.request.session = self.session_manager.load_session_from_cookie( + self.request) + setup_user_in_request(self.request) + self.in_pwg_session = True + return self + + def __exit__(self, *args): + # Restore state + self.request.session = self.old_session + self.request.user = self.old_user + self.in_pwg_session = False + + def save_to_cookie(self, response): + assert self.in_pwg_session + self.session_manager.save_session_to_cookie(self.request.session, + self.request, response) diff --git a/mediagoblin/plugins/piwigo/views.py b/mediagoblin/plugins/piwigo/views.py index 837d8eca..6a246f18 100644 --- a/mediagoblin/plugins/piwigo/views.py +++ b/mediagoblin/plugins/piwigo/views.py @@ -20,10 +20,11 @@ import re from werkzeug.exceptions import MethodNotAllowed, BadRequest, NotImplemented from werkzeug.wrappers import BaseResponse -from mediagoblin import mg_globals from mediagoblin.meddleware.csrf import csrf_exempt from mediagoblin.submit.lib import check_file_field -from .tools import CmdTable, PwgNamedArray, response_xml, check_form +from mediagoblin.auth.lib import fake_login_attempt +from .tools import CmdTable, PwgNamedArray, response_xml, check_form, \ + PWGSession from .forms import AddSimpleForm, AddForm @@ -35,12 +36,21 @@ def pwg_login(request): username = request.form.get("username") password = request.form.get("password") _log.info("Login for %r/%r...", username, password) + user = request.db.User.query.filter_by(username=username).first() + if not user: + fake_login_attempt() + return False + if not user.check_login(password): + return False + request.session["user_id"] = user.id + request.session.save() return True @CmdTable("pwg.session.logout") def pwg_logout(request): _log.info("Logout") + request.session.delete() return True @@ -154,11 +164,13 @@ def ws_php(request): request.args, request.form) raise NotImplemented() - result = func(request) + with PWGSession(request) as session: + result = func(request) - if isinstance(result, BaseResponse): - return result + if isinstance(result, BaseResponse): + return result - response = response_xml(result) + response = response_xml(result) + session.save_to_cookie(response) - return response + return response From 665946033e1c9eef775a84a538ceebe9f52c657e Mon Sep 17 00:00:00 2001 From: Elrond Date: Fri, 29 Mar 2013 14:48:08 +0100 Subject: [PATCH 4/5] piwigo: Let getStatus return the current user. If there is a user logged in, show his name. --- mediagoblin/plugins/piwigo/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mediagoblin/plugins/piwigo/views.py b/mediagoblin/plugins/piwigo/views.py index 6a246f18..5d7d371d 100644 --- a/mediagoblin/plugins/piwigo/views.py +++ b/mediagoblin/plugins/piwigo/views.py @@ -61,7 +61,11 @@ def pwg_getversion(request): @CmdTable("pwg.session.getStatus") def pwg_session_getStatus(request): - return {'username': "fake_user"} + if request.user: + username = request.user.username + else: + username = "guest" + return {'username': username} @CmdTable("pwg.categories.getList") From f035ec3db4c67e4a4d03d78dccd57ea82b9a6a5d Mon Sep 17 00:00:00 2001 From: Elrond Date: Sun, 5 May 2013 17:02:00 +0200 Subject: [PATCH 5/5] piwigo: Better logging for login. --- mediagoblin/plugins/piwigo/views.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mediagoblin/plugins/piwigo/views.py b/mediagoblin/plugins/piwigo/views.py index 5d7d371d..b59247ad 100644 --- a/mediagoblin/plugins/piwigo/views.py +++ b/mediagoblin/plugins/piwigo/views.py @@ -35,13 +35,16 @@ _log = logging.getLogger(__name__) def pwg_login(request): username = request.form.get("username") password = request.form.get("password") - _log.info("Login for %r/%r...", username, password) + _log.debug("Login for %r/%r...", username, password) user = request.db.User.query.filter_by(username=username).first() if not user: + _log.info("User %r not found", username) fake_login_attempt() return False if not user.check_login(password): + _log.warn("Wrong password for %r", username) return False + _log.info("Logging %r in", username) request.session["user_id"] = user.id request.session.save() return True