Commit bad15ffe authored by 张宇's avatar 张宇

patch process_single_request

parent a46dbd94
# -*- coding: utf-8 -*-
import json
import logging
import os
import traceback
import uuid
from django.conf import settings
from gm_logging.internal.instance import request_logging_guard_maker
from gm_rpcd.internals.common import now_str
from gm_rpcd.internals.configuration.model import Config, environ, from_property
from gm_rpcd.internals.configuration.model import Config, environ, \
from_property
from gm_rpcd.internals.configuration.model_base import literal, xml_text, xml_text_list, \
DefaultConfigPropertyWrapper, EnvironmentConfigProperty
from gm_rpcd.internals.context import Context
from gm_rpcd.internals.dispatcher import json_encode_with_length
from gm_rpcd.internals.dynamic_scopes import dynamic_scope, Keys
from gm_rpcd.internals.protocol.request import Request, \
dump_request_to_json_value
from gm_rpcd.internals.protocol.response import FaultResponse, SuccessResponse, \
response_to_v1_json_value, SystemErrorResponse
from gm_rpcd.internals.protocol.response_extras import \
make_method_not_found_response
from gm_rpcd.internals.utils import strict_check_json_value
from gm_tracer.context import current_tracer
from rpc_framework.exceptions import RPCViewBaseException
DISPATCHER = None
......@@ -77,6 +95,112 @@ class DjangoRpcdConfig(Config):
return os.path.join(self.log_dir, 'gm_rpcd_request_error.log')
def process_single_request(self, request):
assert isinstance(request, Request)
tracer = current_tracer()
method_function = self._method_table.get_method_function(request.method)
if not method_function:
return make_method_not_found_response(request)
request_json_value = dump_request_to_json_value(request)
request_json_str = json.dumps(request_json_value, ensure_ascii=False)
now = now_str()
request_info = self._request_info_extractor(request)
log_id = request_info.log_id
span_id = getattr(request_info, 'span_id', '')
parent_span_id = getattr(request_info, 'parent_span_id', '')
gm_request_id = getattr(request_info, 'gm_request_id', '')
log_locating_info = 'log_id={} span_id={} parent_span_id={}'.format(
log_id, span_id, parent_span_id
)
with request_logging_guard_maker(request_info) as guard:
context = Context(
request=request,
request_info=request_info,
logger=guard.logger,
helios_base_invoker=self._helios_base_invoker,
)
with dynamic_scope.set(Keys.CONTEXT, context), dynamic_scope.set(Keys.REQUEST_INFO, request_info):
try:
result = method_function(**request.params)
strict_check_json_value(result)
response = SuccessResponse(
request_id=request.request_id,
result=result,
)
except RPCViewBaseException as e:
guard.set_errno(e.code)
response = FaultResponse(
request_id=request.request_id,
error={
# 最终相应为: {'error': '..','message': '..', 'data': ''}
'code': e.code,
'message': e.message,
}
)
except Exception as e:
guard.set_errno_unexpected()
if self._raven_client:
self._raven_client.captureException(
tags={
'log_id': log_id,
'gm_request_id': gm_request_id,
'span_id': span_id,
'parent_span_id': parent_span_id,
},
extra={
'log_id': log_id,
'span_id': span_id,
'parent_span_id': parent_span_id,
'request': request_json_value,
'gm_request_id': gm_request_id,
}
)
format_exc = traceback.format_exc()
log_message = json.dumps({
'timestamp': now,
'gm_request_id': gm_request_id,
'log_locating_info': log_locating_info,
'format_exc': format_exc,
'request_json_str': request_json_str,
}, ensure_ascii=False)
self._request_error_logger.error(log_message)
from gm_rpcd.internals.configuration.model import config
if config.is_develop_mode:
debug_message = '{}\nrequest = {}'.format(
format_exc,
request_json_str,
)
else:
debug_message = None
response = SystemErrorResponse(
message='Unexpected exception on server side. {}'.format(log_locating_info),
debug_message=debug_message,
)
if tracer:
# tracer.add_annotation({'error': time.time()})
tracer.add_binary_annotation({
'error': 'true',
'exc.info': format_exc,
'request_json_str': request_json_str,
})
self._request_logger.info(json.dumps({
'timestamp': now,
'gm_request_id': gm_request_id,
'log_locating_info': log_locating_info,
'request_json_value': json_encode_with_length(request_json_value),
'response_json_value': json_encode_with_length(response_to_v1_json_value(response)),
}, ensure_ascii=False))
return response
def setup_rpcd():
logger = logging.getLogger('django.start')
logger.info('****** setup rpcd ******')
......@@ -91,6 +215,9 @@ def setup_rpcd():
from gm_rpcd.internals import configuration
configuration.__dict__['config'] = config_wrapper
from gm_rpcd.internals.dispatcher import Dispatcher
Dispatcher.process_single_request = process_single_request
####################### end PATCH ##########################
from gm_rpcd.internals.initializations import initialize
......
......@@ -20,7 +20,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'd2#)2cd!efrj@to&45yn=+=82-b*&of3i*&fxgfq%v53h1&1qx'
SECRET_KEY = 'fl-lv3j^czg(dupo@hbs(+_+djby9zelwt&rh71@b1x*ptmj4#(^' # gaia dev
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
......@@ -34,10 +34,11 @@ INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
# 'django.contrib.sessions',
'django.contrib.sessions',
'django.contrib.messages',
# 'django.contrib.staticfiles',
'api'
'api',
'rpc_framework'
]
MIDDLEWARE = [
......@@ -275,3 +276,9 @@ ES_SEARCH_TIMEOUT = '10s'
DATABASE_ROUTERS = ['courier.db_routers.MessageRouter']
MESSAGE_DB_NAME = 'default'
MESSAGE_SLAVE_DB_NAME = 'slave'
# AUTHENTICATION_BACKENDS = (AUTH_WE_CHAT_BACKEND, AUTH_DJANGO_DEFAULT_BACKEND)
AUTH_USER_USING_GAIA_DB = True
# SESSION_ENGINE = 'django.contrib.sessions.backends.db'
# SESSION_ENGINE = 'rpc_framework.auth'
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' # fuck gaia
\ No newline at end of file
......@@ -4,3 +4,4 @@ from gm_types.utils.enum import Enum, unique
class Error(int, Enum):
PARAMS_INVALID = (10001, '非法参数')
AUTH_FAILED = (40300, '登录信息不正确或没有权限')
\ No newline at end of file
# -*- coding: utf-8 -*-
from typing import List, Dict, Optional
from django.conf import settings
from six import string_types
import dateutil
from django.db.models import Q
......@@ -11,9 +12,12 @@ from adapter.old_system import bind_prefix
from adapter.rpcd.exceptions import RPCPermanentError
from api.models.message import ConversationUserStatus
from rpc import gaia_client
from rpc.gaia_client import get_doctor_basic_info, get_single_doctor_basic_info
from rpc_framework import exceptions
from rpc_framework.context import RPCViewContext
from rpc_framework.decorators import rpc_view, interceptor_classes
from rpc_framework.interceptors import CalleeParametersInterceptor
from rpc_framework.interceptors import CalleeParametersInterceptor, \
SessionUserInterceptor
from search.utils import search_conversation_from_es
from services.unread.stat import UserUnread
......@@ -236,6 +240,29 @@ def message_conversation_list_v3(user_ids: List[int],
@rpc_view('message/conversation/can_send')
@interceptor_classes([CalleeParametersInterceptor])
@interceptor_classes([CalleeParametersInterceptor, SessionUserInterceptor])
def check_can_send_message(context: RPCViewContext, target_uid: str) -> Dict:
return {'a': 'b'}
user = context.user
# doctor_info = get_single_doctor_basic_info(user.id)
#
# target_user_id = doctor_info.get('user_id')
# print('target_user_id:', target_user_id)
# raise exceptions.NotFoundException
# if not target_user_id:
# raise 26872687
# target_doctor = get_doctor_by_user_id(target_user.id)
#
# if doctor or target_doctor:
# return {'can_send': True}
#
# user_follow = user.fans.filter(follow=target_user, bond=True).exists()
# target_follow = target_user.fans.filter(follow=user, bond=True).exists()
#
# if user_follow and target_follow:
# return {'can_send': True}
# if target_user.person.id.hex == settings.KEFU_PERSION_ID:
# return {'can_send': True}
return {'can_send': False}
......@@ -44,6 +44,12 @@ def get_doctor_basic_info(doctor_ids: List[str]) -> List[Dict]:
).unwrap()
def get_single_doctor_basic_info(doctor_id: str) -> Dict:
return get_rpc_remote_invoker()['api/doctor/basic_info'](
doctor_ids=[doctor_id]
).unwrap()[0]
def batch_get_doctor_info_by_user_ids(user_ids: List[int]) -> Dict:
return get_rpc_remote_invoker()['api/batch_get_doctor_info_by_user_ids'](
user_ids=user_ids,
......
# -*- coding: utf-8 -*-
from importlib import import_module
from typing import Type
import django
from django.conf import settings
from django.contrib.auth import BACKEND_SESSION_KEY, load_backend, \
HASH_SESSION_KEY, SESSION_KEY
from django.db import models
from django.utils.crypto import constant_time_compare
from rpc_framework.context import RPCViewContext
from rpc_framework.models import GaiaUser
from django.contrib.sessions.models import Session as DjangoSession
from django.contrib.sessions.backends.db import SessionStore as DjangoSessionStore
# class Session(DjangoSession):
# objects = models.Manager().db_manager(using='gaia')
# class SessionStore(DjangoSessionStore):
# @classmethod
# def get_model_class(cls):
# Avoids a circular import and allows importing SessionStore when
# django.contrib.sessions is not in INSTALLED_APPS.
# return Session
def get_user(context):
"""
Return the user model instance associated with the given request session.
If no user is retrieved, return an instance of `AnonymousUser`.
"""
if getattr(settings, 'AUTH_USER_USING_GAIA_DB', False):
return get_user_using_gaia_db(context)
else:
return get_user_using_passport_service(context)
def get_session_store_class() -> Type[django.contrib.sessions.backends.db.SessionStore]:
# django SessionMiddleware
engine = import_module(settings.SESSION_ENGINE)
return engine.SessionStore
def get_user_using_gaia_db(context: RPCViewContext):
from django.contrib.auth.models import AnonymousUser
user = None
try:
session_store_class = get_session_store_class()
session = session_store_class(context._context.session_id)
user_id = GaiaUser._meta.pk.to_python(session[SESSION_KEY])
backend_path = session[BACKEND_SESSION_KEY]
except KeyError:
pass
else:
if backend_path in settings.AUTHENTICATION_BACKENDS:
backend = load_backend(backend_path)
user = backend.get_user(user_id)
# Verify the session
if hasattr(user, 'get_session_auth_hash'):
session_hash = session.get(HASH_SESSION_KEY)
session_hash_verified = session_hash and constant_time_compare(
session_hash,
user.get_session_auth_hash()
)
if not session_hash_verified:
session.flush()
user = None
return user or AnonymousUser()
def get_user_using_passport_service(context):
pass
"""
Provides various authentication policies.
"""
from __future__ import unicode_literals
class BaseAuthentication(object):
"""
All authentication classes should extend BaseAuthentication.
"""
def authenticate(self, request):
"""
Authenticate the request and return a two-tuple of (user, token).
"""
raise NotImplementedError(".authenticate() must be overridden.")
def authenticate_header(self, request):
"""
Return a string to be used as the value of the `WWW-Authenticate`
header in a `401 Unauthenticated` response, or `None` if the
authentication scheme should return `403 Permission Denied` responses.
"""
pass
class SessionAuthentication(BaseAuthentication):
"""
Use Django's session framework for authentication.
"""
def authenticate(self, context):
"""
Returns a `User` if the request session currently has a logged in user.
Otherwise returns `None`.
"""
# Get the session-based user from the underlying HttpRequest object
user = getattr(context.context, 'user', None)
# Unauthenticated, CSRF validation not required
if not user or not user.is_active:
return None
# self.enforce_csrf(context)
# CSRF passed with authenticated user
return (user, None)
\ No newline at end of file
......@@ -82,7 +82,7 @@ class RPCViewContext(object):
instance, ensuring that it is available to any middleware in the stack.
"""
self._user = value
self._request.user = value
self._context.user = value
@property
def request(self) -> Optional[Request]:
......
# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod
from typing import Sequence
from typing import Sequence, Optional, Union, Dict, List
from gm_rpcd.internals.exceptions import RPCDFaultException
from pydantic import ValidationError, create_model
from pydantic.error_wrappers import ErrorList
from pydantic.error_wrappers import ErrorList, ErrorWrapper
from extension.types import Error
......@@ -22,29 +22,41 @@ def _check_methods(C, *methods):
return True
class RPCViewBaseException(metaclass=ABCMeta):
class RPCViewBaseException(Exception, metaclass=ABCMeta):
code: int
message: str
data: Optional[Union[Dict, List]]
__slots__ = ()
@classmethod
def __subclasshook__(cls, C):
if cls is RPCViewBaseException:
return _check_methods(C, "as_rpcd_fault")
return _check_methods(C, "as_rpc_view_exception")
return NotImplemented
@abstractmethod
def as_rpcd_fault(self):
return RPCDFaultException(error=self.error, message=self.message)
RequestErrorModel = create_model("Request")
class CalleeTypeHintValidationError(ValidationError):
def __init__(self, errors: Sequence[ErrorList]) -> None:
return super().__init__(errors, RequestErrorModel)
class CalleeTypeHintValidationError(RPCViewBaseException):
code = Error.PARAMS_INVALID
message = ''
@classmethod
def from_error_wrapper_list(cls, errors:List[ErrorWrapper]):
validation_error = ValidationError(errors, RequestErrorModel)
self = cls()
self.message = validation_error.json()
return self
class AuthenticationFailed(RPCViewBaseException):
code = Error.AUTH_FAILED
message = Error.getDesc(Error.AUTH_FAILED)
class NotFoundException(RPCViewBaseException):
code = 1404
message = "Not Found"
# default_message = "Not Found"
def as_rpcd_fault(self):
return RPCDFaultException(code=Error.PARAMS_INVALID, message=self.json())
......@@ -4,6 +4,7 @@ from abc import ABCMeta, abstractmethod
from copy import deepcopy
from typing import Type, Any, List, Optional, Dict, Tuple
from django.utils.functional import SimpleLazyObject
from pydantic import BaseModel, BaseConfig, MissingError
from pydantic.error_wrappers import ErrorWrapper
from pydantic.fields import ModelField, Required, FieldInfo, SHAPE_SINGLETON, \
......@@ -11,7 +12,7 @@ from pydantic.fields import ModelField, Required, FieldInfo, SHAPE_SINGLETON, \
from pydantic.schema import get_annotation_from_field_info
from pydantic.utils import lenient_issubclass
from rpc_framework import params
from rpc_framework import params, auth, exceptions
from rpc_framework.context import RPCViewContext
from rpc_framework.exceptions import CalleeTypeHintValidationError
from rpc_framework.inspector import get_typed_signature
......@@ -165,7 +166,6 @@ def check_parameters_fit_signature(
else:
values[field.name] = deepcopy(field.default)
continue
print(value, values)
v_, errors_ = field.validate(value, values, loc=("body", field.alias))
if isinstance(errors_, ErrorWrapper):
errors.append(errors_)
......@@ -182,5 +182,15 @@ class CalleeParametersInterceptor(Interceptor):
callee_field_list = get_callee_field_list(rpc_view.callee)
validated_values, errors = check_parameters_fit_signature(callee_field_list, context.request.params)
if errors:
raise CalleeTypeHintValidationError(errors)
raise CalleeTypeHintValidationError.from_error_wrapper_list(errors)
return True
class SessionUserInterceptor(Interceptor):
def intercept(self, context: RPCViewContext, rpc_view: RPCView) -> bool:
user = auth.get_user(context)
context.user = user
if context.user:
return True
raise exceptions.AuthenticationFailed()
# -*- coding: utf-8 -*-
from django.contrib.auth.models import User
from django.db import models
class BaseUser(User):
class Meta(User.Meta):
db_table = 'auth_user'
class GaiaUser(User):
# objects = models.Manager().db_manager(using='gaia')
pass
\ No newline at end of file
......@@ -14,10 +14,11 @@ def set_rollback():
def exception_handler(exc, wrapped_context) -> None:
if issubclass(exc.__class__, exceptions.RPCViewBaseException):
set_rollback()
raise exc.as_rpcd_fault() # type: RPCDFaultException
if isinstance(exc, exceptions.RPCViewBaseException):
raise exc
elif issubclass(exc.__class__, exceptions.RPCViewBaseException):
raise exc.as_rpc_view_exception()
return None
......@@ -76,7 +77,6 @@ class RPCView(RPCAbstractView):
result = handler(context, *args, **kwargs)
except Exception as exc:
print('error::', exc, type(exc))
result = self.handle_exception(exc)
self.result = self.finalize_response(context, result, *args, **kwargs)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment