~ubuntu-branches/ubuntu/precise/keystone/precise-security

« back to all changes in this revision

Viewing changes to keystone/test/unit/base.py

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2011-08-23 10:18:22 UTC
  • Revision ID: james.westby@ubuntu.com-20110823101822-enve6zceb3lqhuvj
Tags: upstream-1.0~d4~20110823.1078
ImportĀ upstreamĀ versionĀ 1.0~d4~20110823.1078

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
# Copyright (c) 2011 OpenStack, LLC.
 
3
#
 
4
# Licensed under the Apache License, Version 2.0 (the "License");
 
5
# you may not use this file except in compliance with the License.
 
6
# You may obtain a copy of the License at
 
7
#
 
8
#    http://www.apache.org/licenses/LICENSE-2.0
 
9
#
 
10
# Unless required by applicable law or agreed to in writing, software
 
11
# distributed under the License is distributed on an "AS IS" BASIS,
 
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 
13
# implied.
 
14
# See the License for the specific language governing permissions and
 
15
# limitations under the License.
 
16
 
 
17
"""Base test case classes for the unit tests"""
 
18
 
 
19
import datetime
 
20
import functools
 
21
import httplib
 
22
import logging
 
23
import pprint
 
24
import sys
 
25
import os
 
26
sys.path.append(os.path.abspath(os.path.join(os.path.abspath(__file__),
 
27
                                '..', '..', '..', '..', '..', 'keystone')))
 
28
import unittest2 as unittest
 
29
 
 
30
from lxml import etree, objectify
 
31
import webob
 
32
 
 
33
from keystone import server
 
34
import keystone.backends.sqlalchemy as db
 
35
import keystone.backends.api as db_api
 
36
 
 
37
logger = logging.getLogger('test.unit.base')
 
38
 
 
39
 
 
40
class ServiceAPITest(unittest.TestCase):
 
41
 
 
42
    """
 
43
    Base test case class for any unit test that tests the main service API.
 
44
    """
 
45
 
 
46
    """
 
47
    The `api` attribute for this base class is the `server.KeystoneAPI`
 
48
    controller.
 
49
    """
 
50
    api_class = server.ServiceApi
 
51
 
 
52
    """
 
53
    Set of dicts of tenant attributes we start each test case with
 
54
    """
 
55
    tenant_fixtures = [
 
56
        {'id': 'tenant1',
 
57
         'enabled': True,
 
58
         'desc': 'tenant1'}]
 
59
 
 
60
    """
 
61
    Attributes of the user the test creates for each test case that
 
62
    will authenticate against the API. The `auth_user` attribute
 
63
    will contain the created user with the following attributes.
 
64
    """
 
65
    auth_user_attrs = {'id': 'auth_user',
 
66
                       'password': 'auth_pass',
 
67
                       'email': 'auth_user@example.com',
 
68
                       'enabled': True,
 
69
                       'tenant_id': 'tenant1'}
 
70
    """
 
71
    Special attribute that is the identifier of the token we use in
 
72
    authenticating. Makes it easy to test the authentication process.
 
73
    """
 
74
    auth_token_id = 'SPECIALAUTHTOKEN'
 
75
 
 
76
    """
 
77
    Content-type of requests. Generally, you don't need to manually
 
78
    change this. Instead, :see test.unit.decorators
 
79
    """
 
80
    content_type = 'json'
 
81
 
 
82
    """
 
83
    Version of the API to test
 
84
    """
 
85
    api_version = '2.0'
 
86
 
 
87
    """
 
88
    Dict of configuration options to pass to the API controller
 
89
    """
 
90
    options = {
 
91
        'backends': "keystone.backends.sqlalchemy",
 
92
        'keystone.backends.sqlalchemy': {
 
93
            'sql_connection': 'sqlite://',  # in-memory db
 
94
            'verbose': False,
 
95
            'debug': False,
 
96
            'backend_entities':
 
97
                "['UserRoleAssociation', 'Endpoints', 'Role', 'Tenant', "
 
98
                "'Tenant', 'User', 'Credentials', 'EndpointTemplates', "
 
99
                "'Token', 'Service']",
 
100
        },
 
101
        'keystone-admin-role': 'Admin',
 
102
        'keystone-service-admin-role': 'KeystoneServiceAdmin',
 
103
    }
 
