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

« back to all changes in this revision

Viewing changes to nova/auth/manager.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:
1
 
# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
 
 
3
 
# Copyright 2010 United States Government as represented by the
4
 
# Administrator of the National Aeronautics and Space Administration.
5
 
# All Rights Reserved.
6
 
#
7
 
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
8
 
#    not use this file except in compliance with the License. You may obtain
9
 
#    a copy of the License at
10
 
#
11
 
#         http://www.apache.org/licenses/LICENSE-2.0
12
 
#
13
 
#    Unless required by applicable law or agreed to in writing, software
14
 
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
 
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
 
#    License for the specific language governing permissions and limitations
17
 
#    under the License.
18
 
 
19
 
"""
20
 
WARNING: This code is deprecated and will be removed.
21
 
Keystone is the recommended solution for auth management.
22
 
 
23
 
Nova authentication management
24
 
"""
25
 
 
26
 
import os
27
 
import string  # pylint: disable=W0402
28
 
import uuid
29
 
import zipfile
30
 
 
31
 
from nova.auth import signer
32
 
from nova import context
33
 
from nova import crypto
34
 
from nova import db
35
 
from nova import exception
36
 
from nova import flags
37
 
from nova.openstack.common import cfg
38
 
from nova.openstack.common import importutils
39
 
from nova.openstack.common import log as logging
40
 
from nova import utils
41
 
 
42
 
 
43
 
auth_opts = [
44
 
    cfg.ListOpt('allowed_roles',
45
 
                default=[
46
 
                  'cloudadmin',
47
 
                  'itsec',
48
 
                  'sysadmin',
49
 
                  'netadmin',
50
 
                  'developer'
51
 
                  ],
52
 
                help='Allowed roles for project'),
53
 
 
54
 
    # NOTE(vish): a user with one of these roles will be a superuser and
55
 
    #             have access to all api commands
56
 
    cfg.ListOpt('superuser_roles',
57
 
                default=['cloudadmin'],
58
 
                help='Roles that ignore authorization checking completely'),
59
 
 
60
 
    # NOTE(vish): a user with one of these roles will have it for every
61
 
    #             project, even if he or she is not a member of the project
62
 
    cfg.ListOpt('global_roles',
63
 
                default=['cloudadmin', 'itsec'],
64
 
                help='Roles that apply to all projects'),
65
 
 
66
 
    cfg.StrOpt('credentials_template',
67
 
               default='$pybasedir/nova/auth/novarc.template',
68
 
               help='Template for creating users rc file'),
69
 
    cfg.StrOpt('vpn_client_template',
70
 
               default='$pybasedir/nova/cloudpipe/client.ovpn.template',
71
 
               help='Template for creating users vpn file'),
72
 
    cfg.StrOpt('credential_vpn_file',
73
 
               default='nova-vpn.conf',
74
 
               help='Filename of certificate in credentials zip'),
75
 
    cfg.StrOpt('credential_key_file',
76
 
               default='pk.pem',
77
 
               help='Filename of private key in credentials zip'),
78
 
    cfg.StrOpt('credential_cert_file',
79
 
               default='cert.pem',
80
 
               help='Filename of certificate in credentials zip'),
81
 
    cfg.StrOpt('credential_rc_file',
82
 
               default='%src',
83
 
               help='Filename of rc in credentials zip %s will be replaced by '
84
 
                    'name of the region (nova by default)'),
85
 
    cfg.StrOpt('auth_driver',
86
 
               default='nova.auth.dbdriver.DbDriver',
87
 
               help='Driver that auth manager uses'),
88
 
    ]
89
 
 
90
 
FLAGS = flags.FLAGS
91
 
FLAGS.register_opts(auth_opts)
92
 
 
93
 
flags.DECLARE('osapi_compute_listen_port', 'nova.service')
94
 
 
95
 
LOG = logging.getLogger(__name__)
96
 
 
97
 
 
98
 
if FLAGS.memcached_servers:
99
 
    import memcache
100
 
else:
101
 
    from nova.common import memorycache as memcache
102
 
 
103
 
 
104
 
class AuthBase(object):
105
 
    """Base class for objects relating to auth
106
 
 
107
 
    Objects derived from this class should be stupid data objects with
108
 
    an id member. They may optionally contain methods that delegate to
109
 
    AuthManager, but should not implement logic themselves.
110
 
    """
111
 
 
112
 
    @classmethod
113
 
    def safe_id(cls, obj):
