<commit_before># Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
#
# 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.

# Default allowed headers
import copy
import logging

from oslo_config import cfg
from oslo_middleware import base
import six
import webob.dec
import webob.exc
import webob.response

LOG = logging.getLogger(__name__)

CORS_OPTS = [
    cfg.ListOpt('allowed_origin',
                default=None,
                help='Indicate whether this resource may be shared with the '
                     'domain received in the requests "origin" header.'),
    cfg.BoolOpt('allow_credentials',
                default=True,
                help='Indicate that the actual request can include user '
                     'credentials'),
    cfg.ListOpt('expose_headers',
                default=['Content-Type', 'Cache-Control', 'Content-Language',
                         'Expires', 'Last-Modified', 'Pragma'],
                help='Indicate which headers are safe to expose to the API. '
                     'Defaults to HTTP Simple Headers.'),
    cfg.IntOpt('max_age',
               default=3600,
               help='Maximum cache age of CORS preflight requests.'),
    cfg.ListOpt('allow_methods',
                default=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
                help='Indicate which methods can be used during the actual '
                     'request.'),
    cfg.ListOpt('allow_headers',
                default=['Content-Type', 'Cache-Control', 'Content-Language',
                         'Expires', 'Last-Modified', 'Pragma'],
                help='Indicate which header field names may be used during '
                     'the actual request.')
]

class InvalidOriginError(Exception):
    """Exception raised when Origin is invalid."""

    def __init__(self, origin):
        self.origin = origin
        super(InvalidOriginError, self).__init__(
            'CORS request from origin \'%s\' not permitted.' % origin)

class CORS(base.ConfigurableMiddleware):
    """CORS Middleware.

    This middleware allows a WSGI app to serve CORS headers for multiple
    configured domains.

    For more information, see http://www.w3.org/TR/cors/
    """

    simple_headers = [
        'Accept',
        'Accept-Language',
        'Content-Type',
        'Cache-Control',
        'Content-Language',
        'Expires',
        'Last-Modified',
        'Pragma'
    ]

    def __init__(self, application, *args, **kwargs):
        super(CORS, self).__init__(application, *args, **kwargs)
        # Begin constructing our configuration hash.
        self.allowed_origins = {}
        self._init_conf()

        def sanitize(csv_list):
            try:
                return [str.strip(x) for x in csv_list.split(',')]
            except Exception:
                return None

        self.set_latent(
            allow_headers=sanitize(self.conf.get('latent_allow_headers')),
            expose_headers=sanitize(self.conf.get('latent_expose_headers')),
            allow_methods=sanitize(self.conf.get('latent_allow_methods'))
        )

    @classmethod
    def factory(cls, global_conf, **local_conf):
        """factory method for paste.deploy

        allowed_origin: Protocol, host, and port for the allowed origin.
        allow_credentials: Whether to permit credentials.
        expose_headers: A list of headers to expose.
        max_age: Maximum cache duration.
        allow_methods: List of HTTP methods to permit.
        allow_headers: List of HTTP headers to permit from the client.
        """
        if ('allowed_origin' not in local_conf
           and 'oslo_config_project' not in local_conf):
            raise TypeError("allowed_origin or oslo_config_project "
                            "is required")
        return super(CORS, cls).factory(global_conf, **local_conf)

    def _init_conf(self):
        '''Initialize this middleware from an oslo.config instance.'''

        # Set up a location for our latent configuration options
        self._latent_configuration = {
            'allow_headers': [],
            'expose_headers': [],
            'methods': []
        }

        # First, check the configuration and register global options.
        self.oslo_conf.register_opts(CORS_OPTS, 'cors')

        allowed_origin = self._conf_get('allowed_origin', 'cors')
        allow_credentials = self._conf_get('allow_credentials', 'cors')
        expose_headers = self._conf_get('expose_headers', 'cors')
        max_age = self._conf_get('max_age', 'cors')
        allow_methods = self._conf_get('allow_methods', 'cors')
        allow_headers = self._conf_get('allow_headers', 'cors')

        # Clone our original CORS_OPTS, and set the defaults to whatever is
        # set in the global conf instance. This is done explicitly (instead
        # of **kwargs), since we don't accidentally want to catch
        # allowed_origin.
        subgroup_opts = copy.deepcopy(CORS_OPTS)
        cfg.set_defaults(subgroup_opts,
                         allow_credentials=allow_credentials,
                         expose_headers=expose_headers,
                         max_age=max_age,
                         allow_methods=allow_methods,
                         allow_headers=allow_headers)

        # If the default configuration contains an allowed_origin, don't
        # forget to register that.
        self.add_origin(allowed_origin=allowed_origin,
                        allow_credentials=allow_credentials,
                        expose_headers=expose_headers,
                        max_age=max_age,
                        allow_methods=allow_methods,
                        allow_headers=allow_headers)

        # Iterate through all the loaded config sections, looking for ones
        # prefixed with 'cors.'
        for section in self.oslo_conf.list_all_sections():
            if section.startswith('cors.'):
                # Register with the preconstructed defaults
                self.oslo_conf.register_opts(subgroup_opts, section)
                self.add_origin(**self.oslo_conf[section])

    def add_origin(self, allowed_origin, allow_credentials=True,
                   expose_headers=None, max_age=None, allow_methods=None,
                   allow_headers=None):
        '''Add another origin to this filter.

        :param allowed_origin: Protocol, host, and port for the allowed origin.
        :param allow_credentials: Whether to permit credentials.
        :param expose_headers: A list of headers to expose.
        :param max_age: Maximum cache duration.
        :param allow_methods: List of HTTP methods to permit.
        :param allow_headers: List of HTTP headers to permit from the client.
        :return:
        '''

        # NOTE(dims): Support older code that still passes in
        # a string for allowed_origin instead of a list
        if isinstance(allowed_origin, six.string_types):
            allowed_origin = [allowed_origin]

        if allowed_origin:
            for origin in allowed_origin:

                if origin in self.allowed_origins:
                    LOG.warn('Allowed origin [%s] already exists, skipping' % (
                        allowed_origin,))
                    continue

                self.allowed_origins[origin] = {
                    'allow_credentials': allow_credentials,
                    'expose_headers': expose_headers,
                    'max_age': max_age,
                    'allow_methods': allow_methods,
                    'allow_headers': allow_headers
                }

    def set_latent(self, allow_headers=None, allow_methods=None,
                   expose_headers=None):
        '''Add a new latent property for this middleware.

        Latent properties are those values which a system requires for
        operation. API-specific headers, for example, may be added by an
        engineer so that they ship with the codebase, and thus do not require
        extra documentation or passing of institutional knowledge.

        :param allow_headers: HTTP headers permitted in client requests.
        :param allow_methods: HTTP methods permitted in client requests.
        :param expose_headers: HTTP Headers exposed to clients.
        '''

        if allow_headers:
            if isinstance(allow_headers, list):
                self._latent_configuration['allow_headers'] = allow_headers
            else:
                raise TypeError("allow_headers must be a list or None.")

        if expose_headers:
            if isinstance(expose_headers, list):
                self._latent_configuration['expose_headers'] = expose_headers
            else:
                raise TypeError("expose_headers must be a list or None.")

        if allow_methods:
            if isinstance(allow_methods, list):
                self._latent_configuration['methods'] = allow_methods
            else:
                raise TypeError("allow_methods parameter must be a list or"
                                " None.")

    def process_response(self, response, request=None):
        '''Check for CORS headers, and decorate if necessary.

        Perform two checks. First, if an OPTIONS request was issued, let the
        application handle it, and (if necessary) decorate the response with
        preflight headers. In this case, if a 404 is thrown by the underlying
        application (i.e. if the underlying application does not handle
        OPTIONS requests, the response code is overridden.

        In the case of all other requests, regular request headers are applied.
        '''

        # Sanity precheck: If we detect CORS headers provided by something in
        # in the middleware chain, assume that it knows better.
        if 'Access-Control-Allow-Origin' in response.headers:
            return response

        # Doublecheck for an OPTIONS request.
        if request.method == 'OPTIONS':
            return self._apply_cors_preflight_headers(request=request,
                                                      response=response)

        # Apply regular CORS headers.
        self._apply_cors_request_headers(request=request, response=response)

        # Finally, return the response.
        return response

    @staticmethod
    def _split_header_values(request, header_name):
        """Convert a comma-separated header value into a list of values."""
        values = []
        if header_name in request.headers:
            for value in request.headers[header_name].rsplit(','):
                value = value.strip()
                if value:
                    values.append(value)
        return values

    def _apply_cors_preflight_headers(self, request, response):
        """Handle CORS Preflight (Section 6.2)

        Given a request and a response, apply the CORS preflight headers
        appropriate for the request.
        """

        # If the response contains a 2XX code, we have to assume that the
        # underlying middleware's response content needs to be persisted.
        # Otherwise, create a new response.
        if 200 > response.status_code or response.status_code >= 300:
            response = webob.response.Response(status=webob.exc.HTTPOk.code)

        # Does the request have an origin header? (Section 6.2.1)
        if 'Origin' not in request.headers:
            return response

        # Is this origin registered? (Section 6.2.2)
        try:
            origin, cors_config = self._get_cors_config_by_origin(
                request.headers['Origin'])
        except InvalidOriginError:
            return response

        # If there's no request method, exit. (Section 6.2.3)
        if 'Access-Control-Request-Method' not in request.headers:
            LOG.debug('CORS request does not contain '
                      'Access-Control-Request-Method header.')
            return response
        request_method = request.headers['Access-Control-Request-Method']

        # Extract Request headers. If parsing fails, exit. (Section 6.2.4)
        try:
            request_headers = \
                self._split_header_values(request,
                                          'Access-Control-Request-Headers')
        except Exception:
            LOG.debug('Cannot parse request headers.')
            return response

        # Compare request method to permitted methods (Section 6.2.5)
        permitted_methods = (
            cors_config['allow_methods'] +
            self._latent_configuration['methods']
        )
        if request_method not in permitted_methods:
            LOG.debug('Request method \'%s\' not in permitted list: %s'
                      % (request_method, permitted_methods))
            return response

        # Compare request headers to permitted headers, case-insensitively.
        # (Section 6.2.6)
        permitted_headers = [header.upper() for header in
                             (cors_config['allow_headers'] +
                              self.simple_headers +
                              self._latent_configuration['allow_headers'])]
        for requested_header in request_headers:
            upper_header = requested_header.upper()
            if upper_header not in permitted_headers:
                LOG.debug('Request header \'%s\' not in permitted list: %s'
                          % (requested_header, permitted_headers))
                return response

        # Set the default origin permission headers. (Sections 6.2.7, 6.4)
        response.headers['Vary'] = 'Origin'
        response.headers['Access-Control-Allow-Origin'] = origin

        # Does this CORS configuration permit credentials? (Section 6.2.7)
        if cors_config['allow_credentials']:
            response.headers['Access-Control-Allow-Credentials'] = 'true'

        # Attach Access-Control-Max-Age if appropriate. (Section 6.2.8)
        if 'max_age' in cors_config and cors_config['max_age']:
            response.headers['Access-Control-Max-Age'] = \
                str(cors_config['max_age'])

        # Attach Access-Control-Allow-Methods. (Section 6.2.9)
        response.headers['Access-Control-Allow-Methods'] = request_method

        # Attach  Access-Control-Allow-Headers. (Section 6.2.10)
        if request_headers:
            response.headers['Access-Control-Allow-Headers'] = \
                ','.join(request_headers)

        return response

    def _get_cors_config_by_origin(self, origin):
        if origin not in self.allowed_origins:
            if '*' in self.allowed_origins:
                origin = '*'
            else:
                LOG.debug('CORS request from origin \'%s\' not permitted.'
                          % origin)
                raise InvalidOriginError(origin)
        return origin, self.allowed_origins[origin]

    def _apply_cors_request_headers(self, request, response):
        """Handle Basic CORS Request (Section 6.1)

        Given a request and a response, apply the CORS headers appropriate
        for the request to the response.
        """

        # Does the request have an origin header? (Section 6.1.1)
        if 'Origin' not in request.headers:
            return

        # Is this origin registered? (Section 6.1.2)
        try:
            origin, cors_config = self._get_cors_config_by_origin(
                request.headers['Origin'])
        except InvalidOriginError:
            return

        # Set the default origin permission headers. (Sections 6.1.3 & 6.4)
        response.headers['Vary'] = 'Origin'
        response.headers['Access-Control-Allow-Origin'] = origin

        # Does this CORS configuration permit credentials? (Section 6.1.3)
        if cors_config['allow_credentials']:
            response.headers['Access-Control-Allow-Credentials'] = 'true'

        # Attach the exposed headers and exit. (Section 6.1.4)
        if cors_config['expose_headers']:
            response.headers['Access-Control-Expose-Headers'] = \
                ','.join(cors_config['expose_headers'] +
                         self._latent_configuration['expose_headers'])