104
 
 
105
    def setUp(self):
 
106
        self.api = self.api_class(self.options)
 
107
 
 
108
        dt = datetime
 
109
        self.expires = dt.datetime.utcnow() + dt.timedelta(days=1)
 
110
        self.clear_all_data()
 
111
 
 
112
        # Create all our base tenants
 
113
        for tenant in self.tenant_fixtures:
 
114
            self.fixture_create_tenant(**tenant)
 
115
 
 
116
        # Create the user we will authenticate with
 
117
        self.auth_user = self.fixture_create_user(**self.auth_user_attrs)
 
118
        self.auth_token = self.fixture_create_token(
 
119
            id=self.auth_token_id,
 
120
            user_id=self.auth_user['id'],
 
121
            tenant_id=self.auth_user['tenant_id'],
 
122
            expires=self.expires,
 
123
        )
 
124
 
 
125
        self.add_verify_status_helpers()
 
126
 
 
127
    def tearDown(self):
 
128
        self.clear_all_data()
 
129
        setattr(self, 'req', None)
 
130
        setattr(self, 'res', None)
 
131
 
 
132
    def clear_all_data(self):
 
133
        """
 
134
        Purges the database of all data
 
135
        """
 
136
        db.unregister_models()
 
137
        logger.debug("Cleared all data from database")
 
138
        opts = self.options
 
139
        db.register_models(options=opts['keystone.backends.sqlalchemy'])
 
140
 
 
141
    def fixture_create_credentials(self, **kwargs):
 
142
        """
 
143
        Creates a tenant fixture.
 
144
 
 
145
        :params **kwargs: Attributes of the tenant to create
 
146
        """
 
147
        values = kwargs.copy()
 
148
        credentials = db_api.CREDENTIALS.create(values)
 
149
        logger.debug("Created credentials fixture %s", credentials['id'])
 
150
        return credentials
 
151
 
 
152
    def fixture_create_tenant(self, **kwargs):
 
153
        """
 
154
        Creates a tenant fixture.
 
155
 
 
156
        :params **kwargs: Attributes of the tenant to create
 
157
        """
 
158
        values = kwargs.copy()
 
159
        tenant = db_api.TENANT.create(values)
 
160
        logger.debug("Created tenant fixture %s", values['id'])
 
161
        return tenant
 
162
 
 
163
    def fixture_create_user(self, **kwargs):
 
164
        """
 
165
        Creates a user fixture. If the user's tenant ID is set, and the tenant
 
166
        does not exist in the database, the tenant is created.
 
167
 
 
168
        :params **kwargs: Attributes of the user to create
 
169
        """
 
170
        values = kwargs.copy()
 
171
        tenant_id = values.get('tenant_id')
 
172
        if tenant_id:
 
173
            if not db_api.TENANT.get(tenant_id):
 
174
                db_api.TENANT.create({'id': tenant_id,
 
175
                                      'enabled': True,
 
176
                                      'desc': tenant_id})
 
177
        user = db_api.USER.create(values)
 
178
        logger.debug("Created user fixture %s", values['id'])
 
179
        return user
 
180
 
 
181
    def fixture_create_token(self, **kwargs):
 
182
        """
 
183
        Creates a token fixture.
 
184
 
 
185
        :params **kwargs: Attributes of the token to create
 
186
        """
 
187
        values = kwargs.copy()
 
188
        token = db_api.TOKEN.create(values)
 
189
        logger.debug("Created token fixture %s", values['id'])
 
190
        return token
 
191
 
 
192
    def get_request(self, method, url, headers=None):
 
193
        """
 
194
        Sets the `req` attribute to a `webob.Request` object that
 
195
        is constructed with the supplied method and url. Supplied
 
196
        headers are added to appropriate Content-type headers.
 
197
        """
 
198
        headers = headers or {}
 
199
        self.req = webob.Request.blank(url)
 
200
        self.req.method = method
 
201
        self.req.headers = headers
 
202
        if 'content-type' not in headers:
 
203
            ct = 'application/%s' % self.content_type
 
204
            self.req.headers['content-type'] = ct
 
205
            self.req.headers['accept'] = ct
 
206
        return self.req
 
207
 
 
208
    def get_response(self):
 
