async def leave_group_not_owner(self, leave_member, leave_member_by, group): # leave group service for group not created in this server for user leave_member, called by leave_member_by logger.info('leave_group_not_owner') owner_workspace_domain = get_owner_workspace_domain() tmp_list_client = json.loads(group.group_clients) # PROCESS IN THIS SERVER # case new member is same server (not owner group) if leave_member.workspace_domain == owner_workspace_domain: # remove group client add more group client key group_client_key = GroupClientKey().get(group.id, leave_member.id) if group_client_key: group_client_key.delete() # remove group with owner group group.delete() # update all group with owner group lst_group = self.model.get_by_group_owner(group.owner_group_id) for group in lst_group: group.group_clients = group.group_clients group.total_member = len(tmp_list_client) group.update() # push notification for member active group_ids = (group.id for group in lst_group) list_clients = GroupClientKey().get_clients_in_groups(group_ids) push_service = NotifyPushService() for client in list_clients: data = { 'client_id': client.GroupClientKey.client_id, 'client_workspace_domain': owner_workspace_domain, 'group_id': str(group.id), 'leave_member': leave_member.id, 'leave_member_display_name': leave_member.display_name, 'leave_member_workspace_domain': leave_member.workspace_domain, 'leave_member_by': leave_member_by.id, 'leave_member_by_display_name': leave_member_by.display_name, 'leave_member_by_workspace_domain': leave_member_by.workspace_domain } logger.info(data) # TODO: maybe handling push to owner await push_service.push_text_to_client( to_client_id=client.GroupClientKey.client_id, title="Member leave", body="user leave group", from_client_id=leave_member_by.id, notify_type="member_leave", data=json.dumps(data) ) # END PROCESS IN THIS SERVER # CALL LEAVE MEMBER TO OWNER SERVER leave_group_request = group_pb2.LeaveGroupRequest( leave_member=leave_member, leave_member_by=leave_member_by, group_id=group.owner_group_id, ) ClientGroup(group.owner_workspace_domain).leave_group(leave_group_request) # for compatible with old code, should be remove in future? return group_pb2.BaseResponse()
def group_register_client_key(self, client_id, request): # register a group key for client_id client_group_key = GroupClientKey().set_key( request.groupId, client_id, None, None, request.deviceId, request.clientKeyDistribution, request.senderKeyId, request.senderKey, request.publicKey, request.privateKey) new_group_key = client_group_key.add() if new_group_key is None: raise Exception(Message.REGISTER_CLIENT_GROUP_FAILED_AVAILABLE)
async def workspace_leave_group(self, leave_member, leave_member_by, group): # workspace called leave group, with additional info about leave_member, leave_member_by, group info logger.info('workspace_leave_group') owner_workspace_domain = get_owner_workspace_domain() tmp_list_client = json.loads(group.group_clients) owner_group_id = None group_this_workspace = self.get_group_info(group.group_id) if group_this_workspace: owner_group_id = group_this_workspace.owner_group_id # update all group with owner group if owner_group_id: lst_group = self.model.get_by_group_owner(owner_group_id) for gr in lst_group: gr.group_clients = group.group_clients gr.total_member = len(tmp_list_client) gr.update() # push notification for member active group_ids = (gr.id for gr in lst_group) list_clients = GroupClientKey().get_clients_in_groups(group_ids) push_service = NotifyPushService() for client in list_clients: data = { 'client_id': client.GroupClientKey.client_id, 'client_workspace_domain': owner_workspace_domain, 'group_id': str(client.GroupClientKey.group_id), 'leave_member': leave_member.id, 'leave_member_display_name': leave_member.display_name, 'leave_member_workspace_domain': leave_member.workspace_domain, 'leave_member_by': leave_member_by.id, 'leave_member_by_display_name': leave_member_by.display_name, 'leave_member_by_workspace_domain': leave_member_by.workspace_domain } logger.info(data) # TODO: maybe handling push to owwner await push_service.push_text_to_client( to_client_id=client.GroupClientKey.client_id, title="Member leave", body="user leave group", from_client_id=leave_member_by.id, notify_type="member_leave", data=json.dumps(data) ) if leave_member.workspace_domain == owner_workspace_domain and leave_member.id == client.GroupClientKey.client_id: client.GroupClientKey.delete() #remove group leave_member_group = self.get_group_info(client.GroupClientKey.group_id) if leave_member_group: leave_member_group.delete() # for compatible with old code, should be remove in future? return group_pb2.BaseResponse()
def update_call(self, update_type, group_id, from_client_id): # update a call from_client_id to group_id and notify all user in this group lst_client_in_groups = GroupClientKey().get_clients_in_group(group_id) notify_inapp_service = NotifyInAppService() for client in lst_client_in_groups: if client.User.id != from_client_id: notify_inapp_service.notify_client_update_call( update_type, client.User.id, from_client_id, group_id) return True
def add_group_workspace(self, group_name, group_type, from_client_id, client_id, lst_client, owner_group_id, owner_workspace_domain): # workspace call for create a mapping group with reference info (owner_group_id, owner_workspace_domain) to origin group self.model = GroupChat( group_name=group_name, group_type=group_type, group_clients=lst_client, owner_group_id=owner_group_id, owner_workspace_domain=owner_workspace_domain, created_by=from_client_id, updated_at=datetime.datetime.now() ) new_group = self.model.add() # add to signal group key client_group_key = GroupClientKey().set_key(new_group.id, client_id) client_group_key.add() client_name = "" user_info = User().get(client_id) if user_info: client_name = user_info.display_name list_client_in_group = json.loads(lst_client) created_by_user = None for obj in list_client_in_group: if obj['id'] == from_client_id: created_by_user = obj # notify to client if group_type == "peer": self.notify_service.notify_invite_peer(client_id, from_client_id, new_group.id, owner_workspace_domain, created_by_user['display_name']) else: self.notify_service.notify_invite_group(client_id, from_client_id, new_group.id, owner_workspace_domain, created_by_user['display_name']) client_workspace_domain = get_owner_workspace_domain() return group_pb2.CreateGroupWorkspaceResponse( group_id=new_group.id, client_id=client_id, client_name=client_name, client_workspace_domain=client_workspace_domain )
def group_bulk_update_client_key(self, client_id, list_group_client_key): # update client_id's multi group keys is_updated = GroupClientKey().update_bulk_client_key( client_id, list_group_client_key) if not is_updated: raise Exception(Message.UPDATE_CLIENT_KEY_GROUPS_FAILED)
def __init__(self): super().__init__(None) self.group_client_key_model = GroupClientKey() self.peer_model = PeerClientKey() self.group_chat_model = GroupChat()
class SignalService(BaseService): """ SignalService, handling register/update/get/delete client peer/group key """ def __init__(self): super().__init__(None) self.group_client_key_model = GroupClientKey() self.peer_model = PeerClientKey() self.group_chat_model = GroupChat() def peer_register_client_key(self, client_id, request): # register peer key for new client_id, with fully information of a peer key try: client_peer_key = PeerClientKey().set_key( client_id, request.registrationId, request.deviceId, request.identityKeyPublic, request.preKeyId, request.preKey, request.signedPreKeyId, request.signedPreKey, request.signedPreKeySignature, request.identityKeyEncrypted) key_added = client_peer_key.add() # Check chatting available and push notify inapp for refreshing key, this should be unneeded as client_id now must register peer key when create account self.client_update_key_notify(client_id) except Exception as e: logger.error(e) raise Exception(Message.REGISTER_CLIENT_SIGNAL_KEY_FAILED) def client_update_peer_key(self, client_id, request): # update peer key for client_id, with fully information of a new peer key client = self.peer_model.get_by_client_id(client_id) if client is None: raise Exception(Message.UPDATE_CLIENT_SIGNAL_KEY_FAILED) client.set_key(client_id, request.registrationId, request.deviceId, request.identityKeyPublic, request.preKeyId, request.preKey, request.signedPreKeyId, request.signedPreKey, request.signedPreKeySignature, request.identityKeyEncrypted) client.update() # Check chatting available and push notify inapp for refreshing key self.client_update_key_notify(client_id) def client_update_identity_key(self, client_id, identity_key_encrypted): # update identity_key_encrypted of peer key for client_id, useful when changing password - which should make identity_key_encrypted changing too # return old identity_key_encrypted for get back in case needed client = self.peer_model.get_by_client_id(client_id) if client is None: raise Exception(Message.UPDATE_CLIENT_SIGNAL_KEY_FAILED) old_identity_key_encrypted = client.identity_key_encrypted client.identity_key_encrypted = identity_key_encrypted client.update() return old_identity_key_encrypted def peer_get_client_key(self, client_id): # get peer key of client_id return self.peer_model.get_by_client_id(client_id) def group_register_client_key(self, client_id, request): # register a group key for client_id client_group_key = GroupClientKey().set_key( request.groupId, client_id, None, None, request.deviceId, request.clientKeyDistribution, request.senderKeyId, request.senderKey, request.publicKey, request.privateKey) new_group_key = client_group_key.add() if new_group_key is None: raise Exception(Message.REGISTER_CLIENT_GROUP_FAILED_AVAILABLE) def group_bulk_update_client_key(self, client_id, list_group_client_key): # update client_id's multi group keys is_updated = GroupClientKey().update_bulk_client_key( client_id, list_group_client_key) if not is_updated: raise Exception(Message.UPDATE_CLIENT_KEY_GROUPS_FAILED) def group_get_client_key(self, group_id, client_id): # get client_id's group key in group_id client_key = self.group_client_key_model.get(group_id, client_id) if client_key: return client_key return None def group_by_owner_get_client_key(self, group_id, client_id): # get client_id's group key in owner_group with group_id client_key = self.group_chat_model.get_client_key_by_owner_group( group_id, client_id) if client_key: return client_key return None def group_get_all_client_key(self, group_id): # get all client's group keys in group_id return self.group_client_key_model.get_all_in_group(group_id) def client_update_key_notify(self, client_id): # list all client in peer chat with client_id about client_id's updating key event try: lst_group_peer = self.group_chat_model.get_joined_group_type( client_id, "peer") notify_inapp_service = NotifyInAppService() for group_peer in lst_group_peer: if group_peer.group_clients: lst_client_id = ast.literal_eval(group_peer.group_clients) for client_peer_id in lst_client_id: if client_peer_id != client_id: notify_inapp_service.notify_client_update_peer_key( client_peer_id, client_id, group_peer.id) except Exception as e: logger.error(e) def delete_client_peer_key(self, client_id): # delete a client peer key client_peer_key = self.peer_model.get_by_client_id(client_id) client_peer_key.delete() return True
async def workspace_add_member(self, added_member_info, adding_member_info, owner_group_info): # workspace adding member to group, with additional info about adding member, added member and original group logger.info('workspace_add_member') owner_workspace_domain = get_owner_workspace_domain() tmp_list_client = json.loads(owner_group_info.group_clients) is_member_workspace = False ref_group_id = None # create for new member if added_member_info.workspace_domain == owner_workspace_domain: is_member_workspace = True # add group with owner group self.model = GroupChat( owner_group_id=owner_group_info.group_id, owner_workspace_domain=owner_group_info.group_workspace_domain, group_name=owner_group_info.group_name, group_type=owner_group_info.group_type, group_clients=owner_group_info.group_clients, group_rtc_token="", total_member=len(tmp_list_client), created_by=owner_group_info.created_by, ) new_group = self.model.add() ref_group_id = new_group.id # add more group client key group_client_key = GroupClientKey().set_key( new_group.id, added_member_info.id) group_client_key.add() # update all group with owner group lst_group = self.model.get_by_group_owner(owner_group_info.group_id) for group in lst_group: group.group_clients = owner_group_info.group_clients group.total_member = len(tmp_list_client) group.update() # push notification for member active group_ids = (group.id for group in lst_group) list_clients = GroupClientKey().get_clients_in_groups(group_ids) push_service = NotifyPushService() for client in list_clients: data = { 'client_id': client.GroupClientKey.client_id, 'client_workspace_domain': owner_workspace_domain, 'group_id': str(client.GroupClientKey.group_id), 'added_member_id': added_member_info.id, 'added_member_display_name': added_member_info.display_name, 'added_member_workspace_domain': added_member_info.workspace_domain, 'adding_member_id': adding_member_info.id, 'adding_member_display_name': adding_member_info.display_name, 'adding_member_workspace_domain': adding_member_info.workspace_domain } logger.info(data) # TODO: maybe handling push to owwner await push_service.push_text_to_client( to_client_id=client.GroupClientKey.client_id, title="Member Add", body="A user has been added to the group", from_client_id=adding_member_info.id, notify_type="new_member", data=json.dumps(data) ) return group_pb2.AddMemberWorkspaceResponse( is_member_workspace=is_member_workspace, ref_group_id=ref_group_id )
async def add_member_to_group_owner(self, added_member_info, adding_member_info, group): # adding member to group created in this server, with additional info about adding member and added member logger.info('add_member_to_group_owner') owner_workspace_domain = get_owner_workspace_domain() # add more group client key for group owner client_workspace_domain = None if added_member_info.workspace_domain != owner_workspace_domain: client_workspace_domain = added_member_info.workspace_domain group_client_key = GroupClientKey().set_key( group.id, added_member_info.id, client_workspace_domain, added_member_info.ref_group_id).add() # push notification for other member in server lst_client_in_group = self.get_clients_in_group(group.id) push_service = NotifyPushService() for client in lst_client_in_group: if client.GroupClientKey.client_workspace_domain is None or client.GroupClientKey.client_workspace_domain == owner_workspace_domain: if client.GroupClientKey.client_id != adding_member_info.id: data = { 'client_id': client.GroupClientKey.client_id, 'client_workspace_domain': owner_workspace_domain, 'group_id': str(group.id), 'added_member_id': added_member_info.id, 'added_member_display_name': added_member_info.display_name, 'added_member_workspace_domain': owner_workspace_domain, 'adding_member_id': adding_member_info.id, 'adding_member_display_name': adding_member_info.display_name, 'adding_member_workspace_domain': owner_workspace_domain } logger.info(data) # TODO: maybe handling push to owwner await push_service.push_text_to_client( to_client_id=client.GroupClientKey.client_id, title="Member Add", body="A user has been added to the group", from_client_id=adding_member_info.id, notify_type="new_member", data=json.dumps(data) ) # request add member to other server informed_workspace_domain = [] for client in lst_client_in_group: if client.GroupClientKey.client_workspace_domain and client.GroupClientKey.client_workspace_domain != owner_workspace_domain \ and client.GroupClientKey.client_workspace_domain != adding_member_info.workspace_domain: # prevent loop call if client.GroupClientKey.client_workspace_domain in informed_workspace_domain: continue informed_workspace_domain.append(client.GroupClientKey.client_workspace_domain) owner_group_req = group_pb2.GroupInfo( group_id=group.id, group_name=group.group_name, group_type=group.group_type, group_clients=group.group_clients, group_workspace_domain=owner_workspace_domain, created_by=group.created_by, ) request = group_pb2.AddMemberWorkspaceRequest( added_member_info=added_member_info, adding_member_info=adding_member_info, owner_group=owner_group_req ) logger.info( "call add member to workspace domain {}".format(client.GroupClientKey.client_workspace_domain)) response = ClientGroup(client.GroupClientKey.client_workspace_domain).workspace_add_member(request) if response.is_member_workspace: logger.info("update ref_group to main server {}".format(response.ref_group_id)) group_client_key.client_workspace_group_id = response.ref_group_id group_client_key.update() # for compatible with old code, should be remove in future? return group_pb2.BaseResponse()
async def add_member_to_group_not_owner(self, added_member_info, adding_member_info, group): # adding member to group not created in this server, with additional info about adding member and added member logger.info('add_member_to_group_not_owner') owner_workspace_domain = get_owner_workspace_domain() tmp_list_client = json.loads(group.group_clients) # PROCESS IN THIS SERVER # case new member is same server (not owner group) if added_member_info.workspace_domain == owner_workspace_domain: # add group with owner group self.model = GroupChat( owner_group_id=group.owner_group_id, owner_workspace_domain=group.owner_workspace_domain, group_name=group.group_name, group_type=group.group_type, group_clients=group.group_clients, group_rtc_token="", total_member=len(tmp_list_client), created_by=group.created_by, ) new_group = self.model.add() added_member_info.ref_group_id = new_group.id # add more group client key group_client_key = GroupClientKey().set_key( new_group.id, added_member_info.id) group_client_key.add() # update all group with owner group lst_group = self.model.get_by_group_owner(group.owner_group_id) for group in lst_group: group.group_clients = group.group_clients group.total_member = len(tmp_list_client) group.update() # push notification for member active group_ids = (group.id for group in lst_group) list_clients = GroupClientKey().get_clients_in_groups(group_ids) push_service = NotifyPushService() for client in list_clients: data = { 'client_id': client.GroupClientKey.client_id, 'client_workspace_domain': owner_workspace_domain, 'group_id': str(client.GroupClientKey.group_id), 'added_member_id': added_member_info.id, 'added_member_display_name': added_member_info.display_name, 'added_member_workspace_domain': added_member_info.workspace_domain, 'adding_member_id': adding_member_info.id, 'adding_member_display_name': adding_member_info.display_name, 'adding_member_workspace_domain': adding_member_info.workspace_domain } logger.info(data) # TODO: maybe handling push to owwner await push_service.push_text_to_client( to_client_id=client.GroupClientKey.client_id, title="Member Add", body="A user has been added to the group", from_client_id=adding_member_info.id, notify_type="new_member", data=json.dumps(data) ) # END PROCESS IN THIS SERVER # CALL ADD MEMBER TO OWNER SERVER add_member_request = group_pb2.AddMemberRequest( added_member_info=added_member_info, adding_member_info=adding_member_info, group_id=group.owner_group_id, ) ClientGroup(group.owner_workspace_domain).add_member(add_member_request) # for compatible with old code, should be remove in future? return group_pb2.BaseResponse()
def get_clients_in_group_owner(self, group_owner_id): # get all client in groups lst_group = self.model.get_by_group_owner(group_owner_id) group_ids = (group.id for group in lst_group) return GroupClientKey().get_clients_in_groups(group_ids)
def get_clients_in_group(self, group_id): # get all client in group return GroupClientKey().get_clients_in_group(group_id)
def add_group(self, group_name, group_type, lst_client, created_by): # service for create new group with following info: # group_name: name of group in request, using for searching group # group_type: type of group in request, must be one of this values: [peer/group] # lst_client: list of client in this group # create_by: user_id of creator tmp_list_client = [] created_by_user = None for obj in lst_client: if obj.id == created_by: created_by_user = obj tmp_list_client.append( {"id": obj.id, "display_name": obj.display_name, "workspace_domain": obj.workspace_domain, "status": "active"}) self.model = GroupChat( group_name=group_name, group_type=group_type, group_clients=json.dumps(tmp_list_client), group_rtc_token=secrets.token_hex(10), total_member=len(tmp_list_client), created_by=created_by, updated_at=datetime.datetime.now() ) new_group = self.model.add() res_obj = group_pb2.GroupObjectResponse( group_id=new_group.id, group_name=new_group.group_name, group_type=new_group.group_type, group_avatar=new_group.group_avatar, created_by_client_id=new_group.created_by, created_at=int(new_group.created_at.timestamp() * 1000), updated_by_client_id=new_group.updated_by, group_rtc_token=new_group.group_rtc_token ) if new_group.updated_at is not None: res_obj.updated_at = int(new_group.updated_at.timestamp() * 1000) owner_workspace_domain = get_owner_workspace_domain() for obj in lst_client: # list client in group client_in = group_pb2.ClientInGroupResponse( id=obj.id, display_name=obj.display_name, workspace_domain=obj.workspace_domain, status="active" ) res_obj.lst_client.append(client_in) # add to signal group key if obj.workspace_domain == owner_workspace_domain: client_group_key = GroupClientKey().set_key(new_group.id, obj.id) client_group_key.add() # notify per client if group_type == "peer": self.notify_service.notify_invite_peer( obj.id, created_by, new_group.id, created_by_user.workspace_domain, created_by_user.display_name ) else: self.notify_service.notify_invite_group( obj.id, created_by, new_group.id, created_by_user.workspace_domain, created_by_user.display_name ) else: # need to call other workspace and create group key request = group_pb2.CreateGroupWorkspaceRequest( group_name=group_name, group_type=group_type, from_client_id=created_by, client_id=obj.id, lst_client=new_group.group_clients, owner_group_id=new_group.id, owner_workspace_domain=owner_workspace_domain ) group_res_object = \ ClientGroup(obj.workspace_domain).create_group_workspace( request ) client_group_key = GroupClientKey().set_key( new_group.id, obj.id, group_res_object.client_workspace_domain, group_res_object.group_id) client_group_key.add() return res_obj
def get_group(self, group_id, client_id): # get infor of client related to group stored in database by its group_id and client_id stored_client_key = GroupClientKey().get(group_id, client_id) group = self.model.get(group_id) if group is not None: obj = group.GroupChat res_obj = group_pb2.GroupObjectResponse( group_id=obj.id, group_name=obj.group_name, group_type=obj.group_type, group_avatar=obj.group_avatar, created_by_client_id=obj.created_by, created_at=int(obj.created_at.timestamp() * 1000), updated_by_client_id=obj.updated_by, group_rtc_token=obj.group_rtc_token ) if obj.updated_at is not None: res_obj.updated_at = int(obj.updated_at.timestamp() * 1000) # list client in group group_clients = json.loads(obj.group_clients) for client in group_clients: client_in = group_pb2.ClientInGroupResponse( id=client['id'], display_name=client['display_name'], workspace_domain=client['workspace_domain'], status=client['status'] ) res_obj.lst_client.append(client_in) if stored_client_key is not None: res_obj.client_key.workspace_domain = get_owner_workspace_domain() res_obj.client_key.clientId = stored_client_key.client_id if stored_client_key.device_id is not None: res_obj.client_key.deviceId = stored_client_key.device_id if stored_client_key.client_key is not None: res_obj.client_key.clientKeyDistribution = stored_client_key.client_key if stored_client_key.client_sender_key_id is not None: res_obj.client_key.senderKeyId = stored_client_key.client_sender_key_id if stored_client_key.client_sender_key is not None: res_obj.client_key.senderKey = stored_client_key.client_sender_key if stored_client_key.client_public_key is not None: res_obj.client_key.publicKey = stored_client_key.client_public_key if stored_client_key.client_private_key is not None: res_obj.client_key.privateKey = stored_client_key.client_private_key if obj.last_message_at: res_obj.last_message_at = int(obj.last_message_at.timestamp() * 1000) # get last message if group.Message: last_message = group.Message res_obj.last_message.id = last_message.id res_obj.last_message.group_id = last_message.group_id res_obj.last_message.from_client_id = last_message.from_client_id res_obj.last_message.message = last_message.message res_obj.last_message.created_at = int(last_message.created_at.timestamp() * 1000) if last_message.client_id: res_obj.last_message.client_id = last_message.client_id if last_message.updated_at: res_obj.last_message.updated_at = int(last_message.updated_at.timestamp() * 1000) if last_message.client_id: res_obj.last_message.group_type = "peer" else: res_obj.last_message.group_type = "group" return res_obj else: return None