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/23 14:27:56 UTC

[01/11] allura git commit: [#7832] ticket:731 Add rest api controllers for app admin

Repository: allura
Updated Branches:
  refs/heads/ib/7832 [created] 42c0cfcaa


[#7832] ticket:731 Add rest api controllers for app admin


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

Branch: refs/heads/ib/7832
Commit: 43000407f2c033daadb906ef3311c3f0b1b04a59
Parents: 746ee51
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 09:48:05 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 23 09:45:13 2015 +0000

----------------------------------------------------------------------
 Allura/allura/app.py                  |  5 +++++
 Allura/allura/ext/admin/admin_main.py |  7 +++++++
 Allura/allura/lib/repository.py       | 20 ++++++++++++++++++++
 3 files changed, 32 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/43000407/Allura/allura/app.py
----------------------------------------------------------------------
diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index b9fd6b7..4e1ed5d 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -211,6 +211,10 @@ class Application(object):
     :cvar Controller api_root: Serves API access at
         /rest/<neighborhood>/<project>/<app>/. Default is None - subclasses
         should override to expose API access to the Application.
+    :cvar Controller admin_api_root: Serves Admin API access at
+        /rest/<neighborhood>/<project>/admin/<app>/. Default is None -
+        subclasses should override to expose Admin API access to the
+        Application.
     :ivar Controller admin: Serves admin functions at
         /<neighborhood>/<project>/<admin>/<app>/. Default is a
         :class:`DefaultAdminController` instance.
@@ -227,6 +231,7 @@ class Application(object):
     script_name = None
     root = None  # root controller
     api_root = None
+    admin_api_root = None
     permissions = []
     permissions_desc = {
         'unmoderated_post': 'Post comments without moderation.',

http://git-wip-us.apache.org/repos/asf/allura/blob/43000407/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 b812b0d..d5ae068 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -884,6 +884,13 @@ class ProjectAdminRestController(BaseController):
                     % (tool, mount_point, mount_label)
         }
 
+    @expose()
+    def _lookup(self, name, *remainder):
+        app = c.project.app_instance(name)
+        if app is None or app.admin_api_root is None:
+            raise exc.HTTPNotFound, name
+        return app.admin_api_root, remainder
+
 
 class PermissionsController(BaseController):
     def _check_security(self):

http://git-wip-us.apache.org/repos/asf/allura/blob/43000407/Allura/allura/lib/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index 9a4966f..d5d467b 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -28,6 +28,7 @@ from ming.utils import LazyProperty
 
 import allura.tasks
 from allura import version
+from allura.controllers.base import BaseController
 from allura.lib import helpers as h
 from allura import model as M
 from allura.lib import security
@@ -75,6 +76,7 @@ class RepositoryApp(Application):
     def __init__(self, project, config):
         Application.__init__(self, project, config)
         self.admin = RepoAdminController(self)
+        self.admin_api_root = RepoAdminRestController(self)
 
     def main_menu(self):
         '''Apps should provide their entries to be added to the main nav
@@ -274,3 +276,21 @@ class RepoAdminController(DefaultAdminController):
         else:
             flash("Invalid external checkout URL: %s" % c.form_errors['external_checkout_url'], "error")
         redirect(c.project.url() + 'admin/tools')
+
+
+class RepoAdminRestController(BaseController):
+    def __init__(self, app):
+        self.app = app
+        self.webhooks = RestWebhooksLookup(app)
+
+
+class RestWebhooksLookup(BaseController):
+    def __init__(self, app):
+        self.app = app
+
+    @expose('json:')
+    def index(self, **kw):
+        webhooks = self.app._webhooks
+        if len(webhooks) == 0:
+            raise exc.HTTPNotFound()
+        return {'test': 'works'}


[11/11] allura git commit: [#7832] ticket:731 Test fixes and some amends

Posted by je...@apache.org.
[#7832] ticket:731 Test fixes and some amends

- Raise 404 instead of 500 when no app name in _lookup in admin REST controller
- Add tests for bearer token via headers and update existing tests
- Don't test that webhook values was not changed: mim does not play nicely with
  it, it works with actual requests, though


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

Branch: refs/heads/ib/7832
Commit: 42c0cfcaaadd0c4ffa0988a214453b2b9c00565e
Parents: 55c0748
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Feb 23 12:57:44 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 23 12:57:44 2015 +0000

----------------------------------------------------------------------
 Allura/allura/ext/admin/admin_main.py       |  5 +-
 Allura/allura/tests/functional/test_rest.py | 59 ++++++++++++++++++++++++
 Allura/allura/tests/test_webhooks.py        |  4 --
 3 files changed, 63 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/42c0cfca/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 d5ae068..b51c816 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -885,7 +885,10 @@ class ProjectAdminRestController(BaseController):
         }
 
     @expose()
-    def _lookup(self, name, *remainder):
+    def _lookup(self, *args):
+        if len(args) == 0:
+            raise exc.HTTPNotFound, args
+        name, remainder = args[0], args[1:]
         app = c.project.app_instance(name)
         if app is None or app.admin_api_root is None:
             raise exc.HTTPNotFound, name

http://git-wip-us.apache.org/repos/asf/allura/blob/42c0cfca/Allura/allura/tests/functional/test_rest.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_rest.py b/Allura/allura/tests/functional/test_rest.py
index 51ae930..fca1078 100644
--- a/Allura/allura/tests/functional/test_rest.py
+++ b/Allura/allura/tests/functional/test_rest.py
@@ -39,6 +39,7 @@ class TestRestHome(TestRestApiBase):
     @mock.patch('allura.controllers.rest.M.OAuthAccessToken')
     @mock.patch('allura.controllers.rest.request')
     def test_bearer_token_non_bearer(self, request, OAuthAccessToken):
+        request.headers = {}
         request.params = {'access_token': 'foo'}
         request.scheme = 'https'
         self._patch_token(OAuthAccessToken)
@@ -51,6 +52,7 @@ class TestRestHome(TestRestApiBase):
     @mock.patch('allura.controllers.rest.M.OAuthAccessToken')
     @mock.patch('allura.controllers.rest.request')
     def test_bearer_token_invalid(self, request, OAuthAccessToken):
+        request.headers = {}
         request.params = {'access_token': 'foo'}
         request.scheme = 'https'
         self._patch_token(OAuthAccessToken)
@@ -80,11 +82,68 @@ class TestRestHome(TestRestApiBase):
             is_bearer=True,
         )
         ThreadLocalODMSession.flush_all()
+        request.headers = {}
         request.params = {'access_token': access_token.api_key}
         request.scheme = 'https'
         r = self.api_post('/rest/p/test/wiki', access_token='foo')
         assert_equal(r.status_int, 200)
 
+    @mock.patch('allura.controllers.rest.M.OAuthAccessToken')
+    @mock.patch('allura.controllers.rest.request')
+    def test_bearer_token_non_bearer_via_headers(self, request, OAuthAccessToken):
+        request.headers = {
+            'Authorization': 'OAuth BearerToken access_token=foo'
+        }
+        request.scheme = 'https'
+        self._patch_token(OAuthAccessToken)
+        access_token = OAuthAccessToken.query.get.return_value
+        access_token.is_bearer = False
+        r = self.api_post('/rest/p/test/wiki', access_token='foo')
+        assert_equal(r.status_int, 403)
+        OAuthAccessToken.query.get.assert_called_once_with(api_key='foo')
+
+    @mock.patch('allura.controllers.rest.M.OAuthAccessToken')
+    @mock.patch('allura.controllers.rest.request')
+    def test_bearer_token_invalid_via_headers(self, request, OAuthAccessToken):
+        request.headers = {
+            'Authorization': 'OAuth BearerToken access_token=foo'
+        }
+        request.scheme = 'https'
+        self._patch_token(OAuthAccessToken)
+        OAuthAccessToken.query.get.return_value = None
+        r = self.api_post('/rest/p/test/wiki', access_token='foo')
+        assert_equal(r.status_int, 403)
+
+    @mock.patch('allura.controllers.rest.request')
+    @td.with_wiki
+    def test_bearer_token_valid_via_headers(self, request):
+        user = M.User.by_username('test-admin')
+        consumer_token = M.OAuthConsumerToken(
+            name='foo',
+            description='foo app',
+        )
+        request_token = M.OAuthRequestToken(
+            consumer_token_id=consumer_token._id,
+            user_id=user._id,
+            callback='manual',
+            validation_pin=h.nonce(20),
+            is_bearer=True,
+        )
+        access_token = M.OAuthAccessToken(
+            consumer_token_id=consumer_token._id,
+            request_token_id=request_token._id,
+            user_id=user._id,
+            is_bearer=True,
+        )
+        ThreadLocalODMSession.flush_all()
+        token = access_token.api_key
+        request.headers = {
+            'Authorization': 'OAuth BearerToken access_token={}'.format(token)
+        }
+        request.scheme = 'https'
+        r = self.api_post('/rest/p/test/wiki', access_token='foo')
+        assert_equal(r.status_int, 200)
+
     def test_bad_path(self):
         r = self.api_post('/rest/1/test/wiki/')
         assert r.status_int == 404

http://git-wip-us.apache.org/repos/asf/allura/blob/42c0cfca/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index a336f32..e86dbd8 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -868,16 +868,12 @@ class TestWebhookRestController(TestRestApiBase):
     def test_edit_duplicates(self):
         webhook = self.webhooks[0]
         url = '{}/repo-push/{}'.format(self.url, webhook._id)
-        # change only url
         data = {'url': 'http://httpbin.org/post/1'}
         r = self.api_post(url, status=400, **data)
         expected = {u'result': u'error',
                     u'error': u'_the_form: "repo-push" webhook already '
                               u'exists for Git http://httpbin.org/post/1'}
         assert_equal(r.json, expected)
-        webhook = M.Webhook.query.get(_id=webhook._id)
-        assert_equal(webhook.hook_url, 'http://httpbin.org/post/0')
-        assert_equal(webhook.secret, 'secret-0')
 
     def test_delete_validation(self):
         url = '{}/repo-push/invalid'.format(self.url)


[06/11] allura git commit: [#7832] ticket:731 Add support for DELETE to test app

Posted by je...@apache.org.
[#7832] ticket:731 Add support for DELETE to test app


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

Branch: refs/heads/ib/7832
Commit: 3bb19e9e21f51ce908045cd121e5f8365495c310
Parents: 3bc94d0
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 16:28:30 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 23 09:45:16 2015 +0000

----------------------------------------------------------------------
 AlluraTest/alluratest/controller.py | 11 +++++++----
 AlluraTest/alluratest/validation.py |  7 +++++++
 2 files changed, 14 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/3bb19e9e/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index 4237624..4398a82 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -209,7 +209,7 @@ class TestRestApiBase(TestController):
 
         return self._token_cache[username]
 
-    def _api_getpost(self, method, path, wrap_args=None, user='test-admin', status=None, **params):
+    def _api_call(self, method, path, wrap_args=None, user='test-admin', status=None, **params):
         '''
         If you need to use one of the method kwargs as a URL parameter,
         pass params={...} as a dict instead of **kwargs
@@ -224,7 +224,7 @@ class TestRestApiBase(TestController):
 
         params['access_token'] = self.token(user).api_key
 
-        fn = self.app.post if method == 'POST' else self.app.get
+        fn = getattr(self.app, method.lower())
 
         response = fn(
             str(path),
@@ -236,7 +236,10 @@ class TestRestApiBase(TestController):
             return response
 
     def api_get(self, path, wrap_args=None, user='test-admin', status=None, **params):
-        return self._api_getpost('GET', path, wrap_args, user, status, **params)
+        return self._api_call('GET', path, wrap_args, user, status, **params)
 
     def api_post(self, path, wrap_args=None, user='test-admin', status=None, **params):
-        return self._api_getpost('POST', path, wrap_args, user, status, **params)
+        return self._api_call('POST', path, wrap_args, user, status, **params)
+
+    def api_delete(self, path, wrap_args=None, user='test-admin', status=None, **params):
+        return self._api_call('DELETE', path, wrap_args, user, status, **params)

http://git-wip-us.apache.org/repos/asf/allura/blob/3bb19e9e/AlluraTest/alluratest/validation.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/validation.py b/AlluraTest/alluratest/validation.py
index 1bf6b22..97691df 100644
--- a/AlluraTest/alluratest/validation.py
+++ b/AlluraTest/alluratest/validation.py
@@ -315,3 +315,10 @@ class ValidatingTestApp(PostParamCheckingTestApp):
         if not self.validate_skip and not val_params['validate_skip']:
             self._validate(resp, 'post', val_params)
         return resp
+
+    def delete(self, *args, **kw):
+        val_params, kw = self._get_validation_params(kw)
+        resp = super(ValidatingTestApp, self).delete(*args, **kw)
+        if not self.validate_skip and not val_params['validate_skip']:
+            self._validate(resp, 'delete', val_params)
+        return resp


[03/11] allura git commit: [#7832] ticket:731 List webhooks API

Posted by je...@apache.org.
[#7832] ticket:731 List webhooks API


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

Branch: refs/heads/ib/7832
Commit: d150c5c16455fe9eef0a6b8305d475eed8b1fb44
Parents: 4300040
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 11:07:16 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 23 09:45:14 2015 +0000

----------------------------------------------------------------------
 Allura/allura/lib/repository.py      |  6 ++-
 Allura/allura/model/webhook.py       | 10 +++++
 Allura/allura/tests/test_webhooks.py | 72 ++++++++++++++++++++++++++++++-
 3 files changed, 86 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/d150c5c1/Allura/allura/lib/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index d5d467b..41e671d 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -293,4 +293,8 @@ class RestWebhooksLookup(BaseController):
         webhooks = self.app._webhooks
         if len(webhooks) == 0:
             raise exc.HTTPNotFound()
-        return {'test': 'works'}
+        configured_hooks = M.Webhook.query.find({
+            'type': {'$in': [wh.type for wh in webhooks]},
+            'app_config_id': self.app.config._id}
+        ).sort('_id', 1).all()
+        return {'webhooks': [hook.__json__() for hook in configured_hooks]}

http://git-wip-us.apache.org/repos/asf/allura/blob/d150c5c1/Allura/allura/model/webhook.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/webhook.py b/Allura/allura/model/webhook.py
index d1165ff..0d5140e 100644
--- a/Allura/allura/model/webhook.py
+++ b/Allura/allura/model/webhook.py
@@ -22,6 +22,7 @@ from paste.deploy.converters import asint
 from tg import config
 
 from allura.model import Artifact
+from allura.lib import helpers as h
 
 
 class Webhook(Artifact):
@@ -53,3 +54,12 @@ class Webhook(Artifact):
     def update_limit(self):
         self.last_sent = dt.datetime.utcnow()
         session(self).flush(self)
+
+    def __json__(self):
+        return {
+            '_id': unicode(self._id),
+            'url': h.absurl(u'/rest' + self.url()),
+            'type': unicode(self.type),
+            'hook_url': unicode(self.hook_url),
+            'mod_date': self.mod_date,
+        }

http://git-wip-us.apache.org/repos/asf/allura/blob/d150c5c1/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 330c4bd..d750d9d 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -27,6 +27,7 @@ from nose.tools import (
     assert_not_in,
     assert_in,
 )
+from datadiff import tools as dd
 from formencode import Invalid
 from ming.odm import session
 from pylons import tmpl_context as c
@@ -42,7 +43,11 @@ from allura.webhooks import (
     SendWebhookHelper,
 )
 from allura.tests import decorators as td
-from alluratest.controller import setup_basic_test, TestController
+from alluratest.controller import (
+    setup_basic_test,
+    TestController,
+    TestRestApiBase,
+)
 
 
 # important to be distinct from 'test' and 'test2' which ForgeGit and
@@ -639,3 +644,68 @@ class TestModels(TestWebhookBase):
         self.wh.update_limit()
         session(self.wh).expunge(self.wh)
         assert_equal(M.Webhook.query.get(_id=self.wh._id).last_sent, _now)
+
+    def test_json(self):
+        expected = {
+            '_id': unicode(self.wh._id),
+            'url': u'http://localhost/rest/adobe/adobe-1/admin'
+                   u'/src/webhooks/repo-push/{}'.format(self.wh._id),
+            'type': u'repo-push',
+            'hook_url': u'http://httpbin.org/post',
+            'mod_date': self.wh.mod_date,
+        }
+        dd.assert_equal(self.wh.__json__(), expected)
+
+
+
+class TestWebhookRestController(TestRestApiBase):
+    def setUp(self):
+        super(TestWebhookRestController, self).setUp()
+        self.patches = self.monkey_patch()
+        for p in self.patches:
+            p.start()
+        self.setup_with_tools()
+        self.project = M.Project.query.get(shortname=test_project_with_repo)
+        self.git = self.project.app_instance('src')
+        self.url = str('/rest' + self.git.admin_url + 'webhooks')
+        self.webhooks = []
+        for i in range(3):
+            webhook = M.Webhook(
+                type='repo-push',
+                app_config_id=self.git.config._id,
+                hook_url='http://httpbin.org/post/{}'.format(i),
+                secret='secret-{}'.format(i))
+            session(webhook).flush(webhook)
+            self.webhooks.append(webhook)
+
+    def tearDown(self):
+        super(TestWebhookRestController, self).tearDown()
+        for p in self.patches:
+            p.stop()
+
+    @with_git
+    def setup_with_tools(self):
+        pass
+
+    def monkey_patch(self):
+        gen_secret = patch.object(
+            WebhookController,
+            'gen_secret',
+            return_value='super-secret',
+            autospec=True)
+        # we don't need actual repo here, and this avoids test conflicts when
+        # running in parallel
+        repo_init = patch.object(M.Repository, 'init', autospec=True)
+        return [gen_secret, repo_init]
+
+    def test_webhooks_list(self):
+        r = self.api_get(self.url)
+        webhooks = [{
+            '_id': unicode(wh._id),
+            'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                   '/src/webhooks/repo-push/{}'.format(wh._id),
+            'type': 'repo-push',
+            'hook_url': 'http://httpbin.org/post/{}'.format(n),
+            'mod_date': unicode(wh.mod_date),
+        } for n, wh in enumerate(self.webhooks)]
+        dd.assert_equal(r.json, {'webhooks': webhooks})


[04/11] allura git commit: [#7832] ticket:731 Endpoint for creating webhook

Posted by je...@apache.org.
[#7832] ticket:731 Endpoint for creating webhook


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

Branch: refs/heads/ib/7832
Commit: c25c35a1ba7cbbc969b5f28bd9f83cf4cca3170c
Parents: 216c56a
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 13:52:38 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 23 09:45:15 2015 +0000

----------------------------------------------------------------------
 Allura/allura/tests/test_webhooks.py | 65 ++++++++++++++++++++++++++++++-
 Allura/allura/webhooks.py            | 61 +++++++++++++++++++++++++++++
 2 files changed, 125 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/c25c35a1/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 5bbee21..237de87 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -657,7 +657,6 @@ class TestModels(TestWebhookBase):
         dd.assert_equal(self.wh.__json__(), expected)
 
 
-
 class TestWebhookRestController(TestRestApiBase):
     def setUp(self):
         super(TestWebhookRestController, self).setUp()
@@ -730,3 +729,67 @@ class TestWebhookRestController(TestRestApiBase):
             'mod_date': unicode(webhook.mod_date),
         }
         dd.assert_equal(r.json, expected)
+
+    def test_create_validation(self):
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+        r = self.api_get(self.url + '/repo-push', {}, status=405)
+
+        r = self.api_post(self.url + '/repo-push', {}, status=400)
+        expected = {
+            u'result': u'error',
+            u'error': {u'url': u'Please enter a value'},
+        }
+        assert_equal(r.json, expected)
+
+        data = {'url': 'qwer', 'secret': 'qwe'}
+        r = self.app.post(self.url + '/repo-push', data, status=400)
+        expected = {
+            u'result': u'error',
+            u'error': {
+                u'url': u'You must provide a full domain name (like qwer.com)'
+            },
+        }
+        assert_equal(r.json, expected)
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+
+    def test_create(self):
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+        data = {u'url': u'http://hook.slack.com/abcd'}
+        limit = json.dumps({'git': 10})
+        with h.push_config(config, **{'webhook.repo_push.max_hooks': limit}):
+            r = self.app.post(self.url + '/repo-push', data, status=201)
+        webhook = M.Webhook.query.get(hook_url=data['url'])
+        assert_equal(webhook.secret, 'super-secret')  # secret generated
+        assert_equal(r.json['result'], 'ok')
+        assert_equal(r.json['webhook']['_id'], unicode(webhook._id))
+        assert_equal(r.json['webhook']['type'], 'repo-push')
+        assert_equal(r.json['webhook']['hook_url'], data['url'])
+        assert_equal(
+            r.json['webhook']['url'],
+            u'http://localhost/rest/adobe/adobe-1/admin'
+            u'/src/webhooks/repo-push/{}'.format(webhook._id))
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks) + 1)
+
+    def test_create_duplicates(self):
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+        data = {u'url': self.webhooks[0].hook_url}
+        limit = json.dumps({'git': 10})
+        with h.push_config(config, **{'webhook.repo_push.max_hooks': limit}):
+            r = self.app.post(self.url + '/repo-push', data, status=400)
+        expected = {u'result': u'error',
+                    u'error': u'_the_form: "repo-push" webhook already '
+                              u'exists for Git http://httpbin.org/post/0'}
+        assert_equal(r.json, expected)
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+
+    def test_create_limit_reached(self):
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+        data = {u'url': u'http://hook.slack.com/abcd'}
+        r = self.app.post(self.url + '/repo-push', data, status=400)
+        expected = {
+            u'result': u'error',
+            u'limits': {u'max': 3, u'used': 3},
+            u'error': u'You have exceeded the maximum number of webhooks '
+                      u'you are allowed to create for this project/app'}
+        assert_equal(r.json, expected)
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))

http://git-wip-us.apache.org/repos/asf/allura/blob/c25c35a1/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 74de2cd..d3313c1 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -28,6 +28,7 @@ from bson import ObjectId
 from tg import expose, validate, redirect, flash, config
 from tg.decorators import with_trailing_slash, without_trailing_slash
 from pylons import tmpl_context as c
+from pylons import response
 from formencode import validators as fev, schema, Invalid
 from ming.odm import session
 from webob import exc
@@ -199,6 +200,66 @@ class WebhookRestController(BaseController):
         self.create_form = WebhookController.create_form
         self.edit_form = WebhookController.edit_form
 
+    def _error(self, e):
+        error = getattr(e, 'error_dict', None)
+        if error:
+            _error = {}
+            for k, v in error.iteritems():
+                _error[k] = unicode(v)
+            return _error
+        error = getattr(e, 'msg', None)
+        if not error:
+            error = getattr(e, 'message', '')
+        return error
+
+    def update_webhook(self, wh, url, secret=None):
+        controller = WebhookController(self.sender.__class__, self.app)
+        controller.update_webhook(wh, url, secret)
+
+    @expose('json:')
+    @require_post()
+    def index(self, **kw):
+        response.content_type = 'application/json'
+        try:
+            params = {'secret': kw.pop('secret', ''),
+                      'url': kw.pop('url', None)}
+            valid = self.create_form().to_python(params)
+        except Exception as e:
+            response.status_int = 400
+            return {'result': 'error', 'error': self._error(e)}
+        if self.sender.enforce_limit(self.app):
+            webhook = M.Webhook(
+                type=self.sender.type,
+                app_config_id=self.app.config._id)
+            try:
+                self.update_webhook(webhook, valid['url'], valid['secret'])
+            except Invalid as e:
+                response.status_int = 400
+                return {'result': 'error', 'error': self._error(e)}
+            M.AuditLog.log('add webhook %s %s %s',
+                           webhook.type, webhook.hook_url,
+                           webhook.app_config.url())
+            response.status_int = 201
+            return {'result': 'ok', 'webhook': webhook.__json__()}
+        else:
+            limits = {
+                'max': M.Webhook.max_hooks(
+                    self.sender.type,
+                    self.app.config.tool_name),
+                'used': M.Webhook.query.find({
+                    'type': self.sender.type,
+                    'app_config_id': self.app.config._id,
+                }).count(),
+            }
+            resp = {
+                'result': 'error',
+                'error': 'You have exceeded the maximum number of webhooks '
+                         'you are allowed to create for this project/app',
+                'limits': limits,
+            }
+            response.status_int = 400
+            return resp
+
     @expose('json:')
     def _default(self, webhook, **kw):
         form = self.edit_form(self.sender, self.app)


[02/11] allura git commit: [#7832] ticket:731 Add limits to list API

Posted by je...@apache.org.
[#7832] ticket:731 Add limits to list API


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

Branch: refs/heads/ib/7832
Commit: 703b1249acc9c672aa7c5c3613e06fa7dbb93283
Parents: d150c5c
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 11:58:19 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 23 09:45:14 2015 +0000

----------------------------------------------------------------------
 Allura/allura/lib/repository.py      | 13 ++++++++++++-
 Allura/allura/model/webhook.py       |  7 +++++++
 Allura/allura/tests/test_webhooks.py |  6 +++++-
 Allura/allura/webhooks.py            |  5 ++---
 4 files changed, 26 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/703b1249/Allura/allura/lib/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index 41e671d..07b1b06 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -22,6 +22,7 @@ from pylons import tmpl_context as c, app_globals as g
 from pylons import request
 from tg import expose, redirect, flash, validate
 from tg.decorators import with_trailing_slash, without_trailing_slash
+from webob import exc
 from bson import ObjectId
 
 from ming.utils import LazyProperty
@@ -297,4 +298,14 @@ class RestWebhooksLookup(BaseController):
             'type': {'$in': [wh.type for wh in webhooks]},
             'app_config_id': self.app.config._id}
         ).sort('_id', 1).all()
-        return {'webhooks': [hook.__json__() for hook in configured_hooks]}
+        limits = {
+            wh.type: {
+                'max': M.Webhook.max_hooks(wh.type, self.app.config.tool_name),
+                'used': M.Webhook.query.find({
+                    'type': wh.type,
+                    'app_config_id': self.app.config._id,
+                }).count(),
+            } for wh in webhooks
+        }
+        return {'webhooks': [hook.__json__() for hook in configured_hooks],
+                'limits': limits}

http://git-wip-us.apache.org/repos/asf/allura/blob/703b1249/Allura/allura/model/webhook.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/webhook.py b/Allura/allura/model/webhook.py
index 0d5140e..bb36e5f 100644
--- a/Allura/allura/model/webhook.py
+++ b/Allura/allura/model/webhook.py
@@ -16,6 +16,7 @@
 #       under the License.
 
 import datetime as dt
+import json
 
 from ming.odm import FieldProperty, session
 from paste.deploy.converters import asint
@@ -55,6 +56,12 @@ class Webhook(Artifact):
         self.last_sent = dt.datetime.utcnow()
         session(self).flush(self)
 
+    @classmethod
+    def max_hooks(self, type, tool_name):
+        type = type.replace('-', '_')
+        limits = json.loads(config.get('webhook.%s.max_hooks' % type, '{}'))
+        return limits.get(tool_name.lower(), 3)
+
     def __json__(self):
         return {
             '_id': unicode(self._id),

http://git-wip-us.apache.org/repos/asf/allura/blob/703b1249/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index d750d9d..c758df1 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -708,4 +708,8 @@ class TestWebhookRestController(TestRestApiBase):
             'hook_url': 'http://httpbin.org/post/{}'.format(n),
             'mod_date': unicode(wh.mod_date),
         } for n, wh in enumerate(self.webhooks)]
