summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Prykhodchenko <me@romcheg.me>2014-01-14 18:14:32 +0200
committerRoman Prykhodchenko <me@romcheg.me>2014-03-14 12:35:24 +0200
commitde3de047d1163775cebda55ef04603e0a93777b2 (patch)
tree46c8185ad8d4967d7aac25f602dfec7dba1c121e
parent9e15b9c511d2c2d4be5e3319a1642381212eef1c (diff)
downloadironic-de3de047d1163775cebda55ef04603e0a93777b2.tar.gz
Process public API list as regular expressions
In order to allow flexible patterns for public API specification and to speed up the process of checking whether some request is performed against the public API, it's reasonable to interpret the list of public endpoints as regular expressions. This patch changes the logic of checking whether a request requires authorisation from searching the endpoint in the set to checking the endpoint against a list of regular expressions. Closes-bug: #1251880 Change-Id: I638ca0e20fa7e44fbeeae0d1e4c2f4188fb597a5
-rw-r--r--ironic/api/hooks.py5
-rw-r--r--ironic/api/middleware/auth_token.py25
-rw-r--r--ironic/common/exception.py4
-rw-r--r--ironic/tests/api/test_acl.py13
4 files changed, 38 insertions, 9 deletions
diff --git a/ironic/api/hooks.py b/ironic/api/hooks.py
index 519bba0c2..bc4c9f3d1 100644
--- a/ironic/api/hooks.py
+++ b/ironic/api/hooks.py
@@ -21,7 +21,6 @@ from pecan import hooks
from webob import exc
from ironic.common import context
-from ironic.common import utils
from ironic.conductor import rpcapi
from ironic.db import api as dbapi
from ironic.openstack.common import policy
@@ -75,11 +74,9 @@ class ContextHook(hooks.PecanHook):
auth_token = state.request.headers.get('X-Auth-Token')
creds = {'roles': state.request.headers.get('X-Roles', '').split(',')}
+ is_public_api = state.request.environ.get('is_public_api', False)
is_admin = policy.check('admin', state.request.headers, creds)
- path = utils.safe_rstrip(state.request.path, '/')
- is_public_api = path in self.public_api_routes
-
state.request.context = context.RequestContext(
auth_token=auth_token,
user=user_id,
diff --git a/ironic/api/middleware/auth_token.py b/ironic/api/middleware/auth_token.py
index d9c32b111..9cad079b3 100644
--- a/ironic/api/middleware/auth_token.py
+++ b/ironic/api/middleware/auth_token.py
@@ -12,9 +12,15 @@
# License for the specific language governing permissions and limitations
# under the License.
+import re
+
from keystoneclient.middleware import auth_token
+from ironic.common import exception
from ironic.common import utils
+from ironic.openstack.common import log
+
+LOG = log.getLogger(__name__)
class AuthTokenMiddleware(auth_token.AuthProtocol):
@@ -25,14 +31,29 @@ class AuthTokenMiddleware(auth_token.AuthProtocol):
"""
def __init__(self, app, conf, public_api_routes=[]):
- self.public_api_routes = set(public_api_routes)
+ route_pattern_tpl = '%s(\.json|\.xml)?$'
+
+ try:
+ self.public_api_routes = [re.compile(route_pattern_tpl % route_tpl)
+ for route_tpl in public_api_routes]
+ except re.error as e:
+ msg = _('Cannot compile public API routes: %s') % e
+
+ LOG.error(msg)
+ raise exception.ConfigInvalid(error_msg=msg)
super(AuthTokenMiddleware, self).__init__(app, conf)
def __call__(self, env, start_response):
path = utils.safe_rstrip(env.get('PATH_INFO'), '/')
- if path in self.public_api_routes:
+ # The information whether the API call is being performed against the
+ # public API is required for some other components. Saving it to the
+ # WSGI environment is reasonable thereby.
+ env['is_public_api'] = any(map(lambda pattern: re.match(pattern, path),
+ self.public_api_routes))
+
+ if env['is_public_api']:
return self.app(env, start_response)
return super(AuthTokenMiddleware, self).__call__(env, start_response)
diff --git a/ironic/common/exception.py b/ironic/common/exception.py
index 3a604127b..7fdadec73 100644
--- a/ironic/common/exception.py
+++ b/ironic/common/exception.py
@@ -349,3 +349,7 @@ class NoFreeConductorWorker(TemporaryFailure):
class VendorPassthruException(IronicException):
pass
+
+
+class ConfigInvalid(IronicException):
+ message = _("Invalid configuration file. %(error_msg)s")
diff --git a/ironic/tests/api/test_acl.py b/ironic/tests/api/test_acl.py
index 57056fc4e..0dfb8a499 100644
--- a/ironic/tests/api/test_acl.py
+++ b/ironic/tests/api/test_acl.py
@@ -83,6 +83,13 @@ class TestACL(base.FunctionalTest):
# expect_errors should be set to True: If expect_errors is set to False
# the response gets converted to JSON and we cannot read the response
# code so easy.
- response = self.get_json('/', expect_errors=True)
-
- self.assertEqual(200, response.status_int)
+ for route in ('/', '/v1'):
+ response = self.get_json(route,
+ path_prefix='', expect_errors=True)
+ self.assertEqual(200, response.status_int)
+
+ def test_public_api_with_path_extensions(self):
+ for route in ('/v1/', '/v1.json', '/v1.xml'):
+ response = self.get_json(route,
+ path_prefix='', expect_errors=True)
+ self.assertEqual(200, response.status_int)