# NOTE(sileht): Shortcut for backwards compatibility
filter_factory = CORS.factory
<commit_msg>Python 3 deprecated the logger.warn method in favor of warning<commit_after># Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
#
# 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.

# Default allowed headers
import copy
import logging

from oslo_config import cfg
from oslo_middleware import base
import six
import webob.dec
import webob.exc
import webob.response

LOG = logging.getLogger(__name__)

CORS_OPTS = [
    cfg.ListOpt('allowed_origin',
                default=None,
                help='Indicate whether this resource may be shared with the '
                     'domain received in the requests "origin" header.'),
    cfg.BoolOpt('allow_credentials',
                default=True,
                help='Indicate that the actual request can include user '
                     'credentials'),
    cfg.ListOpt('expose_headers',
                default=['Content-Type', 'Cache-Control', 'Content-Language',
                         'Expires', 'Last-Modified', 'Pragma'],
                help='Indicate which headers are safe to expose to the API. '
                     'Defaults to HTTP Simple Headers.'),
    cfg.IntOpt('max_age',
               default=3600,
               help='Maximum cache age of CORS preflight requests.'),
    cfg.ListOpt('allow_methods',
                default=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
                help='Indicate which methods can be used during the actual '
                     'request.'),
    cfg.ListOpt('allow_headers',
                default=['Content-Type', 'Cache-Control', 'Content-Language',
                         'Expires', 'Last-Modified', 'Pragma'],
                help='Indicate which header field names may be used during '
                     'the actual request.')
]

class InvalidOriginError(Exception):
    """Exception raised when Origin is invalid."""

    def __init__(self, origin):
        self.origin = origin
        super(InvalidOriginError, self).__init__(
            'CORS request from origin \'%s\' not permitted.' % origin)