114
 
        """Safely get object id.
115
 
 
116
 
        This method will return the id of the object if the object
117
 
        is of this class, otherwise it will return the original object.
118
 
        This allows methods to accept objects or ids as parameters.
119
 
        """
120
 
        if isinstance(obj, cls):
121
 
            return obj.id
122
 
        else:
123
 
            return obj
124
 
 
125
 
 
126
 
class User(AuthBase):
127
 
    """Object representing a user
128
 
 
129
 
    The following attributes are defined:
130
 
 
131
 
    ``id``
132
 
      A system identifier for the user.  A string (for LDAP)
133
 
    ``name``
134
 
      The user name, potentially in some more friendly format
135
 
    ``access``
136
 
      The 'username' for EC2 authentication
137
 
    ``secret``
138
 
      The 'password' for EC2 authenticatoin
139
 
    ``admin``
140
 
      ???
141
 
    """
142
 
 
143
 
    def __init__(self, id, name, access, secret, admin):
144
 
        AuthBase.__init__(self)
145
 
        assert isinstance(id, basestring)
146
 
        self.id = id
147
 
        self.name = name
148
 
        self.access = access
149
 
        self.secret = secret
150
 
        self.admin = admin
151
 
 
152
 
    def is_superuser(self):
153
 
        return AuthManager().is_superuser(self)
154
 
 
155
 
    def is_admin(self):
156
 
        return AuthManager().is_admin(self)
157
 
 
158
 
    def has_role(self, role):
159
 
        return AuthManager().has_role(self, role)
160
 
 
161
 
    def add_role(self, role):
162
 
        return AuthManager().add_role(self, role)
163
 
 
164
 
    def remove_role(self, role):
165
 
        return AuthManager().remove_role(self, role)
166
 
 
167
 
    def is_project_member(self, project):
168
 
        return AuthManager().is_project_member(self, project)
169
 
 
170
 
    def is_project_manager(self, project):
171
 
        return AuthManager().is_project_manager(self, project)
172
 
 
173
 
    def __repr__(self):
174
 
        return "User('%s', '%s')" % (self.id, self.name)
175
 
 
176
 
 
177
 
class Project(AuthBase):
178
 
    """Represents a Project returned from the datastore"""
179
 
 
180
 
    def __init__(self, id, name, project_manager_id, description, member_ids):
181
 
        AuthBase.__init__(self)
182
 
        self.id = id
183
 
        self.name = name
184
 
        self.project_manager_id = project_manager_id
185
 
        self.description = description
186
 
        self.member_ids = member_ids
187
 
 
188
 
    @property
189
 
    def project_manager(self):
190
 
        return AuthManager().get_user(self.project_manager_id)
191
 
 
192
 
    @property
193
 
    def vpn_ip(self):
194
 
        ip, _port = AuthManager().get_project_vpn_data(self)
195
 
        return ip
196
 
 
197
 
    @property
198
 
    def vpn_port(self):
199
 
        _ip, port = AuthManager().get_project_vpn_data(self)
200
 
        return port
201
 
 
202
 
    def has_manager(self, user):
203
 
        return AuthManager().is_project_manager(user, self)
204
 
 
205
 
    def has_member(self, user):
206
 
        return AuthManager().is_project_member(user, self)
207
 
 
208
 
    def add_role(self, user, role):
209
 
        return AuthManager().add_role(user, role, self)
210
 
 
211
 
    def remove_role(self, user, role):
212
 
        return AuthManager().remove_role(user, role, self)
213
 
 
214
 
    def has_role(self, user, role):
215
 
        return AuthManager().has_role(user, role, self)
216
 
 
217
 
    def get_credentials(self, user):
218
 
        return AuthManager().get_credentials(user, self)
219
 
 
220
 
    def __repr__(self):
221
 
        return "Project('%s', '%s')" % (self.id, self.name)
222
 
 
223
 
 
224
 
class AuthManager(object):
225
 
    """Manager Singleton for dealing with Users, Projects, and Keypairs
226
 
 
227
 
    Methods accept objects or ids.
228
 
 
229
 
    AuthManager uses a driver object to make requests to the data backend.
230
 
    See ldapdriver for reference.
231
 
 
232
 
    AuthManager also manages associated data related to Auth objects that
233
 
    need to be more accessible, such as vpn ips and ports.
234
 
    """
235
 
 
236
 
    _instance = None
237
 
    mc = None
238
 
 
239
 
    def __new__(cls, *args, **kwargs):
