You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by je...@apache.org on 2015/02/24 12:48:05 UTC

[11/50] [abbrv] allura git commit: [#4542] ticket:714 Edit webhooks & validation improvements

[#4542] ticket:714 Edit webhooks & validation improvements


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/4e8acf6b
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/4e8acf6b
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/4e8acf6b

Branch: refs/heads/ib/7827
Commit: 4e8acf6b7bba66b7538565c02d1bdbf251f5e015
Parents: 7d8c47b
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Jan 28 13:40:26 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:16:48 2015 +0000

----------------------------------------------------------------------
 Allura/allura/ext/admin/admin_main.py           |   2 +-
 .../ext/admin/templates/webhooks_list.html      |   2 +-
 .../allura/templates/webhooks/create_form.html  |  11 +-
 Allura/allura/webhooks.py                       | 115 ++++++++++++++++---
 4 files changed, 106 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/4e8acf6b/Allura/allura/ext/admin/admin_main.py
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index 52b80f9..787bb8c 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -201,7 +201,7 @@ class WebhooksLookup(BaseController):
         webhooks = [ep.load() for ep in webhooks]
         return webhooks
 
-    @with_trailing_slash
+    @without_trailing_slash
     @expose('jinja:allura.ext.admin:templates/webhooks_list.html')
     def index(self):
         webhooks = self._webhooks

http://git-wip-us.apache.org/repos/asf/allura/blob/4e8acf6b/Allura/allura/ext/admin/templates/webhooks_list.html
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/templates/webhooks_list.html b/Allura/allura/ext/admin/templates/webhooks_list.html
index 39df656..d591173 100644
--- a/Allura/allura/ext/admin/templates/webhooks_list.html
+++ b/Allura/allura/ext/admin/templates/webhooks_list.html
@@ -24,7 +24,7 @@
 {% block content %}
   {% for hook in webhooks %}
     <h1>{{ hook.type }}</h1>
-    <p><a href="{{ hook.type }}">Create</a></p>
+    <p><a href="{{c.app.url}}webhooks/{{ hook.type }}">Create</a></p>
     {% if configured_hooks[hook.type] %}
       <table>
         {% for h in configured_hooks[hook.type] %}

http://git-wip-us.apache.org/repos/asf/allura/blob/4e8acf6b/Allura/allura/templates/webhooks/create_form.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/webhooks/create_form.html b/Allura/allura/templates/webhooks/create_form.html
index f3eb713..c5ccdfb 100644
--- a/Allura/allura/templates/webhooks/create_form.html
+++ b/Allura/allura/templates/webhooks/create_form.html
@@ -18,9 +18,9 @@
 -#}
 {% extends g.theme.master %}
 
-{% block title %}{{c.project.name}} / Create {{webhook.type}} webhook{% endblock %}
+{% block title %}{{c.project.name}} / {{action|capitalize}} {{sender.type}} webhook{% endblock %}
 
-{% block header %}Create {{webhook.type}} webhook{% endblock %}
+{% block header %}{{action|capitalize}} {{sender.type}} webhook{% endblock %}
 
 {% block extra_css %}
   <style type="text/css">
@@ -64,7 +64,7 @@
 {%- endmacro %}
 
 {% block content %}
-<form action="create" method="post" enctype="multipart/form-data">
+<form action="{{action}}" method="post" enctype="multipart/form-data">
   <div>
     <label for="url">url</label>
     <input name="url" value="{{ c.form_values['url'] }}">
@@ -89,7 +89,10 @@
 
   {% block additional_fields %}{% endblock %}
 
-  <input type="submit" value="Create">
+  <input type="submit" value="{{action|capitalize}}">
+  {% if c.form_values['webhook'] %}
+    <input type="hidden" name="webhook" value="{{c.form_values['webhook']}}">
+  {% endif %}
   {{lib.csrf_token()}}
 </form>
 {% endblock %}

http://git-wip-us.apache.org/repos/asf/allura/blob/4e8acf6b/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 9bb99e3..8fb62d5 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -21,15 +21,16 @@ import hmac
 import hashlib
 
 import requests
-from bson import ObjectId
 from tg import expose, validate, redirect
-from tg.decorators import with_trailing_slash
+from tg.decorators import with_trailing_slash, without_trailing_slash
 from pylons import tmpl_context as c
-from formencode import validators as fev, schema
+from formencode import validators as fev, schema, Invalid
 from ming.odm import session
+from webob import exc
 
 from allura.controllers import BaseController
 from allura.lib import helpers as h
+from allura.lib import validators as av
 from allura.lib.decorators import require_post, task
 from allura.lib.utils import DateJSONEncoder
 from allura import model as M
@@ -38,61 +39,139 @@ from allura import model as M
 log = logging.getLogger(__name__)
 
 
+class MingOneOf(av.Ming):
+    def __init__(self, ids, **kw):
+        self.ids = ids
+        super(MingOneOf, self).__init__(**kw)
+
+    def _to_python(self, value, state):
+        result = super(MingOneOf, self)._to_python(value, state)
+        if result and result._id in self.ids:
+            return result
+        raise Invalid(
+            u'Object must be one of: {}, not {}'.format(self.ids, value),
+            value, state)
+
+
+class WebhookValidator(av.Ming):
+    def __init__(self, sender, ac_ids, **kw):
+        self.ac_ids = ac_ids
+        self.sender = sender
+        super(WebhookValidator, self).__init__(cls=M.Webhook, **kw)
+
+    def _to_python(self, value, state):
+        wh = super(WebhookValidator, self)._to_python(value, state)
+        if wh and wh.type == self.sender.type and wh.app_config_id in self.ac_ids:
+            return wh
+        raise Invalid(u'Invalid webhook', value, state)
+
+
 class WebhookCreateForm(schema.Schema):
-    def __init__(self, hook):
+    def __init__(self, sender):
         super(WebhookCreateForm, self).__init__()
         self.triggered_by = [ac for ac in c.project.app_configs
-                             if ac.tool_name.lower() in hook.triggered_by]
-        self.add_field('app', fev.OneOf(
-            [unicode(ac._id) for ac in self.triggered_by]))
+                             if ac.tool_name.lower() in sender.triggered_by]
+        self.add_field('app', MingOneOf(
+            cls=M.AppConfig, ids=[ac._id for ac in self.triggered_by]))
 
     url = fev.URL(not_empty=True)
     secret = fev.UnicodeString()
 
 
+class WebhookEditForm(WebhookCreateForm):
+    def __init__(self, sender):
+        super(WebhookEditForm, self).__init__(sender)
+        self.add_field('webhook', WebhookValidator(
+            sender=sender, ac_ids=[ac._id for ac in self.triggered_by]))
+
+
 class WebhookControllerMeta(type):
-    def __call__(cls, hook, *args, **kw):
+    def __call__(cls, sender, *args, **kw):
         """Decorate the `create` post handler with a validator that references
         the appropriate webhook sender for this controller.
         """
         if hasattr(cls, 'create'):
             cls.create = validate(
-                cls.create_form(hook),
+                cls.create_form(sender),
                 error_handler=cls.index.__func__,
             )(cls.create)
-        return type.__call__(cls, hook, *args, **kw)
+        if hasattr(cls, 'edit'):
+            cls.edit = validate(
+                cls.edit_form(sender),
+                error_handler=cls._default.__func__,
+            )(cls.edit)
+        return type.__call__(cls, sender, *args, **kw)
 
 
 class WebhookController(BaseController):
     __metaclass__ = WebhookControllerMeta
     create_form = WebhookCreateForm
+    edit_form = WebhookEditForm
 
-    def __init__(self, hook):
+    def __init__(self, sender):
         super(WebhookController, self).__init__()
-        self.webhook = hook
+        self.sender = sender
+
+    def gen_secret(self):
+        return h.cryptographic_nonce(20)
 
     @with_trailing_slash
     @expose('jinja:allura:templates/webhooks/create_form.html')
     def index(self, **kw):
-        return {'webhook': self.webhook,
-                'form': self.create_form(self.webhook)}
+        return {'sender': self.sender,
+                'action': 'create',
+                'form': self.create_form(self.sender)}
 
     @expose()
     @require_post()
-    def create(self, url, app, secret=None):
+    def create(self, url, app, secret):
         if not secret:
-            secret = h.cryptographic_nonce(20)
+            secret = self.gen_secret()
         # TODO: catch DuplicateKeyError
         wh = M.Webhook(
             hook_url=url,
             secret=secret,
-            app_config_id=ObjectId(app),
-            type=self.webhook.type)
+            app_config_id=app._id,
+            type=self.sender.type)
         session(wh).flush(wh)
         M.AuditLog.log('add webhook %s %s %s',
                        wh.type, wh.hook_url, wh.app_config.url())
         redirect(c.project.url() + 'admin/webhooks/')
 