class CORS(base.ConfigurableMiddleware):
    """CORS Middleware.

    This middleware allows a WSGI app to serve CORS headers for multiple
    configured domains.

    For more information, see http://www.w3.org/TR/cors/
    """

    simple_headers = [
        'Accept',
        'Accept-Language',
        'Content-Type',
        'Cache-Control',
        'Content-Language',
        'Expires',
        'Last-Modified',
        'Pragma'
    ]

    def __init__(self, application, *args, **kwargs):
        super(CORS, self).__init__(application, *args, **kwargs)
        # Begin constructing our configuration hash.
        self.allowed_origins = {}
        self._init_conf()

        def sanitize(csv_list):
            try:
                return [str.strip(x) for x in csv_list.split(',')]
            except Exception:
                return None

        self.set_latent(
            allow_headers=sanitize(self.conf.get('latent_allow_headers')),
            expose_headers=sanitize(self.conf.get('latent_expose_headers')),
            allow_methods=sanitize(self.conf.get('latent_allow_methods'))
        )

    @classmethod
    def factory(cls, global_conf, **local_conf):
        """factory method for paste.deploy

        allowed_origin: Protocol, host, and port for the allowed origin.
        allow_credentials: Whether to permit credentials.
        expose_headers: A list of headers to expose.
        max_age: Maximum cache duration.
        allow_methods: List of HTTP methods to permit.
        allow_headers: List of HTTP headers to permit from the client.
        """
        if ('allowed_origin' not in local_conf
           and 'oslo_config_project' not in local_conf):
            raise TypeError("allowed_origin or oslo_config_project "
                            "is required")
        return super(CORS, cls).factory(global_conf, **local_conf)

    def _init_conf(self):
        '''Initialize this middleware from an oslo.config instance.'''

        # Set up a location for our latent configuration options
        self._latent_configuration = {
            'allow_headers': [],
            'expose_headers': [],
            'methods': []
        }

        # First, check the configuration and register global options.
        self.oslo_conf.register_opts(CORS_OPTS, 'cors')

        allowed_origin = self._conf_get('allowed_origin', 'cors')
        allow_credentials = self._conf_get('allow_credentials', 'cors')
        expose_headers = self._conf_get('expose_headers', 'cors')
        max_age = self._conf_get('max_age', 'cors')
        allow_methods = self._conf_get('allow_methods', 'cors')
        allow_headers = self._conf_get('allow_headers', 'cors')

        # Clone our original CORS_OPTS, and set the defaults to whatever is
        # set in the global conf instance. This is done explicitly (instead
        # of **kwargs), since we don't accidentally want to catch
        # allowed_origin.
        subgroup_opts = copy.deepcopy(CORS_OPTS)
        cfg.set_defaults(subgroup_opts,
                         allow_credentials=allow_credentials,
                         expose_headers=expose_headers,
                         max_age=max_age,
                         allow_methods=allow_methods,
                         allow_headers=allow_headers)

        # If the default configuration contains an allowed_origin, don't
        # forget to register that.
        self.add_origin(allowed_origin=allowed_origin,
                        allow_credentials=allow_credentials,
                        expose_headers=expose_headers,
                        max_age=max_age,
                        allow_methods=allow_methods,
                        allow_headers=allow_headers)

        # Iterate through all the loaded config sections, looking for ones
        # prefixed with 'cors.'
        for section in self.oslo_conf.list_all_sections():
            if section.startswith('cors.'):
                # Register with the preconstructed defaults
                self.oslo_conf.register_opts(subgroup_opts, section)
                self.add_origin(**self.oslo_conf[section])

    def add_origin(self, allowed_origin, allow_credentials=True,
                   expose_headers=None, max_age=None, allow_methods=None,
                   allow_headers=None):
        '''Add another origin to this filter.

        :param allowed_origin: Protocol, host, and port for the allowed origin.
        :param allow_credentials: Whether to permit credentials.
        :param expose_headers: A list of headers to expose.
        :param max_age: Maximum cache duration.
        :param allow_methods: List of HTTP methods to permit.
        :param allow_headers: List of HTTP headers to permit from the client.
        :return:
        '''

        # NOTE(dims): Support older code that still passes in
        # a string for allowed_origin instead of a list
        if isinstance(allowed_origin, six.string_types):
            allowed_origin = [allowed_origin]

        if allowed_origin:
            for origin in allowed_origin:

                if origin in self.allowed_origins:
                    LOG.warning('Allowed origin [%s] already exists, skipping'
                                % (allowed_origin,))
                    continue

                self.allowed_origins[origin] = {
                    'allow_credentials': allow_credentials,
                    'expose_headers': expose_headers,
                    'max_age': max_age,
                    'allow_methods': allow_methods,
                    'allow_headers': allow_headers
                }

    def set_latent(self, allow_headers=None, allow_methods=None,
                   expose_headers=None):
        '''Add a new latent property for this middleware.

        Latent properties are those values which a system requires for
        operation. API-specific headers, for example, may be added by an
        engineer so that they ship with the codebase, and thus do not require
        extra documentation or passing of institutional knowledge.

        :param allow_headers: HTTP headers permitted in client requests.
        :param allow_methods: HTTP methods permitted in client requests.
        :param expose_headers: HTTP Headers exposed to clients.
        '''

        if allow_headers:
            if isinstance(allow_headers, list):
                self._latent_configuration['allow_headers'] = allow_headers
            else:
                raise TypeError("allow_headers must be a list or None.")

        if expose_headers:
            if isinstance(expose_headers, list):
                self._latent_configuration['expose_headers'] = expose_headers
            else:
                raise TypeError("expose_headers must be a list or None.")

        if allow_methods:
            if isinstance(allow_methods, list):
                self._latent_configuration['methods'] = allow_methods
            else:
                raise TypeError("allow_methods parameter must be a list or"
                                " None.")

    def process_response(self, response, request=None):
        '''Check for CORS headers, and decorate if necessary.

        Perform two checks. First, if an OPTIONS request was issued, let the
        application handle it, and (if necessary) decorate the response with
        preflight headers. In this case, if a 404 is thrown by the underlying
        application (i.e. if the underlying application does not handle
        OPTIONS requests, the response code is overridden.

        In the case of all other requests, regular request headers are applied.
        '''

        # Sanity precheck: If we detect CORS headers provided by something in
        # in the middleware chain, assume that it knows better.
        if 'Access-Control-Allow-Origin' in response.headers:
            return response

        # Doublecheck for an OPTIONS request.
        if request.method == 'OPTIONS':
            return self._apply_cors_preflight_headers(request=request,
                                                      response=response)

        # Apply regular CORS headers.
        self._apply_cors_request_headers(request=request, response=response)

        # Finally, return the response.
        return response

    @staticmethod
    def _split_header_values(request, header_name):
        """Convert a comma-separated header value into a list of values."""
        values = []
        if header_name in request.headers:
            for value in request.headers[header_name].rsplit(','):
                value = value.strip()
                if value:
                    values.append(value)
        return values

    def _apply_cors_preflight_headers(self, request, response):
        """Handle CORS Preflight (Section 6.2)

        Given a request and a response, apply the CORS preflight headers
        appropriate for the request.
        """

        # If the response contains a 2XX code, we have to assume that the
        # underlying middleware's response content needs to be persisted.
        # Otherwise, create a new response.
        if 200 > response.status_code or response.status_code >= 300:
            response = webob.response.Response(status=webob.exc.HTTPOk.code)

        # Does the request have an origin header? (Section 6.2.1)
        if 'Origin' not in request.headers:
            return response

        # Is this origin registered? (Section 6.2.2)
        try:
            origin, cors_config = self._get_cors_config_by_origin(
                request.headers['Origin'])
        except InvalidOriginError:
            return response

        # If there's no request method, exit. (Section 6.2.3)
        if 'Access-Control-Request-Method' not in request.headers:
            LOG.debug('CORS request does not contain '
                      'Access-Control-Request-Method header.')
            return response
        request_method = request.headers['Access-Control-Request-Method']

        # Extract Request headers. If parsing fails, exit. (Section 6.2.4)
        try:
            request_headers = \
                self._split_header_values(request,
                                          'Access-Control-Request-Headers')
        except Exception:
            LOG.debug('Cannot parse request headers.')
            return response

        # Compare request method to permitted methods (Section 6.2.5)
        permitted_methods = (
            cors_config['allow_methods'] +
            self._latent_configuration['methods']
        )
        if request_method not in permitted_methods:
            LOG.debug('Request method \'%s\' not in permitted list: %s'
                      % (request_method, permitted_methods))
            return response

        # Compare request headers to permitted headers, case-insensitively.
        # (Section 6.2.6)
        permitted_headers = [header.upper() for header in
                             (cors_config['allow_headers'] +
                              self.simple_headers +
                              self._latent_configuration['allow_headers'])]
        for requested_header in request_headers:
            upper_header = requested_header.upper()
            if upper_header not in permitted_headers:
                LOG.debug('Request header \'%s\' not in permitted list: %s'
                          % (requested_header, permitted_headers))
                return response

        # Set the default origin permission headers. (Sections 6.2.7, 6.4)
        response.headers['Vary'] = 'Origin'
        response.headers['Access-Control-Allow-Origin'] = origin

        # Does this CORS configuration permit credentials? (Section 6.2.7)
        if cors_config['allow_credentials']:
            response.headers['Access-Control-Allow-Credentials'] = 'true'

        # Attach Access-Control-Max-Age if appropriate. (Section 6.2.8)
        if 'max_age' in cors_config and cors_config['max_age']:
            response.headers['Access-Control-Max-Age'] = \
                str(cors_config['max_age'])

        # Attach Access-Control-Allow-Methods. (Section 6.2.9)
        response.headers['Access-Control-Allow-Methods'] = request_method

        # Attach  Access-Control-Allow-Headers. (Section 6.2.10)
        if request_headers:
            response.headers['Access-Control-Allow-Headers'] = \
                ','.join(request_headers)

        return response

    def _get_cors_config_by_origin(self, origin):
        if origin not in self.allowed_origins:
            if '*' in self.allowed_origins:
                origin = '*'
            else:
                LOG.debug('CORS request from origin \'%s\' not permitted.'
                          % origin)
                raise InvalidOriginError(origin)
        return origin, self.allowed_origins[origin]

    def _apply_cors_request_headers(self, request, response):
        """Handle Basic CORS Request (Section 6.1)

        Given a request and a response, apply the CORS headers appropriate
        for the request to the response.
        """

        # Does the request have an origin header? (Section 6.1.1)
        if 'Origin' not in request.headers:
            return

        # Is this origin registered? (Section 6.1.2)
        try:
            origin, cors_config = self._get_cors_config_by_origin(
                request.headers['Origin'])
        except InvalidOriginError:
            return

        # Set the default origin permission headers. (Sections 6.1.3 & 6.4)
        response.headers['Vary'] = 'Origin'
        response.headers['Access-Control-Allow-Origin'] = origin

        # Does this CORS configuration permit credentials? (Section 6.1.3)
        if cors_config['allow_credentials']:
            response.headers['Access-Control-Allow-Credentials'] = 'true'

        # Attach the exposed headers and exit. (Section 6.1.4)
        if cors_config['expose_headers']:
            response.headers['Access-Control-Expose-Headers'] = \
                ','.join(cors_config['expose_headers'] +
                         self._latent_configuration['expose_headers'])

# NOTE(sileht): Shortcut for backwards compatibility
filter_factory = CORS.factory
<|endoftext|>


<commit_before>import unittest

from mock import patch

import app  # otherwise broken by circular imports :(
from brain.user_management.register_city import RegisterCity
from brain.user_management.register_result import RegisterResult

class RegisterCityTests(unittest.TestCase):
    @patch.object(RegisterCity, '_user_email_is_valid')
    def test_register_invalid_email(self, validator_mock):
        validator_mock.return_value = False
        result = RegisterCity.register('test@test.test', 'test_man', 'password')
        self.assertEqual(result, RegisterResult.INVALID_EMAIL)

    @patch.object(RegisterCity, '_user_already_exists')
    @patch.object(RegisterCity, '_user_email_is_valid')
    def test_register_duplicate_email(self, validator_mock, exists_mock):
        validator_mock.return_value = True
        exists_mock.return_value = True
        result = RegisterCity.register('test@test.test', 'test_man', 'password')
        self.assertEqual(result, RegisterResult.EMAIL_ALREADY_EXISTS)

    @patch.object(RegisterCity, '_add_user_to_database')
    @patch.object(RegisterCity, '_user_already_exists')
    @patch.object(RegisterCity, '_user_email_is_valid')
    def test_register_successful_registration(self, validator_mock, exists_mock, add_to_db_mock):
        validator_mock.return_value = True
        exists_mock.return_value = False
        result = RegisterCity.register('test@test.test', 'test_man', 'password')
        self.assertEqual(result, RegisterResult.REGISTERED)
<commit_msg>user_email_is_valid tests<commit_after>import unittest

from mock import patch

import app  # otherwise broken by circular imports :(
from brain.user_management.register_city import RegisterCity
from brain.user_management.register_result import RegisterResult

class RegisterCityTests(unittest.TestCase):
    # register tests
    @patch.object(RegisterCity, '_user_email_is_valid')
    def test_register_invalid_email(self, validator_mock):
        validator_mock.return_value = False
        result = RegisterCity.register('test@test.test', 'test_man', 'password')
        self.assertEqual(result, RegisterResult.INVALID_EMAIL)

    @patch.object(RegisterCity, '_user_already_exists')
    @patch.object(RegisterCity, '_user_email_is_valid')
    def test_register_duplicate_email(self, validator_mock, exists_mock):
        validator_mock.return_value = True
        exists_mock.return_value = True
        result = RegisterCity.register('test@test.test', 'test_man', 'password')
        self.assertEqual(result, RegisterResult.EMAIL_ALREADY_EXISTS)

    @patch.object(RegisterCity, '_add_user_to_database')
    @patch.object(RegisterCity, '_user_already_exists')
    @patch.object(RegisterCity, '_user_email_is_valid')
    def test_register_successful_registration(self, validator_mock, exists_mock, add_to_db_mock):
        validator_mock.return_value = True
        exists_mock.return_value = False
        result = RegisterCity.register('test@test.test', 'test_man', 'password')
        self.assertEqual(result, RegisterResult.REGISTERED)

    # _user_email_is_valid_tests
    def test_user_email_is_valid_no_at(self):
        is_valid = RegisterCity._user_email_is_valid('hi.')
        self.assertFalse(is_valid)

    def test_user_email_is_valid_no_dot(self):
        is_valid = RegisterCity._user_email_is_valid('hi@')
        self.assertFalse(is_valid)

    def test_user_email_is_valid_valid_email(self):
        is_valid = RegisterCity._user_email_is_valid('hi@.')
        self.assertTrue(is_valid)
<|endoftext|>


<commit_before># ------------------------------------------------------------------------------
# Copyright (c) 2010-2014, EVEthing team
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
#     Redistributions of source code must retain the above copyright notice, this
#       list of conditions and the following disclaimer.
#     Redistributions in binary form must reproduce the above copyright notice,
#       this list of conditions and the following disclaimer in the documentation
#       and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
# OF SUCH DAMAGE.
# ------------------------------------------------------------------------------

from datetime import datetime, timedelta

from django.db.models import Q

from .apitask import APITask

from thing.models import Contract, Event