-        dd.assert_equal(r.json, {'webhooks': webhooks})
+        expected = {
+            'webhooks': webhooks,
+            'limits': {'repo-push': {'max': 3, 'used': 3}},
+        }
+        dd.assert_equal(r.json, expected)

http://git-wip-us.apache.org/repos/asf/allura/blob/703b1249/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index fc21bf4..abcae63 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -313,13 +313,12 @@ class WebhookSender(object):
         Checks if limit of webhooks created for given project/app is reached.
         Returns False if limit is reached, True otherwise.
         '''
-        _type = self.type.replace('-', '_')
-        limits = json.loads(config.get('webhook.%s.max_hooks' % _type, '{}'))
         count = M.Webhook.query.find(dict(
             app_config_id=app.config._id,
             type=self.type,
         )).count()
-        return count < limits.get(app.config.tool_name.lower(), 3)
+        limit = M.Webhook.max_hooks(self.type, app.config.tool_name)
+        return count < limit
 
 
 class RepoPushWebhookSender(WebhookSender):


[07/11] allura git commit: [#7832] ticket:731 Endpoint for editing webhooks

Posted by je...@apache.org.
[#7832] ticket:731 Endpoint for editing webhooks


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

Branch: refs/heads/ib/7832
Commit: 3bc94d0917728523bef84e1310ba2fcbb1514595
Parents: c25c35a
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 16:27:06 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 23 09:45:16 2015 +0000

----------------------------------------------------------------------
 Allura/allura/tests/test_webhooks.py | 125 +++++++++++++++++++++++++-----
 Allura/allura/webhooks.py            |  38 ++++++++-
 2 files changed, 140 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/3bc94d09/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 237de87..ee9cd0b 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -721,20 +721,23 @@ class TestWebhookRestController(TestRestApiBase):
         webhook = self.webhooks[0]
         r = self.api_get('{}/repo-push/{}'.format(self.url, webhook._id))
         expected = {
-            '_id': unicode(webhook._id),
-            'url': 'http://localhost/rest/adobe/adobe-1/admin'
-                   '/src/webhooks/repo-push/{}'.format(webhook._id),
-            'type': 'repo-push',
-            'hook_url': 'http://httpbin.org/post/0',
-            'mod_date': unicode(webhook.mod_date),
+            u'result': u'ok',
+            u'webhook': {
+                '_id': unicode(webhook._id),
+                'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                       '/src/webhooks/repo-push/{}'.format(webhook._id),
+                'type': 'repo-push',
+                'hook_url': 'http://httpbin.org/post/0',
+                'mod_date': unicode(webhook.mod_date),
+            },
         }
         dd.assert_equal(r.json, expected)
 
     def test_create_validation(self):
         assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
-        r = self.api_get(self.url + '/repo-push', {}, status=405)
+        r = self.api_get(self.url + '/repo-push', status=405)
 
-        r = self.api_post(self.url + '/repo-push', {}, status=400)
+        r = self.api_post(self.url + '/repo-push', status=400)
         expected = {
             u'result': u'error',
             u'error': {u'url': u'Please enter a value'},
@@ -742,7 +745,7 @@ class TestWebhookRestController(TestRestApiBase):
         assert_equal(r.json, expected)
 
         data = {'url': 'qwer', 'secret': 'qwe'}
-        r = self.app.post(self.url + '/repo-push', data, status=400)
+        r = self.api_post(self.url + '/repo-push', status=400, **data)
         expected = {
             u'result': u'error',
             u'error': {
@@ -757,17 +760,24 @@ class TestWebhookRestController(TestRestApiBase):
         data = {u'url': u'http://hook.slack.com/abcd'}
         limit = json.dumps({'git': 10})
         with h.push_config(config, **{'webhook.repo_push.max_hooks': limit}):
-            r = self.app.post(self.url + '/repo-push', data, status=201)
+            msg = 'add webhook repo-push {} {}'.format(
+                data['url'], self.git.config.url())
+            with td.audits(msg):
+                r = self.api_post(self.url + '/repo-push', status=201, **data)
         webhook = M.Webhook.query.get(hook_url=data['url'])
         assert_equal(webhook.secret, 'super-secret')  # secret generated
-        assert_equal(r.json['result'], 'ok')
-        assert_equal(r.json['webhook']['_id'], unicode(webhook._id))
-        assert_equal(r.json['webhook']['type'], 'repo-push')
-        assert_equal(r.json['webhook']['hook_url'], data['url'])
-        assert_equal(
-            r.json['webhook']['url'],
-            u'http://localhost/rest/adobe/adobe-1/admin'
-            u'/src/webhooks/repo-push/{}'.format(webhook._id))
+        expected = {
+            u'result': u'ok',
+            u'webhook': {
+                '_id': unicode(webhook._id),
+                'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                       '/src/webhooks/repo-push/{}'.format(webhook._id),
+                'type': 'repo-push',
+                'hook_url': data['url'],
+                'mod_date': unicode(webhook.mod_date),
+            },
+        }
+        dd.assert_equal(r.json, expected)
         assert_equal(M.Webhook.query.find().count(), len(self.webhooks) + 1)
 
     def test_create_duplicates(self):
@@ -775,7 +785,7 @@ class TestWebhookRestController(TestRestApiBase):
         data = {u'url': self.webhooks[0].hook_url}
         limit = json.dumps({'git': 10})
         with h.push_config(config, **{'webhook.repo_push.max_hooks': limit}):
-            r = self.app.post(self.url + '/repo-push', data, status=400)
+            r = self.api_post(self.url + '/repo-push', status=400, **data)
         expected = {u'result': u'error',
                     u'error': u'_the_form: "repo-push" webhook already '
                               u'exists for Git http://httpbin.org/post/0'}
@@ -785,7 +795,7 @@ class TestWebhookRestController(TestRestApiBase):
     def test_create_limit_reached(self):
         assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
         data = {u'url': u'http://hook.slack.com/abcd'}
-        r = self.app.post(self.url + '/repo-push', data, status=400)
+        r = self.api_post(self.url + '/repo-push', status=400, **data)
         expected = {
             u'result': u'error',
             u'limits': {u'max': 3, u'used': 3},
@@ -793,3 +803,78 @@ class TestWebhookRestController(TestRestApiBase):
                       u'you are allowed to create for this project/app'}
         assert_equal(r.json, expected)
         assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+
+    def test_edit_validation(self):
+        webhook = self.webhooks[0]
+        url = u'{}/repo-push/{}'.format(self.url, webhook._id)
+        data = {'url': 'qwe', 'secret': 'qwe'}
+        r = self.api_post(url, status=400, **data)
+        expected = {
+            u'result': u'error',
+            u'error': {
+                u'url': u'You must provide a full domain name (like qwe.com)'
+            },
+        }
+        assert_equal(r.json, expected)
+
+    def test_edit(self):
+        webhook = self.webhooks[0]
+        url = '{}/repo-push/{}'.format(self.url, webhook._id)
+        # change only url
+        data = {'url': 'http://hook.slack.com/abcd'}
+        msg = ('edit webhook repo-push\n'
+               'http://httpbin.org/post/0 => http://hook.slack.com/abcd\n')
+        with td.audits(msg):
+            r = self.api_post(url, status=200, **data)
+        webhook = M.Webhook.query.get(_id=webhook._id)
+        assert_equal(webhook.hook_url, data['url'])
+        assert_equal(webhook.secret, 'secret-0')
+        expected = {
+            u'result': u'ok',
+            u'webhook': {
+                '_id': unicode(webhook._id),
+                'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                       '/src/webhooks/repo-push/{}'.format(webhook._id),
+                'type': 'repo-push',
+                'hook_url': data['url'],
+                'mod_date': unicode(webhook.mod_date),
+            },
+        }
+        dd.assert_equal(r.json, expected)
+
+        # change only secret
+        data = {'secret': 'new-secret'}
+        msg = ('edit webhook repo-push\n'
+               'http://hook.slack.com/abcd => http://hook.slack.com/abcd\n'
+               'secret changed')
+        with td.audits(msg):
+            r = self.api_post(url, status=200, **data)
+        webhook = M.Webhook.query.get(_id=webhook._id)
+        assert_equal(webhook.hook_url, 'http://hook.slack.com/abcd')
+        assert_equal(webhook.secret, 'new-secret')
+        expected = {
+            u'result': u'ok',
+            u'webhook': {
+                '_id': unicode(webhook._id),
+                'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                       '/src/webhooks/repo-push/{}'.format(webhook._id),
+                'type': 'repo-push',
+                'hook_url': 'http://hook.slack.com/abcd',
+                'mod_date': unicode(webhook.mod_date),
+            },
+        }
+        dd.assert_equal(r.json, expected)
+
+    def test_edit_duplicates(self):
+        webhook = self.webhooks[0]
+        url = '{}/repo-push/{}'.format(self.url, webhook._id)
+        # change only url
+        data = {'url': 'http://httpbin.org/post/1'}
+        r = self.api_post(url, status=400, **data)
+        expected = {u'result': u'error',
+                    u'error': u'_the_form: "repo-push" webhook already '
+                              u'exists for Git http://httpbin.org/post/1'}
+        assert_equal(r.json, expected)
+        webhook = M.Webhook.query.get(_id=webhook._id)
+        assert_equal(webhook.hook_url, 'http://httpbin.org/post/0')
+        assert_equal(webhook.secret, 'secret-0')

http://git-wip-us.apache.org/repos/asf/allura/blob/3bc94d09/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index d3313c1..6f06602 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -28,7 +28,7 @@ from bson import ObjectId
 from tg import expose, validate, redirect, flash, config
 from tg.decorators import with_trailing_slash, without_trailing_slash
 from pylons import tmpl_context as c
-from pylons import response
+from pylons import response, request
 from formencode import validators as fev, schema, Invalid
 from ming.odm import session
 from webob import exc
@@ -240,7 +240,10 @@ class WebhookRestController(BaseController):
                            webhook.type, webhook.hook_url,
                            webhook.app_config.url())
             response.status_int = 201
-            return {'result': 'ok', 'webhook': webhook.__json__()}
+            # refetch updated values (e.g. mod_date)
+            session(webhook).expunge(webhook)
+            webhook = M.Webhook.query.get(_id=webhook._id)
+            return {'result': 'ok', 'webhook': webhook}
         else:
             limits = {
                 'max': M.Webhook.max_hooks(
@@ -267,7 +270,36 @@ class WebhookRestController(BaseController):
             wh = form.fields['webhook'].to_python(webhook)
         except Invalid:
             raise exc.HTTPNotFound()
-        return wh.__json__()
+        if request.method == 'POST':
+            return self._edit(wh, form, **kw)
+        else:
+            return {'result': 'ok', 'webhook': wh}
+
+    def _edit(self, webhook, form, **kw):
+        old_secret = webhook.secret
+        old_url = webhook.hook_url
+        try:
+            params = {'secret': kw.pop('secret', old_secret),
+                      'url': kw.pop('url', old_url),
+                      'webhook': unicode(webhook._id)}
+            valid = form.to_python(params)
+        except Exception as e:
+            response.status_int = 400
+            return {'result': 'error', 'error': self._error(e)}
+        try:
+            self.update_webhook(webhook, valid['url'], valid['secret'])
+        except Invalid as e:
+            response.status_int = 400
+            return {'result': 'error', 'error': self._error(e)}
+        M.AuditLog.log(
+            'edit webhook %s\n%s => %s\n%s',
+            webhook.type, old_url, valid['url'],
+            'secret changed' if old_secret != valid['secret'] else '')
+        # refetch updated values (e.g. mod_date)
+        session(webhook).expunge(webhook)
+        webhook = M.Webhook.query.get(_id=webhook._id)
+        return {'result': 'ok',
+                'webhook': webhook}
 
 
 class SendWebhookHelper(object):


[05/11] allura git commit: [#7832] ticket:731 Webhook GET endpoint

Posted by je...@apache.org.
[#7832] ticket:731 Webhook GET endpoint


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

Branch: refs/heads/ib/7832
Commit: 216c56a5b2d2e1588a51ba6961fef02f5a1ee3c0
Parents: 703b124
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 12:14:28 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 23 09:45:15 2015 +0000

----------------------------------------------------------------------
 Allura/allura/lib/repository.py      |  7 +++++++
 Allura/allura/tests/test_webhooks.py | 17 +++++++++++++++++
 Allura/allura/webhooks.py            | 19 +++++++++++++++++++
 3 files changed, 43 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/216c56a5/Allura/allura/lib/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index 07b1b06..8fd414f 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -309,3 +309,10 @@ class RestWebhooksLookup(BaseController):
         }
         return {'webhooks': [hook.__json__() for hook in configured_hooks],
                 'limits': limits}
+
+    @expose()
+    def _lookup(self, name, *remainder):
+        for hook in self.app._webhooks:
+            if hook.type == name and hook.api_controller:
+                return hook.api_controller(hook, self.app), remainder
+        raise exc.HTTPNotFound, name

http://git-wip-us.apache.org/repos/asf/allura/blob/216c56a5/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index c758df1..5bbee21 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -713,3 +713,20 @@ class TestWebhookRestController(TestRestApiBase):
             'limits': {'repo-push': {'max': 3, 'used': 3}},
         }
         dd.assert_equal(r.json, expected)
+
+    def test_webhook_GET_404(self):
+        r = self.api_get(self.url + '/repo-push/invalid')
+        assert_equal(r.status_int, 404)
+
+    def test_webhook_GET(self):
+        webhook = self.webhooks[0]
+        r = self.api_get('{}/repo-push/{}'.format(self.url, webhook._id))
+        expected = {
+            '_id': unicode(webhook._id),
+            'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                   '/src/webhooks/repo-push/{}'.format(webhook._id),
+            'type': 'repo-push',
+            'hook_url': 'http://httpbin.org/post/0',
+            'mod_date': unicode(webhook.mod_date),
+        }
+        dd.assert_equal(r.json, expected)

http://git-wip-us.apache.org/repos/asf/allura/blob/216c56a5/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index abcae63..74de2cd 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -191,6 +191,24 @@ class WebhookController(BaseController):
                 'form': form}
 
 
+class WebhookRestController(BaseController):
+    def __init__(self, sender, app):
+        super(WebhookRestController, self).__init__()
+        self.sender = sender()
+        self.app = app
+        self.create_form = WebhookController.create_form
+        self.edit_form = WebhookController.edit_form
+
+    @expose('json:')
+    def _default(self, webhook, **kw):
+        form = self.edit_form(self.sender, self.app)
+        try:
+            wh = form.fields['webhook'].to_python(webhook)
+        except Invalid:
+            raise exc.HTTPNotFound()
+        return wh.__json__()
+
+
 class SendWebhookHelper(object):
     def __init__(self, webhook, payload):
         self.webhook = webhook
@@ -278,6 +296,7 @@ class WebhookSender(object):
     type = None
     triggered_by = []
     controller = WebhookController
+    api_controller = WebhookRestController
 
     def get_payload(self, **kw):
         """Return a dict with webhook payload"""


[10/11] allura git commit: [#7832] ticket:731 Support oauth via headers

Posted by je...@apache.org.
[#7832] ticket:731 Support oauth via headers

Currently, we check only body of the request to determine if oauth tokens are
provided. We need to be able to authenticate via headers also. This is useful
for some HTTP methods, which don't expect parameters in request body (e.g.
DELETE).


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

Branch: refs/heads/ib/7832
Commit: 55c074833d813825cba03b418d37e710c5e89395
Parents: ebc351f
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Feb 23 12:00:09 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 23 12:00:09 2015 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/rest.py   | 19 +++++++++++++++----
 AlluraTest/alluratest/controller.py |  6 +++++-
 2 files changed, 20 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/55c07483/Allura/allura/controllers/rest.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
index 1001343..2c1a042 100644
--- a/Allura/allura/controllers/rest.py
+++ b/Allura/allura/controllers/rest.py
@@ -47,7 +47,12 @@ class RestController(object):
 
     def _authenticate_request(self):
         'Based on request.params or oauth, authenticate the request'
-        if 'oauth_token' in request.params or 'access_token' in request.params:
+        headers_auth = 'Authorization' in request.headers
+        params_auth = 'oauth_token' in request.params
+        params_auth = params_auth or 'access_token' in request.params
+        log.error(headers_auth)
+        log.error(request.headers)
+        if headers_auth or params_auth:
             return self.oauth._authenticate()
         else:
             return None
@@ -104,15 +109,21 @@ class OAuthNegotiator(object):
         return result
 
     def _authenticate(self):
-        if 'access_token' in request.params:
+        bearer_token_prefix = 'OAuth BearerToken access_token='
+        auth = request.headers.get('Authorization')
+        log.error(auth)
+        if auth and auth.startswith(bearer_token_prefix):
+            access_token = auth[len(bearer_token_prefix):]
+        else:
+            access_token = request.params.get('access_token')
+        if access_token:
             # handle bearer tokens
             # skip https check if auth invoked from tests
             testing = request.environ.get('paste.testing', False)
             if not testing and request.scheme != 'https':
                 request.environ['pylons.status_code_redirect'] = True
                 raise exc.HTTPForbidden
-            access_token = M.OAuthAccessToken.query.get(
-                api_key=request.params['access_token'])
+            access_token = M.OAuthAccessToken.query.get(api_key=access_token)
             if not (access_token and access_token.is_bearer):
                 request.environ['pylons.status_code_redirect'] = True
                 raise exc.HTTPForbidden

http://git-wip-us.apache.org/repos/asf/allura/blob/55c07483/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index 4398a82..c64ea5f 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -222,13 +222,17 @@ class TestRestApiBase(TestController):
             status = [200, 201, 301, 302, 400, 403, 404]
         params = variabledecode.variable_encode(params, add_repetitions=False)
 
-        params['access_token'] = self.token(user).api_key
+        token = self.token(user).api_key
+        headers = {
+            'Authorization': 'OAuth BearerToken access_token={}'.format(token)
+        }
 
         fn = getattr(self.app, method.lower())
 
         response = fn(
             str(path),
             params=params,
+            headers=headers,
             status=status)
         if response.status_int in [301, 302]:
             return response.follow()


[09/11] allura git commit: [#7832] ticket:731 Test for permissions

Posted by je...@apache.org.
[#7832] ticket:731 Test for permissions


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

Branch: refs/heads/ib/7832
Commit: ebc351f250aaef6ce9054e915bf9a618dc34b6dd
Parents: 63f7e57
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Feb 23 10:34:35 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 23 10:34:35 2015 +0000

----------------------------------------------------------------------
 Allura/allura/tests/test_webhooks.py | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/ebc351f2/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 50a9803..a336f32 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -894,3 +894,17 @@ class TestWebhookRestController(TestRestApiBase):
         dd.assert_equal(r.json, {u'result': u'ok'})
         assert_equal(M.Webhook.query.find().count(), 2)
         assert_equal(M.Webhook.query.get(_id=webhook._id), None)
+
+    def test_permissions(self):
+        self.api_get(self.url, user='test-user', status=403)
+        self.api_get(self.url, user='*anonymous', status=401)
+        url = self.url + '/repo-push/'
+        self.api_post(url, user='test-user', status=403)
+        self.api_post(url, user='*anonymous', status=401)
+        url = self.url + '/repo-push/' + str(self.webhooks[0]._id)
+        self.api_get(url, user='test-user', status=403)
+        self.api_get(url, user='*anonymous', status=401)
+        self.api_post(url, user='test-user', status=403)
+        self.api_post(url, user='*anonymous', status=401)
+        self.api_delete(url, user='test-user', status=403)
+        self.api_delete(url, user='*anonymous', status=401)


[08/11] allura git commit: [#7832] ticket:731 Delete webhook endpoint

Posted by je...@apache.org.
[#7832] ticket:731 Delete webhook endpoint


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

Branch: refs/heads/ib/7832
Commit: 63f7e57520f0d1da2d71f72711ae7a70a79af9d0
Parents: 3bb19e9
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Feb 23 09:58:09 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 23 09:58:09 2015 +0000

----------------------------------------------------------------------
 Allura/allura/tests/test_webhooks.py | 16 ++++++++++++++++
 Allura/allura/webhooks.py            | 11 +++++++++++
 2 files changed, 27 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/63f7e575/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index ee9cd0b..50a9803 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -878,3 +878,19 @@ class TestWebhookRestController(TestRestApiBase):
         webhook = M.Webhook.query.get(_id=webhook._id)
         assert_equal(webhook.hook_url, 'http://httpbin.org/post/0')
         assert_equal(webhook.secret, 'secret-0')
+
+    def test_delete_validation(self):
+        url = '{}/repo-push/invalid'.format(self.url)
+        self.api_delete(url, status=404)
+
+    def test_delete(self):
+        assert_equal(M.Webhook.query.find().count(), 3)
+        webhook = self.webhooks[0]
+        url = '{}/repo-push/{}'.format(self.url, webhook._id)
+        msg = 'delete webhook repo-push {} {}'.format(
+            webhook.hook_url, self.git.config.url())
+        with td.audits(msg):
+            r = self.api_delete(url, status=200)
+        dd.assert_equal(r.json, {u'result': u'ok'})
+        assert_equal(M.Webhook.query.find().count(), 2)
+        assert_equal(M.Webhook.query.get(_id=webhook._id), None)

http://git-wip-us.apache.org/repos/asf/allura/blob/63f7e575/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 6f06602..9b5b41c 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -272,6 +272,8 @@ class WebhookRestController(BaseController):
             raise exc.HTTPNotFound()
         if request.method == 'POST':
             return self._edit(wh, form, **kw)
+        elif request.method == 'DELETE':
+            return self._delete(wh)
         else:
             return {'result': 'ok', 'webhook': wh}
 
@@ -301,6 +303,15 @@ class WebhookRestController(BaseController):
         return {'result': 'ok',
                 'webhook': webhook}
 
+    def _delete(self, webhook):
+        webhook.delete()
+        M.AuditLog.log(
+            'delete webhook %s %s %s',
+            webhook.type,
+            webhook.hook_url,
+            webhook.app_config.url())
+        return {'result': 'ok'}
+
 
 class SendWebhookHelper(object):
     def __init__(self, webhook, payload):