209
        """
 
210
        Sets the appropriate headers for the `req` attribute for
 
211
        the current content type, then calls `req.get_response()` and
 
212
        sets the `res` attribute to the returned `webob.Response` object
 
213
        """
 
214
        self.res = self.req.get_response(self.api)
 
215
        logger.debug("%s %s returned %s", self.req.method, self.req.path_qs,
 
216
                     self.res.status)
 
217
        if self.res.status_int != httplib.OK:
 
218
            logger.debug("Response Body:")
 
219
            for line in self.res.body.split("\n"):
 
220
                logger.debug(line)
 
221
        return self.res
 
222
 
 
223
    def verify_status(self, status_code):
 
224
        """
 
225
        Simple convenience wrapper for validating a response's status
 
226
        code.
 
227
        """
 
228
        if not getattr(self, 'res'):
 
229
            raise RuntimeError("Called verify_status() before calling "
 
230
                               "get_response()!")
 
231
 
 
232
        self.assertEqual(status_code, self.res.status_int,
 
233
                         "Incorrect status code %d. Expected %d" %
 
234
                         (self.res.status_int, status_code))
 
235
 
 
236
    def add_verify_status_helpers(self):
 
237
        """
 
238
        Adds some convenience helpers using partials...
 
239
        """
 
240
        self.status_ok = functools.partial(self.verify_status,
 
241
                                           httplib.OK)
 
242
        self.status_not_found = functools.partial(self.verify_status,
 
243
                                           httplib.NOT_FOUND)
 
244
        self.status_unauthorized = functools.partial(self.verify_status,
 
245
                                           httplib.UNAUTHORIZED)
 
246
        self.status_bad_request = functools.partial(self.verify_status,
 
247
                                           httplib.BAD_REQUEST)
 
248
 
 
249
    def assert_dict_equal(self, expected, got):
 
250
        """
 
251
        Compares two dicts for equality and prints the dictionaries
 
252
        nicely formatted for easy comparison if there is a failure.
 
253
        """
 
254
        self.assertEqual(expected, got, "Mappings are not equal.\n"
 
255
                         "Got:\n%s\nExpected:\n%s" %
 
256
                         (pprint.pformat(got),
 
257
                          pprint.pformat(expected)))
 
258
 
 
259
    def assert_xml_strings_equal(self, expected, got):
 
260
        """
 
261
        Compares two XML strings for equality by parsing them both
 
262
        into DOMs.  Prints the DOMs nicely formatted for easy comparison
 
263
        if there is a failure.
 
264
        """
 
265
        # This is a nice little trick... objectify.fromstring() returns
 
266
        # a DOM different from etree.fromstring(). The objectify version
 
267
        # removes any different whitespacing...
 
268
        got = objectify.fromstring(got)
 
269
        expected = objectify.fromstring(expected)
 
270
        self.assertEqual(etree.tostring(expected),
 
271
                         etree.tostring(got), "DOMs are not equal.\n"
 
272
                         "Got:\n%s\nExpected:\n%s" %
 
273
                         (etree.tostring(got, pretty_print=True),
 
274
                          etree.tostring(expected, pretty_print=True)))
 
275
 
 
276
 
 
277
class AdminAPITest(ServiceAPITest):
 
278
 
 
279
    """
 
280
    Base test case class for any unit test that tests the admin API. The
 
281
    """
 
282
 
 
283
    """
 
284
    The `api` attribute for this base class is the `server.KeystoneAdminAPI`
 
285
    controller.
 
286
    """
 
287
    api_class = server.AdminApi
 
288
 
 
289
    """
 
290
    Set of dicts of tenant attributes we start each test case with
 
291
    """
 
292
    tenant_fixtures = [
 
293
        {'id': 'tenant1',
 
294
         'enabled': True,
 
295
         'desc': 'tenant1'},
 
296
        {'id': 'tenant2',
 
297
         'enabled': True,
 
298
         'desc': 'tenant2'}]
 
299
 
 
300
    """
 
301
    Attributes of the user the test creates for each test case that
 
302
    will authenticate against the API.
 
303
    """
 
304
    auth_user_attrs = {'id': 'admin_user',
 
305
                       'password': 'admin_pass',
 
306
                       'email': 'admin_user@example.com',
 
307
                       'enabled': True,
 
308
                       'tenant_id': 'tenant2'}