# Peroidic task to fix contracts that fell off the main contracts list
CONTRACT_URL = '/char/Contracts.xml.aspx'

class FixContracts(APITask):
    name = 'thing.fix_contracts'

    def run(self):
        self.init()

        lookup = {}
        new_events = []
        now = datetime.now()
        hours_ago = now - timedelta(hours=6)

        # We use hours_ago just so we try not to duplicate the chance that this call overlaps with the
        # normal Contracts APITask
        expired_contracts = Contract.objects.filter(date_expired__lte=hours_ago).filter(
            Q(status='Outstanding') | Q(status='Accepted')).prefetch_related('character__apikeys')

        # Group contracts to lookup by APIKey
        for contract in expired_contracts:
            apikey = None
            for key in contract.character.apikeys.all():
                if (key.access_mask & key.CHAR_CONTRACTS_MASK) != 0:
                    apikey = key
                    break

            if apikey is None:
                self.log_error('Could not find APIKey with proper access for contract %d' % contract.id)
                return False

            if apikey.keyid not in lookup:
                lookup[apikey.keyid] = {
                    'key': apikey,
                    'contracts': []
                }

            lookup[apikey.keyid]['contracts'].append(contract)

        # Actually do the lookups
        for apikey, info in lookup.items():
            params = {
                'keyID': info['key'].keyid,
                'vCode': info['key'].vcode,
            }

            for contract in info['contracts']:
                params['contractID'] = contract.contract_id
                params['characterID'] = contract.character_id

                if self.fetch_api(CONTRACT_URL, params, use_auth=False) is False or self.root is None:
                    self.log_error(
                        'Error fetching information about contract %d using characterID %d apikey %s and vcode %s' %
                        (contract.contract_id, params['characterID'], params['keyID'], params['vCode'])
                    )
                    return False

                for row in self.root.findall('result/rowset/row'):
                    # Only care about the stuff we need to update
                    contractID = int(row.attrib['contractID'])

                    acceptorID = int(row.attrib['acceptorID'])

                    dateAccepted = row.attrib['dateAccepted']
                    if dateAccepted:
                        dateAccepted = self.parse_api_date(dateAccepted)
                    else:
                        dateAccepted = None

                    dateCompleted = row.attrib['dateCompleted']
                    if dateCompleted:
                        dateCompleted = self.parse_api_date(dateCompleted)
                    else:
                        dateCompleted = None

                    if contractID == contract.contract_id:
                        if contract.status != row.attrib['status']:
                            text = "Contract %s changed status from '%s' to '%s'" % (
                                contract.contract_id, contract.status, row.attrib['status']
                            )
                            print(text)
                            new_events.append(Event(
                                user_id=info['key'].user.id,
                                issued=now,
                                text=text,
                            ))

                            contract.status = row.attrib['status']
                            contract.date_accepted = dateAccepted
                            contract.date_completed = dateCompleted
                            contract.acceptor_id = acceptorID
                            contract.save()
                    else:
                        self.log_error('Contract %d is somehow different from requested :ccp:' % contractID)
                        return False

        Event.objects.bulk_create(new_events)

        return True
<commit_msg>Accepted isnt a contract state, InProgress is<commit_after># ------------------------------------------------------------------------------
# Copyright (c) 2010-2014, EVEthing team
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
#     Redistributions of source code must retain the above copyright notice, this
#       list of conditions and the following disclaimer.
#     Redistributions in binary form must reproduce the above copyright notice,
#       this list of conditions and the following disclaimer in the documentation
#       and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
# OF SUCH DAMAGE.
# ------------------------------------------------------------------------------

from datetime import datetime, timedelta

from django.db.models import Q

from .apitask import APITask

from thing.models import Contract, Event

# Peroidic task to fix contracts that fell off the main contracts list
CONTRACT_URL = '/char/Contracts.xml.aspx'

class FixContracts(APITask):
    name = 'thing.fix_contracts'

    def run(self):
        self.init()

        lookup = {}
        new_events = []
        now = datetime.now()
        hours_ago = now - timedelta(hours=6)

        # We use hours_ago just so we try not to duplicate the chance that this call overlaps with the
        # normal Contracts APITask
        expired_contracts = Contract.objects.filter(date_expired__lte=hours_ago).filter(
            Q(status='Outstanding') | Q(status='InProgress')).prefetch_related('character__apikeys')

        # Group contracts to lookup by APIKey
        for contract in expired_contracts:
            apikey = None
            for key in contract.character.apikeys.all():
                if (key.access_mask & key.CHAR_CONTRACTS_MASK) != 0:
                    apikey = key
                    break

            if apikey is None:
                self.log_error('Could not find APIKey with proper access for contract %d' % contract.id)
                return False

            if apikey.keyid not in lookup:
                lookup[apikey.keyid] = {
                    'key': apikey,
                    'contracts': []
                }

            lookup[apikey.keyid]['contracts'].append(contract)

        # Actually do the lookups
        for apikey, info in lookup.items():
            params = {
                'keyID': info['key'].keyid,
                'vCode': info['key'].vcode,
            }

            for contract in info['contracts']:
                params['contractID'] = contract.contract_id
                params['characterID'] = contract.character_id

                if self.fetch_api(CONTRACT_URL, params, use_auth=False) is False or self.root is None:
                    self.log_error(
                        'Error fetching information about contract %d using characterID %d apikey %s and vcode %s' %
                        (contract.contract_id, params['characterID'], params['keyID'], params['vCode'])
                    )
                    return False

                for row in self.root.findall('result/rowset/row'):
                    # Only care about the stuff we need to update
                    contractID = int(row.attrib['contractID'])

                    acceptorID = int(row.attrib['acceptorID'])

                    dateAccepted = row.attrib['dateAccepted']
                    if dateAccepted:
                        dateAccepted = self.parse_api_date(dateAccepted)
                    else:
                        dateAccepted = None

                    dateCompleted = row.attrib['dateCompleted']
                    if dateCompleted:
                        dateCompleted = self.parse_api_date(dateCompleted)
                    else:
                        dateCompleted = None

                    if contractID == contract.contract_id:
                        if contract.status != row.attrib['status']:
                            text = "Contract %s changed status from '%s' to '%s'" % (
                                contract.contract_id, contract.status, row.attrib['status']
                            )
                            print(text)
                            new_events.append(Event(
                                user_id=info['key'].user.id,
                                issued=now,
                                text=text,
                            ))

                            contract.status = row.attrib['status']
                            contract.date_accepted = dateAccepted
                            contract.date_completed = dateCompleted
                            contract.acceptor_id = acceptorID
                            contract.save()
                    else:
                        self.log_error('Contract %d is somehow different from requested :ccp:' % contractID)
                        return False

        Event.objects.bulk_create(new_events)

        return True
<|endoftext|>


<commit_before># Copyright 2015 Red Hat, Inc.
# 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.

#: The resource name used for package updates
UPDATE_RESOURCE_NAME = 'UpdateDeployment'

#: The default timeout to pass to Heat stacks
STACK_TIMEOUT_DEFAULT = 240

#: The default name to use for a plan container
DEFAULT_CONTAINER_NAME = 'overcloud'

#: The default name to use for the config files of the container
CONFIG_CONTAINER_NAME = 'overcloud-config'

#: The path to the base directory of tripleo-validations
DEFAULT_VALIDATIONS_BASEDIR = "/usr/share/ansible"

# 60 minutes maximum to build the child layers at the same time.
BUILD_TIMEOUT = 3600

#: List of names of parameters that contain passwords
PASSWORD_PARAMETER_NAMES = (
    'AdminPassword',
    'AodhPassword',
    'BarbicanPassword',
    'BarbicanSimpleCryptoKek',
    'CeilometerMeteringSecret',
    'CeilometerPassword',
    'CephClientKey',
    'CephClusterFSID',
    'CephManilaClientKey',
    'CephRgwKey',
    'CephGrafanaAdminPassword',
    'CephDashboardAdminPassword',
    'CinderPassword',
    'DesignatePassword',
    'DesignateRndcKey',
    'EtcdInitialClusterToken',
    'GlancePassword',
    'GnocchiPassword',
    'HAProxyStatsPassword',
    'HeatAuthEncryptionKey',
    'HeatPassword',
    'HeatStackDomainAdminPassword',
    'HorizonSecret',
    'IronicPassword',
    'LibvirtTLSPassword',
    'KeystoneCredential0',
    'KeystoneCredential1',
    'KeystoneFernetKey0',
    'KeystoneFernetKey1',
    'KeystoneFernetKeys',
    'KeystonePassword',
    'ManilaPassword',
    'MysqlClustercheckPassword',
    'MysqlRootPassword',
    'NeutronMetadataProxySharedSecret',
    'NeutronPassword',
    'NovaPassword',
    'NovajoinPassword',
    'MigrationSshKey',
    'OctaviaServerCertsKeyPassphrase',
    'OctaviaCaKeyPassphrase',
    'OctaviaHeartbeatKey',
    'OctaviaPassword',
    'PacemakerRemoteAuthkey',
    'PcsdPassword',
    'PlacementPassword',
    'RpcPassword',
    'NotifyPassword',
    'RabbitCookie',
    'RabbitPassword',
    'RedisPassword',
    'SnmpdReadonlyUserPassword',
    'SwiftHashSuffix',
    'SwiftPassword',
)

DB_PASSWORD_PARAMETER_NAMES = (
    'AodhPassword',
    'BarbicanPassword',
    'CeilometerPassword',
    'CephGrafanaAdminPassword',
    'CephDashboardAdminPassword',
    'CinderPassword',
    'DesignatePassword',
    'GlancePassword',
    'GnocchiPassword',
    'HeatPassword',
    'HeatStackDomainAdminPassword',
    'IronicPassword',
    'LibvirtTLSPassword',
    'KeystonePassword',
    'ManilaPassword',
    'MysqlClustercheckPassword',
    'MysqlRootPassword',
    'NeutronPassword',
    'NovaPassword',
    'NovajoinPassword',
    'OctaviaPassword',
    'PlacementPassword',
)