240
 
        """Returns the AuthManager singleton"""
241
 
        if not cls._instance or ('new' in kwargs and kwargs['new']):
242
 
            cls._instance = super(AuthManager, cls).__new__(cls)
243
 
        return cls._instance
244
 
 
245
 
    def __init__(self, driver=None, *args, **kwargs):
246
 
        """Inits the driver from parameter or flag
247
 
 
248
 
        __init__ is run every time AuthManager() is called, so we only
249
 
        reset the driver if it is not set or a new driver is specified.
250
 
        """
251
 
        self.network_manager = importutils.import_object(FLAGS.network_manager)
252
 
        if driver or not getattr(self, 'driver', None):
253
 
            self.driver = importutils.import_class(driver or FLAGS.auth_driver)
254
 
        if AuthManager.mc is None:
255
 
            AuthManager.mc = memcache.Client(FLAGS.memcached_servers, debug=0)
256
 
 
257
 
    def authenticate(self, access, signature, params, verb='GET',
258
 
                     server_string='127.0.0.1:8773', path='/',
259
 
                     check_type='ec2', headers=None):
260
 
        """Authenticates AWS request using access key and signature
261
 
 
262
 
        If the project is not specified, attempts to authenticate to
263
 
        a project with the same name as the user. This way, older tools
264
 
        that have no project knowledge will still work.
265
 
 
266
 
        :type access: str
267
 
        :param access: Access key for user in the form "access:project".
268
 
 
269
 
        :type signature: str
270
 
        :param signature: Signature of the request.
271
 
 
272
 
        :type params: list of str
273
 
        :param params: Web paramaters used for the signature.
274
 
 
275
 
        :type verb: str
276
 
        :param verb: Web request verb ('GET' or 'POST').
277
 
 
278
 
        :type server_string: str
279
 
        :param server_string: Web request server string.
280
 
 
281
 
        :type path: str
282
 
        :param path: Web request path.
283
 
 
284
 
        :type check_type: str
285
 
        :param check_type: Type of signature to check. 'ec2' for EC2, 's3' for
286
 
                           S3. Any other value will cause signature not to be
287
 
                           checked.
288
 
 
289
 
        :type headers: list
290
 
        :param headers: HTTP headers passed with the request (only needed for
291
 
                        s3 signature checks)
292
 
 
293
 
        :rtype: tuple (User, Project)
294
 
        :return: User and project that the request represents.
295
 
        """
296
 
        # TODO(vish): check for valid timestamp
297
 
        (access_key, _sep, project_id) = access.partition(':')
298
 
 
299
 
        LOG.debug(_('Looking up user: %r'), access_key)
300
 
        user = self.get_user_from_access_key(access_key)
301
 
        LOG.debug('user: %r', user)
302
 
        if user is None:
303
 
            LOG.audit(_("Failed authorization for access key %s"), access_key)
304
 
            raise exception.AccessKeyNotFound(access_key=access_key)
305
 
 
306
 
        # NOTE(vish): if we stop using project name as id we need better
307
 
        #             logic to find a default project for user
308
 
        if project_id == '':
309
 
            LOG.debug(_("Using project name = user name (%s)"), user.name)
310
 
            project_id = user.name
311
 
 
312
 
        project = self.get_project(project_id)
313
 
        if project is None:
314
 
            pjid = project_id
315
 
            uname = user.name
316
 
            LOG.audit(_("failed authorization: no project named %(pjid)s"
317
 
                    " (user=%(uname)s)") % locals())
318
 
            raise exception.ProjectNotFound(project_id=project_id)
319
 
        if not self.is_admin(user) and not self.is_project_member(user,
320
 
                                                                  project):
321
 
            uname = user.name
322
 
            uid = user.id
323
 
            pjname = project.name
324
 
            pjid = project.id
325
 
            LOG.audit(_("Failed authorization: user %(uname)s not admin"
326
 
                    " and not member of project %(pjname)s") % locals())
327
 
            raise exception.ProjectMembershipNotFound(project_id=pjid,
328
 
                                                      user_id=uid)
329
 
        if check_type == 's3':
330
 
            sign = signer.Signer(user.secret.encode())
331
 
            expected_signature = sign.s3_authorization(headers, verb, path)
332
 
            LOG.debug(_('user.secret: %s'), user.secret)
333
 
            LOG.debug(_('expected_signature: %s'), expected_signature)
