def get_request_user_foaf_post(request_user_full_id): result = [] try: # in this case request_user_id should be foreign user id request_user_host_name = "{uri.scheme}://{uri.netloc}".format( uri=urlparse(request_user_full_id) ) foreign_server = Server.objects.get(url=request_user_host_name) request_user_id = request_user_full_id.split("/author/")[-1] # print(request_user_id, "request_user_id") # fetch friendlist resp = ForeignServerHttpUtils.get(foreign_server, "/author/" + request_user_id + "/friends") if resp.status_code != 200: raise Exception("failed getting the friend_list") request_user_friend_list = resp.json()["authors"] # request_user_friend_list = [friend["id"] for friend in resp.json()["authors"]] # print(request_user_friend_list) local_users = UserProfile.objects.filter(host=None) # print(local_users) for local_user in local_users: local_author_friend_list = list(local_user.get_friends().values_list("url", flat=True)) intersection_friends = list( set(request_user_friend_list) & set(local_author_friend_list) ) if len(intersection_friends) > 0: result.extend( list(Post.objects.filter(author=local_user, unlisted=False, visibility="FOAF")) ) # print(result) return result except Exception as some_error: print(some_error, "foaf1 error") return result
def check_remote_foaf_relationship(self, remote_user_full_id): try: # find the server first remote_user_host_name = "{uri.scheme}://{uri.netloc}".format( uri=urlparse(remote_user_full_id)) foreign_server = Server.objects.get(url=remote_user_host_name) remote_user_id = remote_user_full_id.split("/author/")[-1] # fetch friendlist resp = ForeignServerHttpUtils.get( foreign_server, "/author/" + remote_user_id + "/friends") if resp.status_code != 200: raise Exception("failed getting the friend_list") remote_user_friend_list = resp.json()["authors"] # remote_user_friend_list = [friend["id"] for friend in resp.json()["authors"]] # print(remote_user_friend_list) own_friend_list = list(self.get_friends().values_list("url", flat=True)) intersection_friends = list( set(remote_user_friend_list) & set(own_friend_list)) return len(intersection_friends) > 0 except Exception as some_error: # print(remote_user_full_id) # print(remote_user_host_name) print(some_error, "error in check_remote_foaf_relationship") return False
def list(self, request): """ GET /posts """ is_local = False try: # check if authenticated user is a server or local user request.user.server except User.server.RelatedObjectDoesNotExist: # pylint: disable=no-member # local user shoud not have an one-to-one relationship with Server is_local = True posts = self.queryset.filter(visibility="PUBLIC") serializer = PostSerializer(posts, many=True) data = serializer.data foreign_public_posts = [] if is_local: # handle local user request # also include foreign public posts for server in Server.objects.all(): try: posts_response = ForeignServerHttpUtils.get(server, "/posts") foreign_public_posts += posts_response.json().get("posts", []) except requests.exceptions.RequestException as exception: print(exception) else: pass data += foreign_public_posts return Response({"query": "posts", "count": len(data), "posts": data}, status=200)
def get_auth_posts(self, request): """ GET /author/posts """ # check if local user foreign_posts = [] result = [] try: request.user.server except User.server.RelatedObjectDoesNotExist: # pylint: disable=no-member # local user result = list( self.queryset.filter( Q(visibility="PUBLIC") | Q(author=request.user.profile) | Q(visibility="SERVERONLY") ) ) + Post.not_own_posts_visible_to_me(request.user.profile) # fetch foreign posts concurrently foreign_reqs = [] for server in Server.objects.all(): local_url = request.user.profile.get_url() headers = {"X-Request-User-ID": str(local_url)} foreign_reqs.append( ForeignServerHttpUtils.parallel_get( server, "/author/posts", headers=headers, timeout=5 ) ) responses = grequests.map(foreign_reqs, exception_handler=self._exception_handler) for response in responses: if not response: continue if response.status_code == 200: body = response.json() if isinstance(body, dict): posts = body.get("posts", []) foreign_posts += posts else: # foreign user # grab request user information from request header try: foreign_user_url = request.META["HTTP_X_REQUEST_USER_ID"] # foreign user in our db, get all public posts and posts that # are visible to or such posts' firends to this foreign user profile foreign_user_profile = UserProfile.objects.get(url=foreign_user_url) result = list( self.queryset.filter(visibility="PUBLIC") ) + Post.not_own_posts_visible_to_me(foreign_user_profile) result = result + get_request_user_foaf_post(foreign_user_url) except UserProfile.DoesNotExist: # foreign user is not in our db # directly return public result = list(self.queryset.filter(visibility="PUBLIC")) result = result + get_request_user_foaf_post(foreign_user_url) except KeyError: return Response( {"query": "posts", "success": False, "message": "No X-Request-User-ID"}, status=status.HTTP_400_BAD_REQUEST, ) result = list(set(result)) # remove duplication serializer = PostSerializer(result, many=True) data = serializer.data + foreign_posts return Response({"query": "posts", "count": len(data), "posts": data})
def get_author_id_posts(self, request, pk): """ GET /author/{author_id}/posts """ # target_posts are posts created by author with id = pk target_posts = None # check if local user is_local = False try: request.user.server except User.server.RelatedObjectDoesNotExist: # pylint: disable=no-member is_local = True result = [] if is_local: if pk.isdigit(): # local user # find all PUBLIC and SERVERONLY post create by this author with id=pk # add not_own_posts_visible_to_me posts pk = User.objects.get(pk=pk).profile.id if int(request.user.profile.id) == int(pk): # add unlisted posts result = Post.objects.filter(author=pk) else: target_posts = self.queryset.filter(author=pk) result = list( target_posts.filter(Q(visibility="PUBLIC") | Q(visibility="SERVERONLY")) ) + Post.not_own_posts_visible_to_me( request.user.profile, queryset=target_posts ) else: # foreign user try: parsed_url = urlparse(pk) foreign_server = Server.objects.get( url="{}://{}".format(parsed_url.scheme, parsed_url.netloc) ) foreign_post_id = parsed_url.path.split("/")[-1] response = ForeignServerHttpUtils.get( foreign_server, "/author/{}/posts".format(foreign_post_id), headers={"X-Request-User-ID": request.user.profile.get_url()}, ) if response.status_code == 200: return Response(response.json()) else: return Response( { "query": "getAuthorPost", "success": False, "message": "Foreign server error", "error": json.dumps(response.json()), }, status=status.HTTP_500_INTERNAL_SERVER_ERROR, ) except Server.DoesNotExist: return Response( {"succcess": False, "message": "Foreign server does not exist."}, status=status.HTTP_400_BAD_REQUEST, ) else: # foreign user # grab request user information from request header # print(request.META) pk = User.objects.get(pk=pk).profile.id target_posts = self.queryset.filter(author=pk) try: foreign_user_url = request.META["HTTP_X_REQUEST_USER_ID"] # foreign user in our db, get all public posts created by author with id=pk foreign_user_profile = UserProfile.objects.get(url=foreign_user_url) result = list( target_posts.filter(Q(visibility="PUBLIC")) ) + Post.not_own_posts_visible_to_me(foreign_user_profile, queryset=target_posts) # plus remote foaf result = result + get_request_user_foaf_post_belong_local_author( foreign_user_url, pk ) except UserProfile.DoesNotExist: # foreign user is not in our db # directly return public result = list(target_posts.filter(visibility="PUBLIC")) result = result + get_request_user_foaf_post_belong_local_author( foreign_user_url, pk ) except KeyError: return Response( {"query": "posts", "success": False, "message": "No X-Request-User-ID"}, status=status.HTTP_400_BAD_REQUEST, ) result = list(set(result)) # remove duplication serializer = PostSerializer(result, many=True) data = serializer.data return Response({"query": "posts", "count": len(data), "posts": data})
def unfollow_request(request): # so far only local user can do unfriend action try: body = json.loads(request.body.decode("utf-8")) if body["query"] != "unfollow": raise Exception("query should be unfollow") # get the host from url to compare with host attribute # https://stackoverflow.com/questions/9626535/get-protocol-host-name-from-url # host_name = "{uri.scheme}://{uri.netloc}".format(uri=urlparse(body["author"]["id"])) # if host_name != body["author"]["host"]: # raise Exception("we can't save the profile which host != url.host") friend_host_name = "{uri.scheme}://{uri.netloc}".format( uri=urlparse(body["friend"]["id"])) is_local = False try: _ = request.user.server except User.server.RelatedObjectDoesNotExist: # pylint: disable=no-member is_local = True if is_local: # the request user is local author_id = int(body["author"]["id"].split("/")[-1]) if request.user.id != author_id: raise Exception("request user is not the author") author_profile = User.objects.get(pk=author_id).profile # check if the friend is local or remote if friend_host_name == settings.HYPERION_HOSTNAME: # friend is local friend_profile = User.objects.get( pk=int(body["friend"]["id"].split("/")[-1])).profile else: # friend is remote friend_profile = UserProfile.objects.get( url=body["friend"]["id"]) # send unfollow request to remote server unfollow_body = { "query": "unfollow", "author": UserProfileSerializer( author_profile, context={ "fields": ["id", "host", "display_name", "url"] }).data, "friend": UserProfileSerializer( friend_profile, context={ "fields": ["id", "host", "display_name", "url"] }).data, } foreign_server = Server.objects.get(url=friend_host_name) resp = ForeignServerHttpUtils.post(foreign_server, "/unfollow", json=unfollow_body) if resp.status_code != 200: raise Exception( "send unfollow to remote server failed, reason={}". format(resp.content)) else: # if the author is remote author_profile = UserProfile.objects.get(url=body["author"]["id"]) friend_profile = User.objects.get( pk=int(body["friend"]["id"].split("/")[-1])).profile # if not is_local: # raise Exception("so far, only can handle local user request") # # check if the unfriend person exist on our server # friend_url = body["friend"]["id"] # friend_profile = UserProfile.objects.get(url=friend_url) # # # check and get author profile # author_url = body["author"]["id"] # author_profile = UserProfile.objects.get(url=author_url) # # check if the request user does friend with aim person qs1 = Friend.objects.filter(profile1=friend_profile, profile2=author_profile) qs2 = Friend.objects.filter(profile1=author_profile, profile2=friend_profile) if (not qs1.exists()) and (not qs2.exists()): raise Exception("they are not friend") else: qs1.delete() qs2.delete() content = { "query": "unfollow", "success": True, "message": "unfollow succeed" } return Response(content, status=status.HTTP_200_OK) except Exception as some_error: return Response( _get_error_response("unfollow", False, str(some_error)), status=status.HTTP_400_BAD_REQUEST, )
def action_friend_request(request, friendrequest_id): try: body_unicode = request.body.decode("utf-8") body = json.loads(body_unicode) # get the friend request first friend_request_obj = FriendRequest.objects.get(pk=friendrequest_id) # check if the request user is the user being friend if request.user.id != friend_request_obj.to_profile.author.id: raise Exception( "the request user doesn't have permission to do action in this friend request" ) if body["query"] != "friendrequestAction": raise Exception("query should be friendrequestAction") # check the accepted information if body["accepted"]: # if the friend request author is remote user parsed_friend_uri = urlparse(friend_request_obj.from_profile.url) author_host_name = "{uri.scheme}://{uri.netloc}".format( uri=parsed_friend_uri) # print("asfsdf", author_host_name) if author_host_name != settings.HYPERION_HOSTNAME: # send friendrequest(reverse edition) friend_request_reverse_body = { "query": "friendrequest", "author": UserProfileSerializer( friend_request_obj.to_profile, context={ "fields": ["id", "host", "display_name", "url"] }, ).data, "friend": UserProfileSerializer( friend_request_obj.from_profile, context={ "fields": ["id", "host", "display_name", "url"] }, ).data, } # print(friend_request_reverse_body["friend"]["host"]) foreign_server = Server.objects.get( url=friend_request_reverse_body["friend"]["host"]) resp = ForeignServerHttpUtils.post( foreign_server, "/friendrequest", json=friend_request_reverse_body) if resp.status_code != 200: raise Exception( "send back friendrequest to remote server failed, reason={}" .format(resp.content)) friend_request_obj.to_profile.accept_friend_request( friend_request_obj.from_profile) msg = "accept the friend request" accepted = True else: friend_request_obj.to_profile.decline_friend_request( friend_request_obj.from_profile) msg = "decline the friend request" accepted = False serializer = FriendRequestSerializer( friend_request_obj, context={"user_fields": ["id", "host", "display_name", "url"]}) content = { "query": "friendrequestAction", "friendrequest": serializer.data, "accepted": accepted, "success": True, "message": msg, } return Response(content, status=status.HTTP_200_OK) except FriendRequest.DoesNotExist: return Response( _get_error_response("friendrequestAction", False, "friend request is not exist"), status=status.HTTP_400_BAD_REQUEST, ) except Exception as some_error: return Response( _get_error_response("friendrequestAction", False, str(some_error)), status=status.HTTP_400_BAD_REQUEST, )
def friend_request(request): if request.method == "GET": # get all friend request which to_friend would be request.user friend_request_list = FriendRequest.objects.filter( to_profile=request.user.profile) content = { "query": "friendrequests", "frinedrequests": FriendRequestSerializer( friend_request_list, many=True, context={ "user_fields": ["id", "host", "display_name", "url"] }, ).data, } return Response(content, content_type="application/json", status=status.HTTP_200_OK) elif request.method == "POST": try: body = json.loads(request.body.decode("utf-8")) if body["query"] != "friendrequest": raise Exception("query should be friendrequest") # get the host from url to compare with host attribute # https://stackoverflow.com/questions/9626535/get-protocol-host-name-from-url # host_name = "{uri.scheme}://{uri.netloc}".format(uri=urlparse(body["author"]["id"])) # # if host_name != body["author"]["host"]: # raise Exception("we can't save the profile which host != url.host") friend_host_name = "{uri.scheme}://{uri.netloc}".format( uri=urlparse(body["friend"]["id"])) # check if the request user is local or remote is_local = False try: server = request.user.server except User.server.RelatedObjectDoesNotExist: # pylint: disable=no-member is_local = True if is_local: # if author is local # get the author profile author_profile = User.objects.get( pk=int(body["author"]["id"].split("/")[-1])).profile # check if the to_friend is local or remote if friend_host_name == settings.HYPERION_HOSTNAME: # friend is local friend_profile = User.objects.get( pk=int(body["friend"]["id"].split("/")[-1])).profile else: # friend is remote # check if the friend profile exists try: friend_profile = UserProfile.objects.get( url=body["friend"]["id"]) except UserProfile.DoesNotExist: remote_server_user = User.objects.get( profile__url=friend_host_name) friend_profile = UserProfile.objects.create( # TO DO what's the default value of display_name display_name=body["friend"].get("displayName", ""), host=remote_server_user.server, url=body["friend"]["id"], ) # send friend request to remote server friend_request_body = { "query": "friendrequest", "author": UserProfileSerializer( author_profile, context={ "fields": ["id", "host", "display_name", "url"] }, ).data, "friend": UserProfileSerializer( friend_profile, context={ "fields": ["id", "host", "display_name", "url"] }, ).data, } foreign_server = Server.objects.get(url=friend_host_name) resp = ForeignServerHttpUtils.post( foreign_server, "/friendrequest", json=friend_request_body) if resp.status_code != 200: raise Exception( "send friendrequest to remote server failed, reason={}" .format(resp.content)) else: # if author is remote # check if the author profile exists try: author_profile = UserProfile.objects.get( url=body["author"]["id"]) except UserProfile.DoesNotExist: author_profile = UserProfile.objects.create( display_name=body["author"].get("displayName", ""), host=server, url=body["author"]["id"], ) # friend must be local friend_profile = User.objects.get( pk=int(body["friend"]["id"].split("/")[-1])).profile # if there are already friend => return 204 try: author_profile.send_friend_request(friend_profile) except FriendAlreadyExist: return Response(status=status.HTTP_204_NO_CONTENT) # check if already get reverse edition friend request reverse_friend_request = FriendRequest.objects.filter( from_profile=friend_profile, to_profile=author_profile) if reverse_friend_request.exists(): Friend.objects.create(profile1=author_profile, profile2=friend_profile) FriendRequest.objects.filter( from_profile=author_profile, to_profile=friend_profile).delete() FriendRequest.objects.filter( from_profile=friend_profile, to_profile=author_profile).delete() return Response( { "query": "friendrequest", "success": True, "message": "Two way friendrequests create friendship", }, status=status.HTTP_200_OK, ) return Response( { "query": "friendrequest", "success": True, "message": "friendrequest sent" }, status=status.HTTP_200_OK, ) except Exception as some_error: # template = "An exception of type {0} occurred. Arguments:\n{1!r}" # message = template.format(type(some_error).__name__, some_error.args) # print(message) return Response( _get_error_response("friendrequest", False, str(some_error)), status=status.HTTP_400_BAD_REQUEST, )
def new_comment(self, request, pk=None): # pylint: disable=invalid-name """ POST /posts/{post_id}/comments """ is_local = False try: # check if authenticated user is a server or local user request.user.server except User.server.RelatedObjectDoesNotExist: # pylint: disable=no-member # local user shoud not have an one-to-one relationship with Server is_local = True body = request.data comment_data = body.get("comment", None) author = comment_data.get("author", None) author_id = author.get("id", None) post_id = body.get("post", None) post_url = urlparse(post_id) author_profile = None is_foaf = False if is_local: # handle local author request post_url_host = "{}://{}".format(post_url.scheme, post_url.netloc) if post_url_host == settings.HYPERION_HOSTNAME: # comment on local post post_pk = post_url.path.split("/")[-1] comment_data["post"] = post_pk author_profile = request.user.profile comment_data["author"] = str(author_profile.id) else: # comment on foreign post try: foreign_server = Server.objects.get(url=post_url_host) post_pk = post_url.path.split("/")[-1] resp = ForeignServerHttpUtils.post(foreign_server, "/posts/1/comments", json=body) if resp.status_code != 200: return Response( { "query": "addComment", "success": False, "message": resp.content }, status=status.HTTP_500_INTERNAL_SERVER_ERROR, ) return Response({ "query": "addComment", "success": True, "message": "Comment Created" }) except RequestException as exception: return Response( { "query": "addComment", "success": False, "message": "Not my fault.", "error": exception, }, status=status.HTTP_500_INTERNAL_SERVER_ERROR, ) except Server.DoesNotExist: return Response( { "query": "addComment", "success": False, "message": "Target foreign server is not support", }, status=status.HTTP_400_BAD_REQUEST, ) else: # handle foreign server request server = request.user.server author_profile, _ = UserProfile.objects.filter( Q(url=author_id)).get_or_create(url=author_id, display_name=author.get( "display_name", None), host=server) post_pk = post_url.path.split("/")[-1] comment_data["post"] = post_pk comment_data["author"] = str(author_profile.id) # check if they are foaf is_foaf = Post.objects.get( pk=post_pk).author.check_remote_foaf_relationship(author_id) post_data = get_object_or_404(Post, pk=post_pk) accessible = post_data.is_accessible(post_data, author_profile) # validate query name if body.get("query", None) != "addComment": return Response( { "query": "addComment", "success": False, "message": "Bad Request" }, status=status.HTTP_422_UNPROCESSABLE_ENTITY, ) # validate accessibility if not accessible and not is_foaf: return Response( { "query": "addComment", "success": False, "message": "Forbidden access" }, status=status.HTTP_403_FORBIDDEN, ) serializer = CommentSerializer(data=comment_data) if serializer.is_valid(): serializer.save() else: return Response( { "query": "addComment", "success": False, "message": serializer.errors }, status=status.HTTP_422_UNPROCESSABLE_ENTITY, ) return Response({ "query": "addComment", "success": True, "message": "Comment Created" })