Exemple #1
0
    def _parse_uri(self, client):
        """
        解析请求的 uri
        :type client: Client
        :return:
        """
        try:
            _, req_endpoint, version, uri = self.handler.request.uri.split(
                '/', 3)
        except ValueError:
            raise AuthRequestException(
                'Invalid Request Uri, Fail to Get Endpoint and Version')

        endpoints = client.config.get('endpoints', {})
        endpoint = endpoints.get('%s:%s' % (req_endpoint, version))
        if endpoint is None:
            raise AuthRequestException('No Permission to Access %s' %
                                       req_endpoint)

        if not endpoint.get('enable', True):
            raise AuthRequestException('Disabled Endpoint')

        # version = endpoint.get('version')
        # if version and version != '':
        #     try:
        #         # 处理 uri 前的版本号
        #         _, uri = uri.split(version, 1)
        #     except ValueError:
        #         raise AuthRequestException('Invalid Request API Version')

        if not uri.startswith('/'):
            uri = '/' + uri

        if endpoint.get('builtin_endpoint', False):
            # 如果是内置的 endpoint, 就没有 forward_url
            forward_url = None
        else:
            # 解析要转发的地址
            endpoint_url = endpoint.get('url')
            if endpoint_url is None:
                raise AuthRequestException('No Endpoint Url Config')

            endpoint_netloc = endpoint.get('netloc')
            if endpoint_netloc is None:
                url_parsed = urlparse(endpoint_url)
                endpoint['netloc'] = url_parsed.netloc

            if endpoint_url.endswith('/'):
                forward_url = endpoint_url + uri[1:]
            else:
                forward_url = endpoint_url + uri

        request_data = {
            'endpoint': endpoint,
            'uri': uri,
            'forward_url': forward_url,
        }

        logger.debug(request_data)
        return request_data
Exemple #2
0
    def auth_request(self, request):
        try:
            timestamp = int(request.headers.get('X-Api-Timestamp'))
        except ValueError:
            raise AuthRequestException('Invalid X-Api-Timestamp Header')

        now_ts = int(time.time())
        if abs(timestamp - now_ts) > settings.SIGNATURE_EXPIRE_SECONDS:
            logger.debug('Expired signature, timestamp: %s' % timestamp)
            raise AuthRequestException('Expired Signature')

        signature = request.headers.get('X-Api-Signature')
        if signature:
            del request.headers['X-Api-Signature']
        else:
            logger.debug('No Signature Provided')
            raise AuthRequestException('No Signature Provided')

        string_to_sign = self._request_string_to_sign(request)
        # 如果不是 unicode 输出会引发异常
        # logger.debug('string_to_sign: %s' % string_to_sign.decode('utf-8'))
        hash_value = sha256(utf8(string_to_sign)).hexdigest()
        real_signature = self.sign_string(hash_value)
        if signature != real_signature:
            logger.debug('Signature not match: %s, %s' %
                         (signature, real_signature))
            raise AuthRequestException('Invalid Signature')
Exemple #3
0
    def post(self, *args, **kwargs):
        logger.debug('AuthLoginHandler')
        login_auth_url = self.client.config.get('login_auth_url')
        logger.debug(login_auth_url)
        if login_auth_url is None:
            raise AuthRequestException(
                'Missing Login Auth Url in Client Config')
        # access_token 多少秒后过期
        access_token_ex = self.client.config.get('access_token_ex')
        refresh_token_ex = self.client.config.get('refresh_token_ex')

        # 设置超时时间
        async_http_connect_timeout = ASYNC_HTTP_CONNECT_TIMEOUT
        async_http_request_timeout = ASYNC_HTTP_REQUEST_TIMEOUT
        headers = {'Content-Type': 'application/json; charset=utf-8'}
        try:
            response = yield AsyncHTTPClient().fetch(
                HTTPRequest(url=login_auth_url,
                            method=self.request.method,
                            body=self.request.body,
                            headers=headers,
                            connect_timeout=async_http_connect_timeout,
                            request_timeout=async_http_request_timeout))
        except Exception as e:
            logger.error(e)
            logger.error(traceback.format_exc())
            raise AuthRequestException('Fail to Request Login Auth Url')

        json_data = json.loads(response.body)
        if json_data['code'] == APIStatusCode.SUCCESS:
            user_info = json_data['data']
            token_info = {
                'access_key': self.client.access_key,
                'user_info': user_info
            }

            token_info = RedisHelper.set_token_info(token_info,
                                                    access_token_ex,
                                                    refresh_token_ex)
            if token_info is None:
                self.error(msg='Save Access Token Error')
            else:
                data = {
                    'access_token': token_info['access_token'],
                    'refresh_token': token_info['refresh_token'],
                    # access_token 过期时间
                    'expires_in': int(time.time()) + access_token_ex,
                    'user_info': user_info
                }
                self.success(data)
        else:
            self.fail(msg=json_data['msg'])