334
 
            LOG.debug(_('signature: %s'), signature)
335
 
            if not utils.strcmp_const_time(signature, expected_signature):
336
 
                LOG.audit(_("Invalid signature for user %s"), user.name)
337
 
                raise exception.InvalidSignature(signature=signature,
338
 
                                                 user=user)
339
 
        elif check_type == 'ec2':
340
 
            # NOTE(vish): hmac can't handle unicode, so encode ensures that
341
 
            #             secret isn't unicode
342
 
            expected_signature = signer.Signer(user.secret.encode()).generate(
343
 
                    params, verb, server_string, path)
344
 
            LOG.debug(_('user.secret: %s'), user.secret)
345
 
            LOG.debug(_('expected_signature: %s'), expected_signature)
346
 
            LOG.debug(_('signature: %s'), signature)
347
 
            if not utils.strcmp_const_time(signature, expected_signature):
348
 
                (addr_str, port_str) = utils.parse_server_string(server_string)
349
 
                # If the given server_string contains port num, try without it.
350
 
                if port_str != '':
351
 
                    host_only_signature = signer.Signer(
352
 
                        user.secret.encode()).generate(params, verb,
353
 
                                                       addr_str, path)
354
 
                    LOG.debug(_('host_only_signature: %s'),
355
 
                              host_only_signature)
356
 
                    if utils.strcmp_const_time(signature, host_only_signature):
357
 
                        return (user, project)
358
 
                LOG.audit(_("Invalid signature for user %s"), user.name)
359
 
                raise exception.InvalidSignature(signature=signature,
360
 
                                                 user=user)
361
 
        return (user, project)
362
 
 
363
 
    def get_access_key(self, user, project):
364
 
        """Get an access key that includes user and project"""
365
 
        if not isinstance(user, User):
366
 
            user = self.get_user(user)
367
 
        return "%s:%s" % (user.access, Project.safe_id(project))
368
 
 
369
 
    def is_superuser(self, user):
370
 
        """Checks for superuser status, allowing user to bypass authorization
371
 
 
372
 
        :type user: User or uid
373
 
        :param user: User to check.
374
 
 
375
 
        :rtype: bool
376
 
        :return: True for superuser.
377
 
        """
378
 
        if not isinstance(user, User):
379
 
            user = self.get_user(user)
380
 
        # NOTE(vish): admin flag on user represents superuser
381
 
        if user.admin:
382
 
            return True
383
 
        for role in FLAGS.superuser_roles:
384
 
            if self.has_role(user, role):
385
 
                return True
386
 
 
387
 
    def is_admin(self, user):
388
 
        """Checks for admin status, allowing user to access all projects
389
 
 
390
 
        :type user: User or uid
391
 
        :param user: User to check.
392
 
 
393
 
        :rtype: bool
394
 
        :return: True for admin.
395
 
        """
396
 
        if not isinstance(user, User):
397
 
            user = self.get_user(user)
398
 
        if self.is_superuser(user):
399
 
            return True
400
 
        for role in FLAGS.global_roles:
401
 
            if self.has_role(user, role):
402
 
                return True
403
 
 
404
 
    def _build_mc_key(self, user, role, project=None):
405
 
        key_parts = ['rolecache', User.safe_id(user), str(role)]
406
 
        if project:
407
 
            key_parts.append(Project.safe_id(project))
408
 
        return utils.utf8('-'.join(key_parts))
409
 
 
410
 
    def _clear_mc_key(self, user, role, project=None):
411
 
        # NOTE(anthony): it would be better to delete the key
412
 
        self.mc.set(self._build_mc_key(user, role, project), None)
413
 
 
414
 
    def _has_role(self, user, role, project=None):
415
 
        mc_key = self._build_mc_key(user, role, project)
416
 
        rslt = self.mc.get(mc_key)
417
 
        if rslt is None:
418
 
            with self.driver() as drv:
419
 
                rslt = drv.has_role(user, role, project)
420
 
                self.mc.set(mc_key, rslt)
421
 
                return rslt
422
 
        else:
423
 
            return rslt
424
 
 
425
 
    def has_role(self, user, role, project=None):
