~ubuntu-branches/ubuntu/quantal/nova/quantal-proposed

« back to all changes in this revision

Viewing changes to nova/openstack/common/policy.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-08-16 14:04:11 UTC
  • mto: This revision was merged to the branch mainline in revision 84.
  • Revision ID: package-import@ubuntu.com-20120816140411-0mr4n241wmk30t9l
Tags: upstream-2012.2~f3
ImportĀ upstreamĀ versionĀ 2012.2~f3

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
import urllib
22
22
import urllib2
23
23
 
 
24
from nova.openstack.common.gettextutils import _
24
25
from nova.openstack.common import jsonutils
25
26
 
26
27
 
130
131
 
131
132
class Brain(object):
132
133
    """Implements policy checking."""
 
134
 
 
135
    _checks = {}
 
136
 
 
137
    @classmethod
 
138
    def _register(cls, name, func):
 
139
        cls._checks[name] = func
 
140
 
133
141
    @classmethod
134
142
    def load_json(cls, data, default_rule=None):
135
143
        """Init a brain using json instead of a rules dictionary."""
137
145
        return cls(rules=rules_dict, default_rule=default_rule)
138
146
 
139
147
    def __init__(self, rules=None, default_rule=None):
 
148
        if self.__class__ != Brain:
 
149
            LOG.warning(_("Inheritance-based rules are deprecated; use "
 
150
                          "the default brain instead of %s.") %
 
151
                        self.__class__.__name__)
 
152
 
140
153
        self.rules = rules or {}
141
154
        self.default_rule = default_rule
142
155
 
150
163
            LOG.exception(_("Failed to understand rule %(match)r") % locals())
151
164
            # If the rule is invalid, fail closed
152
165
            return False
 
166
 
 
167
        func = None
153
168
        try:
154
 
            f = getattr(self, '_check_%s' % match_kind)
 
169
            old_func = getattr(self, '_check_%s' % match_kind)
155
170
        except AttributeError:
156
 
            if not self._check_generic(match, target_dict, cred_dict):
157
 
                return False
 
171
            func = self._checks.get(match_kind, self._checks.get(None, None))
158
172
        else:
159
 
            if not f(match_value, target_dict, cred_dict):
160
 
                return False
161
 
        return True
 
173
            LOG.warning(_("Inheritance-based rules are deprecated; update "
 
174
                          "_check_%s") % match_kind)
 
175
            func = (lambda brain, kind, value, target, cred:
 
176
                        old_func(value, target, cred))
 
177
 
 
178
        if not func:
 
179
            LOG.error(_("No handler for matches of kind %s") % match_kind)
 
180
            # Fail closed
 
181
            return False
 
182
 
 
183
        return func(self, match_kind, match_value, target_dict, cred_dict)
162
184
 
163
185
    def check(self, match_list, target_dict, cred_dict):
164
186
        """Checks authorization of some rules against credentials.
182
204
                return True
183
205
        return False
184
206
 
185
 
    def _check_rule(self, match, target_dict, cred_dict):
186
 
        """Recursively checks credentials based on the brains rules."""
187
 
        try:
188
 
            new_match_list = self.rules[match]
189
 
        except KeyError:
190
 
            if self.default_rule and match != self.default_rule:
191
 
                new_match_list = ('rule:%s' % self.default_rule,)
192
 
            else:
193
 
                return False
194
 
 
195
 
        return self.check(new_match_list, target_dict, cred_dict)
196
 
 
197
 
    def _check_role(self, match, target_dict, cred_dict):
198
 
        """Check that there is a matching role in the cred dict."""
199
 
        return match.lower() in [x.lower() for x in cred_dict['roles']]
200
 
 
201
 
    def _check_generic(self, match, target_dict, cred_dict):
202
 
        """Check an individual match.
203
 
 
204
 
        Matches look like:
205
 
 
206
 
            tenant:%(tenant_id)s
207
 
            role:compute:admin
208
 
 
209
 
        """
210
 
 
211
 
        # TODO(termie): do dict inspection via dot syntax
212
 
        match = match % target_dict
213
 
        key, value = match.split(':', 1)
214
 
        if key in cred_dict:
215
 
            return value == cred_dict[key]
216
 
        return False
217
 
 
218
207
 
219
208
class HttpBrain(Brain):
220
209
    """A brain that can check external urls for policy.
