"Fossies" - the Fresh Open Source Software Archive

Member "monasca-api-3.1.0/monasca_api/api/core/log/validation.py" (27 Sep 2019, 7622 Bytes) of package /linux/misc/openstack/monasca-api-3.1.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "validation.py" see the Fossies "Dox" file reference documentation.

    1 # Copyright 2016-2017 FUJITSU LIMITED
    2 #
    3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
    4 # not use this file except in compliance with the License. You may obtain
    5 # a copy of the License at
    6 #
    7 #      http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 # Unless required by applicable law or agreed to in writing, software
   10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   12 # License for the specific language governing permissions and limitations
   13 # under the License.
   14 
   15 import re
   16 
   17 import falcon
   18 from oslo_log import log
   19 import six
   20 
   21 from monasca_api.api.core.log import exceptions
   22 from monasca_api import conf
   23 
   24 LOG = log.getLogger(__name__)
   25 CONF = conf.CONF
   26 
   27 APPLICATION_TYPE_CONSTRAINTS = {
   28     'MAX_LENGTH': 255,
   29     'PATTERN': re.compile('^[a-zA-Z0-9_.\\-]+$')
   30 }
   31 """Application type constraint used in validation.
   32 
   33 See :py:func:`Validations.validate_application_type`
   34 """
   35 DIMENSION_NAME_CONSTRAINTS = {
   36     'MAX_LENGTH': 255,
   37     'PATTERN': re.compile('[^><={}(), \'";&]+$')
   38 }
   39 """Constraint for name of single dimension.
   40 
   41 See :py:func:`Validations.validate_dimensions`
   42 """
   43 DIMENSION_VALUE_CONSTRAINTS = {
   44     'MAX_LENGTH': 255
   45 }
   46 """Constraint for value of single dimension.
   47 
   48 See :py:func:`Validations.validate_dimensions`
   49 """
   50 
   51 
   52 def validate_application_type(application_type=None):
   53     """Validates application type.
   54 
   55        Validation won't take place if application_type is None.
   56        For details see: :py:data:`APPLICATION_TYPE_CONSTRAINTS`
   57 
   58        :param str application_type: application type
   59        """
   60 
   61     def validate_length():
   62         if (len(application_type) >
   63                 APPLICATION_TYPE_CONSTRAINTS['MAX_LENGTH']):
   64             msg = ('Application type {type} must be '
   65                    '{length} characters or less')
   66             raise exceptions.HTTPUnprocessableEntity(
   67                 msg.format(
   68                     type=application_type,
   69                     length=APPLICATION_TYPE_CONSTRAINTS[
   70                         'MAX_LENGTH']
   71                 )
   72             )
   73 
   74     def validate_match():
   75         if (not APPLICATION_TYPE_CONSTRAINTS['PATTERN']
   76                 .match(application_type)):
   77             raise exceptions.HTTPUnprocessableEntity(
   78                 'Application type %s may only contain: "a-z A-Z 0-9 _ - ."'
   79                 % application_type
   80             )
   81 
   82     if application_type:
   83         validate_length()
   84         validate_match()
   85 
   86 
   87 def _validate_dimension_name(name):
   88     try:
   89         if len(name) > DIMENSION_NAME_CONSTRAINTS['MAX_LENGTH']:
   90             raise exceptions.HTTPUnprocessableEntity(
   91                 'Dimension name %s must be 255 characters or less' %
   92                 name
   93             )
   94         if name[0] == '_':
   95             raise exceptions.HTTPUnprocessableEntity(
   96                 'Dimension name %s cannot start with underscore (_)' %
   97                 name
   98             )
   99         if not DIMENSION_NAME_CONSTRAINTS['PATTERN'].match(name):
  100             raise exceptions.HTTPUnprocessableEntity(
  101                 'Dimension name %s may not contain: %s' %
  102                 (name, '> < = { } ( ) \' " , ; &')
  103             )
  104     except (TypeError, IndexError):
  105         raise exceptions.HTTPUnprocessableEntity(
  106             'Dimension name cannot be empty'
  107         )
  108 
  109 
  110 def _validate_dimension_value(value):
  111     try:
  112         value[0]
  113         if len(value) > DIMENSION_VALUE_CONSTRAINTS['MAX_LENGTH']:
  114             raise exceptions.HTTPUnprocessableEntity(
  115                 'Dimension value %s must be 255 characters or less' %
  116                 value
  117             )
  118     except (TypeError, IndexError):
  119         raise exceptions.HTTPUnprocessableEntity(
  120             'Dimension value cannot be empty'
  121         )
  122 
  123 
  124 def validate_dimensions(dimensions):
  125     """Validates dimensions type.
  126 
  127        Empty dimensions are not being validated.
  128        For details see:
  129 
  130        :param dict dimensions: dimensions to validate
  131 
  132        * :py:data:`DIMENSION_NAME_CONSTRAINTS`
  133        * :py:data:`DIMENSION_VALUE_CONSTRAINTS`
  134        """
  135     try:
  136         for dim_name, dim_value in dimensions.items():
  137             _validate_dimension_name(dim_name)
  138             _validate_dimension_value(dim_value)
  139     except AttributeError:
  140         raise exceptions.HTTPUnprocessableEntity(
  141             'Dimensions %s must be a dictionary (map)' % dimensions)
  142 
  143 
  144 def validate_content_type(req, allowed):
  145     """Validates content type.
  146 
  147     Method validates request against correct
  148     content type.
  149 
  150     If content-type cannot be established (i.e. header is missing),
  151     :py:class:`falcon.HTTPMissingHeader` is thrown.
  152     If content-type is not **application/json** or **text/plain**,
  153     :py:class:`falcon.HTTPUnsupportedMediaType` is thrown.
  154 
  155 
  156     :param falcon.Request req: current request
  157     :param iterable allowed: allowed content type
  158 
  159     :exception: :py:class:`falcon.HTTPMissingHeader`
  160     :exception: :py:class:`falcon.HTTPUnsupportedMediaType`
  161     """
  162     content_type = req.content_type
  163 
  164     LOG.debug('Content-Type is %s', content_type)
  165 
  166     if content_type is None or len(content_type) == 0:
  167         raise falcon.HTTPMissingHeader('Content-Type')
  168 
  169     if content_type not in allowed:
  170         sup_types = ', '.join(allowed)
  171         details = ('Only [%s] are accepted as logs representations'
  172                    % str(sup_types))
  173         raise falcon.HTTPUnsupportedMediaType(description=details)
  174 
  175 
  176 def validate_payload_size(req):
  177     """Validates payload size.
  178 
  179     Method validates sent payload size.
  180     It expects that http header **Content-Length** is present.
  181     If it does not, method raises :py:class:`falcon.HTTPLengthRequired`.
  182     Otherwise values is being compared with ::
  183 
  184         [service]
  185         max_log_size = 1048576
  186 
  187     **max_log_size** refers to the maximum allowed content length.
  188     If it is exceeded :py:class:`falcon.HTTPRequestEntityTooLarge` is
  189     thrown.
  190 
  191     :param falcon.Request req: current request
  192 
  193     :exception: :py:class:`falcon.HTTPLengthRequired`
  194     :exception: :py:class:`falcon.HTTPRequestEntityTooLarge`
  195 
  196     """
  197     payload_size = req.content_length
  198     max_size = CONF.service.max_log_size
  199 
  200     LOG.debug('Payload (content-length) is %s', str(payload_size))
  201 
  202     if payload_size is None:
  203         raise falcon.HTTPLengthRequired(
  204             title='Content length header is missing',
  205             description='Content length is required to estimate if '
  206                         'payload can be processed'
  207         )
  208 
  209     if payload_size >= max_size:
  210         raise falcon.HTTPPayloadTooLarge(
  211             title='Log payload size exceeded',
  212             description='Maximum allowed size is %d bytes' % max_size
  213         )
  214 
  215 
  216 def validate_is_delegate(roles):
  217     delegate_roles = CONF.roles_middleware.delegate_roles
  218     if roles and delegate_roles:
  219         roles = roles.split(',') if isinstance(roles, six.string_types) \
  220             else roles
  221         return any(x in set(delegate_roles) for x in roles)
  222     return False
  223 
  224 
  225 def validate_cross_tenant(tenant_id, cross_tenant_id, roles):
  226 
  227     if not validate_is_delegate(roles):
  228         if cross_tenant_id:
  229             raise falcon.HTTPForbidden(
  230                 'Permission denied',
  231                 'Projects %s cannot POST cross tenant logs' % tenant_id
  232             )
  233 
  234 
  235 def validate_log_message(log_object):
  236     """Validates log property.
  237 
  238     Log property should have message property.
  239 
  240     Args:
  241         log_object (dict): log property
  242        """
  243     if 'message' not in log_object:
  244         raise exceptions.HTTPUnprocessableEntity(
  245             'Log property should have message'
  246         )