426
 
        """Checks existence of role for user
427
 
 
428
 
        If project is not specified, checks for a global role. If project
429
 
        is specified, checks for the union of the global role and the
430
 
        project role.
431
 
 
432
 
        Role 'projectmanager' only works for projects and simply checks to
433
 
        see if the user is the project_manager of the specified project. It
434
 
        is the same as calling is_project_manager(user, project).
435
 
 
436
 
        :type user: User or uid
437
 
        :param user: User to check.
438
 
 
439
 
        :type role: str
440
 
        :param role: Role to check.
441
 
 
442
 
        :type project: Project or project_id
443
 
        :param project: Project in which to look for local role.
444
 
 
445
 
        :rtype: bool
446
 
        :return: True if the user has the role.
447
 
        """
448
 
        if role == 'projectmanager':
449
 
            if not project:
450
 
                raise exception.NovaException(_("Must specify project"))
451
 
            return self.is_project_manager(user, project)
452
 
 
453
 
        global_role = self._has_role(User.safe_id(user),
454
 
                                     role,
455
 
                                     None)
456
 
 
457
 
        if not global_role:
458
 
            return global_role
459
 
 
460
 
        if not project or role in FLAGS.global_roles:
461
 
            return global_role
462
 
 
463
 
        return self._has_role(User.safe_id(user),
464
 
                              role,
465
 
                              Project.safe_id(project))
466
 
 
467
 
    def add_role(self, user, role, project=None):
468
 
        """Adds role for user
469
 
 
470
 
        If project is not specified, adds a global role. If project
471
 
        is specified, adds a local role.
472
 
 
473
 
        The 'projectmanager' role is special and can't be added or removed.
474
 
 
475
 
        :type user: User or uid
476
 
        :param user: User to which to add role.
477
 
 
478
 
        :type role: str
479
 
        :param role: Role to add.
480
 
 
481
 
        :type project: Project or project_id
482
 
        :param project: Project in which to add local role.
483
 
        """
484
 
        if role not in FLAGS.allowed_roles:
485
 
            raise exception.UserRoleNotFound(role_id=role)
486
 
        if project is not None and role in FLAGS.global_roles:
487
 
            raise exception.GlobalRoleNotAllowed(role_id=role)
488
 
        uid = User.safe_id(user)
489
 
        pid = Project.safe_id(project)
490
 
        if project:
491
 
            LOG.audit(_("Adding role %(role)s to user %(uid)s"
492
 
                    " in project %(pid)s") % locals())
493
 
        else:
494
 
            LOG.audit(_("Adding sitewide role %(role)s to user %(uid)s")
495
 
                    % locals())
496
 
        with self.driver() as drv:
497
 
            self._clear_mc_key(uid, role, pid)
498
 
            drv.add_role(uid, role, pid)
499
 
 
500
 
    def remove_role(self, user, role, project=None):
501
 
        """Removes role for user
502
 
 
503
 
        If project is not specified, removes a global role. If project
504
 
        is specified, removes a local role.
505
 
 
506
 
        The 'projectmanager' role is special and can't be added or removed.
507
 
 
508
 
        :type user: User or uid
509
 
        :param user: User from which to remove role.
510
 
 
511
 
        :type role: str
512
 
        :param role: Role to remove.
513
 
 
514
 
        :type project: Project or project_id
515
 
        :param project: Project in which to remove local role.
516
 
        """
517
 
        uid = User.safe_id(user)
518
 
        pid = Project.safe_id(project)
519
 
        if project:
520
 
            LOG.audit(_("Removing role %(role)s from user %(uid)s"
521
 
                    " on project %(pid)s") % locals())
522
 
        else:
523
 
            LOG.audit(_("Removing sitewide role %(role)s"
524
 
                    " from user %(uid)s") % locals())
525
 
        with self.driver() as drv:
526
 
            self._clear_mc_key(uid, role, pid)
527
 
            drv.remove_role(uid, role, pid)
528
 
 
529
 
    @staticmethod
530
 
    def get_roles(project_roles=True):
531
 
        """Get list of allowed roles"""
532
 
        if project_roles:
533
 
            return list(set(FLAGS.allowed_roles) - set(FLAGS.global_roles))
534
 
        else:
535
 
            return FLAGS.allowed_roles
536
 
 
537
 
    def get_user_roles(self, user, project=None):
538
 
        """Get user global or per-project roles"""
539
 
        with self.driver() as drv:
540
 
            return drv.get_user_roles(User.safe_id(user),
541
 
                                      Project.safe_id(project))
542
 
 
543
 
    def get_active_roles(self, user, project=None):
544
 
        """Get all active roles for context"""
545
 
        if project:
546
 
            roles = FLAGS.allowed_roles + ['projectmanager']
547
 
        else:
548
 
            roles = FLAGS.global_roles
549
 
        return [role for role in roles if self.has_role(user, role, project)]
550
 
 
551
 
    def get_project(self, pid):
552
 
        """Get project object by id"""
553
 
        with self.driver() as drv:
554
 
            project_dict = drv.get_project(pid)
555
 
            if project_dict:
556
 
                return Project(**project_dict)
557
 
 
558
 
    def get_projects(self, user=None):
559
 
        """Retrieves list of projects, optionally filtered by user"""
560
 
        with self.driver() as drv:
561
 
            project_list = drv.get_projects(User.safe_id(user))
562
 
            if not project_list:
563
 
                return []
564
 
            return [Project(**project_dict) for project_dict in project_list]
565
 
 
566
 
    def create_project(self, name, manager_user, description=None,
567
 
                       member_users=None):
568
 
        """Create a project
569
 
 
570
 
        :type name: str
571
 
        :param name: Name of the project to create. The name will also be
572
 
        used as the project id.
573
 
 
574
 
        :type manager_user: User or uid
575
 
        :param manager_user: This user will be the project manager.
576
 
 
577
 
        :type description: str
578
 
        :param project: Description of the project. If no description is
579
 
        specified, the name of the project will be used.
580
 
 
581
 
        :type member_users: list of User or uid
582
 
        :param: Initial project members. The project manager will always be
583
 
        added as a member, even if he isn't specified in this list.
584
 
 
585
 
        :rtype: Project
586
 
        :return: The new project.
587
 
        """
588
 
        if member_users:
589
 
            member_users = [User.safe_id(u) for u in member_users]
590
 
        with self.driver() as drv:
591
 
            project_dict = drv.create_project(name,
592
 
                                              User.safe_id(manager_user),
593
 
                                              description,
594
 
                                              member_users)
595
 
            if project_dict:
596
 
                LOG.audit(_("Created project %(name)s with"
597
 
                        " manager %(manager_user)s") % locals())
598
 
                project = Project(**project_dict)
599
 
                return project
600
 
 
601
 
    def modify_project(self, project, manager_user=None, description=None):
602
 
        """Modify a project
603
 
 
604
 
        :type name: Project or project_id
605
 
        :param project: The project to modify.
606
 
 
607
 
        :type manager_user: User or uid
608
 
        :param manager_user: This user will be the new project manager.
609
 
 
610
 
        :type description: str
611
 
        :param project: This will be the new description of the project.
612
 
 
613
 
        """
614
 
        LOG.audit(_("modifying project %s"), Project.safe_id(project))
615
 
        if manager_user:
616
 
            manager_user = User.safe_id(manager_user)
617
 
        with self.driver() as drv:
618
 
            drv.modify_project(Project.safe_id(project),
619
 
                               manager_user,
620
 
                               description)
621
 
 
622
 
    def add_to_project(self, user, project):
623
 
        """Add user to project"""
624
 
        uid = User.safe_id(user)
625
 
        pid = Project.safe_id(project)
626
 
        LOG.audit(_("Adding user %(uid)s to project %(pid)s") % locals())
627
 
        with self.driver() as drv:
628
 
            return drv.add_to_project(User.safe_id(user),
629
 
                                      Project.safe_id(project))
630
 
 
631
 
    def is_project_manager(self, user, project):
632
 
        """Checks if user is project manager"""
633
 
        if not isinstance(project, Project):
634
 
            project = self.get_project(project)
635
 
        return User.safe_id(user) == project.project_manager_id
636
 
 
637
 
    def is_project_member(self, user, project):
638
 
        """Checks to see if user is a member of project"""
639
 
        if not isinstance(project, Project):
640
 
            project = self.get_project(project)
641
 
        return User.safe_id(user) in project.member_ids
642
 
 
643
 
    def remove_from_project(self, user, project):
644
 
        """Removes a user from a project"""
645
 
        uid = User.safe_id(user)
646
 
        pid = Project.safe_id(project)
647
 
        LOG.audit(_("Remove user %(uid)s from project %(pid)s") % locals())
648
 
        with self.driver() as drv:
649
 
            return drv.remove_from_project(uid, pid)
650
 
 
651
 
    @staticmethod
652
 
    def get_project_vpn_data(project):
653
 
        """Gets vpn ip and port for project
654
 
 
655
 
        :type project: Project or project_id
656
 
        :param project: Project from which to get associated vpn data
657
 
 
658
 
        :rvalue: tuple of (str, str)
659
 
        :return: A tuple containing (ip, port) or None, None if vpn has
660
 
                 not been allocated for user.
661
 
        """
