<commit_before># vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
#    Licensed 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.

"""
Nova authentication management
"""

import logging
import os
import shutil
import string # pylint: disable-msg=W0402
import tempfile
import uuid
import zipfile

from nova import crypto
from nova import db
from nova import exception
from nova import flags
from nova import utils
from nova.auth import signer


FLAGS = flags.FLAGS
flags.DEFINE_list('allowed_roles',
                  ['cloudadmin', 'itsec', 'sysadmin', 'netadmin', 'developer'],
                  'Allowed roles for project')

# NOTE(vish): a user with one of these roles will be a superuser and
#             have access to all api commands
flags.DEFINE_list('superuser_roles', ['cloudadmin'],
                  'Roles that ignore rbac checking completely')

# NOTE(vish): a user with one of these roles will have it for every
#             project, even if he or she is not a member of the project
flags.DEFINE_list('global_roles', ['cloudadmin', 'itsec'],
                  'Roles that apply to all projects')

flags.DEFINE_string('credentials_template',
                    utils.abspath('auth/novarc.template'),
                    'Template for creating users rc file')
flags.DEFINE_string('vpn_client_template',
                    utils.abspath('cloudpipe/client.ovpn.template'),
                    'Template for creating users vpn file')
flags.DEFINE_string('credential_vpn_file', 'nova-vpn.conf',
                    'Filename of certificate in credentials zip')
flags.DEFINE_string('credential_key_file', 'pk.pem',
                    'Filename of private key in credentials zip')
flags.DEFINE_string('credential_cert_file', 'cert.pem',
                    'Filename of certificate in credentials zip')
flags.DEFINE_string('credential_rc_file', 'novarc',
                    'Filename of rc in credentials zip')
flags.DEFINE_string('credential_cert_subject',
                    '/C=US/ST=California/L=MountainView/O=AnsoLabs/'
                    'OU=NovaDev/CN=%s-%s',
                    'Subject for certificate for users')
flags.DEFINE_string('auth_driver', 'nova.auth.ldapdriver.FakeLdapDriver',
                    'Driver that auth manager uses')


class AuthBase(object):
    """Base class for objects relating to auth

    Objects derived from this class should be stupid data objects with
    an id member. They may optionally contain methods that delegate to
    AuthManager, but should not implement logic themselves.
    """

    @classmethod
    def safe_id(cls, obj):
        """Safe get object id

        This method will return the id of the object if the object
        is of this class, otherwise it will return the original object.
        This allows methods to accept objects or ids as paramaters.

        """
        if isinstance(obj, cls):
            return obj.id
        else:
            return obj


class User(AuthBase):
    """Object representing a user"""

    def __init__(self, id, name, access, secret, admin):
        AuthBase.__init__(self)
        self.id = id
        self.name = name
        self.access = access
        self.secret = secret
        self.admin = admin

    def is_superuser(self):
        return AuthManager().is_superuser(self)

    def is_admin(self):
        return AuthManager().is_admin(self)

    def has_role(self, role):
        return AuthManager().has_role(self, role)

    def add_role(self, role):
        return AuthManager().add_role(self, role)

    def remove_role(self, role):
        return AuthManager().remove_role(self, role)

    def is_project_member(self, project):
        return AuthManager().is_project_member(self, project)

    def is_project_manager(self, project):
        return AuthManager().is_project_manager(self, project)

    def __repr__(self):
        return "User('%s', '%s', '%s', '%s', %s)" % (self.id,
                                                     self.name,
                                                     self.access,
                                                     self.secret,
                                                     self.admin)


class Project(AuthBase):
    """Represents a Project returned from the datastore"""

    def __init__(self, id, name, project_manager_id, description, member_ids):
        AuthBase.__init__(self)
        self.id = id
        self.name = name
        self.project_manager_id = project_manager_id
        self.description = description
        self.member_ids = member_ids

    @property
    def project_manager(self):
        return AuthManager().get_user(self.project_manager_id)

    @property
    def vpn_ip(self):
        ip, _port = AuthManager().get_project_vpn_data(self)
        return ip

    @property
    def vpn_port(self):
        _ip, port = AuthManager().get_project_vpn_data(self)
        return port

    def has_manager(self, user):
        return AuthManager().is_project_manager(user, self)

    def has_member(self, user):
        return AuthManager().is_project_member(user, self)

    def add_role(self, user, role):
        return AuthManager().add_role(user, role, self)

    def remove_role(self, user, role):
        return AuthManager().remove_role(user, role, self)

    def has_role(self, user, role):
        return AuthManager().has_role(user, role, self)

    def get_credentials(self, user):
        return AuthManager().get_credentials(user, self)

    def __repr__(self):
        return "Project('%s', '%s', '%s', '%s', %s)" % \
            (self.id, self.name, self.project_manager_id, self.description,
             self.member_ids)