Exemple #4
0
    def process_request(self, *args, **kwargs):
        logger.debug('process_request')
        if 'X-Api-User-Json' in self.handler.request.headers:
            del self.handler.request.headers['X-Api-User-Json']

        endpoint = self.handler.client.request['endpoint']
        require_login = endpoint.get('require_login', False)
        if not require_login:
            return

        access_token = self.handler.get_query_argument('access_token', None)
        token_info = RedisHelper.get_access_token_info(access_token)

        if token_info is not None:
            if token_info['access_key'] != self.handler.client.access_key:
                raise AuthRequestException(
                    'This Access Token is Belongs to Another Client App')

            logger.debug('允许访问')
            try:
                json_str = json.dumps(token_info['user_info'],
                                      ensure_ascii=False)
                # 用户信息使用 json 存储,并编码为 base64
                self.handler.request.headers[
                    'X-Api-User-Json'] = base64.b64encode(
                        json_str.encode('utf8'))
            except Exception, e:
                logger.error('设置 X-Api-User-Json 失败')
                logger.error(e.message)
                logger.error(traceback.format_exc())
Exemple #5
0
    def _acl_filter(self):
        """
        如果启用访问控制列表,就需要检查URI是否允许访问
        :return:
        """
        client = self.handler.client
        uri = client.request['uri']

        endpoint = client.request['endpoint']
        enable_acl = endpoint.get('enable_acl', False)
        if enable_acl:
            acl_rules = endpoint.get('acl_rules', [])
            # 如果都没有找到匹配的规则,默认返回Tue,放行
            allow_access = True
            for r in acl_rules:
                re_uri, is_permit = r['re_uri'], r['is_permit']
                pattern = re.compile(re_uri)
                match = pattern.search(uri)
                if match:
                    allow_access = is_permit
                    break

            # 禁止访问该 uri
            if not allow_access:
                logger.info('forbidden uri %s' % uri)
                raise AuthRequestException('Forbidden Uri')
Exemple #6
0
    def process_request(self, *args, **kwargs):
        request = self.handler.request
        client = self.handler.client
        logger.debug('client: %s' % client)
        client.encrypt_type = request.headers.get('X-Api-Encrypt-Type', 'raw')
        if client.encrypt_type != 'aes':
            return

        def decrypt_data():
            aes_cipher = AESCipher(client.secret_key)
            encrypted_uri = self.handler.request.headers.get(
                'X-Api-Encrypted-Uri')
            if encrypted_uri:
                request.uri = aes_cipher.decrypt(utf8(encrypted_uri))
                logger.debug('decrypted uri %s' % request.uri)

            encrypted_headers = self.handler.request.headers.get(
                'X-Api-Encrypted-Headers')

            if encrypted_headers:
                headers_str = aes_cipher.decrypt(utf8(encrypted_headers))
                headers = dict(json.loads(headers_str))
                logger.debug('raw headers %s' % request.headers)
                for k, v in headers.iteritems():
                    # 要全部使用 text_type,否则会出现有的为 str,有的为 unicode
                    # 导致422错误
                    request.headers[text_type(k)] = text_type(v)

                logger.debug('decrypted headers %s' % request.headers)

            if request.body and len(request.body) > 0:
                logger.debug('解密 body')
                logger.debug(request.body)
                request.body = aes_cipher.decrypt(utf8(request.body))

                # 解密完之后不需要重新计算 Content-Length,
                # 因为请求后端 API 时不带 Content-Length

        try:
            decrypt_data()
        except Exception as e:
            logger.error('解密数据出错')
            logger.error(e)
            logger.error(traceback.format_exc())
            raise AuthRequestException('AES decrypt error')
Exemple #7
0
    def get_client_config(self):
        config_data = RedisHelper.get_client_config(self.access_key)
        if config_data is None:
            raise ClientBadConfigException('No Client Config')
        else:
            # 校验 config 数据是否正确
            v = Validator(self.config_schema, allow_unknown=True)
            if not v.validate(config_data):
                logger.error(v.errors)
                raise ClientBadConfigException('Bad Client Config')

        logger.debug(config_data)
        if not config_data.get('enable', True):
            raise AuthRequestException('Disabled Client')

        self.secret_key = config_data.get('secret_key')
        # 添加内置的 endpoint
        for t in settings.BUILTIN_ENDPOINTS:
            endpoint = t['config']
            k = '%s:%s' % (endpoint['name'], endpoint['version'])
            config_data['endpoints'][k] = endpoint

        self.config = config_data
Exemple #8
0
        endpoint = self.handler.client.request['endpoint']
        require_login = endpoint.get('require_login', False)
        if not require_login:
            return

        access_token = self.handler.get_query_argument('access_token', None)
        token_info = RedisHelper.get_access_token_info(access_token)

        if token_info is not None:
            if token_info['access_key'] != self.handler.client.access_key:
                raise AuthRequestException(
                    'This Access Token is Belongs to Another Client App')

            logger.debug('允许访问')
            try:
                json_str = json.dumps(token_info['user_info'],
                                      ensure_ascii=False)
                # 用户信息使用 json 存储,并编码为 base64
                self.handler.request.headers[
                    'X-Api-User-Json'] = base64.b64encode(
                        json_str.encode('utf8'))
            except Exception, e:
                logger.error('设置 X-Api-User-Json 失败')
                logger.error(e.message)
                logger.error(traceback.format_exc())
        else:
            logger.info('没有获取到用户信息,不允许访问')
            # 获取用户信息失败
            raise AuthRequestException('Expired or Invalid Access Token')