662
 
 
663
 
        networks = db.project_get_networks(context.get_admin_context(),
664
 
                                           Project.safe_id(project), False)
665
 
        if not networks:
666
 
            return (None, None)
667
 
 
668
 
        # TODO(tr3buchet): not sure what you guys plan on doing with this
669
 
        # but it's possible for a project to have multiple sets of vpn data
670
 
        # for now I'm just returning the first one
671
 
        network = networks[0]
672
 
        return (network['vpn_public_address'],
673
 
                network['vpn_public_port'])
674
 
 
675
 
    def delete_project(self, project):
676
 
        """Deletes a project"""
677
 
        LOG.audit(_("Deleting project %s"), Project.safe_id(project))
678
 
        with self.driver() as drv:
679
 
            drv.delete_project(Project.safe_id(project))
680
 
 
681
 
    def get_user(self, uid):
682
 
        """Retrieves a user by id"""
683
 
        with self.driver() as drv:
684
 
            user_dict = drv.get_user(uid)
685
 
            if user_dict:
686
 
                return User(**user_dict)
687
 
 
688
 
    def get_user_from_access_key(self, access_key):
689
 
        """Retrieves a user by access key"""
690
 
        with self.driver() as drv:
691
 
            user_dict = drv.get_user_from_access_key(access_key)
692
 
            if user_dict:
693
 
                return User(**user_dict)
694
 
 
695
 
    def get_users(self):
696
 
        """Retrieves a list of all users"""
697
 
        with self.driver() as drv:
698
 
            user_list = drv.get_users()
699
 
            if not user_list:
700
 
                return []
701
 
            return [User(**user_dict) for user_dict in user_list]
702
 
 
703
 
    def create_user(self, name, access=None, secret=None, admin=False):
704
 
        """Creates a user
705
 
 
706
 
        :type name: str
707
 
        :param name: Name of the user to create.
708
 
 
709
 
        :type access: str
710
 
        :param access: Access Key (defaults to a random uuid)
711
 
 
712
 
        :type secret: str
713
 
        :param secret: Secret Key (defaults to a random uuid)
714
 
 
715
 
        :type admin: bool
716
 
        :param admin: Whether to set the admin flag. The admin flag gives
717
 
        superuser status regardless of roles specified for the user.
718
 
 
719
 
        :type create_project: bool
720
 
        :param: Whether to create a project for the user with the same name.
721
 
 
722
 
        :rtype: User
723
 
        :return: The new user.
724
 
        """
725
 
        if access is None:
726
 
            access = str(uuid.uuid4())
727
 
        if secret is None:
728
 
            secret = str(uuid.uuid4())
729
 
        with self.driver() as drv:
730
 
            user_dict = drv.create_user(name, access, secret, admin)
731
 
            if user_dict:
732
 
                rv = User(**user_dict)
733
 
                rvname = rv.name
734
 
                rvadmin = rv.admin
735
 
                LOG.audit(_("Created user %(rvname)s"
736
 
                        " (admin: %(rvadmin)r)") % locals())
737
 
                return rv
738
 
 
739
 
    def delete_user(self, user):
740
 
        """Deletes a user
741
 
 
742
 
        Additionally deletes all users key_pairs"""
743
 
        uid = User.safe_id(user)
744
 
        LOG.audit(_("Deleting user %s"), uid)
745
 
        db.key_pair_destroy_all_by_user(context.get_admin_context(),
746
 
                                        uid)
747
 
        with self.driver() as drv:
748
 
            drv.delete_user(uid)
749
 
 
750
 
    def modify_user(self, user, access_key=None, secret_key=None, admin=None):
751
 
        """Modify credentials for a user"""
752
 
        uid = User.safe_id(user)
753
 
        if access_key:
754
 
            LOG.audit(_("Access Key change for user %s"), uid)
755
 
        if secret_key:
756
 
            LOG.audit(_("Secret Key change for user %s"), uid)
757
 
        if admin is not None:
758
 
            LOG.audit(_("Admin status set to %(admin)r"
759
 
                    " for user %(uid)s") % locals())
760
 
        with self.driver() as drv:
761
 
            drv.modify_user(uid, access_key, secret_key, admin)
762
 
 
763
 
    def get_credentials(self, user, project=None, use_dmz=True):
