def CreateFeedVerificationHash(self, req, context): self._logger.info( "Request to create a verification hash by user id %s for %s", req.user_id, req.feed_url) user_find_resp = self._db.Users(database_pb2.UsersRequest( request_type=database_pb2.RequestType.FIND, match=database_pb2.UsersEntry( global_id=req.user_id, ) )) if user_find_resp.result_type != general_pb2.ResultType.OK: self._logger.error("Error getting CSS: %s", user_find_resp.error) return users_pb2.CreateFeedVerificationHashResponse( result=general_pb2.ResultType.ERROR, error=user_find_resp.error, ) elif len(user_find_resp.results) != 1: self._logger.error( "Got wrong number of results for user find. Expected 1 got %d", len(user_find_resp.results)) return users_pb2.GetCssResponse( result=general_pb2.ResultType.ERROR, error="Got wrong number of results during user find", ) hash = self._generate_hash(user_find_resp.results[0], req.feed_url) return users_pb2.CreateFeedVerificationHashResponse( result=general_pb2.ResultType.OK, verification_hash=hash, )
def SendCreate(self, req, context): self._logger.debug("Recieved a new create action.") author = self._users_util.get_user_from_db(global_id=req.author_id) # Insert ActivityPub ID into database. # build author entry from scratch to add host into call ue = database_pb2.UsersEntry(handle=author.handle, host=self._host_name) pe = database_pb2.PostsEntry(global_id=req.global_id) ap_id = self._activ_util.build_article_ap_id(ue, pe) article_url = self._activ_util.build_local_article_url(ue, pe) err = self._add_ap_id(req.global_id, ap_id) if err is not None: self._logger.error("Continuing through error: %s", err) # list of follow objects follow_list = self._users_util.get_follower_list(author.global_id) # remove local users foreign_follows = self._users_util.remove_local_users(follow_list) # go through follow send create activity # TODO (sailslick) make async/ parallel in the future for follower in foreign_follows: self._post_create_req(follower, req, ap_id, author, article_url) resp = general_pb2.GeneralResponse() resp.result_type = general_pb2.ResultType.OK return resp
def Create(self, request, context): public_key, private_key = self._create_public_and_private_key() insert_request = database_pb2.UsersRequest( request_type=database_pb2.RequestType.INSERT, entry=database_pb2.UsersEntry( handle=request.handle, display_name=request.display_name, password=self._hash_password(request.password), bio=request.bio, host_is_null=True, public_key=public_key, private_key=private_key, ), ) db_resp = self._db_stub.Users(insert_request) if db_resp.result_type != general_pb2.ResultType.OK: self._logger.warning("Error inserting user into db: %s", db_resp.error) return users_pb2.CreateUserResponse( result_type=general_pb2.ResultType.ERROR, error=db_resp.error, ) return users_pb2.CreateUserResponse( result_type=general_pb2.ResultType.OK, global_id=db_resp.global_id, )
def get_or_create_user_from_db(self, handle=None, host=None, global_id=None, host_is_null=False, attempt_number=0, bio=None): if attempt_number > MAX_FIND_RETRIES: self._logger.error('Retried query too many times.') return None host = self._activ_util.normalise_hostname(host) if host else host user = self.get_user_from_db(handle, host, global_id, host_is_null) if user is not None: return user if global_id is not None or handle is None: # Should not try to create a user and hope it has this ID. # Also shouldn't create a user with no handle. return None user_entry = database_pb2.UsersEntry( handle=handle, host=host, bio=bio, host_is_null=host_is_null ) new_global_id = self._create_user_in_db(user_entry) if new_global_id is not None and host: self.download_profile_pic(host, handle, new_global_id) return self.get_or_create_user_from_db(handle, host, attempt_number=attempt_number + 1)
def get_user_from_db(self, handle=None, host=None, global_id=None, host_is_null=False): self._logger.debug('User %s@%s (id %s) host_is_null: %s requested from database', handle, host, global_id, host_is_null) host = self._activ_util.normalise_hostname(host) if host else host user_entry = database_pb2.UsersEntry( handle=handle, host=host, host_is_null=host_is_null, global_id=global_id ) find_req = database_pb2.UsersRequest( request_type=database_pb2.UsersRequest.FIND, match=user_entry ) find_resp = self._db.Users(find_req) if len(find_resp.results) == 0: self._logger.warning('No user %s@%s (id %s) found', handle, host, global_id) return None elif len(find_resp.results) == 1: self._logger.debug('Found user %s@%s (id %s) from database', handle, host, global_id) return find_resp.results[0] else: self._logger.error('> 1 user found in database for %s@%s (id %s)' + ', returning first one.', handle, host, global_id) return find_resp.results[0]
def GetCss(self, request, context): self._logger.info("Request to get the CSS for user id %s", request.user_id) resp = self._db.Users( database_pb2.UsersRequest( request_type=database_pb2.RequestType.FIND, match=database_pb2.UsersEntry(global_id=request.user_id, ))) if resp.result_type != general_pb2.ResultType.OK: self._logger.error("Error getting CSS: %s", resp.error) return users_pb2.GetCssResponse( result=general_pb2.ResultType.ERROR, error=resp.error, ) elif len(resp.results) != 1: self._logger.error( "Got wrong number of results, expected 1 got %d", len(resp.results)) return users_pb2.GetCssResponse( result=general_pb2.ResultType.ERROR, error="Got wrong number of results", ) return users_pb2.GetCssResponse( result=general_pb2.ResultType.OK, css=resp.results[0].custom_css, )
def _make_user(self, pw=None): pw = self._pw_hash if pw is None else pw return database_pb2.UsersEntry( handle="CianLR", global_id=1, display_name="Cian Ruane", password=pw, bio="A sound lad", )
def delete_user_from_db(self, global_id): self._logger.debug("Deleteing user with global_id %d", global_id) resp = self._db.Users( database_pb2.UsersRequest( request_type=database_pb2.RequestType.DELETE, entry=database_pb2.UsersEntry(global_id=global_id), )) if resp.result_type != general_pb2.ResultType.OK: self._logger.error("Error deleting from db: %s", resp.error) return False return True
def _get_local_user_id(self, handle): user = database_pb2.UsersEntry(handle=handle, host_is_null=True) req = database_pb2.UsersRequest(request_type=database_pb2.UsersRequest.FIND, match=user) resp = self._db.Users(req) if resp.result_type != database_pb2.UsersResponse.OK: self._logger.warning('Error getting user: {}'.format(resp.error)) return None if not len(resp.results): self._logger.warning('Could not find user.') return None return resp.results[0].global_id
def _fetch_public_key_from_database(self, handle): user = database_pb2.UsersEntry( handle=handle, host_is_null=True, ) req = database_pb2.UsersRequest( match=user, request_type=database_pb2.UsersRequest.FIND) resp = self._db_stub.Users(req) if (resp.result_type != database_pb2.UsersResponse.OK or len(resp.results) == 0): self._logger.error('No user found in database') return None return resp.results[0].public_key
def add_user(self, handle=None, host=None): user_entry = database_pb2.UsersEntry( handle=handle, host=host, host_is_null=host is None, ) req = database_pb2.UsersRequest( request_type=database_pb2.RequestType.INSERT, entry=user_entry, ) add_res = self.users.Users(req, self.ctx) self.assertNotEqual(add_res.result_type, general_pb2.ResultType.ERROR) return add_res
def get_user_and_check_pw(logger, db_stub, handle, pw): """ get_user_and_check finds a user and checks their password. Raises: On a database lookup failure, raises a ValueError. Returns: Returns a tuple: (Result, Error) Error is a string containing details a login error. if their password is incorrect: returns None, Error if their password is correct: returns UserEntry, None """ if not handle: err = "Received blank username" logger.warning(err) raise ValueError(err) if not pw: err = "Received blank password" logger.warning(err) raise ValueError(err) find_request = database_pb2.UsersRequest( request_type=database_pb2.RequestType.FIND, match=database_pb2.UsersEntry( handle=handle, host_is_null=True, ), ) db_resp = db_stub.Users(find_request) if db_resp.result_type != general_pb2.ResultType.OK: err = "Error getting user from DB: " + db_resp.error logger.warning(err) raise ValueError(db_resp.error) elif len(db_resp.results) != 1: err = "Got {} users matching handle {}, expecting 1".format( len(db_resp.results), handle) logger.warning(err) raise ValueError("Got wrong number of users matching query") user = db_resp.results[0] if not check_password(logger, pw, user.password): err = "ACCESS DENIED for user %s", handle logger.info(err) return None, err logger.info("*hacker voice* I'm in (%s)", handle) return user, None
def _get_user_by_id(self, _id): """ Get the user object for the given user global_id. If the user could not be retrieved, return None. """ user_resp = self._db.Users( database_pb2.UsersRequest( request_type=database_pb2.UsersRequest.FIND, match=database_pb2.UsersEntry(global_id=_id))) if user_resp.result_type != database_pb2.UsersResponse.OK: self._logger.warning('Could not find user: {}'.format( user_resp.error)) return None if not len(user_resp.results): self._logger.warning('Could not find user.') return None return user_resp.results[0]
def forward_activity_to_followers(self, user_id, activity): """ Sends an activity to all of the hosts with a follower of a given user. Some things to note about the behaviour: - Local users do not receive the activity - An arbitrary user from each host is selected to receive the activity - Any followers or hosts not found are skipped with a warning. """ self._logger.info("Sending activity to followers") resp = self._db.Follow( database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.FIND, match=database_pb2.Follow(followed=user_id), )) if resp.result_type != database_pb2.DbFollowResponse.OK: return resp.error self._logger.info("Have %d users to notify", len(resp.results)) # Gather up the users, filter local and non-unique hosts. hosts_to_users = {} for follow in resp.results: user_resp = self._db.Users( database_pb2.UsersRequest( request_type=database_pb2.UsersRequest.FIND, match=database_pb2.UsersEntry(global_id=follow.follower))) if user_resp.result_type != database_pb2.UsersResponse.OK: self._logger.warning("Error finding user %d, skipping", follow.follower) continue if len(user_resp.results) != 1: self._logger.warning("Couldn't find user %d, skipping", follow.follower) continue user = user_resp.results[0] if not user.host or user.host_is_null: continue # Local user, skip. hosts_to_users[user.host] = user # Send the activities off. for host, user in hosts_to_users.items(): inbox = self.build_inbox_url(user.handle, host) resp, err = self.send_activity(activity, inbox) if err: self._logger.warning( "Error sending activity to '%s' at '%s': %s", user.handle, host, str(err)) return None
def Update(self, request, context): try: user, err = get_user_and_check_pw(self._logger, self._db_stub, request.handle, request.current_password) except ValueError as e: return users_pb2.UpdateUserResponse( result=general_pb2.ResultType.ERROR, error=str(e), ) if err is not None: return users_pb2.UpdateUserResponse( result=general_pb2.ResultType.ERROR_401, ) pw = None if request.new_password: pw = self._hash_password(request.new_password) update_request = database_pb2.UsersRequest( request_type=database_pb2.RequestType.UPDATE, match=user, entry=database_pb2.UsersEntry( display_name=request.display_name, password=pw, bio=request.bio, private=request.private, custom_css=request.custom_css, ), ) db_resp = self._db_stub.Users(update_request) if db_resp.result_type != general_pb2.ResultType.OK: self._logger.warning("Error update user: %s", db_resp.error) return users_pb2.CreateUserResponse( result_type=general_pb2.ResultType.ERROR, error=db_resp.error, ) return users_pb2.UpdateUserResponse( result=general_pb2.ResultType.OK, )
def __init__(self): self.posts_response = database_pb2.PostsResponse( result_type=general_pb2.ResultType.OK, results=[ database_pb2.PostsEntry( global_id=123, author_id=456, title="Minecraft Farming 101", body="Don't bother", ap_id="https://rabble.mojang.com/ap/@minecraft4ever/666", ) ]) self.users_response = database_pb2.UsersResponse( result_type=general_pb2.ResultType.OK, results=[ database_pb2.UsersEntry( global_id=456, handle="minecraft4ever", host="rabble.mojang.com", display_name="Minecraft4Ever", ) ], )
def __init__(self): self.posts_response = dbpb.PostsResponse( result_type=dbpb.PostsResponse.OK, results=[ dbpb.PostsEntry( global_id=3, author_id=123, title="Test", body="Test body", ap_id="https://rabble.cian.com/@cian/3", ), ], ) self.users_response = dbpb.UsersResponse( result_type=dbpb.UsersResponse.OK, results=[ dbpb.UsersEntry( global_id=123, handle="cian", host="rabble.cian.com", display_name="Cian", ), ], )
def lookup_user(self, user): if user not in self.users_dict: return None return database_pb2.UsersEntry(global_id=self.users_dict[user])