# List of passwords that should not be rotated by default using the
# GeneratePasswordAction because they require some special handling
DO_NOT_ROTATE_LIST = (
    'BarbicanSimpleCryptoKek',
    'KeystoneCredential0',
    'KeystoneCredential1',
    'KeystoneFernetKey0',
    'KeystoneFernetKey1',
    'KeystoneFernetKeys',
    'CephClientKey',
    'CephClusterFSID',
    'CephManilaClientKey',
    'CephRgwKey',
    'HeatAuthEncryptionKey',
    'MysqlClustercheckPassword',
    'PacemakerRemoteAuthkey',
    'PcsdPassword',
)

# The default version of the Identity API to set in overcloudrc.
DEFAULT_IDENTITY_API_VERSION = '3'

# The default version of the Compute API to set in overcloudrc.
DEFAULT_COMPUTE_API_VERSION = '2.latest'

# The default version of the Image API to set in overcloudrc.
DEFAULT_IMAGE_API_VERSION = '2'

# The default version of the Volume API to set in overcloudrc.
DEFAULT_VOLUME_API_VERSION = '3'

# Default nested depth when recursing Heat stacks
NESTED_DEPTH = 7

# Resource name for deployment resources when using config download
TRIPLEO_DEPLOYMENT_RESOURCE = 'TripleODeployment'

HOST_NETWORK = 'ctlplane'

DEFAULT_VLAN_ID = "1"

# The key is different in RoleConfig than in RoleData, so we need both so they
# are correctly found.
EXTERNAL_TASKS = ['external_deploy_tasks', 'external_deploy_steps_tasks']

ANSIBLE_ERRORS_FILE = 'ansible-errors.json'

EXCLUSIVE_NEUTRON_DRIVERS = ['ovn', 'openvswitch']

DEFAULT_STEPS_MAX = 6

_PER_STEP_TASK_STRICTNESS = [False for i in range(DEFAULT_STEPS_MAX)]

PER_STEP_TASKS = {
    'upgrade_tasks': _PER_STEP_TASK_STRICTNESS,
    'deploy_steps_tasks': _PER_STEP_TASK_STRICTNESS,
    'update_tasks': _PER_STEP_TASK_STRICTNESS,
    'post_update_tasks': [False, False, False, False]
}

INVENTORY_NETWORK_CONFIG_FILE = 'inventory-network-config.yaml'

# Hard coded name in:
#   tripleo_ansible/ansible_plugins/modules/tripleo_ovn_mac_addresses.py
OVN_MAC_ADDR_NET_NAME = 'ovn_mac_addr_net'
<commit_msg>Export default location of password file<commit_after># Copyright 2015 Red Hat, Inc.
# 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.

import os

#: The resource name used for package updates
UPDATE_RESOURCE_NAME = 'UpdateDeployment'

#: The default timeout to pass to Heat stacks
STACK_TIMEOUT_DEFAULT = 240

#: The default name to use for a plan container
DEFAULT_CONTAINER_NAME = 'overcloud'

#: The default name to use for the config files of the container
CONFIG_CONTAINER_NAME = 'overcloud-config'

#: The path to the base directory of tripleo-validations
DEFAULT_VALIDATIONS_BASEDIR = "/usr/share/ansible"

# 60 minutes maximum to build the child layers at the same time.
BUILD_TIMEOUT = 3600

#: List of names of parameters that contain passwords
PASSWORD_PARAMETER_NAMES = (
    'AdminPassword',
    'AodhPassword',
    'BarbicanPassword',
    'BarbicanSimpleCryptoKek',
    'CeilometerMeteringSecret',
    'CeilometerPassword',
    'CephClientKey',
    'CephClusterFSID',
    'CephManilaClientKey',
    'CephRgwKey',
    'CephGrafanaAdminPassword',
    'CephDashboardAdminPassword',
    'CinderPassword',
    'DesignatePassword',
    'DesignateRndcKey',
    'EtcdInitialClusterToken',
    'GlancePassword',
    'GnocchiPassword',
    'HAProxyStatsPassword',
    'HeatAuthEncryptionKey',
    'HeatPassword',
    'HeatStackDomainAdminPassword',
    'HorizonSecret',
    'IronicPassword',
    'LibvirtTLSPassword',
    'KeystoneCredential0',
    'KeystoneCredential1',
    'KeystoneFernetKey0',
    'KeystoneFernetKey1',
    'KeystoneFernetKeys',
    'KeystonePassword',
    'ManilaPassword',
    'MysqlClustercheckPassword',
    'MysqlRootPassword',
    'NeutronMetadataProxySharedSecret',
    'NeutronPassword',
    'NovaPassword',
    'NovajoinPassword',
    'MigrationSshKey',
    'OctaviaServerCertsKeyPassphrase',
    'OctaviaCaKeyPassphrase',
    'OctaviaHeartbeatKey',
    'OctaviaPassword',
    'PacemakerRemoteAuthkey',
    'PcsdPassword',
    'PlacementPassword',
    'RpcPassword',
    'NotifyPassword',
    'RabbitCookie',
    'RabbitPassword',
    'RedisPassword',
    'SnmpdReadonlyUserPassword',
    'SwiftHashSuffix',
    'SwiftPassword',
)

DB_PASSWORD_PARAMETER_NAMES = (
    'AodhPassword',
    'BarbicanPassword',
    'CeilometerPassword',
    'CephGrafanaAdminPassword',
    'CephDashboardAdminPassword',
    'CinderPassword',
    'DesignatePassword',
    'GlancePassword',
    'GnocchiPassword',
    'HeatPassword',
    'HeatStackDomainAdminPassword',
    'IronicPassword',
    'LibvirtTLSPassword',
    'KeystonePassword',
    'ManilaPassword',
    'MysqlClustercheckPassword',
    'MysqlRootPassword',
    'NeutronPassword',
    'NovaPassword',
    'NovajoinPassword',
    'OctaviaPassword',
    'PlacementPassword',
)

# List of passwords that should not be rotated by default using the
# GeneratePasswordAction because they require some special handling
DO_NOT_ROTATE_LIST = (
    'BarbicanSimpleCryptoKek',
    'KeystoneCredential0',
    'KeystoneCredential1',
    'KeystoneFernetKey0',
    'KeystoneFernetKey1',
    'KeystoneFernetKeys',
    'CephClientKey',
    'CephClusterFSID',
    'CephManilaClientKey',
    'CephRgwKey',
    'HeatAuthEncryptionKey',
    'MysqlClustercheckPassword',
    'PacemakerRemoteAuthkey',
    'PcsdPassword',
)

# The default version of the Identity API to set in overcloudrc.
DEFAULT_IDENTITY_API_VERSION = '3'

# The default version of the Compute API to set in overcloudrc.
DEFAULT_COMPUTE_API_VERSION = '2.latest'

# The default version of the Image API to set in overcloudrc.
DEFAULT_IMAGE_API_VERSION = '2'

# The default version of the Volume API to set in overcloudrc.
DEFAULT_VOLUME_API_VERSION = '3'

# Default nested depth when recursing Heat stacks
NESTED_DEPTH = 7

# Resource name for deployment resources when using config download
TRIPLEO_DEPLOYMENT_RESOURCE = 'TripleODeployment'

HOST_NETWORK = 'ctlplane'

DEFAULT_VLAN_ID = "1"

# The key is different in RoleConfig than in RoleData, so we need both so they
# are correctly found.
EXTERNAL_TASKS = ['external_deploy_tasks', 'external_deploy_steps_tasks']

ANSIBLE_ERRORS_FILE = 'ansible-errors.json'

EXCLUSIVE_NEUTRON_DRIVERS = ['ovn', 'openvswitch']

DEFAULT_STEPS_MAX = 6

_PER_STEP_TASK_STRICTNESS = [False for i in range(DEFAULT_STEPS_MAX)]

PER_STEP_TASKS = {
    'upgrade_tasks': _PER_STEP_TASK_STRICTNESS,
    'deploy_steps_tasks': _PER_STEP_TASK_STRICTNESS,
    'update_tasks': _PER_STEP_TASK_STRICTNESS,
    'post_update_tasks': [False, False, False, False]
}

INVENTORY_NETWORK_CONFIG_FILE = 'inventory-network-config.yaml'

# Hard coded name in:
#   tripleo_ansible/ansible_plugins/modules/tripleo_ovn_mac_addresses.py
OVN_MAC_ADDR_NET_NAME = 'ovn_mac_addr_net'

# Default directory for the overcloud deployment, where all inputs,
# outputs and generated files are stored
DEFAULT_WORKING_DIR_FORMAT = os.path.join(os.environ.get('HOME', '~/'),
                                          'overcloud-deploy', '{}')

# Format for password filename
PASSWORDS_ENV_FORMAT = '{}-passwords.yaml'
<|endoftext|>


<commit_before>from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import Group
from django.core.cache import cache
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from django.template.defaultfilters import urlencode
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
from django.views import generic

from osmaxx.contrib.auth.frontend_permissions import LoginRequiredMixin
from osmaxx.profile.forms import ProfileForm
from osmaxx.profile.models import Profile

class SendVerificationEmailMixin(object):
    RATE_LIMIT_SECONDS = 30

    def _send_email_verification(self, profile):
        if cache.get(profile.associated_user.id):
            return
        to_email = profile.unverified_email
        if to_email:
            cache.set(profile.associated_user.id, 'dummy value', timeout=self.RATE_LIMIT_SECONDS)
            user_administrator_email = settings.OSMAXX['ACCOUNT_MANAGER_EMAIL']
            token = profile.activation_key()
            token_url = '{}?token={}'.format(
                self.request.build_absolute_uri(reverse('profile:activation')), urlencode(token)
            )
            subject = render_to_string('profile/verification_email/subject.txt', context={}).strip()
            subject = ''.join(subject.splitlines())
            message = render_to_string(
                'profile/verification_email/body.txt',
                context=dict(
                    token_url=token_url,
                    username=self.request.user.username,
                    new_email_address=to_email,
                    domain=self.request.get_host(),
                )
            )
            send_mail(
                subject=subject,
                message=message,
                from_email=user_administrator_email,
                recipient_list=[to_email],
            )
            messages.add_message(
                self.request, messages.INFO, _('To activate your email, click the link in the confirmation email.')
            )