764
 
        """Get credential zip for user in project"""
765
 
        if not isinstance(user, User):
766
 
            user = self.get_user(user)
767
 
        if project is None:
768
 
            project = user.id
769
 
        pid = Project.safe_id(project)
770
 
        private_key, signed_cert = crypto.generate_x509_cert(user.id, pid)
771
 
 
772
 
        with utils.tempdir() as tmpdir:
773
 
            zf = os.path.join(tmpdir, "temp.zip")
774
 
            zippy = zipfile.ZipFile(zf, 'w')
775
 
            if use_dmz and FLAGS.region_list:
776
 
                regions = {}
777
 
                for item in FLAGS.region_list:
778
 
                    region, _sep, region_host = item.partition("=")
779
 
                    regions[region] = region_host
780
 
            else:
781
 
                regions = {'nova': FLAGS.ec2_host}
782
 
            for region, host in regions.iteritems():
783
 
                rc = self.__generate_rc(user,
784
 
                                        pid,
785
 
                                        use_dmz,
786
 
                                        host)
787
 
                zippy.writestr(FLAGS.credential_rc_file % region, rc)
788
 
 
789
 
            zippy.writestr(FLAGS.credential_key_file, private_key)
790
 
            zippy.writestr(FLAGS.credential_cert_file, signed_cert)
791
 
 
792
 
            (vpn_ip, vpn_port) = self.get_project_vpn_data(project)
793
 
            if vpn_ip:
794
 
                configfile = open(FLAGS.vpn_client_template, "r")
795
 
                s = string.Template(configfile.read())
796
 
                configfile.close()
797
 
                config = s.substitute(keyfile=FLAGS.credential_key_file,
798
 
                                      certfile=FLAGS.credential_cert_file,
799
 
                                      ip=vpn_ip,
800
 
                                      port=vpn_port)
801
 
                zippy.writestr(FLAGS.credential_vpn_file, config)
802
 
            else:
803
 
                LOG.warn(_("No vpn data for project %s"), pid)
804
 
 
805
 
            zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(pid))
806
 
            zippy.close()
807
 
            with open(zf, 'rb') as f:
808
 
                read_buffer = f.read()
809
 
 
810
 
        return read_buffer
811
 
 
812
 
    def get_environment_rc(self, user, project=None, use_dmz=True):
813
 
        """Get environment rc for user in project"""
814
 
        if not isinstance(user, User):
815
 
            user = self.get_user(user)
816
 
        if project is None:
817
 
            project = user.id
818
 
        pid = Project.safe_id(project)
819
 
        return self.__generate_rc(user, pid, use_dmz)
820
 
 
821
 
    @staticmethod
822
 
    def __generate_rc(user, pid, use_dmz=True, host=None):
823
 
        """Generate rc file for user"""
824
 
        if use_dmz:
825
 
            ec2_host = FLAGS.ec2_dmz_host
826
 
        else:
827
 
            ec2_host = FLAGS.ec2_host
828
 
        # NOTE(vish): Always use the dmz since it is used from inside the
829
 
        #             instance
830
 
        s3_host = FLAGS.s3_dmz
831
 
        if host:
832
 
            s3_host = host
833
 
            ec2_host = host
834
 
        rc = open(FLAGS.credentials_template).read()
835
 
        # NOTE(vish): Deprecated auth uses an access key, no auth uses a
836
 
        #             the user_id in place of it.
837
 
        if FLAGS.auth_strategy == 'deprecated':
838
 
            access = user.access
839
 
        else:
840
 
            access = user.id
841
 
        rc = rc % {'access': access,
842
 
                   'project': pid,
843
 
                   'secret': user.secret,
844
 
                   'ec2': '%s://%s:%s%s' % (FLAGS.ec2_scheme,
845
 
                                            ec2_host,
846
 
                                            FLAGS.ec2_port,
847
 
                                            FLAGS.ec2_path),
848
 
                   's3': 'http://%s:%s' % (s3_host, FLAGS.s3_port),
849
 
                   'os': '%s://%s:%s%s' % (FLAGS.osapi_scheme,
850
 
                                            ec2_host,
851
 
                                            FLAGS.osapi_compute_listen_port,
852
 
                                            FLAGS.osapi_path),
853
 
                   'user': user.name,
854
 
                   'nova': FLAGS.ca_file,
855
 
                   'cert': FLAGS.credential_cert_file,
856
 
                   'key': FLAGS.credential_key_file}
857
 
        return rc