class AuthManager(object):
    """Manager Singleton for dealing with Users, Projects, and Keypairs

    Methods accept objects or ids.

    AuthManager uses a driver object to make requests to the data backend.
    See ldapdriver for reference.

    AuthManager also manages associated data related to Auth objects that
    need to be more accessible, such as vpn ips and ports.
    """

    _instance = None

    def __new__(cls, *args, **kwargs):
        """Returns the AuthManager singleton"""
        if not cls._instance:
            cls._instance = super(AuthManager, cls).__new__(cls)
        return cls._instance

    def __init__(self, driver=None, *args, **kwargs):
        """Inits the driver from parameter or flag

        __init__ is run every time AuthManager() is called, so we only
        reset the driver if it is not set or a new driver is specified.
        """
        self.network_manager = utils.import_object(FLAGS.network_manager)
        if driver or not getattr(self, 'driver', None):
            self.driver = utils.import_class(driver or FLAGS.auth_driver)

    def authenticate(self, access, signature, params, verb='GET',
                     server_string='127.0.0.1:8773', path='/',
                     check_type='ec2', headers=None):
        """Authenticates AWS request using access key and signature

        If the project is not specified, attempts to authenticate to
        a project with the same name as the user. This way, older tools
        that have no project knowledge will still work.

        @type access: str
        @param access: Access key for user in the form "access:project".

        @type signature: str
        @param signature: Signature of the request.

        @type params: list of str
        @param params: Web paramaters used for the signature.

        @type verb: str
        @param verb: Web request verb ('GET' or 'POST').

        @type server_string: str
        @param server_string: Web request server string.

        @type path: str
        @param path: Web request path.

        @type check_type: str
        @param check_type: Type of signature to check. 'ec2' for EC2, 's3' for
                           S3. Any other value will cause signature not to be
                           checked.

        @type headers: list
        @param headers: HTTP headers passed with the request (only needed for
                        s3 signature checks)

        @rtype: tuple (User, Project)
        @return: User and project that the request represents.
        """
        # TODO(vish): check for valid timestamp
        (access_key, _sep, project_id) = access.partition(':')

        logging.info('Looking up user: %r', access_key)
        user = self.get_user_from_access_key(access_key)
        logging.info('user: %r', user)
        if user == None:
            raise exception.NotFound('No user found for access key %s' %
                                     access_key)

        # NOTE(vish): if we stop using project name as id we need better
        #             logic to find a default project for user
        if project_id is '':
            project_id = user.name

        project = self.get_project(project_id)
        if project == None:
            raise exception.NotFound('No project called %s could be found' %
                                     project_id)
        if not self.is_admin(user) and not self.is_project_member(user,
                                                                  project):
            raise exception.NotFound('User %s is not a member of project %s' %
                                     (user.id, project.id))
        if check_type == 's3':
            sign = signer.Signer(user.secret.encode())
            expected_signature = sign.s3_authorization(headers, verb, path)
            logging.debug('user.secret: %s', user.secret)
            logging.debug('expected_signature: %s', expected_signature)
            logging.debug('signature: %s', signature)
            if signature != expected_signature:
                raise exception.NotAuthorized('Signature does not match')
        elif check_type == 'ec2':
            # NOTE(vish): hmac can't handle unicode, so encode ensures that
            #             secret isn't unicode
            expected_signature = signer.Signer(user.secret.encode()).generate(
                    params, verb, server_string, path)
            logging.debug('user.secret: %s', user.secret)
            logging.debug('expected_signature: %s', expected_signature)
            logging.debug('signature: %s', signature)
            if signature != expected_signature:
                raise exception.NotAuthorized('Signature does not match')
        return (user, project)
<commit_msg>Compare project_id to '' using == (equality) rather than 'is' (identity). This is needed because '' isn't the same as u''.<commit_after># vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
#    Licensed 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.

"""
Nova authentication management
"""