class ProfileView(SendVerificationEmailMixin, LoginRequiredMixin, generic.UpdateView):
    form_class = ProfileForm
    template_name = 'profile/profile_edit.html'

    def get(self, *args, **kwargs):
        profile = self.get_object()
        user = self.request.user

        if not (user.email or user.profile.unverified_email):
            messages.add_message(
                self.request,
                messages.WARNING,
                _('You have not set an email address. You must set a valid email address to use OSMaxx.')
            )
        if not profile.has_validated_email():
            messages.add_message(
                self.request,
                messages.WARNING,
                _('Your email has not been validated. Please check your inbox and validate your email address'
                  ' or resend the verification email.')
            )
        if self.is_new_user():
            self._move_email_from_user_to_profile(user, profile)
            self._send_email_verification(profile)
        else:
            self._ensure_profile_has_email(profile, user)
        user.refresh_from_db()
        return super().get(*args, **kwargs)

    def is_new_user(self):
        user = self.request.user
        profile_is_new = user.profile.unverified_email is None
        user_has_already_access = user_in_frontend_group(user)
        return profile_is_new and not user_has_already_access

    def post(self, *args, **kwargs):
        response = super().post(*args, **kwargs)
        if isinstance(response, HttpResponseRedirect):  # successful form validation
            profile = Profile.objects.get(associated_user=self.request.user)
            if not profile.has_validated_email():
                self._send_email_verification(profile=profile)
        return response

    def _ensure_profile_has_email(self, profile, user):
        if not profile.unverified_email:
            profile.unverified_email = user.email
            profile.save()

    def _move_email_from_user_to_profile(self, user, profile):
        if user.email:
            profile.unverified_email = user.email
            profile.save()
            user.email = ''
            user.save()

    def get_object(self, queryset=None):
        profile, _ = Profile.objects.get_or_create(associated_user=self.request.user)
        return profile

    def get_success_url(self):
        messages.add_message(self.request, messages.SUCCESS, _('Profile successfully updated'))
        return reverse('profile:edit_view')

class ActivationView(SendVerificationEmailMixin, LoginRequiredMixin, generic.UpdateView):
    error_msg = _('Verification token too old or invalid. Please resend the confirmation email and try again.')

    def get(self, request, *args, **kwargs):
        user = request.user
        token = request.GET.get('token', None)
        if token:
            data = user.profile.validate_key(activation_key=token)
            if data:
                user.email = data['email']
                user.save()
                self._set_group(user)
                messages.add_message(self.request, messages.SUCCESS, _('Successfully verified your email address.'))
            else:
                messages.add_message(self.request, messages.ERROR, self.error_msg)
        else:
            messages.add_message(self.request, messages.ERROR, self.error_msg)
        return redirect(reverse('profile:edit_view'))

    def _set_group(self, user):
        if settings.REGISTRATION_OPEN and not user_in_frontend_group(user):
            self._grant_user_access(user)

    def _grant_user_access(self, user):
        group = Group.objects.get(name=settings.OSMAXX_FRONTEND_USER_GROUP)
        group.user_set.add(user)

