utils.py 4.72 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
# Copyright (c) 2016, Daniele Venzano
#
# 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.

"""Utility functions needed by the Zoe REST API."""

import base64
import logging
import functools

import tornado.web

from zoe_lib.config import get_conf

from zoe_api.exceptions import ZoeRestAPIException, ZoeNotFoundException, ZoeAuthException, ZoeException
27
from zoe_api.auth.base import BaseAuthenticator  # pylint-bug #1063 pylint: disable=unused-import
28 29 30
from zoe_api.auth.ldap import LDAPAuthenticator
from zoe_api.auth.file import PlainTextAuthenticator

31
log = logging.getLogger(__name__)
32

33

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
def catch_exceptions(func):
    """
    Decorator function used to work around the static exception system available in Flask-RESTful
    :param func:
    :return:
    """
    @functools.wraps(func)
    def func_wrapper(*args, **kwargs):
        """The actual decorator."""
        self = args[0]
        try:
            return func(*args, **kwargs)
        except ZoeRestAPIException as e:
            if e.status_code != 401:
                log.exception(e.message)
            self.set_status(e.status_code)
            self.write({'message': e.message})
        except ZoeNotFoundException as e:
            self.set_status(404)
            self.write({'message': e.message})
        except ZoeAuthException as e:
            self.set_status(401)
            self.write({'message': e.message})
        except ZoeException as e:
            self.set_status(400)
            self.write({'message': e.message})
        except Exception as e:
            self.set_status(500)
            log.exception(str(e))
            self.write({'message': str(e)})

    return func_wrapper


def get_auth(handler: tornado.web.RequestHandler):
    """Try to authenticate a request."""
70 71 72
    if handler.get_secure_cookie('zoe'):
        cookie_val = str(handler.get_secure_cookie('zoe'))
        uid, role = cookie_val[2:-1].split('.')
73
        log.debug('Authentication done using cookie')
74 75
        if role == "guest":
            raise ZoeRestAPIException('Guest users cannot use the API, ask for a role upgrade', 401, {'WWW-Authenticate': 'Basic realm="Login Required"'})
76 77
        return uid, role

78
    auth_header = handler.request.headers.get('Authorization')
79
    if auth_header is None or not (auth_header.startswith('Basic ') or auth_header.startswith('Bearer ')):
80 81
        raise ZoeRestAPIException('missing or wrong authentication information', 401, {'WWW-Authenticate': 'Basic realm="Login Required"'})

Daniele Venzano's avatar
Daniele Venzano committed
82
    # Process for authentication with username, password
83 84 85
    else:
        auth_decoded = base64.decodebytes(bytes(auth_header[6:], 'ascii')).decode('utf-8')
        username, password = auth_decoded.split(':', 2)
86 87 88 89

    if get_conf().auth_type == 'text':
        authenticator = PlainTextAuthenticator()  # type: BaseAuthenticator
    elif get_conf().auth_type == 'ldap':
90
        authenticator = LDAPAuthenticator(sasl=False)  # type: BaseAuthenticator
hxquangnhat's avatar
hxquangnhat committed
91
    elif get_conf().auth_type == 'ldapsasl':
92
        authenticator = LDAPAuthenticator(sasl=True)  # type: BaseAuthenticator
93 94 95 96 97
    else:
        raise ZoeException('Configuration error, unknown authentication method: {}'.format(get_conf().auth_type))
    uid, role = authenticator.auth(username, password)
    if uid is None:
        raise ZoeRestAPIException('missing or wrong authentication information', 401, {'WWW-Authenticate': 'Basic realm="Login Required"'})
98
    log.debug('Authentication done using auth-mechanism')
99

100 101 102
    if role == "guest":
        raise ZoeRestAPIException('Guest users cannot use the API, ask for a role upgrade', 401, {'WWW-Authenticate': 'Basic realm="Login Required"'})

103
    return uid, role
104 105 106 107 108 109 110 111 112 113 114 115


def manage_cors_headers(handler: tornado.web.RequestHandler):
    """Set up the headers for enabling CORS."""
    if handler.request.headers.get('Origin') is None:
        handler.set_header("Access-Control-Allow-Origin", "*")
    else:
        handler.set_header("Access-Control-Allow-Origin", handler.request.headers.get('Origin'))
    handler.set_header("Access-Control-Allow-Credentials", "true")
    handler.set_header("Access-Control-Allow-Headers", "x-requested-with, Content-Type, origin, authorization, accept, client-security-token")
    handler.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, DELETE")
    handler.set_header("Access-Control-Max-Age", "1000")