221
210
 
222
211
    Posts json blobs for target and credentials.
223
212
 
224
 
    """
225
 
 
226
 
    def _check_http(self, match, target_dict, cred_dict):
227
 
        """Check http: rules by calling to a remote server.
228
 
 
229
 
        This example implementation simply verifies that the response is
230
 
        exactly 'True'. A custom brain using response codes could easily
231
 
        be implemented.
232
 
 
233
 
        """
234
 
        url = match % target_dict
235
 
        data = {'target': jsonutils.dumps(target_dict),
236
 
                'credentials': jsonutils.dumps(cred_dict)}
237
 
        post_data = urllib.urlencode(data)
238
 
        f = urllib2.urlopen(url, post_data)
239
 
        return f.read() == "True"
 
213
    Note that this brain is deprecated; the http check is registered
 
214
    by default.
 
215
    """
 
216
 
 
217
    pass
 
218
 
 
219
 
 
220
def register(name, func=None):
 
221
    """
 
222
    Register a function as a policy check.
 
223
 
 
224
    :param name: Gives the name of the check type, e.g., 'rule',
 
225
                 'role', etc.  If name is None, a default function
 
226
                 will be registered.
 
227
    :param func: If given, provides the function to register.  If not
 
228
                 given, returns a function taking one argument to
 
229
                 specify the function to register, allowing use as a
 
230
                 decorator.
 
231
    """
 
232
 
 
233
    # Perform the actual decoration by registering the function.
 
234
    # Returns the function for compliance with the decorator
 
235
    # interface.
 
236
    def decorator(func):
 
237
        # Register the function
 
238
        Brain._register(name, func)
 
239
        return func
 
240
 
 
241
    # If the function is given, do the registration
 
242
    if func:
 
243
        return decorator(func)
 
244
 
 
245
    return decorator
 
246
 
 
247
 
 
248
@register("rule")
 
249
def _check_rule(brain, match_kind, match, target_dict, cred_dict):
 
250
    """Recursively checks credentials based on the brains rules."""
 
251
    try:
 
252
        new_match_list = brain.rules[match]
 
253
    except KeyError:
 
254
        if brain.default_rule and match != brain.default_rule:
 
255
            new_match_list = ('rule:%s' % brain.default_rule,)
 
256
        else:
 
257
            return False
 
258
 
 
259
    return brain.check(new_match_list, target_dict, cred_dict)
 
260
 
 
261
 
 
262
@register("role")
 
263
def _check_role(brain, match_kind, match, target_dict, cred_dict):
 
264
    """Check that there is a matching role in the cred dict."""
 
265
    return match.lower() in [x.lower() for x in cred_dict['roles']]
 
266
 
 
267
 
 
268
@register('http')
 
269
def _check_http(brain, match_kind, match, target_dict, cred_dict):
 
270
    """Check http: rules by calling to a remote server.
 
271
 
 
272
    This example implementation simply verifies that the response is
 
273
    exactly 'True'. A custom brain using response codes could easily
 
274
    be implemented.
 
275
 
 
276
    """
 
277
    url = 'http:' + (match % target_dict)
 
278
    data = {'target': jsonutils.dumps(target_dict),
 
279
            'credentials': jsonutils.dumps(cred_dict)}
 
280
    post_data = urllib.urlencode(data)
 
281
    f = urllib2.urlopen(url, post_data)
 
282
    return f.read() == "True"
 
283
 
 
284
 
 
285
@register(None)
 
286
def _check_generic(brain, match_kind, match, target_dict, cred_dict):
 
287
    """Check an individual match.
 
288
 
 
289
    Matches look like:
 
290
 
 
291
        tenant:%(tenant_id)s
 
292
        role:compute:admin
 
293
 
 
294
    """
 
295
 
 
296
    # TODO(termie): do dict inspection via dot syntax
 
297
    match = match % target_dict
 
298
    if match_kind in cred_dict:
 
299
        return match == cred_dict[match_kind]
 
300
    return False