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/16 12:44:50 UTC
[19/37] 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/4542
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):