+    @expose()
+    @require_post()
+    def edit(self, webhook, url, app, secret):
+        if not secret:
+            secret = self.gen_secret()
+        old_url = webhook.hook_url
+        old_app = webhook.app_config.url()
+        old_secret = webhook.secret
+        webhook.hook_url = url
+        webhook.app_config_id = app._id
+        webhook.secret = secret
+        # TODO: duplicate
+        session(webhook).flush(webhook)
+        M.AuditLog.log('edit webhook %s\n%s => %s\n%s => %s\n%s',
+            webhook.type, old_url, url, old_app, app.url(),
+            'secret changed' if old_secret != secret else '')
+        redirect(c.project.url() + 'admin/webhooks/')
+
+    @without_trailing_slash
+    @expose('jinja:allura:templates/webhooks/create_form.html')
+    def _default(self, webhook, **kw):
+        form = self.edit_form(self.sender)
+        try:
+            wh = form.fields['webhook'].to_python(webhook)
+        except Invalid:
+            raise exc.HTTPNotFound()
+        c.form_values = {'url': kw.get('url') or wh.hook_url,
+                         'app': kw.get('app') or unicode(wh.app_config_id),
+                         'secret': kw.get('secret') or wh.secret,
+                         'webhook': unicode(wh._id)}
+        return {'sender': self.sender,
+                'action': 'edit',
+                'form': form}
+
 
 @task()
 def send_webhook(webhook_id, payload):