Commit d89605dd authored by 张宇's avatar 张宇

[A]passport auth

parent 98b7e35e
from __future__ import unicode_literals
from gm_types.error import ERROR as CODES
class GaiaRPCFaultException(Exception):
def __init__(self, error, message, data):
self.error = error
self.message = message
self.data = data
def __repr__(self):
return "Error %d, %s" % (self.error, self.message)
class UniError(GaiaRPCFaultException):
def __init__(self, message):
self.error = CODES.UNIVERSAL
self.message = message
self.data = None
class GaiaRPCSpecificExceptionBase(GaiaRPCFaultException):
error = None
default_message = None
def __init__(self, message=None, data=None):
error = self.error
if message is None:
message = self.default_message
super(GaiaRPCSpecificExceptionBase, self).__init__(error=error, message=message, data=data)
class RPCPermanentError(GaiaRPCSpecificExceptionBase):
error = 2
default_message = "Permanent Error"
class RPCTemporaryError(GaiaRPCSpecificExceptionBase):
error = 3
default_message = "Temporary Error"
class RPCValidationError(GaiaRPCSpecificExceptionBase):
error = 17
default_message = "Params/Result Validation Error"
class RPCLoginRequiredException(GaiaRPCSpecificExceptionBase):
error = 401
default_message = "Login Required"
class RPCPermissionDeniedException(GaiaRPCSpecificExceptionBase):
error = 1001
default_message = "Permission Denied"
class RPCTagRelationCycleException(RPCPermanentError):
error = CODES.TAG_RELATION_CYCLE
default_message = CODES.getDesc(error)
class RPCStaffRequiredException(GaiaRPCSpecificExceptionBase):
error = 1002
default_message = "Staff Required"
class RPCNotFoundException(GaiaRPCSpecificExceptionBase):
error = 1404
default_message = "Not Found"
class RPCIntegrityError(GaiaRPCSpecificExceptionBase):
error = 1601
default_message = "Integrity Error"
...@@ -278,7 +278,7 @@ MESSAGE_DB_NAME = 'default' ...@@ -278,7 +278,7 @@ MESSAGE_DB_NAME = 'default'
MESSAGE_SLAVE_DB_NAME = 'slave' MESSAGE_SLAVE_DB_NAME = 'slave'
# AUTHENTICATION_BACKENDS = (AUTH_WE_CHAT_BACKEND, AUTH_DJANGO_DEFAULT_BACKEND) # AUTHENTICATION_BACKENDS = (AUTH_WE_CHAT_BACKEND, AUTH_DJANGO_DEFAULT_BACKEND)
AUTH_USER_USING_GAIA_DB = True AUTH_USER_USING_GAIA_DB = False
# SESSION_ENGINE = 'django.contrib.sessions.backends.db' # SESSION_ENGINE = 'django.contrib.sessions.backends.db'
# SESSION_ENGINE = 'rpc_framework.auth' # SESSION_ENGINE = 'rpc_framework.auth'
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' # fuck gaia SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' # fuck gaia
\ No newline at end of file
# -*- coding: utf-8 -*-
...@@ -9,7 +9,7 @@ from gm_types.gaia import MESSAGE_ORDER_TYPE ...@@ -9,7 +9,7 @@ from gm_types.gaia import MESSAGE_ORDER_TYPE
from gm_types.msg import CONVERSATION_TYPE, CONVERSATION_ORDER from gm_types.msg import CONVERSATION_TYPE, CONVERSATION_ORDER
from adapter.old_system import bind_prefix from adapter.old_system import bind_prefix
from adapter.rpcd.exceptions import RPCPermanentError from rpc_framework.exceptions import RPCPermanentException
from api.models.message import ConversationUserStatus from api.models.message import ConversationUserStatus
from rpc import gaia_client from rpc import gaia_client
from rpc.gaia_client import get_doctor_basic_info, \ from rpc.gaia_client import get_doctor_basic_info, \
...@@ -24,8 +24,10 @@ from search.utils import search_conversation_from_es ...@@ -24,8 +24,10 @@ from search.utils import search_conversation_from_es
from services.unread.stat import UserUnread from services.unread.stat import UserUnread
@bind_prefix('message/conversation/list_v2') @rpc_view('message/conversation/list_v2')
def batch_get_conversations_v2(user_ids: List[int], # @interceptor_classes([CalleeParametersInterceptor])
def batch_get_conversations_v2(context: RPCViewContext,
user_ids: List[int],
offset: int, offset: int,
size: int, size: int,
order_by: MESSAGE_ORDER_TYPE=MESSAGE_ORDER_TYPE.READ_STATUS, order_by: MESSAGE_ORDER_TYPE=MESSAGE_ORDER_TYPE.READ_STATUS,
...@@ -49,9 +51,9 @@ def batch_get_conversations_v2(user_ids: List[int], ...@@ -49,9 +51,9 @@ def batch_get_conversations_v2(user_ids: List[int],
:return: Dict :return: Dict
""" """
if not isinstance(user_ids, list): if not isinstance(user_ids, list):
raise RPCPermanentError raise RPCPermanentException
if not isinstance(size, int): if not isinstance(size, int):
raise RPCPermanentError raise RPCPermanentException
if size <= 0 or size >= 50: if size <= 0 or size >= 50:
size = 50 size = 50
if with_fields is None or not isinstance(with_fields, list): if with_fields is None or not isinstance(with_fields, list):
...@@ -146,8 +148,10 @@ def batch_get_conversations_v2(user_ids: List[int], ...@@ -146,8 +148,10 @@ def batch_get_conversations_v2(user_ids: List[int],
} }
@bind_prefix('message/conversation/list_v3') @rpc_view('message/conversation/list_v3')
def message_conversation_list_v3(user_ids: List[int], # @interceptor_classes([CalleeParametersInterceptor])
def message_conversation_list_v3(context: RPCViewContext,
user_ids: List[int],
offset: int, offset: int,
size: int, size: int,
order_by: CONVERSATION_ORDER=CONVERSATION_ORDER.LAST_REPLY_TIME, order_by: CONVERSATION_ORDER=CONVERSATION_ORDER.LAST_REPLY_TIME,
...@@ -174,9 +178,9 @@ def message_conversation_list_v3(user_ids: List[int], ...@@ -174,9 +178,9 @@ def message_conversation_list_v3(user_ids: List[int],
:return: :return:
""" """
if not isinstance(user_ids, list): if not isinstance(user_ids, list):
raise RPCPermanentError raise RPCPermanentException
if not isinstance(size, int): if not isinstance(size, int):
raise RPCPermanentError raise RPCPermanentException
if size <= 0 or size >= 50: if size <= 0 or size >= 50:
size = 50 size = 50
es_filters = {'user_ids': user_ids} es_filters = {'user_ids': user_ids}
...@@ -262,4 +266,4 @@ def check_can_send_message(context: RPCViewContext, target_uid: str) -> Dict: ...@@ -262,4 +266,4 @@ def check_can_send_message(context: RPCViewContext, target_uid: str) -> Dict:
if is_kefu_user(target_user_id): if is_kefu_user(target_user_id):
return {'can_send': True} return {'can_send': True}
return {'can_send': False} return {'can_send': False}
\ No newline at end of file
from importlib import import_module
from django.conf import settings
from django.contrib.auth import SESSION_KEY, BACKEND_SESSION_KEY, HASH_SESSION_KEY, load_backend
from django.utils.translation import LANGUAGE_SESSION_KEY
from django.utils.crypto import constant_time_compare
from django.contrib.auth.models import AnonymousUser
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed
# copy from django.contrib.sessions.middleware.SessionMiddleware
_engine = import_module(settings.SESSION_ENGINE)
_SessionStore = _engine.SessionStore
# copy and modified from django.contrib.sessions.middleware.SessionMiddleware
def get_django_session(session_key):
django_session = _SessionStore(session_key)
return django_session
# copy and modified from django.contrib.auth.get_user
def get_user_from_django_session(django_session):
user = None
try:
user_id = django_session[SESSION_KEY]
backend_path = django_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 ('django.contrib.auth.middleware.SessionAuthenticationMiddleware'
in settings.MIDDLEWARE_CLASSES and hasattr(user, 'get_session_auth_hash')):
session_hash = django_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:
django_session.flush()
user = None
return user or AnonymousUser()
# copy and modified from django.contrib.auth.login
def login(django_session, user):
session_auth_hash = ''
assert user is not None
if hasattr(user, 'get_session_auth_hash'):
session_auth_hash = user.get_session_auth_hash()
if SESSION_KEY in django_session:
if django_session[SESSION_KEY] != user.pk or (
session_auth_hash and
django_session.get(HASH_SESSION_KEY) != session_auth_hash):
# To avoid reusing another user's session, create a new, empty
# session if the existing session corresponds to a different
# authenticated user.
django_session.flush()
else:
django_session.cycle_key()
django_session[SESSION_KEY] = user.pk
django_session[BACKEND_SESSION_KEY] = user.backend
django_session[HASH_SESSION_KEY] = session_auth_hash
django_session.save()
user_logged_in.send(sender=user.__class__, request=None, user=user)
# copy and modified from django.contrib.auth.logout
def logout(django_session, user):
if hasattr(user, 'is_authenticated') and not user.is_authenticated():
user = None
user_logged_out.send(sender=user.__class__, request=None, user=user)
# remember language choice saved to session
# for backwards compatibility django_language is also checked (remove in 1.8)
language = django_session.get(LANGUAGE_SESSION_KEY, django_session.get('django_language'))
django_session.flush()
if language is not None:
django_session[LANGUAGE_SESSION_KEY] = language
user_logged_out.send(sender=user.__class__, request=None, user=user)
import contextlib import contextlib
import threading import threading
from django.contrib.auth.models import AnonymousUser
from django.conf import settings from django.conf import settings
from cached_property import cached_property
from helios.rpc import create_default_invoker from helios.rpc import create_default_invoker
from adapter.log import info_logger, logging_exception
from adapter.rpcd.exceptions import RPCLoginRequiredException
from . import auth
class Session(object):
def __init__(self, session_key=None):
assert session_key is None or isinstance(session_key, str)
if session_key == '': # make empty session_key as empty session
session_key = None
django_session = auth.get_django_session(session_key)
django_user = auth.get_user_from_django_session(django_session)
self._django_session = django_session
self._django_user = django_user
def do_login(self, user):
auth.login(self._django_session, user)
self._django_user = user
self._django_session.save()
def do_logout(self):
auth.logout(self._django_session, self._django_user)
self._django_user = auth.AnonymousUser()
self._django_session.save()
@property
def session_key(self):
return self._django_session.session_key
@property
def has_login(self):
user = self.user
info_logger.info(user)
res = user.is_authenticated()
return res
def login_required(self):
if not self.has_login:
raise RPCLoginRequiredException
def set_wechat_unionid(self, unionid):
if unionid:
sk = "wx_unionid"
self._django_session[sk] = unionid
self._django_session.save()
def get_wechat_unionid(self):
sk = "wx_unionid"
result = self._django_session.get(sk, None)
return result
def set_wechat_openid(self, wechat_appid, openid):
if wechat_appid and openid:
sk = "wx_openid_for_app_{}".format(wechat_appid)
self._django_session[sk] = openid
self._django_session.save()
def get_wechat_openid(self, wechat_appid):
result = None
if wechat_appid:
sk = "wx_openid_for_app_{}".format(wechat_appid)
result = self._django_session.get(sk, None)
return result
@property
def user_id(self):
return self.user.id
@property
def user(self):
user = self._django_user
return user if user.is_active else AnonymousUser()
@property
def groups(self):
return self.user.belong_groups.values_list('name', flat=True)
_base_invoker = create_default_invoker( _base_invoker = create_default_invoker(
debug=settings.DEBUG debug=settings.DEBUG
).with_config( ).with_config(
dump_curl=True dump_curl=True
) )
class NoCurrentContextError(Exception):
pass
def get_current_context_or_throw_exception():
context = ContextManager.get_active_context()
if context:
return context
raise NoCurrentContextError
def _do_get_rpc_remote_invoker():
context = ContextManager.get_active_context()
if context:
return context.rpc_remote
else:
return _base_invoker
def get_rpc_remote_invoker():
return _do_get_rpc_remote_invoker()
def get_gaia_local_invoker():
# TODO: gaia_loal_invoker
context = ContextManager.get_active_context()
# if context:
return context.gaia_local
# else:
# from .nested_invoker import NestedInvoker
# return NestedInvoker(ctx=context)
class Context(object):
has_session = None
logger = None
def __init__(self, session_key, request=None):
self.__session_key = session_key
self.has_session = bool(session_key)
self._request = request
@cached_property
def session(self):
return Session(session_key=self.__session_key)
# @cached_property
# def gaia_local(self):
# from .nested_invoker import NestedInvoker
# return NestedInvoker(self)
@property
def rpc(self):
try:
raise Exception(u'should not use Context.rpc, use Context.gaia_local or Context.rpc_remote')
except Exception:
if settings.DEBUG:
raise
else:
logging_exception()
return self.gaia_local
@cached_property
def rpc_remote(self):
if self._request:
client_info = self._request.client_info
else:
client_info = None
return _base_invoker.with_config(
session_key=self.__session_key,
client_info=client_info,
)
class ContextManager(object): class ContextManager(object):
_active_context_local = threading.local() _active_context_local = threading.local()
...@@ -191,53 +35,11 @@ class ContextManager(object): ...@@ -191,53 +35,11 @@ class ContextManager(object):
return getattr(cls._active_context_local, 'context', None) return getattr(cls._active_context_local, 'context', None)
class ConnectionInfo(object): def get_rpc_remote_invoker():
request = None context = ContextManager.get_active_context()
client_ip = None if context:
return _base_invoker.with_config(
def __init__(self, request, client_ip=None): session_key=context._context.session_id,
self.request = request client_info=context.request.client_info if context.request else None,
)
# Xing Ye tells me that there are these settings on proxy: return _base_invoker
# proxy_set_header X-Real-IP $remote_addr; \ No newline at end of file
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
if not client_ip:
try:
client_ip = request.META.get('HTTP_X_FORWARDED_FOR').split(',')[0]
except Exception:
pass
self.client_ip = client_ip
class Request(object):
method = None
params = None
session_key = None
environment = None
is_nested_call = None
context = None
method_info = None
request_info = None
def __init__(self, method, params, session_key, environment, is_nested_call=False):
self.method = method
self.params = params
self.session_key = session_key
self.environment = environment
self.is_nested_call = is_nested_call
self.context = Context(session_key=session_key, request=self)
@property
def client_info(self):
# return (self.environment or {}).get(u'client_info')
request_info = self.request_info
if request_info:
return request_info.get_client_info()
return None
def create_fake_context():
return Context(session_key=None)
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from typing import List, Dict, Optional, Set, Tuple from typing import List, Dict, Optional, Set, Tuple
from rpc import rpc_invoke_error_handle_decorator from . import rpc_invoke_error_handle_decorator
from rpc.context import get_rpc_remote_invoker from .context import get_rpc_remote_invoker
@rpc_invoke_error_handle_decorator @rpc_invoke_error_handle_decorator
...@@ -78,6 +78,4 @@ def is_kefu_user(user_id): ...@@ -78,6 +78,4 @@ def is_kefu_user(user_id):
try: try:
get_rpc_remote_invoker()['api/is_kefu_user'](user_id=user_id).unwrap() get_rpc_remote_invoker()['api/is_kefu_user'](user_id=user_id).unwrap()
except: except:
return False return False
\ No newline at end of file
# -*- coding: utf-8 -*-
from typing import Optional, Dict
from gm_types.passport import ERROR
from helios import RPCFaultException
from rpc_framework import exceptions
from .context import get_rpc_remote_invoker
def get_user_info_by_session(session_id: str) -> Optional[Dict]:
try:
return get_rpc_remote_invoker()(session_key=session_id)['passport/info/by_session']().unwrap()
except RPCFaultException as e:
if e.error == ERROR.ILLEGAL_SESSION:
raise exceptions.RPCLoginRequiredException
except Exception as e:
return {}
\ No newline at end of file
...@@ -6,11 +6,12 @@ import django ...@@ -6,11 +6,12 @@ import django
from django.conf import settings from django.conf import settings
from django.contrib.auth import BACKEND_SESSION_KEY, load_backend, \ from django.contrib.auth import BACKEND_SESSION_KEY, load_backend, \
HASH_SESSION_KEY, SESSION_KEY HASH_SESSION_KEY, SESSION_KEY
from django.db import models
from django.utils.crypto import constant_time_compare from django.utils.crypto import constant_time_compare
from rpc.passport_client import get_user_info_by_session
from rpc_framework import exceptions
from rpc_framework.context import RPCViewContext from rpc_framework.context import RPCViewContext
from rpc_framework.models import GaiaUser from rpc_framework.models import GaiaUser, dict_to_user_obj
from django.contrib.sessions.models import Session as DjangoSession from django.contrib.sessions.models import Session as DjangoSession
from django.contrib.sessions.backends.db import SessionStore as DjangoSessionStore from django.contrib.sessions.backends.db import SessionStore as DjangoSessionStore
...@@ -74,4 +75,13 @@ def get_user_using_gaia_db(context: RPCViewContext): ...@@ -74,4 +75,13 @@ def get_user_using_gaia_db(context: RPCViewContext):
def get_user_using_passport_service(context): def get_user_using_passport_service(context):
pass session_id = context._context.session_id
if not session_id:
raise exceptions.RPCLoginRequiredException
user_dict = get_user_info_by_session(session_id)
user = dict_to_user_obj(user_dict)
return user
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from gm_types.utils.enum import Enum, unique from gm_types.utils.enum import Enum
class Error(int, Enum): class Error(int, Enum):
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod from abc import ABCMeta
from typing import Sequence, Optional, Union, Dict, List from typing import Optional, Union, Dict, List
from gm_rpcd.internals.exceptions import RPCDFaultException from gm_types.error import ERROR
from pydantic import ValidationError, create_model from pydantic import ValidationError, create_model
from pydantic.error_wrappers import ErrorList, ErrorWrapper from pydantic.error_wrappers import ErrorWrapper
from extension.types import Error from rpc_framework.errors import Error
def _check_methods(C, *methods): def _check_methods(C, *methods):
...@@ -54,9 +54,53 @@ class AuthenticationFailed(RPCViewBaseException): ...@@ -54,9 +54,53 @@ class AuthenticationFailed(RPCViewBaseException):
code = Error.AUTH_FAILED code = Error.AUTH_FAILED
message = Error.getDesc(Error.AUTH_FAILED) message = Error.getDesc(Error.AUTH_FAILED)
# all above are copy from gaia
class NotFoundException(RPCViewBaseException): class NotFoundException(RPCViewBaseException):
code = 1404 # gaia RPCNotFoundException code = 1404
message = "Not Found"
class RPCPermanentException(RPCViewBaseException):
code = 2
message = "Permanent Error"
class RPCTemporaryError(RPCViewBaseException):
code = 3
message = "Temporary Error"
class RPCValidationError(RPCViewBaseException):
code = 17
message = "Params/Result Validation Error"
class RPCLoginRequiredException(RPCViewBaseException):
code = 401
message = "Login Required"
class RPCPermissionDeniedException(RPCViewBaseException):
code = 1001
message = "Permission Denied"
class RPCTagRelationCycleException(RPCPermanentException):
code = ERROR.TAG_RELATION_CYCLE
message = ERROR.getDesc(code)
class RPCStaffRequiredException(RPCViewBaseException):
code = 1002
message = "Staff Required"
class RPCNotFoundException(RPCViewBaseException):
code = 1404
message = "Not Found" message = "Not Found"
# default_message = "Not Found"
class RPCIntegrityError(RPCViewBaseException):
code = 1601
message = "Integrity Error"
\ No newline at end of file
...@@ -193,4 +193,4 @@ class SessionUserInterceptor(Interceptor): ...@@ -193,4 +193,4 @@ class SessionUserInterceptor(Interceptor):
context.user = user context.user = user
if context.user: if context.user:
return True return True
raise exceptions.AuthenticationFailed() raise exceptions.RPCLoginRequiredException()
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from typing import Dict
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models from django.db import models
...@@ -7,6 +9,18 @@ class BaseUser(User): ...@@ -7,6 +9,18 @@ class BaseUser(User):
class Meta(User.Meta): class Meta(User.Meta):
db_table = 'auth_user' db_table = 'auth_user'
class GaiaUser(User): class GaiaUser(User):
# objects = models.Manager().db_manager(using='gaia') # objects = models.Manager().db_manager(using='gaia')
pass pass
\ No newline at end of file
def dict_to_user_obj(user_dict: Dict) -> BaseUser:
if user_dict and user_dict.get('user_id'):
user_dict['id'] = user_dict.pop('user_id')
model_fields = {field.name for field in BaseUser._meta.fields}
user = BaseUser()
for k in set(user_dict.keys()) & model_fields:
setattr(user, k, user_dict[k])
return user
\ No newline at end of file
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