class ResendVerificationEmail(SendVerificationEmailMixin, LoginRequiredMixin, generic.RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        return reverse('profile:edit_view')

    def get(self, request, *args, **kwargs):
        profile = Profile.objects.get(associated_user=self.request.user)
        self._send_email_verification(profile=profile)
        return super().get(request, *args, **kwargs)

def user_in_frontend_group(user):
    return user.groups.filter(name=settings.OSMAXX_FRONTEND_USER_GROUP).exists()
<commit_msg>#819 don't add new users to frontend user group<commit_after>from django.conf import settings
from django.contrib import messages
from django.core.cache import cache
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from django.template.defaultfilters import urlencode
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
from django.views import generic

from osmaxx.contrib.auth.frontend_permissions import LoginRequiredMixin
from osmaxx.profile.forms import ProfileForm
from osmaxx.profile.models import Profile

class SendVerificationEmailMixin(object):
    RATE_LIMIT_SECONDS = 30

    def _send_email_verification(self, profile):
        if cache.get(profile.associated_user.id):
            return
        to_email = profile.unverified_email
        if to_email:
            cache.set(profile.associated_user.id, 'dummy value', timeout=self.RATE_LIMIT_SECONDS)
            user_administrator_email = settings.OSMAXX['ACCOUNT_MANAGER_EMAIL']
            token = profile.activation_key()
            token_url = '{}?token={}'.format(
                self.request.build_absolute_uri(reverse('profile:activation')), urlencode(token)
            )
            subject = render_to_string('profile/verification_email/subject.txt', context={}).strip()
            subject = ''.join(subject.splitlines())
            message = render_to_string(
                'profile/verification_email/body.txt',
                context=dict(
                    token_url=token_url,
                    username=self.request.user.username,
                    new_email_address=to_email,
                    domain=self.request.get_host(),
                )
            )
            send_mail(
                subject=subject,
                message=message,
                from_email=user_administrator_email,
                recipient_list=[to_email],
            )
            messages.add_message(
                self.request, messages.INFO, _('To activate your email, click the link in the confirmation email.')
            )

class ProfileView(SendVerificationEmailMixin, LoginRequiredMixin, generic.UpdateView):
    form_class = ProfileForm
    template_name = 'profile/profile_edit.html'

    def get(self, *args, **kwargs):
        profile = self.get_object()
        user = self.request.user

        if not (user.email or user.profile.unverified_email):
            messages.add_message(
                self.request,
                messages.WARNING,
                _('You have not set an email address. You must set a valid email address to use OSMaxx.')
            )
        if not profile.has_validated_email():
            messages.add_message(
                self.request,
                messages.WARNING,
                _('Your email has not been validated. Please check your inbox and validate your email address'
                  ' or resend the verification email.')
            )
        if self.is_new_user():
            self._move_email_from_user_to_profile(user, profile)
            self._send_email_verification(profile)
        else:
            self._ensure_profile_has_email(profile, user)
        user.refresh_from_db()
        return super().get(*args, **kwargs)

    def is_new_user(self):
        user = self.request.user
        profile_is_new = user.profile.unverified_email is None
        user_has_already_access = user_in_frontend_group(user)
        return profile_is_new and not user_has_already_access

    def post(self, *args, **kwargs):
        response = super().post(*args, **kwargs)
        if isinstance(response, HttpResponseRedirect):  # successful form validation
            profile = Profile.objects.get(associated_user=self.request.user)
            if not profile.has_validated_email():
                self._send_email_verification(profile=profile)
        return response

    def _ensure_profile_has_email(self, profile, user):
        if not profile.unverified_email:
            profile.unverified_email = user.email
            profile.save()

    def _move_email_from_user_to_profile(self, user, profile):
        if user.email:
            profile.unverified_email = user.email
            profile.save()
            user.email = ''
            user.save()

    def get_object(self, queryset=None):
        profile, _ = Profile.objects.get_or_create(associated_user=self.request.user)
        return profile

    def get_success_url(self):
        messages.add_message(self.request, messages.SUCCESS, _('Profile successfully updated'))
        return reverse('profile:edit_view')

class ActivationView(SendVerificationEmailMixin, LoginRequiredMixin, generic.UpdateView):
    error_msg = _('Verification token too old or invalid. Please resend the confirmation email and try again.')

    def get(self, request, *args, **kwargs):
        user = request.user
        token = request.GET.get('token', None)
        if token:
            data = user.profile.validate_key(activation_key=token)
            if data:
                user.email = data['email']
                user.save()
                messages.add_message(self.request, messages.SUCCESS, _('Successfully verified your email address.'))
            else:
                messages.add_message(self.request, messages.ERROR, self.error_msg)
        else:
            messages.add_message(self.request, messages.ERROR, self.error_msg)
        return redirect(reverse('profile:edit_view'))

class ResendVerificationEmail(SendVerificationEmailMixin, LoginRequiredMixin, generic.RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        return reverse('profile:edit_view')

    def get(self, request, *args, **kwargs):
        profile = Profile.objects.get(associated_user=self.request.user)
        self._send_email_verification(profile=profile)
        return super().get(request, *args, **kwargs)

def user_in_frontend_group(user):
    return user.groups.filter(name=settings.OSMAXX_FRONTEND_USER_GROUP).exists()
<|endoftext|>


<commit_before>from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import Group
from django.core.cache import cache
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from django.template.defaultfilters import urlencode
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
from django.views import generic

from osmaxx.contrib.auth.frontend_permissions import LoginRequiredMixin
from osmaxx.profile.forms import ProfileForm
from osmaxx.profile.models import Profile

class SendVerificationEmailMixin(object):
    RATE_LIMIT_SECONDS = 30

    def _send_email_verification(self, profile):
        if cache.get(profile.associated_user.id):
            return
        to_email = profile.unverified_email
        if to_email:
            cache.set(profile.associated_user.id, 'dummy value', timeout=self.RATE_LIMIT_SECONDS)
            user_administrator_email = settings.OSMAXX['ACCOUNT_MANAGER_EMAIL']
            token = profile.activation_key()
            token_url = '{}?token={}'.format(
                self.request.build_absolute_uri(reverse('profile:activation')), urlencode(token)
            )
            subject = render_to_string('profile/verification_email/subject.txt', context={}).strip()
            subject = ''.join(subject.splitlines())
            message = render_to_string('profile/verification_email/body.txt', context={'token_url': token_url})
            send_mail(
                subject=subject,
                message=message,
                from_email=user_administrator_email,
                recipient_list=[to_email],
            )
            messages.add_message(
                self.request, messages.INFO, _('To activate your email, click the link in the confirmation email.')
            )

class ProfileView(SendVerificationEmailMixin, LoginRequiredMixin, generic.UpdateView):
    form_class = ProfileForm
    template_name = 'profile/profile_edit.html'

    def get(self, *args, **kwargs):
        profile = self.get_object()
        user = self.request.user
        if not (user.email or user.profile.unverified_email):
            messages.add_message(
                self.request,
                messages.WARNING,
                _('You have not set an email address. You must set a valid email address to use OSMaxx.')
            )
        if self.is_new_user():
            self._move_email_from_user_to_profile(user, profile)
            self._send_email_verification(profile)
        else:
            self._ensure_profile_has_email(profile, user)
        user.refresh_from_db()
        return super().get(*args, **kwargs)

    def is_new_user(self):
        user = self.request.user
        profile_is_new = user.profile.unverified_email is None
        user_has_already_access = user.groups.filter(name=settings.OSMAXX_FRONTEND_USER_GROUP).exists()
        return profile_is_new and not user_has_already_access

    def post(self, *args, **kwargs):
        response = super().post(*args, **kwargs)
        if isinstance(response, HttpResponseRedirect):  # successful form validation
            profile = Profile.objects.get(associated_user=self.request.user)
            if profile.associated_user.email != profile.unverified_email:
                self._send_email_verification(profile=profile)
        return response

    def _ensure_profile_has_email(self, profile, user):
        if not profile.unverified_email:
            profile.unverified_email = user.email
            profile.save()

    def _move_email_from_user_to_profile(self, user, profile):
        if user.email:
            profile.unverified_email = user.email
            profile.save()
            user.email = ''
            user.save()

    def get_object(self, queryset=None):
        profile, _ = Profile.objects.get_or_create(associated_user=self.request.user)
        return profile

    def get_success_url(self):
        messages.add_message(self.request, messages.SUCCESS, _('Profile successfully updated'))
        return reverse('profile:edit_view')

class ActivationView(SendVerificationEmailMixin, LoginRequiredMixin, generic.UpdateView):
    error_msg = _('Verification token too old or invalid. Please resend the verification email and try again.')

    def get(self, request, *args, **kwargs):
        user = request.user
        token = request.GET.get('token', None)
        if token:
            data = user.profile.validate_key(activation_key=token)
            if data:
                user.email = data['email']
                user.save()
                self._set_group(user)
                messages.add_message(self.request, messages.SUCCESS, _('Successfully verified your email address.'))
            else:
                messages.add_message(self.request, messages.ERROR, self.error_msg)
        else:
            messages.add_message(self.request, messages.ERROR, self.error_msg)
        return redirect(reverse('profile:edit_view'))

    def _set_group(self, user):
        if settings.REGISTRATION_OPEN and not self._user_in_frontend_group(user):
            self._grant_user_access(user)

    def _user_in_frontend_group(self, user):
        return user.groups.filter(name=settings.OSMAXX_FRONTEND_USER_GROUP).exists()

    def _grant_user_access(self, user):
        group = Group.objects.get(name=settings.OSMAXX_FRONTEND_USER_GROUP)
        group.user_set.add(user)

class ResendVerificationEmail(SendVerificationEmailMixin, LoginRequiredMixin, generic.RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        return reverse('profile:edit_view')

    def get(self, request, *args, **kwargs):
        profile = Profile.objects.get(associated_user=self.request.user)
        self._send_email_verification(profile=profile)
        return super().get(request, *args, **kwargs)
<commit_msg>extract user group checking to free function<commit_after>from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import Group
from django.core.cache import cache
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from django.template.defaultfilters import urlencode
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
from django.views import generic

from osmaxx.contrib.auth.frontend_permissions import LoginRequiredMixin
from osmaxx.profile.forms import ProfileForm
from osmaxx.profile.models import Profile

class SendVerificationEmailMixin(object):
    RATE_LIMIT_SECONDS = 30

    def _send_email_verification(self, profile):
        if cache.get(profile.associated_user.id):
            return
        to_email = profile.unverified_email
        if to_email:
            cache.set(profile.associated_user.id, 'dummy value', timeout=self.RATE_LIMIT_SECONDS)
            user_administrator_email = settings.OSMAXX['ACCOUNT_MANAGER_EMAIL']
            token = profile.activation_key()
            token_url = '{}?token={}'.format(
                self.request.build_absolute_uri(reverse('profile:activation')), urlencode(token)
            )
            subject = render_to_string('profile/verification_email/subject.txt', context={}).strip()
            subject = ''.join(subject.splitlines())
            message = render_to_string('profile/verification_email/body.txt', context={'token_url': token_url})
            send_mail(
                subject=subject,
                message=message,
                from_email=user_administrator_email,
                recipient_list=[to_email],
            )
            messages.add_message(
                self.request, messages.INFO, _('To activate your email, click the link in the confirmation email.')
            )

class ProfileView(SendVerificationEmailMixin, LoginRequiredMixin, generic.UpdateView):
    form_class = ProfileForm
    template_name = 'profile/profile_edit.html'

    def get(self, *args, **kwargs):
        profile = self.get_object()
        user = self.request.user
        if not (user.email or user.profile.unverified_email):
            messages.add_message(
                self.request,
                messages.WARNING,
                _('You have not set an email address. You must set a valid email address to use OSMaxx.')
            )
        if self.is_new_user():
            self._move_email_from_user_to_profile(user, profile)
            self._send_email_verification(profile)
        else:
            self._ensure_profile_has_email(profile, user)
        user.refresh_from_db()
        return super().get(*args, **kwargs)

    def is_new_user(self):
        user = self.request.user
        profile_is_new = user.profile.unverified_email is None
        user_has_already_access = user_in_frontend_group(user)
        return profile_is_new and not user_has_already_access

    def post(self, *args, **kwargs):
        response = super().post(*args, **kwargs)
        if isinstance(response, HttpResponseRedirect):  # successful form validation
            profile = Profile.objects.get(associated_user=self.request.user)
            if profile.associated_user.email != profile.unverified_email:
                self._send_email_verification(profile=profile)
        return response

    def _ensure_profile_has_email(self, profile, user):
        if not profile.unverified_email:
            profile.unverified_email = user.email
            profile.save()

    def _move_email_from_user_to_profile(self, user, profile):
        if user.email:
            profile.unverified_email = user.email
            profile.save()
            user.email = ''
            user.save()

    def get_object(self, queryset=None):
        profile, _ = Profile.objects.get_or_create(associated_user=self.request.user)
        return profile

    def get_success_url(self):
        messages.add_message(self.request, messages.SUCCESS, _('Profile successfully updated'))
        return reverse('profile:edit_view')

class ActivationView(SendVerificationEmailMixin, LoginRequiredMixin, generic.UpdateView):
    error_msg = _('Verification token too old or invalid. Please resend the verification email and try again.')

    def get(self, request, *args, **kwargs):
        user = request.user
        token = request.GET.get('token', None)
        if token:
            data = user.profile.validate_key(activation_key=token)
            if data:
                user.email = data['email']
                user.save()
                self._set_group(user)
                messages.add_message(self.request, messages.SUCCESS, _('Successfully verified your email address.'))
            else:
                messages.add_message(self.request, messages.ERROR, self.error_msg)
        else:
            messages.add_message(self.request, messages.ERROR, self.error_msg)
        return redirect(reverse('profile:edit_view'))

    def _set_group(self, user):
        if settings.REGISTRATION_OPEN and not user_in_frontend_group(user):
            self._grant_user_access(user)

    def _grant_user_access(self, user):
        group = Group.objects.get(name=settings.OSMAXX_FRONTEND_USER_GROUP)
        group.user_set.add(user)

class ResendVerificationEmail(SendVerificationEmailMixin, LoginRequiredMixin, generic.RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        return reverse('profile:edit_view')

    def get(self, request, *args, **kwargs):
        profile = Profile.objects.get(associated_user=self.request.user)
        self._send_email_verification(profile=profile)
        return super().get(request, *args, **kwargs)

def user_in_frontend_group(user):
    return user.groups.filter(name=settings.OSMAXX_FRONTEND_USER_GROUP).exists()
<|endoftext|>


<commit_before>#!/usr/bin/env python3

import torch

from ..lazy import (
    lazify,
    BlockDiagLazyTensor,
    CatLazyTensor,
    LazyTensor,
    NonLazyTensor,
)
from .multivariate_normal import MultivariateNormal

class MultitaskMultivariateNormal(MultivariateNormal):
    def __init__(self, mean, covariance_matrix, validate_args=False):
        """
        Constructs a multi-output multivariate Normal random variable, based on mean and covariance
        Can be multi-output multivariate, or a batch of multi-output multivariate Normal

        Passing a matrix mean corresponds to a multi-output multivariate Normal
        Passing a matrix mean corresponds to a batch of multivariate Normals

        Params:
            mean (:obj:`torch.tensor`): An `n x t` or batch `b x n x t` matrix of means for the MVN distribution.
            covar (:obj:`torch.tensor` or :obj:`gpytorch.lazy.LazyTensor`): An `nt x nt` or batch `b x nt x nt`
                covariance matrix of MVN distribution.
        """
        if not torch.is_tensor(mean) and not isinstance(mean, LazyTensor):
            raise RuntimeError("The mean of a MultitaskMultivariateNormal must be a Tensor or LazyTensor")

        if not torch.is_tensor(covariance_matrix) and not isinstance(covariance_matrix, LazyTensor):
            raise RuntimeError("The covariance of a MultitaskMultivariateNormal must be a Tensor or LazyTensor")

        if mean.ndimension() not in {2, 3}:
            raise RuntimeError("mean should be a matrix or a batch matrix (batch mode)")

        self._output_shape = mean.shape
        super(MultitaskMultivariateNormal, self).__init__(
            mean=mean.view(mean.shape[:-2] + torch.Size([-1])),
            covariance_matrix=covariance_matrix,
            validate_args=validate_args,
        )

    @property
    def event_shape(self):
        return self._output_shape

    @classmethod
    def from_independent_mvns(cls, mvns):
        if len(mvns) < 2:
            raise ValueError("Must provide at least 2 MVNs to form a MultitaskMultivariateNormal")
        if not all(m.batch_shape == mvns[0].batch_shape for m in mvns[1:]):
            raise ValueError("All MultivariateNormals must have the same batch shape")
        if not all(m.event_shape == mvns[0].event_shape for m in mvns[1:]):
            raise ValueError("All MultivariateNormals must have the same event shape")
        if len(mvns[0].batch_shape) > 1:
            raise ValueError("Multiple batch dimensions are not supported in from_independent_mvns.")
        mean = torch.stack([mvn.mean for mvn in mvns], -1)
        # TODO: To do the following efficiently, we don't want to evaluate the
        # covariance matrices. Instead, we want to use the lazies directly in the
        # BlockDiagLazyTensor. This will require implementing a new BatchLazyTensor:
        # https://github.com/cornellius-gp/gpytorch/issues/468
        batch_mode = len(mvns[0].covariance_matrix.shape) == 3
        if batch_mode:
            covar_blocks_lazy = CatLazyTensor(
                *[mvn.lazy_covariance_matrix for mvn in mvns],
                dim=0,
                output_device=mean.device
            )
        else:
            covar_blocks_lazy = lazify(torch.cat(
                [mvn.covariance_matrix.unsqueeze(0) for mvn in mvns],
                dim=0
            ))
        covar_lazy = BlockDiagLazyTensor(
            covar_blocks_lazy,
            num_blocks=len(mvns) if batch_mode else None
        )
        return cls(mean=mean, covariance_matrix=covar_lazy)

    def get_base_samples(self, sample_shape=torch.Size()):
        """Get i.i.d. standard Normal samples (to be used with rsample(base_samples=base_samples))"""
        res = super(MultitaskMultivariateNormal, self).get_base_samples(sample_shape)
        res = res.view(*sample_shape, *self._output_shape)
        return res

    def log_prob(self, value):
        return super(MultitaskMultivariateNormal, self).log_prob(value.view(value.shape[:-2] + torch.Size([-1])))

    @property
    def mean(self):
        return super(MultivariateNormal, self).mean.view(self._output_shape)

    @property
    def num_tasks(self):
        return self._output_shape[-1]

    def rsample(self, sample_shape=torch.Size(), base_samples=None):
        if base_samples is not None:
            # Make sure that the base samples agree with the distribution
            if tuple(self.mean.size()) != tuple(self.mean.size()[-self.mean.dim() :]):
                raise RuntimeError(
                    "The size of base_samples (minus sample shape dimensions) should agree with the size "
                    "of self.mean. Expected ...{} but got {}".format(self.loc.size(), base_samples.size())
                )

            sample_shape = torch.Size(tuple(base_samples.size(i) for i in range(base_samples.dim() - self.mean.dim())))
            base_samples = base_samples.view(*sample_shape, *self.loc.shape)

        samples = super(MultitaskMultivariateNormal, self).rsample(sample_shape=sample_shape, base_samples=base_samples)
        samples = samples.view(sample_shape + self._output_shape)
        return samples

    @property
    def variance(self):
        return super(MultivariateNormal, self).variance.view(self._output_shape)
<commit_msg>Clean up imports, use NonLazyTensor rather than lazify<commit_after>#!/usr/bin/env python3

import torch

from ..lazy import BlockDiagLazyTensor, CatLazyTensor, LazyTensor, NonLazyTensor
from .multivariate_normal import MultivariateNormal

class MultitaskMultivariateNormal(MultivariateNormal):
    def __init__(self, mean, covariance_matrix, validate_args=False):
        """
        Constructs a multi-output multivariate Normal random variable, based on mean and covariance
        Can be multi-output multivariate, or a batch of multi-output multivariate Normal

        Passing a matrix mean corresponds to a multi-output multivariate Normal
        Passing a matrix mean corresponds to a batch of multivariate Normals

        Params:
            mean (:obj:`torch.tensor`): An `n x t` or batch `b x n x t` matrix of means for the MVN distribution.
            covar (:obj:`torch.tensor` or :obj:`gpytorch.lazy.LazyTensor`): An `nt x nt` or batch `b x nt x nt`
                covariance matrix of MVN distribution.
        """
        if not torch.is_tensor(mean) and not isinstance(mean, LazyTensor):
            raise RuntimeError("The mean of a MultitaskMultivariateNormal must be a Tensor or LazyTensor")

        if not torch.is_tensor(covariance_matrix) and not isinstance(covariance_matrix, LazyTensor):
            raise RuntimeError("The covariance of a MultitaskMultivariateNormal must be a Tensor or LazyTensor")

        if mean.ndimension() not in {2, 3}:
            raise RuntimeError("mean should be a matrix or a batch matrix (batch mode)")

        self._output_shape = mean.shape
        super(MultitaskMultivariateNormal, self).__init__(
            mean=mean.view(mean.shape[:-2] + torch.Size([-1])),
            covariance_matrix=covariance_matrix,
            validate_args=validate_args,
        )

    @property
    def event_shape(self):
        return self._output_shape

    @classmethod
    def from_independent_mvns(cls, mvns):
        if len(mvns) < 2:
            raise ValueError("Must provide at least 2 MVNs to form a MultitaskMultivariateNormal")
        if not all(m.batch_shape == mvns[0].batch_shape for m in mvns[1:]):
            raise ValueError("All MultivariateNormals must have the same batch shape")
        if not all(m.event_shape == mvns[0].event_shape for m in mvns[1:]):
            raise ValueError("All MultivariateNormals must have the same event shape")
        if len(mvns[0].batch_shape) > 1:
            raise ValueError("Multiple batch dimensions are not supported in from_independent_mvns.")
        mean = torch.stack([mvn.mean for mvn in mvns], -1)
        # TODO: To do the following efficiently, we don't want to evaluate the
        # covariance matrices. Instead, we want to use the lazies directly in the
        # BlockDiagLazyTensor. This will require implementing a new BatchLazyTensor:
        # https://github.com/cornellius-gp/gpytorch/issues/468
        batch_mode = len(mvns[0].covariance_matrix.shape) == 3
        if batch_mode:
            covar_blocks_lazy = CatLazyTensor(
                *[mvn.lazy_covariance_matrix for mvn in mvns],
                dim=0,
                output_device=mean.device
            )
        else:
            covar_blocks_lazy = NonLazyTensor(
                torch.cat(
                    [mvn.covariance_matrix.unsqueeze(0) for mvn in mvns],
                    dim=0
                )
            )
        covar_lazy = BlockDiagLazyTensor(
            covar_blocks_lazy,
            num_blocks=len(mvns) if batch_mode else None
        )
        return cls(mean=mean, covariance_matrix=covar_lazy)

    def get_base_samples(self, sample_shape=torch.Size()):
        """Get i.i.d. standard Normal samples (to be used with rsample(base_samples=base_samples))"""
        res = super(MultitaskMultivariateNormal, self).get_base_samples(sample_shape)
        res = res.view(*sample_shape, *self._output_shape)
        return res

    def log_prob(self, value):
        return super(MultitaskMultivariateNormal, self).log_prob(value.view(value.shape[:-2] + torch.Size([-1])))

    @property
    def mean(self):
        return super(MultivariateNormal, self).mean.view(self._output_shape)

    @property
    def num_tasks(self):
        return self._output_shape[-1]

    def rsample(self, sample_shape=torch.Size(), base_samples=None):
        if base_samples is not None:
            # Make sure that the base samples agree with the distribution
            if tuple(self.mean.size()) != tuple(self.mean.size()[-self.mean.dim() :]):
                raise RuntimeError(
                    "The size of base_samples (minus sample shape dimensions) should agree with the size "
                    "of self.mean. Expected ...{} but got {}".format(self.loc.size(), base_samples.size())
                )

            sample_shape = torch.Size(tuple(base_samples.size(i) for i in range(base_samples.dim() - self.mean.dim())))
            base_samples = base_samples.view(*sample_shape, *self.loc.shape)

        samples = super(MultitaskMultivariateNormal, self).rsample(sample_shape=sample_shape, base_samples=base_samples)
        samples = samples.view(sample_shape + self._output_shape)
        return samples

    @property
    def variance(self):
        return super(MultivariateNormal, self).variance.view(self._output_shape)
<|endoftext|>


<commit_before>from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext
from django.db.models.loading import get_model

from .base import BaseBackend

class NotificationBackend(BaseBackend):
    def deliver(self, recipient, sender, notice_type, extra_context):
        Notification = get_model('notifications', 'Notification')

        context = extra_context

        notification = Notification(
            recipient=recipient,
            actor_content_type=ContentType.objects.get_for_model(sender),
            actor_object_id=sender.pk,
            verb=notice_type['description'],
            public=context.get('public', True),
            description=context.get('description', None)
        )

        for opt in ('target', 'action_object'):
            obj = context.get(opt, None)

            context[opt] = obj.serializer.data

            if obj is not None:
                setattr(notification, '{}_object_id'.format(opt), obj.pk)
                setattr(notification, '{}_content_type'.format(opt),
                        ContentType.objects.get_for_model(obj))

        context.update({
            "recipient": recipient.serializer.data,
            "sender": sender.serializer.data,
            "notice": ugettext(notice_type['display']),
        })

        notification.data = extra_context

        notification.save()
<commit_msg>Fix issue with failing notification delivery<commit_after>from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext
from django.db.models.loading import get_model

from .base import BaseBackend

class NotificationBackend(BaseBackend):
    def deliver(self, recipient, sender, notice_type, extra_context):
        Notification = get_model('notifications', 'Notification')

        context = {}
        context.update(extra_context)

        notification = Notification(
            recipient=recipient,
            actor_content_type=ContentType.objects.get_for_model(sender),
            actor_object_id=sender.pk,
            verb=notice_type['description'],
            public=context.get('public', True),
            description=context.get('description', None)
        )

        for opt in ('target', 'action_object'):
            obj = context.get(opt, None)

            context[opt] = obj.serializer.data

            if obj is not None:
                setattr(notification, '{}_object_id'.format(opt), obj.pk)
                setattr(notification, '{}_content_type'.format(opt),
                        ContentType.objects.get_for_model(obj))

        context.update({
            "recipient": recipient.serializer.data,
            "sender": sender.serializer.data,
            "notice": ugettext(notice_type['display']),
        })

        notification.data = context

        notification.save()
<|endoftext|>