def update_profile(self, user_id, display_name, phone_number, avatar, clear_phone_number): # update profile for user_id, with force clear phone number flag for clearing stored phone number in db try: profile = self.model.get(user_id) if display_name: profile.display_name = display_name if clear_phone_number: user_authen_setting = self.authen_setting.get(user_id) if user_authen_setting is None: user_authen_setting = AuthenSetting(id=user_id).add() user_authen_setting.enable_mfa = False # change phone_number automatically turn off enable_mfa user_authen_setting.update() profile.phone_number = "" elif phone_number: user_authen_setting = self.authen_setting.get(user_id) if user_authen_setting is None: user_authen_setting = AuthenSetting(id=user_id).add() user_authen_setting.enable_mfa = False # change phone_number automatically turn off enable_mfa user_authen_setting.update() profile.phone_number = phone_number if avatar: profile.avatar = avatar return profile.update() except Exception as e: logger.info(e) raise Exception(Message.UPDATE_PROFILE_FAILED)
def init_mfa_state_disabling(self, user_id): # diable mfa for user_id user_authen_setting = self.authen_setting.get(user_id) if user_authen_setting is None: user_authen_setting = AuthenSetting(id=user_id).add() if user_authen_setting.mfa_enable: user_authen_setting.mfa_enable = False user_authen_setting.update() success = True else: success = False next_step = '' return success, next_step
def get_mfa_state(self, user_id): # get mfa state of user_id user_info = self.model.get(user_id) if user_info is None: raise Exception(Message.AUTH_USER_NOT_FOUND) user_authen_setting = self.authen_setting.get(user_id) if user_authen_setting is None: user_authen_setting = AuthenSetting(id=user_id).add() return user_authen_setting.mfa_enable
def init_mfa_state_enabling(self, user_id): # start enable mfa for user_id # if otp in frozen state, raising exception user_authen_setting = self.authen_setting.get(user_id) if user_authen_setting is None: user_authen_setting = AuthenSetting(id=user_id).add() if user_authen_setting.mfa_enable: success = False next_step = '' else: if user_authen_setting.otp_frozen_time > datetime.datetime.now(): raise Exception(Message.FROZEN_STATE_OTP_SERVICE) next_step = 'mfa_validate_password' user_authen_setting.require_action = next_step user_authen_setting.update() success = True return success, next_step
def __init__(self): super().__init__() self.user_db = User() self.authen_setting = AuthenSetting()
class AuthService: """ Authen Service for authenticate action. This service involved in any granted authentication, include register new user, login, logout, forgot password, generate pre_access_token or start mfa service. """ def __init__(self): super().__init__() self.user_db = User() self.authen_setting = AuthenSetting() def token(self, user_name, password): # basic authen service for login with user_name and password try: token = KeyCloakUtils.token(user_name, password) if token: return token except Exception as e: logger.info(e) response_code = e.response_code check_error = json.loads(e.args[0]).get("error") if check_error == "invalid_grant" and response_code == 400: raise Exception(Message.USER_NOT_VERIFY_EMAIL) raise Exception(Message.AUTH_USER_NOT_FOUND) def logout(self, refresh_token): # basic authen service for logout using refresh_token try: KeyCloakUtils.logout(refresh_token) except Exception as e: logger.info(e) # raise Exception(Message.UNAUTHENTICATED) def remove_token(self, client_id, device_id): try: NotifyPushService().delete_token(client_id=client_id, device_id=device_id) except Exception as e: logger.info(e) raise Exception(Message.UNAUTHENTICATED) def forgot_user(self, email, password_verifier, display_name): try: # delete user, then re create new user with activate status is True old_user_id = self.get_user_by_email(email)["id"] self.delete_user(old_user_id) new_user_id = KeyCloakUtils.create_user(email, email, password_verifier, "", display_name) if new_user_id: KeyCloakUtils.active_user(new_user_id) return new_user_id except Exception as e: logger.info(e) raise Exception(Message.REGISTER_USER_FAILED) def register_srp_user(self, email, password_verifier, display_name): # register new user with using email as user name try: user_id = KeyCloakUtils.create_user(email, email, password_verifier, "", display_name) if user_id: a = KeyCloakUtils.send_verify_email(user_id) return user_id except Exception as e: logger.info(e) raise Exception(Message.REGISTER_USER_FAILED) def delete_user(self, userid): # delete user in keycloak server by its userid try: KeyCloakUtils.delete_user(user_id=userid) except Exception as e: logger.info(e) raise Exception(Message.UNAUTHENTICATED) def get_user_by_email(self, email): # get user in keycloak server by its email try: return KeyCloakUtils.get_user_by_email(email) except Exception as e: logger.info(e) raise Exception(Message.USER_NOT_FOUND) def send_forgot_password(self, email): # send an email to user with app link for reset password try: user = self.get_user_by_email(email=email) if not user: raise Exception(Message.USER_NOT_FOUND) except Exception as e: logger.info(e) raise Exception(Message.USER_NOT_FOUND) else: pre_access_token = self.hash_pre_access_token( user['id'], "forgot_password") server_domain = get_owner_workspace_domain() user_info = User().get(user['id']) if user_info.auth_source == 'account': MailerServer.send_reset_password_mail(email, email, pre_access_token, server_domain) return user['id'] else: raise Exception(Message.EMAIL_ALREADY_USED_FOR_SOCIAL_SIGNIN) # login google def google_login(self, google_id_token): # google login by using google id token, return user_name, user_id and boolean variable indicate if this user is new user try: verify_id_token_url = "https://oauth2.googleapis.com/tokeninfo?id_token=" + google_id_token req = requests.get(url=verify_id_token_url) if req.status_code != 200: raise Exception(Message.GOOGLE_AUTH_ID_TOKEN_INVALID) google_token_info = req.json() logger.info("Google login token spec:") logger.info(google_token_info) # check google_token_info["aud"] matching with google app id google_app_id = get_system_config()["google_app_id"] if google_token_info["aud"] != google_app_id[ "ios"] and google_token_info["aud"] != google_app_id[ "android"]: raise Exception(Message.GOOGLE_AUTH_FAILED) google_email = google_token_info["email"] # check account exits user = self.get_user_by_email(email=google_email) #active_user if user: if not user["emailVerified"]: KeyCloakUtils.active_user(user["id"]) user_info = UserService().get_user_by_id(user["id"]) return google_email, user[ "id"], user_info.password_verifier is None or user_info.password_verifier == "" else: # create new user new_user_id = KeyCloakUtils.create_user_without_password( google_email, google_email, "", google_token_info["name"]) new_user = UserService().create_user_social( id=new_user_id, email=google_email, display_name=google_token_info["name"], auth_source='google') if new_user is None: self.delete_user(new_user_id) raise Exception(Message.REGISTER_USER_FAILED) return google_email, new_user_id, True except Exception as e: logger.info(e) raise Exception(Message.GOOGLE_AUTH_FAILED) # login office def office_login(self, office_access_token): # office login by using office access token, return user_name, user_id and boolean variable indicate if this user is new user try: verify_token_url = "https://graph.microsoft.com/v1.0/me" bearer = 'Bearer ' + office_access_token headers = {'Authorization': bearer} req = requests.get(url=verify_token_url, headers=headers) if req.status_code != 200: raise Exception(Message.OFFICE_ACCESS_TOKEN_INVALID) office_token_info = req.json() logger.info("Office login token spec:") logger.info(office_token_info) office_id = office_token_info["id"] # check account exits user = self.get_user_by_email(office_id) if user: user_info = UserService().get_user_by_id(user["id"]) return office_id, user[ "id"], user_info.password_verifier is None or user_info.password_verifier == "" else: display_name = office_token_info["displayName"] email = "" if not display_name: if office_token_info["userPrincipalName"]: user_principal_name = office_token_info[ "userPrincipalName"].split("@") if len(user_principal_name) > 0: display_name = user_principal_name[0] email = office_token_info["userPrincipalName"] # create new user new_user_id = KeyCloakUtils.create_user_without_password( email, office_id, "", display_name) new_user = UserService().create_user_social( id=new_user_id, email=office_token_info["mail"], display_name=display_name, auth_source='office') if new_user is None: self.delete_user(new_user_id) raise Exception(Message.REGISTER_USER_FAILED) return office_id, new_user_id, True except Exception as e: logger.info(e) raise Exception(Message.OFFICE_AUTH_FAILED) # login facebook def facebook_login(self, facebook_access_token): # facebook login by using facebook access token, return user_name, user_id and boolean variable indicate if this user is new user try: # validate access_token facebook_app_id = get_system_config()["facebook_app"] verify_token_app_id = "https://graph.facebook.com/debug_token?input_token={}&access_token={}|{}".format( facebook_access_token, facebook_app_id["app_id"], facebook_app_id["app_secret"]) req = requests.get(url=verify_token_app_id) if req.status_code != 200: raise Exception(Message.FACEBOOK_ACCESS_TOKEN_INVALID) facebook_token_app_id_info = req.json() facebook_token_app_id = facebook_token_app_id_info["data"][ "app_id"] if facebook_token_app_id != facebook_app_id["app_id"]: raise Exception(Message.FACEBOOK_ACCESS_TOKEN_INVALID) verify_token_url = "https://graph.facebook.com/me?fields=id,name,email&access_token=" + facebook_access_token req = requests.get(url=verify_token_url) if req.status_code != 200: raise Exception(Message.FACEBOOK_ACCESS_TOKEN_INVALID) facebook_token_info = req.json() logger.info("Facebook login token spec:") logger.info(facebook_token_info) facebook_id = facebook_token_info["id"] facebook_email = facebook_token_info["email"] facebook_name = facebook_token_info["name"] # check account exits user = self.get_user_by_email(facebook_id) if user: user_info = UserService().get_user_by_id(user["id"]) return facebook_id, user[ "id"], user_info.password_verifier is None or user_info.password_verifier == "" else: # create new user new_user_id = KeyCloakUtils.create_user_without_password( facebook_email, facebook_id, "", facebook_name) new_user = UserService().create_user_social( id=new_user_id, email=facebook_email, display_name=facebook_name, auth_source='facebook') if new_user is None: self.delete_user(new_user_id) raise Exception(Message.REGISTER_USER_FAILED) return facebook_id, new_user_id, True except Exception as e: logger.info(e) raise Exception(Message.FACEBOOK_AUTH_FAILED) def create_otp_service(self, client_id): # start opt service for client if mfa is enabled, raising FROZEN_STATE_OTP_SERVICE if mfa is enabled and in frozen state (request resend otp too many times) user_info = self.user_db.get(client_id) user_authen_setting = self.authen_setting.get(client_id) n_times = user_authen_setting.otp_request_counter + 1 if n_times > OTPServer.valid_resend_time: # reset counter and put otp service into frozen state user_authen_setting.otp_tried_time = 0 user_authen_setting.otp_request_counter = 0 user_authen_setting.otp_frozen_time = OTPServer.cal_frozen_time() user_authen_setting.update() raise Exception(Message.FROZEN_STATE_OTP_SERVICE) if user_authen_setting.otp_frozen_time > datetime.datetime.now(): raise Exception(Message.FROZEN_STATE_OTP_SERVICE) try: hash_otp = OTPServer.get_otp(user_info.phone_number) user_authen_setting.otp = hash_otp user_authen_setting.token_valid_time = OTPServer.get_valid_time() user_authen_setting.otp_request_counter = n_times user_authen_setting.update() success = True # we create hash_key with valid_time to make hash_key will change each request hash_key = OTPServer.hash_uid(client_id, user_authen_setting.token_valid_time) return hash_key except Exception as e: logger.info(e) raise Exception(Message.OTP_SERVER_NOT_RESPONDING) def verify_otp(self, client_id, hash_key, otp): # verify opt for client if mfa is enabled, raising FROZEN_STATE_OTP_SERVICE if mfa is enabled and in frozen state (request resend otp too many times) # if client try to verify an otp too many times, this otp will become invalid user_authen_setting = self.authen_setting.get(client_id) if user_authen_setting is None: raise Exception(Message.GET_MFA_STATE_FALED) success = OTPServer.verify_hash_code( client_id, user_authen_setting.token_valid_time, hash_key) if not success: raise Exception(Message.GET_VALIDATE_HASH_OTP_FAILED) if user_authen_setting.otp_tried_time >= OTPServer.valid_trying_time: user_authen_setting.otp = None user_authen_setting.update() raise Exception(Message.EXCEED_MAXIMUM_TRIED_TIMES_OTP) if datetime.datetime.now() > user_authen_setting.token_valid_time: user_authen_setting.otp = None user_authen_setting.update() raise Exception(Message.EXPIRED_OTP) if OTPServer.check_otp(otp, user_authen_setting.otp) is False: user_authen_setting.otp_tried_time += 1 user_authen_setting.update() raise Exception(Message.WRONG_OTP) else: user_authen_setting.otp = None user_authen_setting.token_valid_time = datetime.datetime.now() user_authen_setting.otp_request_counter = 0 user_authen_setting.otp_tried_time = 0 user_authen_setting.update() return success def resend_otp(self, client_id, hash_key): # resend opt for client if mfa is enabled, raising FROZEN_STATE_OTP_SERVICE if mfa is enabled and in frozen state (request resend otp too many times) user_authen_setting = self.authen_setting.get(client_id) if user_authen_setting is None: logger.info(Message.GET_MFA_STATE_FALED) raise Exception(Message.GET_MFA_STATE_FALED) success = OTPServer.verify_hash_code( client_id, user_authen_setting.token_valid_time, hash_key) if not success: logger.info(Message.GET_VALIDATE_HASH_OTP_FAILED) raise Exception(Message.GET_VALIDATE_HASH_OTP_FAILED) n_times = user_authen_setting.otp_request_counter + 1 if n_times > OTPServer.valid_resend_time: # reset counter and put otp service into frozen state user_authen_setting.otp_tried_time = 0 user_authen_setting.otp_request_counter = 0 user_authen_setting.otp_frozen_time = OTPServer.cal_frozen_time() user_authen_setting.update() logger.info(Message.FROZEN_STATE_OTP_SERVICE) raise Exception(Message.FROZEN_STATE_OTP_SERVICE) if user_authen_setting.otp_frozen_time > datetime.datetime.now(): raise Exception(Message.FROZEN_STATE_OTP_SERVICE) try: user_info = self.user_db.get(client_id) hash_otp = OTPServer.get_otp(user_info.phone_number) user_authen_setting.otp = hash_otp user_authen_setting.otp_tried_time = 0 user_authen_setting.token_valid_time = OTPServer.get_valid_time() user_authen_setting.otp_request_counter = n_times user_authen_setting.update() # we create hash_key with valid_time to make hash_key will change each request hash_key = OTPServer.hash_uid(client_id, user_authen_setting.token_valid_time) return hash_key except Exception as e: logger.info(e) raise Exception(Message.OTP_SERVER_NOT_RESPONDING) def hash_pre_access_token(self, user_name, require_action): # create a pre access_token by signing a jws with user_name as iss, require_action as aud hash_key = OTPServer.sign_message(user_name, require_action) return hash_key def verify_hash_pre_access_token(self, user_name, signed_message, require_action): # verify a pre access_token signed by jws with user_name as iss, require_action as aud try: message = OTPServer.verify_message(signed_message) if message.get("iss", None) != user_name: return False if message.get("aud", None) != require_action: return False if message.get("exp", 0) < int(time.time()): return False return True except Exception as e: logger.error(e) return False
def __init__(self): super().__init__(User()) self.authen_setting = AuthenSetting()
class UserService(BaseService): """ UserService, using when create new user, edit user info, or delete user, or enable/disable mfa flow """ def __init__(self): super().__init__(User()) self.authen_setting = AuthenSetting() def create_new_user_srp(self, id, email, password_verifier, salt, iv_parameter, display_name, auth_source): # create new normal user with these parameter try: self.model = User(id=id, email=email, password_verifier=password_verifier, salt=salt, iv_parameter=iv_parameter, display_name=display_name, auth_source=auth_source) self.model.add() except Exception as e: logger.error(e) raise Exception(Message.REGISTER_USER_FAILED) def create_user_social(self, id, email, display_name, auth_source): # create new social user with these parameter try: self.model = User(id=id, email=email, display_name=display_name, auth_source=auth_source) self.model.add() return self except Exception as e: logger.info(e) return None def forgot_user(self, user_info, new_user_id, password_verifier, salt, iv_parameter): # delete user, then recreate user with new user_id and security information try: self.model = User(id=new_user_id, email=user_info.email, display_name=user_info.display_name, auth_source=user_info.auth_source, password_verifier=password_verifier, salt=salt, iv_parameter=iv_parameter, first_name=user_info.first_name, last_name=user_info.last_name, status=user_info.status, avatar=user_info.avatar, phone_number=user_info.phone_number, created_at=user_info.created_at) self.model.add() self.delete_user(user_info.id) except Exception as e: logger.error(e) raise Exception(Message.REGISTER_USER_FAILED) def get_google_user(self, email, auth_source): # get user with email and auth_source info user_info = self.model.get_google_user(email, auth_source) return user_info def change_password(self, request, old_pass, new_pass, user_id): # change password in keycloak try: user_info = self.model.get(user_id) response = KeyCloakUtils.set_user_password(user_id, new_pass) return user_info except Exception as e: logger.info(e) raise Exception(Message.CHANGE_PASSWORD_FAILED) def get_mfa_state(self, user_id): # get mfa state of user_id user_info = self.model.get(user_id) if user_info is None: raise Exception(Message.AUTH_USER_NOT_FOUND) user_authen_setting = self.authen_setting.get(user_id) if user_authen_setting is None: user_authen_setting = AuthenSetting(id=user_id).add() return user_authen_setting.mfa_enable def init_mfa_state_enabling(self, user_id): # start enable mfa for user_id # if otp in frozen state, raising exception user_authen_setting = self.authen_setting.get(user_id) if user_authen_setting is None: user_authen_setting = AuthenSetting(id=user_id).add() if user_authen_setting.mfa_enable: success = False next_step = '' else: if user_authen_setting.otp_frozen_time > datetime.datetime.now(): raise Exception(Message.FROZEN_STATE_OTP_SERVICE) next_step = 'mfa_validate_password' user_authen_setting.require_action = next_step user_authen_setting.update() success = True return success, next_step def init_mfa_state_disabling(self, user_id): # diable mfa for user_id user_authen_setting = self.authen_setting.get(user_id) if user_authen_setting is None: user_authen_setting = AuthenSetting(id=user_id).add() if user_authen_setting.mfa_enable: user_authen_setting.mfa_enable = False user_authen_setting.update() success = True else: success = False next_step = '' return success, next_step def mfa_validate_password_flow(self, user_id): # check if user_id is in mfa_validate_password action user_authen_setting = self.authen_setting.get(user_id) if user_authen_setting.require_action != 'mfa_validate_password': raise Exception(Message.AUTHEN_SETTING_FLOW_NOT_FOUND) return True def mfa_request_otp(self, user_id, phone_number): # start request otp service for user_id with phone_number user_authen_setting = self.authen_setting.get(user_id) n_times = user_authen_setting.otp_request_counter + 1 if n_times > OTPServer.valid_resend_time: # reset counter and put otp service into frozen state user_authen_setting.otp_tried_time = 0 user_authen_setting.otp_request_counter = 0 user_authen_setting.otp_frozen_time = OTPServer.cal_frozen_time() user_authen_setting.update() raise Exception(Message.FROZEN_STATE_OTP_SERVICE) if user_authen_setting.otp_frozen_time > datetime.datetime.now(): raise Exception(Message.FROZEN_STATE_OTP_SERVICE) try: next_step = 'mfa_validate_otp' user_authen_setting.require_action = next_step hash_otp = OTPServer.get_otp(phone_number) user_authen_setting.otp = hash_otp user_authen_setting.token_valid_time = OTPServer.get_valid_time() user_authen_setting.otp_request_counter = n_times user_authen_setting.update() success = True return success, next_step except Exception as e: logger.error(e) raise Exception(Message.OTP_SERVER_NOT_RESPONDING) def validate_otp(self, user_id, otp): # validate otp service for user_id user_authen_setting = self.authen_setting.get(user_id) if user_authen_setting is None: raise Exception(Message.AUTH_USER_NOT_FOUND) if user_authen_setting.require_action != 'mfa_validate_otp': raise Exception(Message.AUTHEN_SETTING_FLOW_NOT_FOUND) if user_authen_setting.otp_tried_time >= OTPServer.valid_trying_time: user_authen_setting.otp = None user_authen_setting.update() raise Exception(Message.EXCEED_MAXIMUM_TRIED_TIMES_OTP) if datetime.datetime.now() > user_authen_setting.token_valid_time: user_authen_setting.otp = None user_authen_setting.update() raise Exception(Message.EXPIRED_OTP) if OTPServer.check_otp(otp, user_authen_setting.otp) is True: user_authen_setting.mfa_enable = True user_authen_setting.otp = None user_authen_setting.require_action = "" user_authen_setting.token_valid_time = datetime.datetime.now() user_authen_setting.otp_request_counter = 0 user_authen_setting.otp_tried_time = 0 user_authen_setting.update() success = True next_step = '' else: user_authen_setting.otp_tried_time += 1 user_authen_setting.update() raise Exception(Message.WRONG_OTP) return success, next_step def re_init_otp(self, user_id): # retry request otp for user_id user_info = self.model.get(user_id) if user_info is None: raise Exception(Message.AUTH_USER_NOT_FOUND) user_authen_setting = self.authen_setting.get(user_id) if user_authen_setting.require_action != 'mfa_validate_otp': raise Exception(Message.AUTHEN_SETTING_FLOW_NOT_FOUND) n_times = user_authen_setting.otp_request_counter + 1 if n_times > OTPServer.valid_resend_time: # reset counter and put otp service into frozen state user_authen_setting.otp_tried_time = 0 user_authen_setting.otp_request_counter = 0 user_authen_setting.otp_frozen_time = OTPServer.cal_frozen_time() user_authen_setting.update() raise Exception(Message.FROZEN_STATE_OTP_SERVICE) if user_authen_setting.otp_frozen_time > datetime.datetime.now(): raise Exception(Message.FROZEN_STATE_OTP_SERVICE) try: hash_otp = OTPServer.get_otp(user_info.phone_number) user_authen_setting.otp = hash_otp user_authen_setting.otp_tried_time = 0 user_authen_setting.token_valid_time = OTPServer.get_valid_time() user_authen_setting.otp_request_counter = n_times user_authen_setting.update() success = True next_step = 'mfa_validate_otp' return success, next_step except Exception as e: logger.error(e) raise Exception(Message.OTP_SERVER_NOT_RESPONDING) def update_hash_pass(self, user_id, hash_password, salt='', iv_parameter=''): # update hash_password and relate security information for user_id user_info = self.model.get(user_id) user_info.password_verifier = hash_password if salt: user_info.salt = salt if iv_parameter: user_info.iv_parameter = iv_parameter user_info.update() return (user_info.salt, user_info.iv_parameter) def update_hash_pin(self, user_id, hash_pincode, salt='', iv_parameter=''): # update hash_pincode and relate security information for user_id user_info = self.model.get(user_id) user_info.password_verifier = hash_pincode if salt: user_info.salt = salt if iv_parameter: user_info.iv_parameter = iv_parameter user_info.update() return user_info.salt, user_info.iv_parameter def get_profile(self, user_id): # get profile of user_id try: user_info = self.model.get(user_id) if user_info is not None: obj_res = user_pb2.UserProfileResponse( id=user_info.id, display_name=user_info.display_name) if user_info.email: obj_res.email = user_info.email if user_info.phone_number: obj_res.phone_number = user_info.phone_number if user_info.avatar: obj_res.avatar = user_info.avatar return obj_res else: return None except Exception as e: logger.info(e) raise Exception(Message.GET_PROFILE_FAILED) def update_profile(self, user_id, display_name, phone_number, avatar, clear_phone_number): # update profile for user_id, with force clear phone number flag for clearing stored phone number in db try: profile = self.model.get(user_id) if display_name: profile.display_name = display_name if clear_phone_number: user_authen_setting = self.authen_setting.get(user_id) if user_authen_setting is None: user_authen_setting = AuthenSetting(id=user_id).add() user_authen_setting.enable_mfa = False # change phone_number automatically turn off enable_mfa user_authen_setting.update() profile.phone_number = "" elif phone_number: user_authen_setting = self.authen_setting.get(user_id) if user_authen_setting is None: user_authen_setting = AuthenSetting(id=user_id).add() user_authen_setting.enable_mfa = False # change phone_number automatically turn off enable_mfa user_authen_setting.update() profile.phone_number = phone_number if avatar: profile.avatar = avatar return profile.update() except Exception as e: logger.info(e) raise Exception(Message.UPDATE_PROFILE_FAILED) def get_user_info(self, client_id, workspace_domain): # get information of client_id with additional infor about workspace_domain try: user_info = self.model.get(client_id) if user_info is not None: return user_pb2.UserInfoResponse( id=user_info.id, display_name=user_info.display_name, workspace_domain=workspace_domain) else: raise Exception(Message.GET_USER_INFO_FAILED) except Exception as e: logger.info(e) raise Exception(Message.GET_USER_INFO_FAILED) def get_user_by_auth_source(self, email, auth_source): # get fully stored information about user by email and auth_source user_info = self.model.get_user_by_auth_source(email, auth_source) return user_info def get_user_by_id(self, client_id): # get fully stored information about user by user_id user_info = self.model.get(client_id) return user_info def search_user(self, keyword, client_id): # searching user with keyword differ to client_id try: lst_user = self.model.search(keyword, client_id) lst_obj_res = [] for obj in lst_user: obj_res = user_pb2.UserInfoResponse( id=obj.id, display_name=obj.display_name, ) lst_obj_res.append(obj_res) response = user_pb2.SearchUserResponse(lst_user=lst_obj_res) return response except Exception as e: logger.info(e) raise Exception(Message.SEARCH_USER_FAILED) def get_users(self, client_id, workspace_domain): # get other users for client_id within workspace_domain try: lst_user = self.model.get_users(client_id) lst_obj_res = [] for obj in lst_user: obj_res = user_pb2.UserInfoResponse( id=obj.id, display_name=obj.display_name, workspace_domain=workspace_domain, ) lst_obj_res.append(obj_res) response = user_pb2.GetUsersResponse(lst_user=lst_obj_res) return response except Exception as e: logger.info(e) raise Exception(Message.GET_USER_INFO_FAILED) def update_last_login(self, user_id): # update last time login for user_id try: user_info = self.model.get(user_id) user_info.last_login_at = datetime.datetime.now() user_info.update() except Exception as e: logger.info(e) def set_user_status(self, client_id, status): # set status for client_id try: user_info = self.model.get(client_id) if status == "": status = None user_info.status = status user_info.update() client_record = client_records_list_in_memory.get( str(client_id), None) client_record["user_status"] = status except Exception as e: logger.error(e) raise Exception(Message.UPDATE_USER_STATUS_FAILED) def update_client_record(self, client_id): # update client record in temp variable try: client_record = client_records_list_in_memory.get( str(client_id), None) if client_record is None: client_records_list_in_memory.update({ str(client_id): { "last_active": datetime.datetime.now(), "prev_active": None, "user_status": None, } }) else: client_record["prev_active"] = client_record["last_active"] client_record["last_active"] = datetime.datetime.now() except Exception as e: logger.error(e) raise Exception(Message.PING_PONG_SERVER_FAILED) def categorize_workspace_domains(self, list_clients): # create and return categorize_workspace_domains, with client is devided into each workspace workspace_domains_dictionary = {} for client in list_clients: if str(client.workspace_domain ) in workspace_domains_dictionary.keys(): workspace_domains_dictionary[str( client.workspace_domain)].append(client) else: workspace_domains_dictionary.update( {str(client.workspace_domain): [client]}) return workspace_domains_dictionary def get_list_clients_status(self, list_clients, should_get_profile): # get list of clients status, with returning additional basic infor of user is should_get_profile is True logger.info("get_list_clients_status") try: owner_workspace_domain = get_owner_workspace_domain() list_clients_status = [] workspace_domains_dictionary = self.categorize_workspace_domains( list_clients) for workspace_domain in workspace_domains_dictionary.keys(): list_client = workspace_domains_dictionary[workspace_domain] if workspace_domain == owner_workspace_domain: for client in list_client: user_status = self.get_owner_workspace_client_status( client.client_id) tmp_client_response = user_pb2.MemberInfoRes( client_id=client.client_id, workspace_domain=workspace_domain, status=user_status, ) if should_get_profile: user_info = self.model.get(client.client_id) if user_info is not None: if user_info.display_name: tmp_client_response.display_name = user_info.display_name if user_info.phone_number: tmp_client_response.phone_number = user_info.phone_number if user_info.avatar: tmp_client_response.avatar = user_info.avatar logger.info("user info {}: {}".format( client.client_id, tmp_client_response)) list_clients_status.append(tmp_client_response) else: logger.info("workspace request other server {}".format( workspace_domain)) other_clients_response = self.get_other_workspace_clients_status( workspace_domain, list_client, should_get_profile) list_clients_status.extend(other_clients_response) response = user_pb2.GetClientsStatusResponse( lst_client=list_clients_status) return response except Exception as e: logger.error(e) raise Exception(Message.GET_USER_STATUS_FAILED) def get_owner_workspace_client_status(self, client_id): # get client record of client_id in this server client_record = client_records_list_in_memory.get(str(client_id), None) if client_record is not None: leave_time_amount = datetime.datetime.now( ) - client_record["last_active"] if leave_time_amount.seconds > get_system_config().get( "maximum_offline_time_limit"): user_status = "Offline" else: if client_record["user_status"] is not None: user_status = client_record["user_status"] else: user_status = "Online" else: user_status = "Undefined" return user_status def get_other_workspace_clients_status(self, workspace_domain, list_client, should_get_profile=False): # get client record of client_id in other server server_error_resp = [] client = ClientUser(workspace_domain) client_resp = client.get_clients_status(list_client, should_get_profile) if client_resp is None: logger.info("CALL WORKSPACE ERROR", workspace_domain) for client in list_client: tmp_client_response = user_pb2.MemberInfoRes( client_id=client.client_id, workspace_domain=client.workspace_domain, status="Undefined", ) server_error_resp.append(tmp_client_response) return server_error_resp return client_resp.lst_client def base64_enconding_text_to_string(self, text): # encode utf-8 string to ascii string text_bytes = text.encode("ascii") encoded_text_bytes = base64.b64encode(text_bytes) return encoded_text_bytes.decode('ascii') def upload_avatar(self, client_id, file_name, file_content, file_type, file_hash): # upload avatar for client_id m = hashlib.new('md5', file_content).hexdigest() if m != file_hash: raise Exception(Message.UPLOAD_FILE_DATA_LOSS) # start upload to s3 and resize if needed tmp_file_name, file_ext = os.path.splitext(file_name) avatar_file_name = self.base64_enconding_text_to_string( client_id) + file_ext avatar_url = UploadFileService().upload_to_s3(avatar_file_name, file_content, file_type) obj_res = user_pb2.UploadAvatarResponse(file_url=avatar_url) return obj_res def delete_user(self, user_id): # delete user, note that this function must be called only by admin user_info = self.model.get(user_id) user_info.delete() return True