import logging
import os
import shutil
import string # pylint: disable-msg=W0402
import tempfile
import uuid
import zipfile

from nova import crypto
from nova import db
from nova import exception
from nova import flags
from nova import utils
from nova.auth import signer


FLAGS = flags.FLAGS
flags.DEFINE_list('allowed_roles',
                  ['cloudadmin', 'itsec', 'sysadmin', 'netadmin', 'developer'],
                  'Allowed roles for project')

# NOTE(vish): a user with one of these roles will be a superuser and
#             have access to all api commands
flags.DEFINE_list('superuser_roles', ['cloudadmin'],
                  'Roles that ignore rbac checking completely')

# NOTE(vish): a user with one of these roles will have it for every
#             project, even if he or she is not a member of the project
flags.DEFINE_list('global_roles', ['cloudadmin', 'itsec'],
                  'Roles that apply to all projects')

flags.DEFINE_string('credentials_template',
                    utils.abspath('auth/novarc.template'),
                    'Template for creating users rc file')
flags.DEFINE_string('vpn_client_template',
                    utils.abspath('cloudpipe/client.ovpn.template'),
                    'Template for creating users vpn file')
flags.DEFINE_string('credential_vpn_file', 'nova-vpn.conf',
                    'Filename of certificate in credentials zip')
flags.DEFINE_string('credential_key_file', 'pk.pem',
                    'Filename of private key in credentials zip')
flags.DEFINE_string('credential_cert_file', 'cert.pem',
                    'Filename of certificate in credentials zip')
flags.DEFINE_string('credential_rc_file', 'novarc',
                    'Filename of rc in credentials zip')
flags.DEFINE_string('credential_cert_subject',
                    '/C=US/ST=California/L=MountainView/O=AnsoLabs/'
                    'OU=NovaDev/CN=%s-%s',
                    'Subject for certificate for users')
flags.DEFINE_string('auth_driver', 'nova.auth.ldapdriver.FakeLdapDriver',
                    'Driver that auth manager uses')


class AuthBase(object):
    """Base class for objects relating to auth

    Objects derived from this class should be stupid data objects with
    an id member. They may optionally contain methods that delegate to
    AuthManager, but should not implement logic themselves.
    """

    @classmethod
    def safe_id(cls, obj):
        """Safe get object id

        This method will return the id of the object if the object
        is of this class, otherwise it will return the original object.
        This allows methods to accept objects or ids as paramaters.

        """
        if isinstance(obj, cls):
            return obj.id
        else:
            return obj


class User(AuthBase):
    """Object representing a user"""

    def __init__(self, id, name, access, secret, admin):
        AuthBase.__init__(self)
        self.id = id
        self.name = name
        self.access = access
        self.secret = secret
        self.admin = admin

    def is_superuser(self):
        return AuthManager().is_superuser(self)

    def is_admin(self):
        return AuthManager().is_admin(self)

    def has_role(self, role):
        return AuthManager().has_role(self, role)

    def add_role(self, role):
        return AuthManager().add_role(self, role)

    def remove_role(self, role):
        return AuthManager().remove_role(self, role)

    def is_project_member(self, project):
        return AuthManager().is_project_member(self, project)

    def is_project_manager(self, project):
        return AuthManager().is_project_manager(self, project)

    def __repr__(self):
        return "User('%s', '%s', '%s', '%s', %s)" % (self.id,
                                                     self.name,
                                                     self.access,
                                                     self.secret,
                                                     self.admin)


class Project(AuthBase):
    """Represents a Project returned from the datastore"""

    def __init__(self, id, name, project_manager_id, description, member_ids):
        AuthBase.__init__(self)
        self.id = id
        self.name = name
        self.project_manager_id = project_manager_id
        self.description = description
        self.member_ids = member_ids

    @property
    def project_manager(self):
        return AuthManager().get_user(self.project_manager_id)

    @property
    def vpn_ip(self):
        ip, _port = AuthManager().get_project_vpn_data(self)
        return ip

    @property
    def vpn_port(self):
        _ip, port = AuthManager().get_project_vpn_data(self)
        return port

    def has_manager(self, user):
        return AuthManager().is_project_manager(user, self)

    def has_member(self, user):
        return AuthManager().is_project_member(user, self)

    def add_role(self, user, role):
        return AuthManager().add_role(user, role, self)

    def remove_role(self, user, role):
        return AuthManager().remove_role(user, role, self)

    def has_role(self, user, role):
        return AuthManager().has_role(user, role, self)

    def get_credentials(self, user):
        return AuthManager().get_credentials(user, self)

    def __repr__(self):
        return "Project('%s', '%s', '%s', '%s', %s)" % \
            (self.id, self.name, self.project_manager_id, self.description,
             self.member_ids)


