You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2014/06/04 22:52:00 UTC

[01/16] git commit: [#1687] ticket:574 Skip https check during OAuth authentication process, when running tests

Repository: allura
Updated Branches:
  refs/heads/master cf55cdddb -> fd00be035


[#1687] ticket:574 Skip https check during OAuth authentication process, when running tests


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

Branch: refs/heads/master
Commit: 4ce64a4beff502a3e6bb9b6dceea0bb15d5daadd
Parents: 49f7e10
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri May 2 14:05:16 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:21 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/rest.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/4ce64a4b/Allura/allura/controllers/rest.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
index 85a6df1..3625032 100644
--- a/Allura/allura/controllers/rest.py
+++ b/Allura/allura/controllers/rest.py
@@ -106,7 +106,9 @@ class OAuthNegotiator(object):
     def _authenticate(self):
         if 'access_token' in request.params:
             # handle bearer tokens
-            if request.scheme != 'https':
+            # 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(


[05/16] git commit: [#1687] ticket:574 Remove oauth tokens from preferences

Posted by br...@apache.org.
[#1687] ticket:574 Remove oauth tokens from preferences


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

Branch: refs/heads/master
Commit: 65cc6e789d69ed33251071727940b793cf350082
Parents: cf55cdd
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Apr 28 12:07:11 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:21 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/auth.py       | 25 +------------------------
 Allura/allura/templates/user_prefs.html | 23 -----------------------
 2 files changed, 1 insertion(+), 47 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/65cc6e78/Allura/allura/controllers/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index c25e33e..ff2d42f 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -341,11 +341,7 @@ class PreferencesController(BaseController):
     def index(self, **kw):
         provider = plugin.AuthenticationProvider.get(request)
         menu = provider.account_navigation()
-        api_token = M.ApiToken.query.get(user_id=c.user._id)
-        return dict(
-            menu=menu,
-            api_token=api_token,
-        )
+        return dict(menu=menu)
 
     @h.vardec
     @expose()
@@ -392,25 +388,6 @@ class PreferencesController(BaseController):
 
     @expose()
     @require_post()
-    def gen_api_token(self):
-        tok = M.ApiToken.query.get(user_id=c.user._id)
-        if tok is None:
-            tok = M.ApiToken(user_id=c.user._id)
-        else:
-            tok.secret_key = h.cryptographic_nonce()
-        redirect(request.referer)
-
-    @expose()
-    @require_post()
-    def del_api_token(self):
-        tok = M.ApiToken.query.get(user_id=c.user._id)
-        if tok is None:
-            return
-        tok.delete()
-        redirect(request.referer)
-
-    @expose()
-    @require_post()
     @validate(V.NullValidator(), error_handler=index)
     def change_password(self, **kw):
         kw = g.theme.password_change_form.to_python(kw, None)

http://git-wip-us.apache.org/repos/asf/allura/blob/65cc6e78/Allura/allura/templates/user_prefs.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/user_prefs.html b/Allura/allura/templates/user_prefs.html
index 25d1120..fbf47d8 100644
--- a/Allura/allura/templates/user_prefs.html
+++ b/Allura/allura/templates/user_prefs.html
@@ -106,29 +106,6 @@
   </div>
   {% endif %}
 
-  {% if tg.config.get('auth.method', 'local') == 'local' %}
-  <div class="grid-20">
-      <h2>API Token</h2>
-      {% if api_token %}
-        <p>
-          <b>API Key:</b><br/>
-          {{api_token.api_key}}<br/>
-          <b>Secret Key:</b><br/>
-          {{api_token.secret_key}}<br/>
-        </p>
-        <form method="POST" action="del_api_token" class="grid-18">
-          <input type="submit" value="Delete API Token">
-          {{lib.csrf_token()}}
-        </form>
-      {% else %}
-        <p>No API token generated</p>
-      {% endif %}
-      <form method="POST" action="gen_api_token" class="grid-18">
-        <input type="submit" value="(Re)generate API Token">
-        {{lib.csrf_token()}}
-      </form>
-  </div>
-  {% endif %}
 <div class="grid-20">
     <h2>User Messages</h2>
     <form method="POST" action="user_message">


[02/16] git commit: [#1687] ticket:574 Remove RestClient, AlluraImportAPIClient, etc

Posted by br...@apache.org.
[#1687] ticket:574 Remove RestClient, AlluraImportAPIClient, etc


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

Branch: refs/heads/master
Commit: 49f7e10b2da230baadd1336a27ab8781f58c8cdc
Parents: 44a7f5e
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri May 2 13:25:04 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:21 2014 +0000

----------------------------------------------------------------------
 Allura/allura/lib/import_api.py                 |  69 ------
 Allura/allura/lib/rest_api.py                   | 150 -------------
 AlluraTest/alluratest/test_syntax.py            |   1 -
 .../forgetracker/scripts/import_tracker.py      | 144 -------------
 scripts/tracker-rip.py                          | 211 -------------------
 5 files changed, 575 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/49f7e10b/Allura/allura/lib/import_api.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/import_api.py b/Allura/allura/lib/import_api.py
deleted file mode 100644
index d8159c5..0000000
--- a/Allura/allura/lib/import_api.py
+++ /dev/null
@@ -1,69 +0,0 @@
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-import urllib
-import urllib2
-import urlparse
-import hmac
-import hashlib
-import json
-import logging
-from datetime import datetime
-
-log = logging.getLogger(__name__)
-
-
-class AlluraImportApiClient(object):
-
-    def __init__(self, base_url, api_key, secret_key, verbose=False, retry=True):
-        self.base_url = base_url
-        self.api_key = api_key
-        self.secret_key = secret_key
-        self.verbose = verbose
-        self.retry = retry
-
-    def sign(self, path, params):
-        params.append(('api_key', self.api_key))
-        params.append(('api_timestamp', datetime.utcnow().isoformat()))
-        message = path + '?' + urllib.urlencode(sorted(params))
-        digest = hmac.new(self.secret_key, message, hashlib.sha256).hexdigest()
-        params.append(('api_signature', digest))
-        return params
-
-    def call(self, url, **params):
-        url = urlparse.urljoin(self.base_url, url)
-        if self.verbose:
-            log.info("Import API URL: %s", url)
-
-        params = self.sign(urlparse.urlparse(url).path, params.items())
-
-        while True:
-            try:
-                result = urllib2.urlopen(url, urllib.urlencode(params))
-                resp = result.read()
-                return json.loads(resp)
-            except urllib2.HTTPError, e:
-                e.msg += ' ({0})'.format(url)
-                if self.verbose:
-                    error_content = e.read()
-                    e.msg += '. Error response:\n' + error_content
-                raise e
-            except (urllib2.URLError, IOError):
-                if self.retry:
-                    log.exception('Error making API request, will retry')
-                    continue
-                raise

http://git-wip-us.apache.org/repos/asf/allura/blob/49f7e10b/Allura/allura/lib/rest_api.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/rest_api.py b/Allura/allura/lib/rest_api.py
deleted file mode 100644
index dcc0d7d..0000000
--- a/Allura/allura/lib/rest_api.py
+++ /dev/null
@@ -1,150 +0,0 @@
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-import hmac
-import json
-import hashlib
-import urllib
-import urllib2
-import logging
-from urlparse import urljoin, urlparse
-from datetime import datetime
-
-from tg.controllers.util import smart_str
-from formencode import variabledecode
-
-
-log = logging.getLogger(__name__)
-
-
-class RestClient(object):
-
-    def __init__(self, api_key, secret_key, base_uri,
-                 http_username=None, http_password=None):
-        self._api_key = api_key
-        self._secret_key = secret_key
-        self.base_uri = base_uri
-        self.Request = self._request_class()
-        self.RedirectHandler = self._redirect_handler_class()
-        redirect_handler = self.RedirectHandler()
-        if http_username:
-            pw_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
-            pw_mgr.add_password(None, base_uri, http_username, http_password)
-            auth_handler = urllib2.HTTPBasicAuthHandler(pw_mgr)
-            self._opener = urllib2.build_opener(redirect_handler, auth_handler)
-        else:
-            self._opener = urllib2.build_opener(redirect_handler)
-
-    def sign_request(self, path, params):
-        if hasattr(params, 'items'):
-            params = params.items()
-        has_api_key = has_api_timestamp = has_api_signature = False
-        for k, v in params:
-            if k == 'api_key':
-                has_api_key = True
-            if k == 'api_timestamp':
-                has_api_timestamp = True
-            if k == 'api_signature':
-                has_api_signature = True
-        if not has_api_key:
-            params.append(('api_key', self._api_key))
-        if not has_api_timestamp:
-            params.append(('api_timestamp', datetime.utcnow().isoformat()))
-        if not has_api_signature:
-            string_to_sign = path + '?' + urlencode(sorted(params))
-            digest = hmac.new(self._secret_key, string_to_sign, hashlib.sha256)
-            params.append(('api_signature', digest.hexdigest()))
-        return params
-
-    def request(self, method, path, **params):
-        req = self.Request(method, path, params)
-        fp = self._opener.open(req)
-        return json.loads(fp.read())
-
-    def _redirect_handler_class(self):
-        client = self
-
-        class RedirectHandler(urllib2.HTTPRedirectHandler):
-
-            def redirect_request(self, req, fp, code, msg, headers, newurl):
-                m = req.get_method()
-                if (code in (301, 302, 303, 307) and m in ("GET", "HEAD")
-                        or code in (301, 302, 303) and m == "POST"):
-                        newurl = newurl.replace(' ', '%20')
-                        newheaders = dict((k, v) for k, v in req.headers.items()
-                                          if k.lower() not in ("content-length", "content-type")
-                                          )
-                        result = urlparse(newurl)
-                        log.debug('Redirect to %s' % result.path)
-                        return client.Request(
-                            'GET', result.path,
-                            headers=newheaders,
-                            origin_req_host=req.get_origin_req_host(),
-                            unverifiable=True)
-                else:
-                        raise urllib2.HTTPError(
-                            req.get_full_url(), code, msg, headers, fp)
-        return RedirectHandler
-
-    def _request_class(self):
-        client = self
-
-        class Request(urllib2.Request):
-
-            def __init__(self, method, path, params=None, **kwargs):
-                if params is None:
-                    params = {}
-                params = variabledecode.variable_encode(
-                    params, add_repetitions=False)
-                params = client.sign_request(path, params)
-                self._method = method.upper()
-                if self._method == 'GET':
-                    url = urljoin(client.base_uri, path) + \
-                        '?' + urlencode(params)
-                    data = None
-                else:
-                    url = urljoin(client.base_uri, path)
-                    data = urlencode(params)
-                urllib2.Request.__init__(self, url, data=data, **kwargs)
-
-            def get_method(self):
-                return self._method
-        return Request
-
-
-def generate_smart_str(params):
-    if isinstance(params, dict):
-        iterparams = params.iteritems()
-    else:
-        iterparams = iter(params)
-    for key, value in iterparams:
-        if value is None:
-            continue
-        if isinstance(value, (list, tuple)):
-            for item in value:
-                yield smart_str(key), smart_str(item)
-        else:
-            yield smart_str(key), smart_str(value)
-
-
-def urlencode(params):
-    """
-    A version of Python's urllib.urlencode() function that can operate on
-    unicode strings. The parameters are first case to UTF-8 encoded strings and
-    then encoded as per normal.
-    """
-    return urllib.urlencode([i for i in generate_smart_str(params)])

http://git-wip-us.apache.org/repos/asf/allura/blob/49f7e10b/AlluraTest/alluratest/test_syntax.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/test_syntax.py b/AlluraTest/alluratest/test_syntax.py
index c782aac..b30a6b8 100644
--- a/AlluraTest/alluratest/test_syntax.py
+++ b/AlluraTest/alluratest/test_syntax.py
@@ -94,7 +94,6 @@ def test_no_prints():
         'Allura/ez_setup/',
         'Allura/allura/lib/AsciiDammit.py',
         '/scripts/',
-        'Allura/allura/lib/import_api.py',
         'ForgeSVN/setup.py',
     ]
     if run(find_py + " | grep -v '" + "' | grep -v '".join(skips) + "' | xargs grep -v '^ *#' | grep 'print ' | grep -E -v '(pprint|#pragma: ?printok)' ") != 1:

http://git-wip-us.apache.org/repos/asf/allura/blob/49f7e10b/ForgeTracker/forgetracker/scripts/import_tracker.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/scripts/import_tracker.py b/ForgeTracker/forgetracker/scripts/import_tracker.py
deleted file mode 100644
index c991d11..0000000
--- a/ForgeTracker/forgetracker/scripts/import_tracker.py
+++ /dev/null
@@ -1,144 +0,0 @@
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-
-import argparse
-import json
-import logging
-
-from allura.scripts import ScriptTask
-from allura.lib.import_api import AlluraImportApiClient
-
-log = logging.getLogger(__name__)
-
-
-def import_tracker(cli, project, tool, import_options, doc_txt,
-        validate=True, verbose=False, cont=False, neighborhood='p'):
-    url = '/rest/{neighborhood}/{project}/{tool}'.format(
-            neighborhood=neighborhood,
-            project=project,
-            tool=tool,
-            )
-    if validate:
-        url += '/validate_import'
-    else:
-        url += '/perform_import'
-
-    existing_map = {}
-    if cont:
-        existing_tickets = cli.call(url + '/')['tickets']
-        for t in existing_tickets:
-            existing_map[t['ticket_num']] = t['summary']
-
-    doc = json.loads(doc_txt)
-
-    if 'trackers' in doc and 'default' in doc['trackers'] and 'artifacts' in doc['trackers']['default']:
-        tickets_in = doc['trackers']['default']['artifacts']
-        doc['trackers']['default']['artifacts'] = []
-    else:
-        tickets_in = doc
-
-    if verbose:
-        print "Processing %d tickets" % len(tickets_in)
-
-    for cnt, ticket_in in enumerate(tickets_in):
-        if ticket_in['id'] in existing_map:
-            if verbose:
-                print 'Ticket id %d already exists, skipping' % ticket_in['id']
-            continue
-        doc_import = {}
-        doc_import['trackers'] = {}
-        doc_import['trackers']['default'] = {}
-        doc_import['trackers']['default']['artifacts'] = [ticket_in]
-        res = cli.call(url, doc=json.dumps(doc_import),
-                       options=json.dumps(import_options))
-        assert res['status'] and not res['errors'], res['errors']
-        if validate:
-            if res['warnings']:
-                print "Ticket id %s warnings: %s" % (ticket_in['id'], res['warnings'])
-        else:
-            print "Imported ticket id %s" % (ticket_in['id'])
-
-
-class ImportTracker(ScriptTask):
-
-    @classmethod
-    def execute(cls, options):
-        user_map = {}
-        import_options = {}
-        for s in options.import_opts:
-            k, v = s.split('=', 1)
-            if v == 'false':
-                v = False
-            import_options[k] = v
-
-        if options.user_map_file:
-            f = open(options.user_map_file)
-            try:
-                user_map = json.load(f)
-                if type(user_map) is not type({}):
-                    raise ValueError
-                for k, v in user_map.iteritems():
-                    if not isinstance(k, basestring) or not isinstance(v, basestring):
-                        raise ValueError
-            except ValueError:
-                raise '--user-map should specify JSON file with format {"original_user": "sf_user", ...}'
-            finally:
-                f.close()
-        import_options['user_map'] = user_map
-        cli = AlluraImportApiClient(
-            options.base_url, options.api_key, options.secret_key, options.verbose)
-        doc_txt = open(options.file_data).read()
-        import_tracker(
-            cli, options.project, options.tracker, import_options, doc_txt,
-            validate=options.validate,
-            verbose=options.verbose,
-            cont=options.cont)
-
-    @classmethod
-    def parser(cls):
-        parser = argparse.ArgumentParser(
-            description='import tickets from json')
-        parser.add_argument('--nbhd', action='store', default='', dest='nbhd',
-                            help='Restrict update to a particular neighborhood, e.g. /p/.')
-        parser.add_argument('-a', '--api-ticket',
-                            action='store', dest='api_key', help='API ticket')
-        parser.add_argument('-s', '--secret-key', action='store',
-                            dest='secret_key', help='Secret key')
-        parser.add_argument('-p', '--project', action='store',
-                            dest='project', help='Project to import to')
-        parser.add_argument('-t', '--tracker', action='store',
-                            dest='tracker', help='Tracker to import to')
-        parser.add_argument('-u', '--base-url', dest='base_url',
-                            default='https://sourceforge.net', help='Base Allura URL (https://sourceforge.net)')
-        parser.add_argument('-o', dest='import_opts',
-                            default=[], action='store',  help='Specify import option(s)', metavar='opt=val')
-        parser.add_argument('--user-map', dest='user_map_file',
-                            help='Map original users to SF.net users', metavar='JSON_FILE')
-        parser.add_argument('--file_data', dest='file_data',
-                            help='json file', metavar='JSON_FILE')
-        parser.add_argument('--validate', dest='validate',
-                            action='store_true', help='Validate import data')
-        parser.add_argument('-v', '--verbose', dest='verbose',
-                            action='store_true', help='Verbose operation')
-        parser.add_argument('-c', '--continue', dest='cont',
-                            action='store_true', help='Continue import into existing tracker')
-        return parser
-
-
-if __name__ == '__main__':
-    ImportTracker.main()

http://git-wip-us.apache.org/repos/asf/allura/blob/49f7e10b/scripts/tracker-rip.py
----------------------------------------------------------------------
diff --git a/scripts/tracker-rip.py b/scripts/tracker-rip.py
deleted file mode 100755
index aeef1eb..0000000
--- a/scripts/tracker-rip.py
+++ /dev/null
@@ -1,211 +0,0 @@
-#!/usr/bin/python
-
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-import sys
-import getpass
-from urlparse import urljoin
-
-from allura.lib import rest_api
-
-SRC_CRED = dict(
-    api_key='c03efc6cca1cf78be9e9',
-    secret_key='575eda2f25f6490d8cfe5d02f2506c010112894d0ea10660e43157a87a7e620c61ac06397b028af1',
-    http_username=raw_input('LDAP username: '),
-    http_password=getpass.getpass('LDAP password: '))
-SRC_SERVER = 'https://newforge.sf.geek.net/'
-SRC_TOOL = '/rest/p/forge/tickets/'
-
-# Credentials for sf-overlords
-# DST_CRED=dict(
-#     api_key='a4a88c67179137053d70',
-#     secret_key='fcc48a0c31459e99a88cc42cdd7f908fad78b283ca30a86caac1ab65036ff71fc195a18e56534dc5')
-# DST_SERVER='http://sourceforge.net/'
-# DST_TOOL='/rest/p/allura/tickets/'
-DST_CRED = dict(
-    api_key='aa7244645424513d9636',
-    secret_key='cd1d97be98497f7b615b297aa2061177ddf6d42b95a8484193f84690486694234dbf817efc3b2d6e')
-DST_SERVER = 'http://localhost:8080/'
-DST_TOOL = '/rest/p/test/bugs/'
-
-FAKE_TICKET = {
-    u'created_date': u'2010-03-08 17:29:42.802000',
-    u'assigned_to_id': u'',
-    u'assigned_to': u'',
-    u'custom_fields': {'_component': '', '_size': 0, '_priority': '', '_type': ''},
-    u'description': u'Ticket was not present in source',
-    u'milestone': u'',
-    u'reported_by': u'',
-    u'reported_by_id': u'',
-    u'status': u'closed',
-    u'sub_ids': [],
-    u'summary': u'Placeholder ticket',
-    u'super_id': u'None'}
-
-
-def main():
-    src_cli = rest_api.RestClient(
-        base_uri=SRC_SERVER,
-        **SRC_CRED)
-    dst_cli = rest_api.RestClient(
-        base_uri=DST_SERVER,
-        **DST_CRED)
-    src = TicketAPI(src_cli, SRC_TOOL)
-    dst = TicketAPI(dst_cli, DST_TOOL)
-    for ticket in src.iter_tickets(min_ticket=3, check=True):
-        print 'Migrating ticket %s:\n%s' % (ticket['ticket_num'], ticket)
-        print 'Create ticket on %s' % DST_SERVER
-        dst.create_ticket(ticket)
-        print 'Create discussion on %s' % DST_SERVER
-        src_thread = src.load_thread(ticket)
-        if not src_thread or not src_thread['posts']:
-            print '... no posts'
-            continue
-        dst_thread = dst.load_thread(ticket)
-        slug_map = {}
-        for post in src.iter_posts(src_thread):
-            print '... migrate post %s:\n%r' % (post['slug'], post['text'])
-            dst.create_post(dst_thread, post, slug_map)
-
-
-class TicketAPI(object):
-
-    def __init__(self, client, path):
-        self.client = client
-        self.path = path
-
-    def iter_tickets(self, min_ticket=1, max_ticket=None, check=False):
-        if check:
-            tickets = self.client.request('GET', self.path)['tickets']
-            valid_tickets = set(t['ticket_num'] for t in tickets)
-            max_valid_ticket = max(valid_tickets)
-        cur_ticket = min_ticket
-        while True:
-            if check and cur_ticket not in valid_tickets:
-                if cur_ticket > max_valid_ticket:
-                    break
-                yield dict(FAKE_TICKET, ticket_num=cur_ticket)
-                cur_ticket += 1
-                continue
-            ticket = self.client.request(
-                'GET', self.ticket_path(cur_ticket))['ticket']
-            if ticket is None:
-                break
-            yield ticket
-            cur_ticket += 1
-            if max_ticket and cur_ticket > max_ticket:
-                break
-
-    def load_thread(self, ticket):
-        discussion = self.client.request(
-            'GET', self.discussion_path())['discussion']
-        for thd in discussion['threads']:
-            if thd['subject'].startswith('#%d ' % ticket['ticket_num']):
-                break
-        else:
-            return None
-        thread = self.client.request(
-            'GET', self.thread_path(thd['_id']))['thread']
-        return thread
-
-    def iter_posts(self, thread):
-        for p in sorted(thread['posts'], key=lambda p: p['slug']):
-            post = self.client.request(
-                'GET', self.post_path(thread['_id'], p['slug']))['post']
-            yield post
-
-    def create_ticket(self, ticket):
-        ticket = dict(ticket, labels='')
-        ticket['description'] = 'Created by: %s\nCreated date: %s\nAssigned to:%s\n\n%s' % (
-            ticket['reported_by'], ticket['created_date'], ticket['assigned_to'], ticket['description'])
-        for bad_key in ('assigned_to_id', 'created_date', 'reported_by', 'reported_by_id', 'super_id', 'sub_ids', '_id'):
-            if bad_key in ticket:
-                del ticket[bad_key]
-        ticket.setdefault('labels', '')
-        ticket['custom_fields'].setdefault('_size', 0)
-        ticket['custom_fields'].setdefault('_priority', 'low')
-        ticket['custom_fields'].setdefault('_type', 'Bug')
-        ticket['custom_fields'].setdefault('_type', 'Component')
-        if ticket['custom_fields']['_size'] is None:
-            ticket['custom_fields']['_size'] = 0
-        if ticket['milestone'] not in ('backlog', 'public2',  'GA', 'post-GA'):
-            ticket['milestone'] = ''
-        if ticket['status'] not in 'open in-progress code-review validation closed'.split():
-            ticket['status'] = 'open'
-        r = self.client.request(
-            'POST', self.new_ticket_path(), ticket_form=ticket)
-        self.client.request(
-            'POST', self.ticket_path(r['ticket']['ticket_num'], 'save'),
-            ticket_form=ticket)
-
-    def create_post(self, thread, post, slug_map):
-        text = 'Post by %s:\n%s' % (
-            post['author'], post['text'])
-        if '/' in post['slug']:
-            parent_post = slug_map[post['slug'].rsplit('/', 1)[0]]
-            new_post = self.client.request(
-                'POST', self.post_path(thread['_id'], parent_post, 'reply'),
-                text=text)['post']
-        else:
-            new_post = self.client.request(
-                'POST', self.thread_path(thread['_id'], 'new'),
-                text=text)['post']
-        slug_map[post['slug']] = new_post['slug']
-        return new_post
-
-    def new_ticket_path(self):
-        return urljoin(self.path, 'new')
-
-    def ticket_path(self, ticket_num, suffix=''):
-        return urljoin(self.path, str(ticket_num)) + '/' + suffix
-
-    def discussion_path(self):
-        return '%s_discuss/' % (self.path)
-
-    def thread_path(self, thread_id, suffix=''):
-        return '%s_discuss/thread/%s/%s' % (self.path, thread_id, suffix)
-
-    def post_path(self, thread_id, post_slug, suffix=''):
-        return '%s_discuss/thread/%s/%s/%s' % (self.path, thread_id, post_slug, suffix)
-
-
-def pm(etype, value, tb):  # pragma no cover
-    import pdb
-    import traceback
-    try:
-        from IPython.ipapi import make_session
-        make_session()
-        from IPython.Debugger import Pdb
-        sys.stderr.write('Entering post-mortem IPDB shell\n')
-        p = Pdb(color_scheme='Linux')
-        p.reset()
-        p.setup(None, tb)
-        p.print_stack_trace()
-        sys.stderr.write('%s: %s\n' % (etype, value))
-        p.cmdloop()
-        p.forget()
-        # p.interaction(None, tb)
-    except ImportError:
-        sys.stderr.write('Entering post-mortem PDB shell\n')
-        traceback.print_exception(etype, value, tb)
-        pdb.post_mortem(tb)
-
-sys.excepthook = pm
-
-if __name__ == '__main__':
-    main()


[11/16] git commit: [#1687] ticket:574 Remove/fix tests in Allura core

Posted by br...@apache.org.
[#1687] ticket:574 Remove/fix tests in Allura core


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

Branch: refs/heads/master
Commit: 5b0522b1f328ad4ac0f5b0e51a96410fe6c714c1
Parents: 7b65cf2
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri May 2 17:14:37 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:22 2014 +0000

----------------------------------------------------------------------
 Allura/allura/tests/functional/test_auth.py     | 12 ---
 Allura/allura/tests/functional/test_rest.py     | 31 ++-----
 .../tests/functional/test_rest_api_tickets.py   | 87 --------------------
 Allura/allura/tests/model/test_auth.py          | 17 ----
 4 files changed, 6 insertions(+), 141 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/5b0522b1/Allura/allura/tests/functional/test_auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_auth.py b/Allura/allura/tests/functional/test_auth.py
index 0d4f8f7..f35f5f1 100644
--- a/Allura/allura/tests/functional/test_auth.py
+++ b/Allura/allura/tests/functional/test_auth.py
@@ -196,18 +196,6 @@ class TestAuth(TestController):
         r = self.app.get('/auth/subscriptions/')
         assert '<option selected value="both">Combined</option>' in r
 
-    def test_api_key(self):
-        r = self.app.get('/auth/preferences/')
-        assert 'No API token generated' in r
-        r = self.app.post('/auth/preferences/gen_api_token', status=302)
-        r = self.app.get('/auth/preferences/')
-        assert 'No API token generated' not in r
-        assert 'API Key:' in r
-        assert 'Secret Key:' in r
-        r = self.app.post('/auth/preferences/del_api_token', status=302)
-        r = self.app.get('/auth/preferences/')
-        assert 'No API token generated' in r
-
     def test_create_account(self):
         r = self.app.get('/auth/create_account')
         assert 'Create an Account' in r

http://git-wip-us.apache.org/repos/asf/allura/blob/5b0522b1/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 83889c1..51ae930 100644
--- a/Allura/allura/tests/functional/test_rest.py
+++ b/Allura/allura/tests/functional/test_rest.py
@@ -17,8 +17,6 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
-from datetime import datetime, timedelta
-
 from pylons import app_globals as g
 import mock
 from nose.tools import assert_equal, assert_in, assert_not_in
@@ -30,38 +28,20 @@ from allura.lib import helpers as h
 from allura.lib.exceptions import Invalid
 from allura import model as M
 
-from forgetracker.tracker_main import ForgeTrackerApp
-
 
 class TestRestHome(TestRestApiBase):
 
-    def test_bad_signature(self):
-        r = self.api_post('/rest/p/test/wiki/', api_signature='foo')
-        assert r.status_int == 403
-
-    def test_bad_token(self):
-        r = self.api_post('/rest/p/test/wiki/', api_key='foo')
-        assert r.status_int == 403
-
-    def test_bad_timestamp(self):
-        r = self.api_post('/rest/p/test/wiki/',
-                          api_timestamp=(datetime.utcnow() + timedelta(days=1)).isoformat())
-        assert r.status_int == 403
-
-    @mock.patch('allura.controllers.rest.M.OAuthAccessToken')
-    @mock.patch('allura.controllers.rest.request')
-    def test_bearer_token_non_ssl(self, request, OAuthAccessToken):
-        request.params = {'access_token': 'foo'}
-        request.scheme = 'http'
-        r = self.api_post('/rest/p/test/wiki', access_token='foo')
-        assert_equal(r.status_int, 403)
-        assert_equal(OAuthAccessToken.query.get.call_count, 0)
+    def _patch_token(self, OAuthAccessToken):
+        at = OAuthAccessToken.return_value
+        at.__ming__ = mock.MagicMock()
+        at.api_key = 'foo'
 
     @mock.patch('allura.controllers.rest.M.OAuthAccessToken')
     @mock.patch('allura.controllers.rest.request')
     def test_bearer_token_non_bearer(self, request, OAuthAccessToken):
         request.params = {'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')
@@ -73,6 +53,7 @@ class TestRestHome(TestRestApiBase):
     def test_bearer_token_invalid(self, request, OAuthAccessToken):
         request.params = {'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)

http://git-wip-us.apache.org/repos/asf/allura/blob/5b0522b1/Allura/allura/tests/functional/test_rest_api_tickets.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_rest_api_tickets.py b/Allura/allura/tests/functional/test_rest_api_tickets.py
deleted file mode 100644
index 8185d5d..0000000
--- a/Allura/allura/tests/functional/test_rest_api_tickets.py
+++ /dev/null
@@ -1,87 +0,0 @@
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-from datetime import datetime, timedelta
-
-from ming.orm import session
-
-from allura import model as M
-from allura.tests import decorators as td
-from alluratest.controller import TestRestApiBase
-
-
-class TestApiTicket(TestRestApiBase):
-
-    def set_api_ticket(self, expire=None):
-        if not expire:
-            expire = timedelta(days=1)
-        test_admin = M.User.query.get(username='test-admin')
-        api_ticket = M.ApiTicket(
-            user_id=test_admin._id, capabilities={
-                'import': ['Projects', 'test']},
-            expires=datetime.utcnow() + expire)
-        session(api_ticket).flush()
-        self.set_api_token(api_ticket)
-
-    def test_bad_signature(self):
-        self.set_api_ticket()
-        r = self.api_post('/rest/p/test/wiki/', api_signature='foo')
-        assert r.status_int == 403
-
-    def test_bad_token(self):
-        self.set_api_ticket()
-        r = self.api_post('/rest/p/test/wiki/', api_key='foo')
-        assert r.status_int == 403
-
-    def test_bad_timestamp(self):
-        self.set_api_ticket()
-        r = self.api_post('/rest/p/test/wiki/',
-                          api_timestamp=(datetime.utcnow() + timedelta(days=1)).isoformat())
-        assert r.status_int == 403
-
-    def test_bad_path(self):
-        self.set_api_ticket()
-        r = self.api_post('/rest/1/test/wiki/')
-        assert r.status_int == 404
-        r = self.api_post('/rest/p/1223/wiki/')
-        assert r.status_int == 404
-        r = self.api_post('/rest/p/test/12wiki/')
-        assert r.status_int == 404
-
-    def test_no_api(self):
-        self.set_api_ticket()
-        r = self.api_post('/rest/p/test/admin/')
-        assert r.status_int == 404
-
-    @td.with_wiki
-    def test_project_ping(self):
-        self.set_api_ticket()
-        r = self.api_get('/rest/p/test/wiki/Home/')
-        assert r.status_int == 200
-        assert r.json['title'] == 'Home', r.json
-
-    def test_project_ping_expired_ticket(self):
-        self.set_api_ticket(timedelta(seconds=-1))
-        r = self.api_post('/rest/p/test/wiki/')
-        assert r.status_int == 403
-
-    @td.with_tool('test/sub1', 'Wiki', 'wiki')
-    def test_subproject_ping(self):
-        self.set_api_ticket()
-        r = self.api_get('/rest/p/test/sub1/wiki/Home/')
-        assert r.status_int == 200
-        assert r.json['title'] == 'Home', r.json

http://git-wip-us.apache.org/repos/asf/allura/blob/5b0522b1/Allura/allura/tests/model/test_auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/model/test_auth.py b/Allura/allura/tests/model/test_auth.py
index 1c76611..35f056e 100644
--- a/Allura/allura/tests/model/test_auth.py
+++ b/Allura/allura/tests/model/test_auth.py
@@ -173,23 +173,6 @@ def test_default_project_roles():
 
 
 @with_setup(setUp)
-def test_dup_api_token():
-    from ming.orm import session
-    u = M.User.register(dict(username='nosetest-user'))
-    ThreadLocalORMSession.flush_all()
-    tok = M.ApiToken(user_id=u._id)
-    session(tok).flush()
-    tok2 = M.ApiToken(user_id=u._id)
-    try:
-        session(tok2).flush()
-        assert False, "Entry with duplicate unique key was inserted"
-    except DuplicateKeyError:
-        pass
-    assert len(M.ApiToken.query.find().all()
-               ) == 1, "Duplicate entries with unique key found"
-
-
-@with_setup(setUp)
 def test_email_address_claimed_by_user():
     addr = M.EmailAddress(_id='test_admin@domain.net',
                           claimed_by_user_id=c.user._id)


[07/16] git commit: [#1687] ticket:582 Add upsert to OAuthConsumerToken

Posted by br...@apache.org.
[#1687] ticket:582 Add upsert to OAuthConsumerToken


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

Branch: refs/heads/master
Commit: 66753ddc4c6cee448d4710d766543c937e03e0f6
Parents: d178196
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue May 6 14:26:36 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:22 2014 +0000

----------------------------------------------------------------------
 Allura/allura/model/oauth.py | 15 +++++++++++++++
 1 file changed, 15 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/66753ddc/Allura/allura/model/oauth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/oauth.py b/Allura/allura/model/oauth.py
index 14b30d1..fd250bb 100644
--- a/Allura/allura/model/oauth.py
+++ b/Allura/allura/model/oauth.py
@@ -20,7 +20,9 @@ import logging
 import oauth2 as oauth
 from pylons import tmpl_context as c, app_globals as g
 
+import pymongo
 from ming import schema as S
+from ming.orm import session
 from ming.orm import FieldProperty, RelationProperty, ForeignIdProperty
 from ming.orm.declarative import MappedClass
 
@@ -68,6 +70,19 @@ class OAuthConsumerToken(OAuthToken):
 
     user = RelationProperty('User')
 
+    @classmethod
+    def upsert(cls, name):
+        t = cls.query.get(name=name)
+        if t is not None:
+            return t
+        try:
+            t = cls(name=name)
+            session(t).flush(t)
+        except pymongo.errors.DuplicateKeyError:
+            session(t).expunge(t)
+            t = cls.query.get(name=name)
+        return t
+
     @property
     def description_html(self):
         return g.markdown.cached_convert(self, 'description')


[13/16] git commit: Revert "[#1687] ticket:582 Don't check capabilities in discussion import"

Posted by br...@apache.org.
Revert "[#1687] ticket:582 Don't check capabilities in discussion import"

This reverts commit 8f53750ad841cadfc1d8c7a3b3f098339606589f.


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

Branch: refs/heads/master
Commit: 0f99ab7325441a56c677dd11595ad0de70519aab
Parents: acabd59
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed May 28 12:45:28 2014 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:23 2014 +0000

----------------------------------------------------------------------
 AlluraTest/alluratest/controller.py             |  3 +-
 .../forgediscussion/controllers/root.py         |  4 ++
 .../tests/functional/test_import.py             | 40 ++++++++++++++++++++
 3 files changed, 46 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/0f99ab73/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index 433431f..57d0fe4 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -197,7 +197,8 @@ class TestRestApiBase(TestController):
                 consumer_token_id=consumer_token._id,
                 user_id=user._id,
                 callback='manual',
-                validation_pin=h.nonce(20))
+                validation_pin=h.nonce(20),
+                is_bearer=True)
             token = M.OAuthAccessToken(
                 consumer_token_id=consumer_token._id,
                 request_token_id=request_token._id,

http://git-wip-us.apache.org/repos/asf/allura/blob/0f99ab73/ForgeDiscussion/forgediscussion/controllers/root.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/controllers/root.py b/ForgeDiscussion/forgediscussion/controllers/root.py
index 7002109..b315dca 100644
--- a/ForgeDiscussion/forgediscussion/controllers/root.py
+++ b/ForgeDiscussion/forgediscussion/controllers/root.py
@@ -349,6 +349,10 @@ class RootRestController(BaseController):
         require_access(c.project, 'admin')
         if username_mapping is None:
             username_mapping = '{}'
+        if c.api_token.get_capability('import') != [c.project.neighborhood.name, c.project.shortname]:
+            log.error('Import capability is not enabled for %s',
+                      c.project.shortname)
+            raise exc.HTTPForbidden(detail='Import is not allowed')
         try:
             doc = json.loads(doc)
             username_mapping = json.loads(username_mapping)

http://git-wip-us.apache.org/repos/asf/allura/blob/0f99ab73/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_import.py b/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
index b4f4158..deeb349 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
@@ -35,12 +35,34 @@ class TestImportController(TestRestApiBase):  # TestController):
         self.app.get('/discussion/')
         self.json_text = open(here_dir + '/data/sf.json').read()
 
+    def test_no_capability(self):
+        self.set_api_ticket({'import2': ['Projects', 'test']})
+        resp = self.api_post('/rest/p/test/discussion/perform_import',
+                             doc=self.json_text)
+        assert resp.status_int == 403
+
+        self.set_api_ticket({'import': ['Projects', 'test2']})
+        resp = self.api_post('/rest/p/test/discussion/perform_import',
+                             doc=self.json_text)
+        assert resp.status_int == 403
+
+        self.set_api_ticket({'import': ['Projects', 'test']})
+        resp = self.api_post('/rest/p/test/discussion/perform_import',
+                             doc=self.json_text)
+        assert resp.status_int == 200
+
     def test_validate_import(self):
         r = self.api_post('/rest/p/test/discussion/validate_import',
                           doc=self.json_text)
         assert not r.json['errors']
 
     def test_import_anon(self):
+        api_ticket = M.ApiTicket(
+            user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
+            expires=datetime.utcnow() + timedelta(days=1))
+        ming.orm.session(api_ticket).flush()
+        self.set_api_token(api_ticket)
+
         r = self.api_post('/rest/p/test/discussion/perform_import',
                           doc=self.json_text)
         assert not r.json['errors'], r.json['errors']
@@ -56,6 +78,12 @@ class TestImportController(TestRestApiBase):  # TestController):
         assert 'Anonymous' in str(r)
 
     def test_import_map(self):
+        api_ticket = M.ApiTicket(
+            user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
+            expires=datetime.utcnow() + timedelta(days=1))
+        ming.orm.session(api_ticket).flush()
+        self.set_api_token(api_ticket)
+
         r = self.api_post('/rest/p/test/discussion/perform_import',
                           doc=self.json_text,
                           username_mapping=json.dumps(dict(rick446='test-user')))
@@ -73,6 +101,12 @@ class TestImportController(TestRestApiBase):  # TestController):
         assert 'Anonymous' not in str(r)
 
     def test_import_create(self):
+        api_ticket = M.ApiTicket(
+            user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
+            expires=datetime.utcnow() + timedelta(days=1))
+        ming.orm.session(api_ticket).flush()
+        self.set_api_token(api_ticket)
+
         r = self.api_post('/rest/p/test/discussion/perform_import',
                           doc=self.json_text, create_users='True')
         assert not r.json['errors'], r.json['errors']
@@ -88,6 +122,12 @@ class TestImportController(TestRestApiBase):  # TestController):
         assert 'Anonymous' not in str(r)
         assert 'test-rick446' in str(r)
 
+    def set_api_ticket(self, caps={'import': ['Projects', 'test']}):
+        api_ticket = M.ApiTicket(user_id=c.user._id, capabilities=caps,
+                                 expires=datetime.utcnow() + timedelta(days=1))
+        ming.orm.session(api_ticket).flush()
+        self.set_api_token(api_ticket)
+
     @staticmethod
     def time_normalize(t):
         return t.replace('T', ' ').replace('Z', '')


[10/16] git commit: [#1687] ticket:582 Use oauth bearer tokens for AlluraImportApiClient

Posted by br...@apache.org.
[#1687] ticket:582 Use oauth bearer tokens for AlluraImportApiClient


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

Branch: refs/heads/master
Commit: d178196291fdfe232c561c8e1de8d5389f407f34
Parents: 5b0522b
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue May 6 12:32:06 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:22 2014 +0000

----------------------------------------------------------------------
 Allura/allura/lib/import_api.py               | 64 ++++++++++++++++++++++
 ForgeTracker/forgetracker/scripts/__init__.py | 16 ------
 scripts/allura_import.py                      | 30 +++-------
 3 files changed, 73 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/d1781962/Allura/allura/lib/import_api.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/import_api.py b/Allura/allura/lib/import_api.py
new file mode 100644
index 0000000..60754a6
--- /dev/null
+++ b/Allura/allura/lib/import_api.py
@@ -0,0 +1,64 @@
+#       Licensed to the Apache Software Foundation (ASF) under one
+#       or more contributor license agreements.  See the NOTICE file
+#       distributed with this work for additional information
+#       regarding copyright ownership.  The ASF licenses this file
+#       to you under the Apache License, Version 2.0 (the
+#       "License"); you may not use this file except in compliance
+#       with the License.  You may obtain a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#       Unless required by applicable law or agreed to in writing,
+#       software distributed under the License is distributed on an
+#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#       KIND, either express or implied.  See the License for the
+#       specific language governing permissions and limitations
+#       under the License.
+
+import urllib
+import urllib2
+import urlparse
+import hmac
+import hashlib
+import json
+import logging
+from datetime import datetime
+
+log = logging.getLogger(__name__)
+
+
+class AlluraImportApiClient(object):
+
+    def __init__(self, base_url, token, verbose=False, retry=True):
+        self.base_url = base_url
+        self.token = token
+        self.verbose = verbose
+        self.retry = retry
+
+    def sign(self, path, params):
+        params.append(('access_token', self.token))
+        return params
+
+    def call(self, url, **params):
+        url = urlparse.urljoin(self.base_url, url)
+        if self.verbose:
+            log.info("Import API URL: %s", url)
+
+        params = self.sign(urlparse.urlparse(url).path, params.items())
+
+        while True:
+            try:
+                result = urllib2.urlopen(url, urllib.urlencode(params))
+                resp = result.read()
+                return json.loads(resp)
+            except urllib2.HTTPError, e:
+                e.msg += ' ({0})'.format(url)
+                if self.verbose:
+                    error_content = e.read()
+                    e.msg += '. Error response:\n' + error_content
+                raise e
+            except (urllib2.URLError, IOError):
+                if self.retry:
+                    log.exception('Error making API request, will retry')
+                    continue
+                raise

http://git-wip-us.apache.org/repos/asf/allura/blob/d1781962/ForgeTracker/forgetracker/scripts/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/scripts/__init__.py b/ForgeTracker/forgetracker/scripts/__init__.py
deleted file mode 100644
index 144e298..0000000
--- a/ForgeTracker/forgetracker/scripts/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.

http://git-wip-us.apache.org/repos/asf/allura/blob/d1781962/scripts/allura_import.py
----------------------------------------------------------------------
diff --git a/scripts/allura_import.py b/scripts/allura_import.py
index 270d988..d4e51fd 100644
--- a/scripts/allura_import.py
+++ b/scripts/allura_import.py
@@ -19,7 +19,6 @@ import json
 from optparse import OptionParser
 
 from allura.lib.import_api import AlluraImportApiClient
-from forgetracker.scripts.import_tracker import import_tracker
 from tracwikiimporter.scripts.wiki_from_trac.loaders import import_wiki
 
 
@@ -52,22 +51,15 @@ def main():
 
     import_options['user_map'] = user_map
 
-    cli = AlluraImportApiClient(
-        options.base_url, options.api_key, options.secret_key, options.verbose)
+    cli = AlluraImportApiClient(options.base_url, options.token, options.verbose)
     doc_txt = open(args[0]).read()
 
-    # import the tracker (if any)
-    if options.tracker:
-        import_tracker(
-            cli, options.project, options.tracker, import_options, options, doc_txt,
-            validate=options.validate, verbose=options.verbose,
-            neighborhood=options.neighborhood)
-    elif options.forum:
+    if options.forum:
         import_forum(cli, options.project, options.forum, user_map, doc_txt,
-                validate=options.validate, neighborhood=options.neighborhood)
+                     validate=options.validate, neighborhood=options.neighborhood)
     elif options.wiki:
-        import_wiki(cli, options.project, options.wiki, options, doc_txt,
-                neighborhood=options.neighborhood)
+        import_wiki(cli, options.project, options.wiki, options, doc_txt)
+
 
 
 def import_forum(cli, project, tool, user_map, doc_txt, validate=True,
@@ -89,17 +81,13 @@ def parse_options():
     optparser = OptionParser(usage='''%prog [options] <JSON dump>
 
 Import project data dump in JSON format into an Allura project.''')
-    optparser.add_option('-a', '--api-ticket',
-                         dest='api_key', help='API ticket')
-    optparser.add_option('-s', '--secret-key',
-                         dest='secret_key', help='Secret key')
+    optparser.add_option('-t', '--token', dest='token',
+                         help='OAuth bearer token (generate at /auth/oauth/)')
     optparser.add_option('-p', '--project', dest='project',
                          help='Project to import to')
     optparser.add_option('-n', '--neighborhood', dest='neighborhood',
                          help="URL prefix of destination neighborhood (default is 'p')",
                          default='p')
-    optparser.add_option('-t', '--tracker', dest='tracker',
-                         help='Tracker to import to')
     optparser.add_option('-f', '--forum', dest='forum',
                          help='Forum tool to import to')
     optparser.add_option('-w', '--wiki', dest='wiki',
@@ -119,8 +107,8 @@ Import project data dump in JSON format into an Allura project.''')
     options, args = optparser.parse_args()
     if len(args) != 1:
         optparser.error("Wrong number of arguments")
-    if not options.api_key or not options.secret_key:
-        optparser.error("Keys are required")
+    if not options.token:
+        optparser.error("OAuth bearer token is required")
     if not options.project:
         optparser.error("Target project is required")
     options.neighborhood = options.neighborhood.strip('/')


[14/16] git commit: [#1687] ticket:582 Don't check capabilities in discussion import

Posted by br...@apache.org.
[#1687] ticket:582 Don't check capabilities in discussion import

Since OAuth authentication scheme doesn't support it.


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

Branch: refs/heads/master
Commit: acabd593561e9190da51d84117c69b30ece0399c
Parents: 19a50da
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue May 6 15:49:38 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:23 2014 +0000

----------------------------------------------------------------------
 AlluraTest/alluratest/controller.py             |  3 +-
 .../forgediscussion/controllers/root.py         |  4 --
 .../tests/functional/test_import.py             | 40 --------------------
 3 files changed, 1 insertion(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/acabd593/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index 57d0fe4..433431f 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -197,8 +197,7 @@ class TestRestApiBase(TestController):
                 consumer_token_id=consumer_token._id,
                 user_id=user._id,
                 callback='manual',
-                validation_pin=h.nonce(20),
-                is_bearer=True)
+                validation_pin=h.nonce(20))
             token = M.OAuthAccessToken(
                 consumer_token_id=consumer_token._id,
                 request_token_id=request_token._id,

http://git-wip-us.apache.org/repos/asf/allura/blob/acabd593/ForgeDiscussion/forgediscussion/controllers/root.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/controllers/root.py b/ForgeDiscussion/forgediscussion/controllers/root.py
index b315dca..7002109 100644
--- a/ForgeDiscussion/forgediscussion/controllers/root.py
+++ b/ForgeDiscussion/forgediscussion/controllers/root.py
@@ -349,10 +349,6 @@ class RootRestController(BaseController):
         require_access(c.project, 'admin')
         if username_mapping is None:
             username_mapping = '{}'
-        if c.api_token.get_capability('import') != [c.project.neighborhood.name, c.project.shortname]:
-            log.error('Import capability is not enabled for %s',
-                      c.project.shortname)
-            raise exc.HTTPForbidden(detail='Import is not allowed')
         try:
             doc = json.loads(doc)
             username_mapping = json.loads(username_mapping)

http://git-wip-us.apache.org/repos/asf/allura/blob/acabd593/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_import.py b/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
index deeb349..b4f4158 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
@@ -35,34 +35,12 @@ class TestImportController(TestRestApiBase):  # TestController):
         self.app.get('/discussion/')
         self.json_text = open(here_dir + '/data/sf.json').read()
 
-    def test_no_capability(self):
-        self.set_api_ticket({'import2': ['Projects', 'test']})
-        resp = self.api_post('/rest/p/test/discussion/perform_import',
-                             doc=self.json_text)
-        assert resp.status_int == 403
-
-        self.set_api_ticket({'import': ['Projects', 'test2']})
-        resp = self.api_post('/rest/p/test/discussion/perform_import',
-                             doc=self.json_text)
-        assert resp.status_int == 403
-
-        self.set_api_ticket({'import': ['Projects', 'test']})
-        resp = self.api_post('/rest/p/test/discussion/perform_import',
-                             doc=self.json_text)
-        assert resp.status_int == 200
-
     def test_validate_import(self):
         r = self.api_post('/rest/p/test/discussion/validate_import',
                           doc=self.json_text)
         assert not r.json['errors']
 
     def test_import_anon(self):
-        api_ticket = M.ApiTicket(
-            user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
-            expires=datetime.utcnow() + timedelta(days=1))
-        ming.orm.session(api_ticket).flush()
-        self.set_api_token(api_ticket)
-
         r = self.api_post('/rest/p/test/discussion/perform_import',
                           doc=self.json_text)
         assert not r.json['errors'], r.json['errors']
@@ -78,12 +56,6 @@ class TestImportController(TestRestApiBase):  # TestController):
         assert 'Anonymous' in str(r)
 
     def test_import_map(self):
-        api_ticket = M.ApiTicket(
-            user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
-            expires=datetime.utcnow() + timedelta(days=1))
-        ming.orm.session(api_ticket).flush()
-        self.set_api_token(api_ticket)
-
         r = self.api_post('/rest/p/test/discussion/perform_import',
                           doc=self.json_text,
                           username_mapping=json.dumps(dict(rick446='test-user')))
@@ -101,12 +73,6 @@ class TestImportController(TestRestApiBase):  # TestController):
         assert 'Anonymous' not in str(r)
 
     def test_import_create(self):
-        api_ticket = M.ApiTicket(
-            user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
-            expires=datetime.utcnow() + timedelta(days=1))
-        ming.orm.session(api_ticket).flush()
-        self.set_api_token(api_ticket)
-
         r = self.api_post('/rest/p/test/discussion/perform_import',
                           doc=self.json_text, create_users='True')
         assert not r.json['errors'], r.json['errors']
@@ -122,12 +88,6 @@ class TestImportController(TestRestApiBase):  # TestController):
         assert 'Anonymous' not in str(r)
         assert 'test-rick446' in str(r)
 
-    def set_api_ticket(self, caps={'import': ['Projects', 'test']}):
-        api_ticket = M.ApiTicket(user_id=c.user._id, capabilities=caps,
-                                 expires=datetime.utcnow() + timedelta(days=1))
-        ming.orm.session(api_ticket).flush()
-        self.set_api_token(api_ticket)
-
     @staticmethod
     def time_normalize(t):
         return t.replace('T', ' ').replace('Z', '')


[16/16] git commit: [#1687] ticket:589 Add back fix for token creation in tests after revert

Posted by br...@apache.org.
[#1687] ticket:589 Add back fix for token creation in tests after revert


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

Branch: refs/heads/master
Commit: 2392ea7f620437418efd62063fb5b91d95b4bd6f
Parents: 0f99ab7
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed May 28 12:46:46 2014 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:23 2014 +0000

----------------------------------------------------------------------
 AlluraTest/alluratest/controller.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/2392ea7f/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index 57d0fe4..433431f 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -197,8 +197,7 @@ class TestRestApiBase(TestController):
                 consumer_token_id=consumer_token._id,
                 user_id=user._id,
                 callback='manual',
-                validation_pin=h.nonce(20),
-                is_bearer=True)
+                validation_pin=h.nonce(20))
             token = M.OAuthAccessToken(
                 consumer_token_id=consumer_token._id,
                 request_token_id=request_token._id,


[09/16] git commit: [#1687] ticket:574 Fix DuplicateKeyError while creating oauth tokens in tests

Posted by br...@apache.org.
[#1687] ticket:574 Fix DuplicateKeyError while creating oauth tokens in tests


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

Branch: refs/heads/master
Commit: 506d4d017a16ed93fd51db702102fa5b06a41422
Parents: 4ce64a4
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri May 2 14:13:20 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:22 2014 +0000

----------------------------------------------------------------------
 AlluraTest/alluratest/controller.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/506d4d01/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index 6e3bb85..57d0fe4 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -190,8 +190,9 @@ class TestRestApiBase(TestController):
         if username not in self._token_cache:
             user = M.User.query.get(username=username)
             consumer_token = M.OAuthConsumerToken(
-                name='test',
-                description='test-app')
+                name='test-%s' % str(user._id),
+                description='test-app-%s' % str(user._id),
+                user_id=user._id)
             request_token = M.OAuthRequestToken(
                 consumer_token_id=consumer_token._id,
                 user_id=user._id,


[15/16] git commit: [#1687] ticket:582 Use OAuth instead of obsolete ApiTicket auth in wiki-post script

Posted by br...@apache.org.
[#1687] ticket:582 Use OAuth instead of obsolete ApiTicket auth in wiki-post script


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

Branch: refs/heads/master
Commit: 19a50da24466f464191eb00a257df97eb9f8d497
Parents: c102aa9
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue May 6 15:13:25 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:23 2014 +0000

----------------------------------------------------------------------
 scripts/wiki-post.py | 27 ++++++++-------------------
 1 file changed, 8 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/19a50da2/scripts/wiki-post.py
----------------------------------------------------------------------
diff --git a/scripts/wiki-post.py b/scripts/wiki-post.py
index 038cf70..7e16bb1 100755
--- a/scripts/wiki-post.py
+++ b/scripts/wiki-post.py
@@ -19,9 +19,6 @@
 
 import types
 from sys import stdout
-import hmac
-import hashlib
-from datetime import datetime
 import os
 import urllib
 from urllib2 import urlopen, HTTPError
@@ -76,18 +73,13 @@ def urlencode(params):
 
 class Signer(object):
 
-    def __init__(self, secret_key, api_key):
-        self.secret_key = secret_key
-        self.api_key = api_key
+    def __init__(self, token):
+        self.token = token
 
     def __call__(self, path, params):
-        if self.api_key is None:
+        if self.token is None:
             return params
-        params.append(('api_key', self.api_key))
-        params.append(('api_timestamp', datetime.utcnow().isoformat()))
-        message = path + '?' + urlencode(sorted(params))
-        digest = hmac.new(self.secret_key, message, hashlib.sha256).hexdigest()
-        params.append(('api_signature', digest))
+        params.append(('token', self.token))
         return params
 
 
@@ -95,8 +87,7 @@ def main():
     usage = 'usage: %prog [options] [PageName [file]]'
     op = OptionParser(usage=usage)
     op.add_option('-c', '--config', metavar='CONFIG')
-    op.add_option('-a', '--api-key', metavar='KEY')
-    op.add_option('-s', '--secret-key', metavar='KEY')
+    op.add_option('-t', '--token', metavar='TOKEN')
     op.add_option('', '--anon', action='store_true')
     op.add_option('-u', '--url', metavar='URL')
     (options, args) = op.parse_args()
@@ -119,18 +110,16 @@ def main():
     config.read(
         [str(os.path.expanduser('~/.forge-api.ini')), str(options.config)])
 
-    api_key = None
-    secret_key = None
+    token = None
     if not options.anon:
-        api_key = options.api_key or config.get('keys', 'api-key')
-        secret_key = options.secret_key or config.get('keys', 'secret-key')
+        token = options.token or config.get('keys', 'token')
 
     url = options.url or config.get('wiki', 'url')
     if pagename_given:
         url = urljoin(url, urllib.quote(pagename))
     print url
 
-    sign = Signer(secret_key, api_key)
+    sign = Signer(token)
     params = [('text', markdown)] if method == 'PUT' else []
     params = sign(urlparse(url).path, params)
     try:


[06/16] git commit: [#1687] ticket:582 Add if_missing to OAuthConsumerToken.description

Posted by br...@apache.org.
[#1687] ticket:582 Add if_missing to OAuthConsumerToken.description

Prevents /auth/oauth/ from raising errors about markdown_html being
absent (may happen when token created without description, e.g. in trac
importer)


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

Branch: refs/heads/master
Commit: c102aa909a3000d93cbfdb60b0dbbab617eeec1c
Parents: 66753dd
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue May 6 15:10:08 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:22 2014 +0000

----------------------------------------------------------------------
 Allura/allura/model/oauth.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/c102aa90/Allura/allura/model/oauth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/oauth.py b/Allura/allura/model/oauth.py
index fd250bb..86dcb42 100644
--- a/Allura/allura/model/oauth.py
+++ b/Allura/allura/model/oauth.py
@@ -65,7 +65,7 @@ class OAuthConsumerToken(OAuthToken):
     type = FieldProperty(str, if_missing='consumer')
     user_id = AlluraUserProperty(if_missing=lambda: c.user._id)
     name = FieldProperty(str)
-    description = FieldProperty(str)
+    description = FieldProperty(str, if_missing='')
     description_cache = FieldProperty(MarkdownCache)
 
     user = RelationProperty('User')


[12/16] git commit: [#1687] ticket:589 Check if token allowed to import forum

Posted by br...@apache.org.
[#1687] ticket:589 Check if token allowed to import forum


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

Branch: refs/heads/master
Commit: fd00be035d37c0b7e4218f1e33d7ddaccbc6baaf
Parents: 2392ea7
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed May 28 13:47:51 2014 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:23 2014 +0000

----------------------------------------------------------------------
 Allura/allura/model/oauth.py                    |   8 ++
 .../forgediscussion/controllers/root.py         |   5 +-
 .../tests/functional/test_import.py             | 134 ++++++++-----------
 3 files changed, 65 insertions(+), 82 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/fd00be03/Allura/allura/model/oauth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/oauth.py b/Allura/allura/model/oauth.py
index 86dcb42..b09a8ec 100644
--- a/Allura/allura/model/oauth.py
+++ b/Allura/allura/model/oauth.py
@@ -21,6 +21,8 @@ import oauth2 as oauth
 from pylons import tmpl_context as c, app_globals as g
 
 import pymongo
+from paste.deploy.converters import aslist
+from tg import config
 from ming import schema as S
 from ming.orm import session
 from ming.orm import FieldProperty, RelationProperty, ForeignIdProperty
@@ -134,3 +136,9 @@ class OAuthAccessToken(OAuthToken):
         if user is None:
             user = c.user
         return cls.query.find(dict(user_id=user._id, type='access')).all()
+
+    def can_import_forum(self):
+        tokens = aslist(config.get('oauth.can_import_forum', ''), ',')
+        if self.api_key in tokens:
+            return True
+        return False

http://git-wip-us.apache.org/repos/asf/allura/blob/fd00be03/ForgeDiscussion/forgediscussion/controllers/root.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/controllers/root.py b/ForgeDiscussion/forgediscussion/controllers/root.py
index b315dca..9256662 100644
--- a/ForgeDiscussion/forgediscussion/controllers/root.py
+++ b/ForgeDiscussion/forgediscussion/controllers/root.py
@@ -349,9 +349,8 @@ class RootRestController(BaseController):
         require_access(c.project, 'admin')
         if username_mapping is None:
             username_mapping = '{}'
-        if c.api_token.get_capability('import') != [c.project.neighborhood.name, c.project.shortname]:
-            log.error('Import capability is not enabled for %s',
-                      c.project.shortname)
+        if not c.api_token.can_import_forum():
+            log.error('Import capability is not enabled for %s', c.project.shortname)
             raise exc.HTTPForbidden(detail='Import is not allowed')
         try:
             doc = json.loads(doc)

http://git-wip-us.apache.org/repos/asf/allura/blob/fd00be03/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_import.py b/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
index deeb349..73b0a8f 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
@@ -21,9 +21,11 @@ from datetime import datetime, timedelta
 from nose.tools import assert_equal
 
 import ming
+from tg import config
 from pylons import tmpl_context as c
 
 from allura import model as M
+from allura.lib import helpers as h
 from alluratest.controller import TestRestApiBase
 
 
@@ -36,20 +38,15 @@ class TestImportController(TestRestApiBase):  # TestController):
         self.json_text = open(here_dir + '/data/sf.json').read()
 
     def test_no_capability(self):
-        self.set_api_ticket({'import2': ['Projects', 'test']})
-        resp = self.api_post('/rest/p/test/discussion/perform_import',
-                             doc=self.json_text)
-        assert resp.status_int == 403
+        with h.push_config(config, **{'oauth.can_import_forum': 'some,fake,tokens'}):
+            resp = self.api_post('/rest/p/test/discussion/perform_import',
+                                 doc=self.json_text)
+            assert resp.status_int == 403
 
-        self.set_api_ticket({'import': ['Projects', 'test2']})
-        resp = self.api_post('/rest/p/test/discussion/perform_import',
-                             doc=self.json_text)
-        assert resp.status_int == 403
-
-        self.set_api_ticket({'import': ['Projects', 'test']})
-        resp = self.api_post('/rest/p/test/discussion/perform_import',
-                             doc=self.json_text)
-        assert resp.status_int == 200
+        with h.push_config(config, **{'oauth.can_import_forum': self.token('test-admin').api_key}):
+            resp = self.api_post('/rest/p/test/discussion/perform_import',
+                                 doc=self.json_text)
+            assert resp.status_int == 200
 
     def test_validate_import(self):
         r = self.api_post('/rest/p/test/discussion/validate_import',
@@ -57,76 +54,55 @@ class TestImportController(TestRestApiBase):  # TestController):
         assert not r.json['errors']
 
     def test_import_anon(self):
-        api_ticket = M.ApiTicket(
-            user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
-            expires=datetime.utcnow() + timedelta(days=1))
-        ming.orm.session(api_ticket).flush()
-        self.set_api_token(api_ticket)
-
-        r = self.api_post('/rest/p/test/discussion/perform_import',
-                          doc=self.json_text)
-        assert not r.json['errors'], r.json['errors']
-        r = self.app.get('/p/test/discussion/')
-        assert 'Open Discussion' in str(r)
-        assert 'Welcome to Open Discussion' in str(r)
-        for link in r.html.findAll('a'):
-            if 'Welcome to Open Discussion' in str(link):
-                break
-        r = self.app.get(link.get('href'))
-        assert '2009-11-19' in str(r)
-        assert 'Welcome to Open Discussion' in str(r)
-        assert 'Anonymous' in str(r)
+        with h.push_config(config, **{'oauth.can_import_forum': self.token('test-admin').api_key}):
+            r = self.api_post('/rest/p/test/discussion/perform_import',
+                              doc=self.json_text)
+            assert not r.json['errors'], r.json['errors']
+            r = self.app.get('/p/test/discussion/')
+            assert 'Open Discussion' in str(r)
+            assert 'Welcome to Open Discussion' in str(r)
+            for link in r.html.findAll('a'):
+                if 'Welcome to Open Discussion' in str(link):
+                    break
+            r = self.app.get(link.get('href'))
+            assert '2009-11-19' in str(r)
+            assert 'Welcome to Open Discussion' in str(r)
+            assert 'Anonymous' in str(r)
 
     def test_import_map(self):
-        api_ticket = M.ApiTicket(
-            user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
-            expires=datetime.utcnow() + timedelta(days=1))
-        ming.orm.session(api_ticket).flush()
-        self.set_api_token(api_ticket)
-
-        r = self.api_post('/rest/p/test/discussion/perform_import',
-                          doc=self.json_text,
-                          username_mapping=json.dumps(dict(rick446='test-user')))
-        assert not r.json['errors'], r.json['errors']
-        r = self.app.get('/p/test/discussion/')
-        assert 'Open Discussion' in str(r)
-        assert 'Welcome to Open Discussion' in str(r)
-        for link in r.html.findAll('a'):
-            if 'Welcome to Open Discussion' in str(link):
-                break
-        r = self.app.get(link.get('href'))
-        assert '2009-11-19' in str(r)
-        assert 'Welcome to Open Discussion' in str(r)
-        assert 'Test User' in str(r)
-        assert 'Anonymous' not in str(r)
+        with h.push_config(config, **{'oauth.can_import_forum': self.token('test-admin').api_key}):
+            r = self.api_post('/rest/p/test/discussion/perform_import',
+                              doc=self.json_text,
+                              username_mapping=json.dumps(dict(rick446='test-user')))
+            assert not r.json['errors'], r.json['errors']
+            r = self.app.get('/p/test/discussion/')
+            assert 'Open Discussion' in str(r)
+            assert 'Welcome to Open Discussion' in str(r)
+            for link in r.html.findAll('a'):
+                if 'Welcome to Open Discussion' in str(link):
+                    break
+            r = self.app.get(link.get('href'))
+            assert '2009-11-19' in str(r)
+            assert 'Welcome to Open Discussion' in str(r)
+            assert 'Test User' in str(r)
+            assert 'Anonymous' not in str(r)
 
     def test_import_create(self):
-        api_ticket = M.ApiTicket(
-            user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
-            expires=datetime.utcnow() + timedelta(days=1))
-        ming.orm.session(api_ticket).flush()
-        self.set_api_token(api_ticket)
-
-        r = self.api_post('/rest/p/test/discussion/perform_import',
-                          doc=self.json_text, create_users='True')
-        assert not r.json['errors'], r.json['errors']
-        r = self.app.get('/p/test/discussion/')
-        assert 'Open Discussion' in str(r)
-        assert 'Welcome to Open Discussion' in str(r)
-        for link in r.html.findAll('a'):
-            if 'Welcome to Open Discussion' in str(link):
-                break
-        r = self.app.get(link.get('href'))
-        assert '2009-11-19' in str(r)
-        assert 'Welcome to Open Discussion' in str(r)
-        assert 'Anonymous' not in str(r)
-        assert 'test-rick446' in str(r)
-
-    def set_api_ticket(self, caps={'import': ['Projects', 'test']}):
-        api_ticket = M.ApiTicket(user_id=c.user._id, capabilities=caps,
-                                 expires=datetime.utcnow() + timedelta(days=1))
-        ming.orm.session(api_ticket).flush()
-        self.set_api_token(api_ticket)
+        with h.push_config(config, **{'oauth.can_import_forum': self.token('test-admin').api_key}):
+            r = self.api_post('/rest/p/test/discussion/perform_import',
+                              doc=self.json_text, create_users='True')
+            assert not r.json['errors'], r.json['errors']
+            r = self.app.get('/p/test/discussion/')
+            assert 'Open Discussion' in str(r)
+            assert 'Welcome to Open Discussion' in str(r)
+            for link in r.html.findAll('a'):
+                if 'Welcome to Open Discussion' in str(link):
+                    break
+            r = self.app.get(link.get('href'))
+            assert '2009-11-19' in str(r)
+            assert 'Welcome to Open Discussion' in str(r)
+            assert 'Anonymous' not in str(r)
+            assert 'test-rick446' in str(r)
 
     @staticmethod
     def time_normalize(t):


[04/16] git commit: [#1687] ticket:574 Remove ApiToken & ApiTicket

Posted by br...@apache.org.
[#1687] ticket:574 Remove ApiToken & ApiTicket


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

Branch: refs/heads/master
Commit: 6365229700600fc6e5720b4d90ab58bab39b2715
Parents: 65cc6e7
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri May 2 12:31:46 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:21 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/rest.py               | 18 ----
 Allura/allura/controllers/site_admin.py         | 49 ----------
 Allura/allura/model/__init__.py                 |  2 +-
 Allura/allura/model/auth.py                     | 99 --------------------
 .../templates/site_admin_api_tickets.html       | 66 -------------
 5 files changed, 1 insertion(+), 233 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/63652297/Allura/allura/controllers/rest.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
index abe6454..85a6df1 100644
--- a/Allura/allura/controllers/rest.py
+++ b/Allura/allura/controllers/rest.py
@@ -19,7 +19,6 @@
 
 """REST Controller"""
 import logging
-from urllib import quote, unquote
 
 import oauth2 as oauth
 from webob import exc
@@ -50,23 +49,6 @@ class RestController(object):
         'Based on request.params or oauth, authenticate the request'
         if 'oauth_token' in request.params or 'access_token' in request.params:
             return self.oauth._authenticate()
-        elif 'api_key' in request.params:
-            api_key = request.params.get('api_key')
-            token = M.ApiTicket.get(api_key)
-            if not token:
-                token = M.ApiToken.get(api_key)
-            else:
-                log.info('Authenticating with API ticket')
-            # Sometimes a path might be only partially escaped like /FAQ-Development,%20Bug%20Reporting,
-            # I don't know why.
-            path = quote(unquote(request.path))
-            if path != request.path:
-                log.info('Canonicalized %s to %s', request.path, path)
-            if token is not None and token.authenticate_request(path, request.params):
-                return token
-            else:
-                log.info('API authentication failure')
-                raise exc.HTTPForbidden
         else:
             return None
 

http://git-wip-us.apache.org/repos/asf/allura/blob/63652297/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index 352d35f..3cf4973 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -21,8 +21,6 @@ from datetime import datetime, timedelta
 
 from tg import expose, validate, flash, config, redirect
 from tg.decorators import with_trailing_slash, without_trailing_slash
-from ming.orm import session
-import pymongo
 import bson
 import tg
 from pylons import app_globals as g
@@ -77,7 +75,6 @@ class SiteAdminController(object):
         base_url = '/nf/admin/'
         links = [
             SitemapEntry('Home', base_url, ui_icon=g.icons['admin']),
-            SitemapEntry('API Tickets', base_url + 'api_tickets', ui_icon=g.icons['admin']),
             SitemapEntry('Add Subscribers', base_url + 'add_subscribers', ui_icon=g.icons['admin']),
             SitemapEntry('New Projects', base_url + 'new_projects', ui_icon=g.icons['admin']),
             SitemapEntry('Reclone Repo', base_url + 'reclone_repo', ui_icon=g.icons['admin']),
@@ -92,52 +89,6 @@ class SiteAdminController(object):
     def index(self):
         return {}
 
-    @expose('jinja:allura:templates/site_admin_api_tickets.html')
-    @without_trailing_slash
-    def api_tickets(self, **data):
-        import json
-        import dateutil.parser
-        if request.method == 'POST':
-            log.info('api_tickets: %s', data)
-            ok = True
-            for_user = M.User.by_username(data['for_user'])
-            if not for_user:
-                ok = False
-                flash('User not found')
-            caps = None
-            try:
-                caps = json.loads(data['caps'])
-            except ValueError:
-                ok = False
-                flash('JSON format error')
-            if type(caps) is not type({}):
-                ok = False
-                flash(
-                    'Capabilities must be a JSON dictionary, mapping capability name to optional discriminator(s) (or "")')
-            try:
-                expires = dateutil.parser.parse(data['expires'])
-            except ValueError:
-                ok = False
-                flash('Date format error')
-            if ok:
-                tok = None
-                try:
-                    tok = M.ApiTicket(user_id=for_user._id,
-                                      capabilities=caps, expires=expires)
-                    session(tok).flush()
-                    log.info('New token: %s', tok)
-                    flash('API Ticket created')
-                except:
-                    log.exception('Could not create API ticket:')
-                    flash('Error creating API ticket')
-        elif request.method == 'GET':
-            data = {'expires': datetime.utcnow() + timedelta(days=2)}
-
-        data['token_list'] = M.ApiTicket.query.find().sort(
-            'mod_date', pymongo.DESCENDING).all()
-        log.info(data['token_list'])
-        return data
-
     def subscribe_artifact(self, url, user):
         artifact_url = urlparse(url).path[1:-1].split("/")
         neighborhood = M.Neighborhood.query.find({

http://git-wip-us.apache.org/repos/asf/allura/blob/63652297/Allura/allura/model/__init__.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/__init__.py b/Allura/allura/model/__init__.py
index bc40872..a74bff6 100644
--- a/Allura/allura/model/__init__.py
+++ b/Allura/allura/model/__init__.py
@@ -25,7 +25,7 @@ from .index import ArtifactReference, Shortlink
 from .artifact import Artifact, MovedArtifact, Message, VersionedArtifact, Snapshot, Feed, AwardFile, Award, AwardGrant, VotableArtifact
 from .discuss import Discussion, Thread, PostHistory, Post, DiscussionAttachment
 from .attachments import BaseAttachment
-from .auth import AuthGlobals, User, ProjectRole, EmailAddress, ApiToken, ApiTicket, OldProjectRole
+from .auth import AuthGlobals, User, ProjectRole, EmailAddress, OldProjectRole
 from .auth import AuditLog, audit_log, AlluraUserProperty
 from .filesystem import File
 from .notification import Notification, Mailbox

http://git-wip-us.apache.org/repos/asf/allura/blob/63652297/Allura/allura/model/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index 7d56e8c..6a9d279 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -25,7 +25,6 @@ import hashlib
 from urlparse import urlparse
 from email import header
 from hashlib import sha256
-import uuid
 from pytz import timezone
 from datetime import timedelta, datetime, time
 
@@ -110,104 +109,6 @@ class AlluraUserProperty(ForeignIdProperty):
         super(AlluraUserProperty, self).__init__('User', allow_none=True, **kwargs)
 
 
-class ApiAuthMixIn(object):
-
-    def authenticate_request(self, path, params):
-        try:
-            # Validate timestamp
-            timestamp = iso8601.parse_date(params['api_timestamp'])
-            timestamp_utc = timestamp.replace(
-                tzinfo=None) - timestamp.utcoffset()
-            if abs(datetime.utcnow() - timestamp_utc) > timedelta(minutes=10):
-                return False
-            # Validate signature
-            api_signature = params['api_signature']
-            params = sorted((k, v)
-                            for k, v in params.iteritems() if k != 'api_signature')
-            string_to_sign = path + '?' + urlencode(params)
-            digest = hmac.new(self.secret_key, string_to_sign, hashlib.sha256)
-            return digest.hexdigest() == api_signature
-        except KeyError:
-            return False
-
-    def sign_request(self, path, params):
-        if hasattr(params, 'items'):
-            params = params.items()
-        has_api_key = has_api_timestamp = has_api_signature = False
-        for k, v in params:
-            if k == 'api_key':
-                has_api_key = True
-            if k == 'api_timestamp':
-                has_api_timestamp = True
-            if k == 'api_signature':
-                has_api_signature = True
-        if not has_api_key:
-            params.append(('api_key', self.api_key))
-        if not has_api_timestamp:
-            params.append(('api_timestamp', datetime.utcnow().isoformat()))
-        if not has_api_signature:
-            string_to_sign = urllib.quote(path) + \
-                '?' + urlencode(sorted(params))
-            digest = hmac.new(self.secret_key, string_to_sign, hashlib.sha256)
-            params.append(('api_signature', digest.hexdigest()))
-        return params
-
-    def get_capability(self, key):
-        return None
-
-
-class ApiToken(MappedClass, ApiAuthMixIn):
-
-    class __mongometa__:
-        name = 'api_token'
-        session = main_orm_session
-        unique_indexes = ['user_id']
-
-    _id = FieldProperty(S.ObjectId)
-    user_id = AlluraUserProperty()
-    api_key = FieldProperty(str, if_missing=lambda: str(uuid.uuid4()))
-    secret_key = FieldProperty(str, if_missing=h.cryptographic_nonce)
-
-    user = RelationProperty('User')
-
-    @classmethod
-    def get(cls, api_key):
-        return cls.query.get(api_key=api_key)
-
-
-class ApiTicket(MappedClass, ApiAuthMixIn):
-
-    class __mongometa__:
-        name = 'api_ticket'
-        session = main_orm_session
-    PREFIX = 'tck'
-
-    _id = FieldProperty(S.ObjectId)
-    user_id = AlluraUserProperty()
-    api_key = FieldProperty(
-        str, if_missing=lambda: ApiTicket.PREFIX + h.nonce(20))
-    secret_key = FieldProperty(str, if_missing=h.cryptographic_nonce)
-    expires = FieldProperty(datetime, if_missing=None)
-    capabilities = FieldProperty({str: None})
-    mod_date = FieldProperty(datetime, if_missing=datetime.utcnow)
-
-    user = RelationProperty('User')
-
-    @classmethod
-    def get(cls, api_ticket):
-        if not api_ticket.startswith(cls.PREFIX):
-            return None
-        return cls.query.get(api_key=api_ticket)
-
-    def authenticate_request(self, path, params):
-        if self.expires and datetime.utcnow() > self.expires:
-            return False
-        return ApiAuthMixIn.authenticate_request(self, path, params)
-
-    def get_capability(self, key):
-        return self.capabilities.get(key)
-
-
 class EmailAddress(MappedClass):
     re_format = re.compile('^.* <(.*)>$')
 

http://git-wip-us.apache.org/repos/asf/allura/blob/63652297/Allura/allura/templates/site_admin_api_tickets.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_api_tickets.html b/Allura/allura/templates/site_admin_api_tickets.html
deleted file mode 100644
index 0dba2ea..0000000
--- a/Allura/allura/templates/site_admin_api_tickets.html
+++ /dev/null
@@ -1,66 +0,0 @@
-{#-
-       Licensed to the Apache Software Foundation (ASF) under one
-       or more contributor license agreements.  See the NOTICE file
-       distributed with this work for additional information
-       regarding copyright ownership.  The ASF licenses this file
-       to you under the Apache License, Version 2.0 (the
-       "License"); you may not use this file except in compliance
-       with the License.  You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-       Unless required by applicable law or agreed to in writing,
-       software distributed under the License is distributed on an
-       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-       KIND, either express or implied.  See the License for the
-       specific language governing permissions and limitations
-       under the License.
--#}
-{% set page="api_tickets" %}
-{% extends 'allura:templates/site_admin.html' %}
-
-{% block content %}
-<h1>Special API Tickets</h1>
-<p>API Tickets give access to special APIs which should be used under supervision.
-</p>
-
-<form name="ticket_form" method="POST">
-<table>
-<tr>
-<td>Username:</td> <td><input name="for_user" type="text" value="{{for_user}}"></td>
-</tr>
-<tr>
-<td>Capabilities (JSON):</td> <td><input name="caps" type="text" value="{{caps}}"></td>
-</tr>
-<tr>
-<td>Expiration date:</td> <td><input name="expires" type="text" value="{{expires}}"></td>
-</tr>
-<tr>
-<td><input type="submit" value="Save"><td>
-</tr>
-</table>
-{{lib.csrf_token()}}
-</form>
-
-<table>
-<tr>
-<th>Username</th>
-<th>Expiration Date</th>
-<th>Capabilities</th>
-</tr>
-{% for token in token_list %}
-<tr>
-<td>{{token.user.username}}</td>
-<td>{{token.expires}}</td>
-<td>{{h.json.dumps(token.capabilities)}}</td>
-<td></td>
-</tr>
-<tr>
-<td colspan="3">
-&nbsp;&nbsp;&nbsp;API Ticket: {{token.api_key}}<br/>
-&nbsp;&nbsp;&nbsp;Secret Key: {{token.secret_key}}</td>
-</tr>
-{% endfor %}
-</table>
-
-{% endblock %}


[08/16] git commit: [#1687] ticket:574 Remove old tracker import and related tests

Posted by br...@apache.org.
[#1687] ticket:574 Remove old tracker import and related tests


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

Branch: refs/heads/master
Commit: 7b65cf2a29216dbf76adcabe52bc10dfa7f9140e
Parents: 506d4d0
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri May 2 17:10:37 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:22 2014 +0000

----------------------------------------------------------------------
 .../tests/functional/test_import.py             | 210 -------------------
 ForgeTracker/forgetracker/tracker_main.py       |  28 ---
 2 files changed, 238 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/7b65cf2a/ForgeTracker/forgetracker/tests/functional/test_import.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/functional/test_import.py b/ForgeTracker/forgetracker/tests/functional/test_import.py
deleted file mode 100644
index 9d47543..0000000
--- a/ForgeTracker/forgetracker/tests/functional/test_import.py
+++ /dev/null
@@ -1,210 +0,0 @@
-# -*- coding: utf-8 -*-
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-import os
-import json
-from formencode.variabledecode import variable_encode
-from datetime import datetime, timedelta
-from nose.tools import assert_equal, assert_false
-
-import ming
-from pylons import app_globals as g
-from pylons import tmpl_context as c
-
-from allura import model as M
-from allura.lib import helpers as h
-from alluratest.controller import TestRestApiBase
-from allura.tests import decorators as td
-
-
-class TestImportController(TestRestApiBase):
-
-    def new_ticket(self, mount_point='/bugs/', **kw):
-        response = self.app.get(mount_point + 'new/')
-        form = response.forms[1]
-        for k, v in kw.iteritems():
-            form['ticket_form.%s' % k] = v
-        resp = form.submit()
-        if resp.status_int == 200:
-            resp.showbrowser()
-            assert 0, "form error?"
-        return resp.follow()
-
-    def set_api_ticket(self, caps={'import': ['Projects', 'test']}):
-        api_ticket = M.ApiTicket(user_id=c.user._id, capabilities=caps,
-                                 expires=datetime.utcnow() + timedelta(days=1))
-        ming.orm.session(api_ticket).flush()
-        self.set_api_token(api_ticket)
-
-    @td.with_tracker
-    def test_no_capability(self):
-        here_dir = os.path.dirname(__file__)
-
-        self.set_api_ticket({'import2': ['Projects', 'test']})
-        resp = self.api_post('/rest/p/test/bugs/perform_import',
-                             doc=open(here_dir + '/data/sf.json').read(), options='{}')
-        assert resp.status_int == 403
-
-        self.set_api_ticket({'import': ['Projects', 'test2']})
-        resp = self.api_post('/rest/p/test/bugs/perform_import',
-                             doc=open(here_dir + '/data/sf.json').read(), options='{}')
-        assert resp.status_int == 403
-
-        self.set_api_ticket({'import': ['Projects', 'test']})
-        resp = self.api_post('/rest/p/test/bugs/perform_import',
-                             doc=open(here_dir + '/data/sf.json').read(), options='{}')
-        assert resp.status_int == 200
-
-    @staticmethod
-    def time_normalize(t):
-        return t.replace('T', ' ').replace('Z', '')
-
-    def verify_ticket(self, from_api, org):
-        assert_equal(from_api['status'], org['status'])
-        assert_equal(from_api['description'], org['description'])
-        assert_equal(from_api['summary'], org['summary'])
-        assert_equal(from_api['ticket_num'], org['id'])
-        assert_equal(from_api['created_date'],
-                     self.time_normalize(org['date']))
-        assert_equal(from_api['mod_date'],
-                     self.time_normalize(org['date_updated']))
-        assert_equal(from_api['custom_fields']
-                     ['_resolution'], org['resolution'])
-        assert_false('_cc' in from_api['custom_fields'])
-        assert_equal(from_api['custom_fields']['_private'], org['private'])
-
-    @td.with_tracker
-    def test_validate_import(self):
-        here_dir = os.path.dirname(__file__)
-        doc_text = open(here_dir + '/data/sf.json').read()
-        r = self.api_post('/rest/p/test/bugs/validate_import',
-                          doc=doc_text, options='{}')
-        assert not r.json['errors']
-
-    @td.with_tracker
-    def test_import_custom_field(self):
-        params = dict(
-            custom_fields=[
-                dict(name='_resolution', label='Resolution', type='select',
-                     options='oné "one and á half" two'),
-            ],
-            open_status_names='aa bb',
-            closed_status_names='cc',
-        )
-        self.app.post(
-            '/admin/bugs/set_custom_fields',
-            params=variable_encode(params))
-        here_dir = os.path.dirname(__file__)
-        api_ticket = M.ApiTicket(
-            user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
-            expires=datetime.utcnow() + timedelta(days=1))
-        ming.orm.session(api_ticket).flush()
-        self.set_api_token(api_ticket)
-
-        doc_text = open(here_dir + '/data/sf.json').read()
-        doc_json = json.loads(doc_text)
-        ticket_json = doc_json['trackers']['default']['artifacts'][0]
-        r = self.api_post('/rest/p/test/bugs/perform_import',
-                          doc=doc_text, options='{"user_map": {"hinojosa4": "test-admin", "ma_boehm": "test-user"}}')
-        assert r.json['status'], r.json
-
-        ming.orm.ThreadLocalORMSession.flush_all()
-        M.MonQTask.run_ready()
-        ming.orm.ThreadLocalORMSession.flush_all()
-
-        r = self.app.get('/p/test/bugs/204/')
-        assert '<option selected value="fixed">fixed</option>' in r
-        assert '<option value="one and á half">one and á half</option>' in r
-
-    @td.with_tracker
-    def test_import(self):
-        here_dir = os.path.dirname(__file__)
-        api_ticket = M.ApiTicket(
-            user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
-            expires=datetime.utcnow() + timedelta(days=1))
-        ming.orm.session(api_ticket).flush()
-        self.set_api_token(api_ticket)
-
-        doc_text = open(here_dir + '/data/sf.json').read()
-        doc_json = json.loads(doc_text)
-        ticket_json = doc_json['trackers']['default']['artifacts'][0]
-        r = self.api_post('/rest/p/test/bugs/perform_import',
-                          doc=doc_text, options='{"user_map": {"hinojosa4": "test-admin", "ma_boehm": "test-user"}}')
-        assert r.json['status']
-        assert r.json['errors'] == []
-
-        ming.orm.ThreadLocalORMSession.flush_all()
-        M.MonQTask.run_ready()
-        ming.orm.ThreadLocalORMSession.flush_all()
-
-        indexed_tickets = filter(
-            lambda a: a['type_s'] == 'Ticket', g.solr.db.values())
-        assert_equal(len(indexed_tickets), 1)
-        assert_equal(indexed_tickets[0]['summary_t'], ticket_json['summary'])
-        assert_equal(indexed_tickets[0]['ticket_num_i'], ticket_json['id'])
-
-        r = self.app.get('/rest/p/test/bugs/204/')
-        self.verify_ticket(r.json['ticket'], ticket_json)
-        assert r.json['ticket']["reported_by"] == "test-user"
-        assert r.json['ticket']["assigned_to"] == "test-admin"
-
-        r = self.app.get('/rest/p/test/bugs/')
-        assert len(r.json['tickets']) == 1
-        assert_equal(r.json['tickets'][0]['ticket_num'], ticket_json['id'])
-        assert_equal(r.json['tickets'][0]['summary'], ticket_json['summary'])
-
-        r = self.app.get('/p/test/bugs/204/')
-        assert '<option value="2.0">2.0</option>' in r
-        assert '<option selected value="test_milestone">test_milestone</option>' in r
-        assert ticket_json['summary'] in r
-        r = self.app.get('/p/test/bugs/')
-        assert ticket_json['summary'] in r
-
-    @td.with_tracker
-    def test_milestone_status(self):
-        """When importing, if all tickets in a milestone are closed, the
-        milestone itself should also be closed.
-
-        """
-        here_dir = os.path.dirname(__file__)
-        api_ticket = M.ApiTicket(
-            user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
-            expires=datetime.utcnow() + timedelta(days=1))
-        ming.orm.session(api_ticket).flush()
-        self.set_api_token(api_ticket)
-
-        doc_text = open(here_dir + '/data/milestone-tickets.json').read()
-        r = self.api_post('/rest/p/test/bugs/perform_import', doc=doc_text,
-                          options='{"user_map": {"hinojosa4": "test-admin", "ma_boehm": "test-user"}}')
-        assert r.json['status'], r.json
-
-        ming.orm.ThreadLocalORMSession.flush_all()
-        M.MonQTask.run_ready()
-        ming.orm.ThreadLocalORMSession.flush_all()
-
-        with h.push_context('test', mount_point='bugs', neighborhood='Projects'):
-            for milestone_fld in c.app.globals.milestone_fields:
-                milestone_names = [ms['name']
-                                   for ms in milestone_fld['milestones']]
-                assert 'open_milestone' in milestone_names, milestone_names
-                assert 'closed_milestone' in milestone_names, milestone_names
-                for milestone in milestone_fld['milestones']:
-                    if milestone['name'] == 'open_milestone':
-                        assert milestone['complete'] == False
-                    if milestone['name'] == 'closed_milestone':
-                        assert milestone['complete'] == True

http://git-wip-us.apache.org/repos/asf/allura/blob/7b65cf2a/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index 542f512..d37b5b1 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -1716,34 +1716,6 @@ class RootRestController(BaseController):
         redirect(str(ticket.ticket_num) + '/')
 
     @expose('json:')
-    def validate_import(self, doc=None, options=None, **post_data):
-        require_access(c.project, 'admin')
-        migrator = ImportSupport()
-        try:
-            status = migrator.validate_import(doc, options, **post_data)
-            return status
-        except Exception, e:
-            log.exception(e)
-            return dict(status=False, errors=[repr(e)])
-
-    @expose('json:')
-    def perform_import(self, doc=None, options=None, **post_data):
-        with h.notifications_disabled(c.project):
-            require_access(c.project, 'admin')
-            if c.api_token.get_capability('import') != [c.project.neighborhood.name, c.project.shortname]:
-                log.error('Import capability is not enabled for %s',
-                          c.project.shortname)
-                raise exc.HTTPForbidden(detail='Import is not allowed')
-
-            migrator = ImportSupport()
-            try:
-                status = migrator.perform_import(doc, options, **post_data)
-                return status
-            except Exception, e:
-                log.exception(e)
-                return dict(status=False, errors=[str(e)])
-
-    @expose('json:')
     def search(self, q=None, limit=100, page=0, sort=None, **kw):
         results = TM.Ticket.paged_search(
             c.app.config, c.user, q, limit, page, sort, show_deleted=False)


[03/16] git commit: [#1687] ticket:574 Change TestRestApiBase to use oauth

Posted by br...@apache.org.
[#1687] ticket:574 Change TestRestApiBase to use oauth


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

Branch: refs/heads/master
Commit: 44a7f5ec2e474c4b59bda024798c98c7c8b4c465
Parents: 6365229
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri May 2 13:06:15 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jun 3 15:27:21 2014 +0000

----------------------------------------------------------------------
 AlluraTest/alluratest/controller.py | 41 +++++++++++++++++---------------
 1 file changed, 22 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/44a7f5ec/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index a7131ef..6e3bb85 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -189,15 +189,28 @@ class TestRestApiBase(TestController):
         # only create token once, else ming gets dupe key error
         if username not in self._token_cache:
             user = M.User.query.get(username=username)
-            token = M.ApiToken(user_id=user._id)
+            consumer_token = M.OAuthConsumerToken(
+                name='test',
+                description='test-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)
+            token = M.OAuthAccessToken(
+                consumer_token_id=consumer_token._id,
+                request_token_id=request_token._id,
+                user_id=user._id,
+                is_bearer=True)
+            ming.orm.session(consumer_token).flush()
+            ming.orm.session(request_token).flush()
             ming.orm.session(token).flush()
             self._token_cache[username] = token
 
         return self._token_cache[username]
 
-    def _api_getpost(
-            self, method, path, api_key=None, api_timestamp=None, api_signature=None,
-            wrap_args=None, user='test-admin', status=None, **params):
+    def _api_getpost(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
@@ -209,14 +222,8 @@ class TestRestApiBase(TestController):
         if status is None:
             status = [200, 201, 301, 302, 400, 403, 404]
         params = variabledecode.variable_encode(params, add_repetitions=False)
-        if api_key:
-            params['api_key'] = api_key
-        if api_timestamp:
-            params['api_timestamp'] = api_timestamp
-        if api_signature:
-            params['api_signature'] = api_signature
 
-        params = self.token(user).sign_request(path, params)
+        params['access_token'] = self.token(user).api_key
 
         fn = self.app.post if method == 'POST' else self.app.get
 
@@ -229,12 +236,8 @@ class TestRestApiBase(TestController):
         else:
             return response
 
-    def api_get(
-            self, path, api_key=None, api_timestamp=None, api_signature=None,
-            wrap_args=None, user='test-admin', status=None, **params):
-        return self._api_getpost('GET', path, api_key, api_timestamp, api_signature, wrap_args, user, status, **params)
+    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)
 
-    def api_post(
-            self, path, api_key=None, api_timestamp=None, api_signature=None,
-            wrap_args=None, user='test-admin', status=None, **params):
-        return self._api_getpost('POST', path, api_key, api_timestamp, api_signature, 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)