class AuthManager(object):
    """Manager Singleton for dealing with Users, Projects, and Keypairs

    Methods accept objects or ids.

    AuthManager uses a driver object to make requests to the data backend.
    See ldapdriver for reference.

    AuthManager also manages associated data related to Auth objects that
    need to be more accessible, such as vpn ips and ports.
    """

    _instance = None

    def __new__(cls, *args, **kwargs):
        """Returns the AuthManager singleton"""
        if not cls._instance:
            cls._instance = super(AuthManager, cls).__new__(cls)
        return cls._instance

    def __init__(self, driver=None, *args, **kwargs):
        """Inits the driver from parameter or flag

        __init__ is run every time AuthManager() is called, so we only
        reset the driver if it is not set or a new driver is specified.
        """
        self.network_manager = utils.import_object(FLAGS.network_manager)
        if driver or not getattr(self, 'driver', None):
            self.driver = utils.import_class(driver or FLAGS.auth_driver)

    def authenticate(self, access, signature, params, verb='GET',
                     server_string='127.0.0.1:8773', path='/',
                     check_type='ec2', headers=None):
        """Authenticates AWS request using access key and signature

        If the project is not specified, attempts to authenticate to
        a project with the same name as the user. This way, older tools
        that have no project knowledge will still work.

        @type access: str
        @param access: Access key for user in the form "access:project".

        @type signature: str
        @param signature: Signature of the request.

        @type params: list of str
        @param params: Web paramaters used for the signature.

        @type verb: str
        @param verb: Web request verb ('GET' or 'POST').

        @type server_string: str
        @param server_string: Web request server string.

        @type path: str
        @param path: Web request path.

        @type check_type: str
        @param check_type: Type of signature to check. 'ec2' for EC2, 's3' for
                           S3. Any other value will cause signature not to be
                           checked.

        @type headers: list
        @param headers: HTTP headers passed with the request (only needed for
                        s3 signature checks)

        @rtype: tuple (User, Project)
        @return: User and project that the request represents.
        """
        # TODO(vish): check for valid timestamp
        (access_key, _sep, project_id) = access.partition(':')

        logging.info('Looking up user: %r', access_key)
        user = self.get_user_from_access_key(access_key)
        logging.info('user: %r', user)
        if user == None:
            raise exception.NotFound('No user found for access key %s' %
                                     access_key)

        # NOTE(vish): if we stop using project name as id we need better
        #             logic to find a default project for user
        if project_id == '':
            project_id = user.name

        project = self.get_project(project_id)
        if project == None:
            raise exception.NotFound('No project called %s could be found' %
                                     project_id)
        if not self.is_admin(user) and not self.is_project_member(user,
                                                                  project):
            raise exception.NotFound('User %s is not a member of project %s' %
                                     (user.id, project.id))
        if check_type == 's3':
            sign = signer.Signer(user.secret.encode())
            expected_signature = sign.s3_authorization(headers, verb, path)
            logging.debug('user.secret: %s', user.secret)
            logging.debug('expected_signature: %s', expected_signature)
            logging.debug('signature: %s', signature)
            if signature != expected_signature:
                raise exception.NotAuthorized('Signature does not match')
        elif check_type == 'ec2':
            # NOTE(vish): hmac can't handle unicode, so encode ensures that
            #             secret isn't unicode
            expected_signature = signer.Signer(user.secret.encode()).generate(
                    params, verb, server_string, path)
            logging.debug('user.secret: %s', user.secret)
            logging.debug('expected_signature: %s', expected_signature)
            logging.debug('signature: %s', signature)
            if signature != expected_signature:
                raise exception.NotAuthorized('Signature does not match')
        return (user, project)

{'fraction_non_alphanumeric': 0.08816256799892101, 'fraction_numerical': 0.0030571415726295915, 'mean_word_length': 2.287128712871287, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3786348', 'n_tokens_mistral': 5910, 'n_tokens_neox': 5698, 'n_words': 2002}