def check_is_following(self, request, pk): try: Author.objects.get(pk=pk) except: return Response({ "success": False, "message": "Invalid author ID url parameter specified" }, status=404) try: followed = request.data["author"] if ("/author/" not in followed): followed = get_author_url(followed) pk_url = get_author_url(pk) follow = Follow.objects.filter(follower=pk_url, followed=followed) reverse = Follow.objects.filter(follower=followed, followed=pk_url) except: return Response({ "success": False, "message": "The author field was incorrect" }, status=400) return Response({ "isFollowingUser": follow.exists(), "isOtherFollowing": reverse.exists(), "isOtherFriendRequest": FriendRequest.objects.filter(requester=followed, friend=pk_url).exists() }, status=200)
def test_two_friend_requests_diff_users(self): author1Url = get_author_url(str(self.author1.id)) author2Url = get_author_url(str(self.author2.id)) author3Url = get_author_url(str(self.author3.id)) FriendRequest.objects.create(requester=author1Url, friend=author2Url) FriendRequest.objects.create(requester=author3Url, friend=author1Url) response = self.client.get( get_pending_requests_path(str(self.author2.id))) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data), 1) self.assertEqual( response.data[0], { 'displayName': self.author1.get_display_name(), 'id': author1Url, 'host': author1Url.split("/author/")[0], 'url': author1Url }) response = self.client.get( get_pending_requests_path(str(self.author1.id))) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data), 1) self.assertEqual( response.data[0], { 'displayName': self.author3.get_display_name(), 'id': author3Url, 'host': author3Url.split("/author/")[0], 'url': author3Url })
def test_two_friends(self): author1Url = get_author_url(str(self.author1.id)) author2Url = get_author_url(str(self.author2.id)) author3Url = get_author_url(str(self.author3.id)) Follow.objects.create(follower=author2Url, followed=author1Url) Follow.objects.create(follower=author1Url, followed=author2Url) Follow.objects.create(follower=author3Url, followed=author1Url) Follow.objects.create(follower=author1Url, followed=author3Url) response = self.client.get(get_friends_path(str(self.author1.id))) self.assertEqual(response.status_code, 200) self.assertEqual(response.data["query"], "friends") self.assertEqual(len(response.data["authors"]), 2) self.assertTrue(author2Url in response.data["authors"]) self.assertTrue(author3Url in response.data["authors"]) response = self.client.get(get_friends_path(str(self.author2.id))) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data["authors"]), 1) self.assertEqual(response.data["authors"][0], author1Url) response = self.client.get(get_friends_path(str(self.author3.id))) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data["authors"]), 1) self.assertEqual(response.data["authors"][0], author1Url)
def test_one_friend_request_other_user(self): author1Url = get_author_url(str(self.author1.id)) author2Url = get_author_url(str(self.author2.id)) FriendRequest.objects.create(requester=author1Url, friend=author2Url) response = self.client.get( get_pending_requests_path(str(self.author1.id))) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data), 0)
def test_request_from_other_friend(self): author1Url = get_author_url(str(self.author1.id)) author3Url = get_author_url(str(self.author3.id)) FriendRequest.objects.create(requester=author3Url, friend=author1Url) body = get_respond_to_request_body(self.author2.id, False) response = self.client.post(get_respond_to_requests_path( str(self.author1.id)), data=body, content_type="application/json") self.assertEqual(response.status_code, 404)
def test_authenticated_wrong_user(self): self.client.login(username="******", password="******") author1Url = get_author_url(str(self.author1.id)) author3Url = get_author_url(str(self.author3.id)) FriendRequest.objects.create(requester=author3Url, friend=author1Url) body = get_respond_to_request_body(self.author3.id, False) response = self.client.post(get_respond_to_requests_path( str(self.author1.id)), data=body, content_type="application/json") self.assertEqual(response.status_code, 403)
def test_successful_reject_response(self): author1Url = get_author_url(str(self.author1.id)) author3Url = get_author_url(str(self.author3.id)) FriendRequest.objects.create(requester=author3Url, friend=author1Url) body = get_respond_to_request_body(self.author3.id, False) response = self.client.post(get_respond_to_requests_path( str(self.author1.id)), data=body, content_type="application/json") self.assertEqual(response.status_code, 200) self.assertEqual(len(FriendRequest.objects.all()), 0) self.assertEqual(len(Follow.objects.all()), 0)
def test_invalid_user(self): author = setupUser("test") deletedId = str(author.id) author.delete() response = self.client.post(get_path(deletedId), data=get_body(get_author_url(deletedId)), content_type="application/json") self.assertEqual(response.status_code, 404)
def retrieve(self, request, pk): try: author = Author.objects.get(pk=pk) except: print("DID WE GET HERE?", pk) return Response("Invalid author ID specified", status=404) url = get_author_url(pk) data = { "id": url, "host": get_host_url(), "displayName": author.displayName or author.user.username, "url": url, "friends": get_author_summaries(get_friends(url)) } if (author.github): data["github"] = author.github if (author.user.first_name): data["firstName"] = author.user.first_name if (author.user.last_name): data["lastName"] = author.user.last_name if (author.user.email): data["email"] = author.user.email if (author.bio): data["bio"] = author.bio return Response(data)
def get_external_posts(self, request): print("get_external_posts endpoint:", request) user = request.user if ServerUtil.is_server(user): return Response( "Foreign Nodes may not grab posts from this endpoint.", status=401) postUrl = request.query_params.get("postUrl", False) if postUrl: authorUrl = get_author_url( str(request.user.author.pk )) if request.user.is_authenticated else "" sUtil = ServerUtil(postUrl=postUrl) if not sUtil.is_valid(): return Response("No foreign node with the base url: " + postUrl, status=404) success, post = sUtil.get_post( postUrl.split("/posts/")[1], authorUrl) if not success: return Response("Failed to grab foreign post: " + postUrl, status=500) return Response(post) posts = ServerUtil.get_external_posts_aggregate() return Response({"posts": posts})
def handle_friends_post(request, pk): try: authorUrl = get_author_url(pk) success, message = parse_is_friends_with_any(request.data, authorUrl) except: return Response( { "query": "friends", "author": authorUrl, "message": "There was an error parsing the request body", "success": False }, status=400) if not success: return Response( { "query": "friends", "author": authorUrl, "message": message, "success": False }, status=400) authors = set(request.data["authors"]) return Response( { "query": "friends", "author": authorUrl, "authors": list(authors.intersection(get_friends_set(authorUrl))) }, status=200)
def handle_friends_get(request, pk): authorUrl = get_author_url(pk) return Response({ "query": "friends", "authors": get_friends(authorUrl) }, status=200)
def test_no_matching_friends(self, friends_set_mock): friends_set_mock.return_value = set(["http://127.0.0.1:8000/author/yaw"]) data = get_friends_with_any_body(str(self.author1.id), ["http://127.0.0.1:8000/author/yeet"]) response = self.client.post(get_friends_path(str(self.author1.id)), data, content_type="application/json") self.assertEqual(response.status_code, 200) self.assertEqual(response.data["query"], "friends") self.assertEqual(len(response.data["authors"]), 0) self.assertEqual(response.data["author"], get_author_url(str(self.author1.id)))
def test_successful_approve_response_multiple_requests(self): author1Url = get_author_url(str(self.author1.id)) author2Url = get_author_url(str(self.author2.id)) author3Url = get_author_url(str(self.author3.id)) FriendRequest.objects.create(requester=author2Url, friend=author1Url) FriendRequest.objects.create(requester=author3Url, friend=author1Url) body = get_respond_to_request_body(self.author3.id, True) response = self.client.post(get_respond_to_requests_path( str(self.author1.id)), data=body, content_type="application/json") self.assertEqual(response.status_code, 200) self.assertEqual(len(FriendRequest.objects.all()), 1) self.assertIsNotNone( FriendRequest.objects.get(friend=author1Url, requester=author2Url)) self.assertEqual(len(Follow.objects.all()), 1) self.assertIsNotNone( Follow.objects.get(follower=author1Url, followed=author3Url))
def get_friend_requests(self, request, pk): try: author = Author.objects.get(pk=pk) except: return Response("Invalid author ID specified", status=404) requests = FriendRequest.objects.filter(friend=get_author_url(pk)) print("requests found", len(requests)) urls = [] for pending_request in requests: urls.append(pending_request.requester) formatted_requests = get_author_summaries(urls) return Response(formatted_requests, status=200)
def test_two_friends(self, friends_set_mock): commonAuthor = "http://127.0.0.1:8000/author/yeet" commonAuthor2 = "http://127.0.0.1:8000/author/yyy" friends_set_mock.return_value = set([commonAuthor, commonAuthor2]) data = get_friends_with_any_body(str(self.author1.id), [commonAuthor, commonAuthor2, "http://127.0.0.1:8000/author/yaw"]) response = self.client.post(get_friends_path(str(self.author1.id)), data, content_type="application/json") self.assertEqual(response.status_code, 200) self.assertEqual(response.data["query"], "friends") self.assertEqual(len(response.data["authors"]), 2) self.assertTrue(commonAuthor in response.data["authors"]) self.assertTrue(commonAuthor2 in response.data["authors"]) self.assertEqual(response.data["author"], get_author_url(str(self.author1.id)))
def friend_to_friend_query(self, request, pk, other_user): try: author_url = Author.objects.get(pk=pk).get_url() other_url = other_user if "http" in other_user else get_author_url(other_user) follow = Follow.objects.filter(follower=author_url, followed=other_url) reverse = Follow.objects.filter(follower=other_url, followed=author_url) except: return Response({ "success": False, "message": "Invalid author ID url parameter specified", "query": "friends" }, status=404) return Response({ "query": "friends", "authors": [author_url, other_url], "friends": follow.exists() and reverse.exists() }, status=200)
def test_author_private(self): author = setupUser("test_author_foaf_user") aName = "test_author_foaf_user2" aPassword = "******" author2 = setupUser(aName, aPassword) middleAuthor = setupUser("middleman_test_author_foaf") url = getAuthorPostUrl(author) self.client.login(username=aName, password=aPassword) createPostForAuthor(author, "friend content", "FRIENDS") createPostForAuthor(author, "foaf content", "FOAF") createPostForAuthor(author, "private content", "PRIVATE") createPostForAuthor(author, "probably noodz", "PRIVATE", makeVisibleTo=[get_author_url(str(author2.pk))]) # only the visibleTo post should be visible resp = self.client.get(url).data self.assertEqual(1, resp["count"])
def test_see_private(self): aName = "test_see_private_user" aPassword = "******" author1 = setupUser(aName, aPassword) author2 = setupUser("test_see_private_user2") self.client.login(username=aName, password=aPassword) # login to the first author numPublicPosts = Posts.objects.all().filter( visibility__in=["PUBLIC", "SERVERONLY"]).count() # Number of posts should be equal resp = self.client.get("/author/posts/").data self.assertEqual(numPublicPosts, resp["count"]) # add a private post that author 1 is allowed to see createPostForAuthor(author2, "test_see_own_user author's public post", "PUBLIC", [get_author_url(str(author1.pk))]) resp = self.client.get("/author/posts/").data self.assertEqual(numPublicPosts + 1, resp["count"]) self.client.logout()
def create_external_comment(self, request): if (not request.user.is_authenticated): return Response( { "query": "createExternalComment", "message": "You must be authenticated", "success": False }, status=403) try: postUrl = request.data["postUrl"] authorUrl = get_author_url(str(request.user.author.pk)) sUtil = ServerUtil(postUrl=postUrl) if not sUtil.is_valid(): return Response("No foreign node with the base url: " + postUrl, status=404) data = request.data comment = data.get("comment", None) if (isinstance(comment, str)): comment = json.loads(comment) success, res = sUtil.create_comment( postUrl.split("/posts/")[1], authorUrl, comment, postUrl) if not success: return Response("Failed to post foreign comment: " + postUrl, status=500) return Response(res) except Exception as e: print(e) return Response( { "query": "createExternalComment", "message": e, "success": False }, status=400)
def __do_a_get_post(self, user, data, pk): try: post = Posts.objects.get(pk=pk) except: return Response( { "success": False, "message": "No post was found with that ID", "query": "getPost" }, status=404) visibility = post.visibility requestingAuthorUrl = data.get("author", {}).get("url", None) if not requestingAuthorUrl: return Response( "You must specify the URL of the author who is requesting the post.", status=400) postAuthorUrl = get_author_url(str(post.author.pk)) sUtil = ServerUtil(authorUrl=requestingAuthorUrl) if not sUtil.is_valid(): return Response( "Could not find a foreign node matching the reqesting author's url.", status=400) # TODO block pictures or posts based on content type if not sUtil.should_share_posts(): return Response( "This node is currently not sharing posts with the requesting foreign node.", status=400) if visibility == "PUBLIC": serializer = PostsSerializer(post) return Response({ "query": "posts", "count": 1, "size": 1, "posts": [serializer.data] }) # If they are direct friends they can still see a FOAF post if visibility == "FRIENDS" or visibility == "FOAF": local, remote_follow = are_friends(postAuthorUrl, requestingAuthorUrl) success, remote = sUtil.check_direct_friendship( requestingAuthorUrl, postAuthorUrl) if not success: return Response( "Failed to communicate with external server to check friendship.", status=500) if not remote: remote_follow.delete() elif local: # remote = true, local = true, can respond with post return Response({ "query": "posts", "count": 1, "size": 1, "posts": [serializer.data] }) # If we reach here, we know that they are not direct friends # We need to find all the friends of the post writer # and then ask the remote server if any of those friends are friends with the requesting author if visibility == "FOAF": postAuthorFriends = get_friends(postAuthorUrl) success, foafs = sUtil.check_at_least_one_friend( requestingAuthorUrl, postAuthorFriends) if not success: return Response( "Failed to communicate with external server to check foaf-ship.", status=500) if foafs: return Response({ "query": "posts", "count": 1, "size": 1, "posts": [serializer.data] }) if visibility == "PRIVATE": print("UGHHH") return Response({"query": "posts", "count": 0, "size": 1, "posts": []})
def visible_posts(self, request): print("visible_posts endpoint hit") xUser = request.META.get("HTTP_X_REQUEST_USER_ID") page = int(request.query_params.get("page", 0)) + 1 # Must offset page by 1 if page < 1: return Response({ "query": "posts", "message": "Page number must be positive", "success": False }, status=400) size = int(request.query_params.get("size", DEFAULT_POST_PAGE_SIZE)) if size < 0 or size > 100: return Response({ "query": "posts", "message": "Size was invalid", "success": False }, status=400) # Only return public posts if the user isn't authenticated if request.user.is_anonymous: posts = Posts.objects.all().filter(visibility__in=["PUBLIC"], unlisted=False) elif ServerUtil.is_server(request.user): sUtil = ServerUtil(user=request.user) if not sUtil.is_valid(): return Response("This shouldn't happen, server=server!", status=500) elif not xUser: print("No xUser specified, sending all public posts") posts = Posts.objects.all().filter(visibility__in=["PUBLIC"]) elif not sUtil.author_from_this_server(xUser): return Response( "You're trying to access posts for a user that doesn't belong to you. user: "******" server: " + sUtil.get_base_url(), status=400) else: followedByXUser = Follow.objects.values_list("followed", flat=True).filter(follower=xUser) friendsOfXUser = Follow.objects.values_list("follower", flat=True).filter(followed=xUser, follower__in=followedByXUser) friends = [] friends += sUtil.get_friends_of(xUser.split("/author/")[1]) friends += friendsOfXUser friends = list(set(friends)) foafs = [] foafs += friends for friend in friends: print("friend of", xUser, ":", friend) # First check if it's an external user sUtil = ServerUtil(authorUrl=friend) if sUtil.is_valid(): foafs += sUtil.get_friends_of(friend.split("/author/")[1]) else: # it's not external (local), or we don't have that node anymore peopleFollowedByFriend = Follow.objects.values_list("followed", flat=True).filter( follower=friend) friendFriends = Follow.objects.values_list("follower", flat=True).filter(followed=friend, follower__in=peopleFollowedByFriend) foafs += friendFriends baseUrl = get_host_url() foafs = list(set(foafs)) friends = [get_author_id(x) for x in friends if x.startswith(baseUrl)] foafs = [get_author_id(x) for x in foafs if x.startswith(baseUrl)] posts = Posts.objects.all().filter(visibility="PUBLIC", unlisted=False) posts |= Posts.objects.all().filter(visibility="FRIENDS", author_id__in=friends, unlisted=False) posts |= Posts.objects.all().filter(visibility="FOAF", author_id__in=foafs, unlisted=False) posts |= Posts.objects.all().filter(visibility="PRIVATE", visibleTo__contains=[xUser], unlisted=False) else: requestingAuthor = request.user.author.id # Should be guaranteed because not anon # Get direct friends and FOAFs into a dictionary requesterFriends = {} requesterFOAFs = {} for friend in get_friends_from_pk(requestingAuthor): # friend = friend.split("/")[-1] # these are actually "urls", so grab the uuid requesterFriends[friend] = True for friend in requesterFriends: for foaf in get_friends(friend): # friend = friend.split("/")[-1] # these are actually "urls", so grab the uuid # Ensure we don't add direct friends as an FOAF if not requesterFriends.get(foaf, False): requesterFOAFs[foaf] = True try: # Grab the requesting user's posts posts = Posts.objects.all().filter(author=requestingAuthor, unlisted=False) # Grab all public posts posts |= Posts.objects.all().filter(visibility__in=["PUBLIC"], unlisted=False) host_url = get_host_url() # Grab posts from direct friends for friend in requesterFriends: if not friend.startswith(host_url): continue posts |= Posts.objects.all().filter(author=get_author_id(friend), visibility__in=["FRIENDS", "FOAF", "SERVERONLY"], unlisted=False) # Posts from FOAFs for friend in requesterFOAFs: if not friend.startswith(host_url): continue posts |= Posts.objects.all().filter(author=get_author_id(friend), visibility__in=["FOAF"], unlisted=False) # PRIVATE posts that the author can see posts |= Posts.objects.all().filter(visibility="PRIVATE", visibleTo__contains=[get_author_url(str(requestingAuthor))], unlisted=False) except: print("got except!") return Response(status=500) pages = Paginator(posts, size) current_page = pages.page(page) posts = PostsSerializer(current_page, many=True) response = { "query": "posts", "count": pages.count, "size": size, "posts": posts.data } add_page_details_to_response(request, response, current_page, page - 1) return Response(response, status=200)
def author_posts(self, request, pk=None): print("test, hit: author_posts with:", request, pk) page = int(request.query_params.get("page", 0)) + 1 # Must offset page by 1 if page < 1: return Response({ "query": "posts", "message": "Page number must be positive", "success": False }, status=400) size = int(request.query_params.get("size", DEFAULT_POST_PAGE_SIZE)) if size < 0: return Response({ "query": "posts", "message": "Size must be positive", "success": False }, status=400) elif size > 100: return Response({ "query": "posts", "message": "The page size can not be greater than 100", "success": False }, status=400) try: if (is_encoded_external_host(pk)): return get_external_author_posts(request, pk) author = Author.objects.get(pk=pk) except: return Response({ "query": "posts", "message": "You must specify an author.", "success": False }, status=400) # Only return public posts if the user isn't authenticated if request.user.is_anonymous: posts = Posts.objects.all().filter(author=pk, visibility__in=["PUBLIC"], unlisted=False) # else if is other_server: # posts = Posts.objects.all().filter(author=pk, visibility__in=["PUBLIC"]) elif (request.user.author == author): posts = Posts.objects.all().filter(author=pk, unlisted=False) else: requestingAuthor = request.user.author.id # Should be guaranteed because we checked above # post_types will track what level of posts a user can see post_types = ["PUBLIC"] # convert to dict for dat O(1) # Note: this is terrible, we should be using the database more directly requesterFriends = {} for friend in get_friends_from_pk(requestingAuthor): friend = friend.split("/")[-1] requesterFriends[friend] = True # Check if they are direct friends if requesterFriends.get(str(pk), False): post_types += ["FRIENDS", "FOAF", "SERVERONLY"] else: # They are not direct friends, so we should check if they share any friends for friend in get_friends_from_pk(pk): friend = friend.split("/")[-1] if requesterFriends.get(friend, False): post_types += ["FOAF"] break # we don't need to check any more friends try: posts = Posts.objects.all().filter(author=pk, visibility__in=post_types, unlisted=False) # TODO: requestingAuthor is the one it should be visibleTo posts |= Posts.objects.all().filter(author=pk, visibility="PRIVATE", visibleTo__contains=[get_author_url(str(requestingAuthor))], unlisted=False) except: print("got except!") return Response(status=500) github_stream = get_github_activity(author) combined_stream = merge_posts_with_github_activity(posts, github_stream) pages = Paginator(combined_stream, size) current_page = pages.page(page) posts = PostsSerializer(current_page, many=True) response = { "query": "posts", "count": pages.count, "size": size, "posts": posts.data } add_page_details_to_response(request, response, current_page, page - 1) return Response(response, status=200)
def get_friends_from_pk(pk): pk = str(pk) return get_friends(get_author_url(pk))
def validate_friend_request_response(body, pk): success = True message = "Your response has been recorded" friend_request = None friend_data = None if body["query"] != "friendResponse": success = False message = "The query value was not correct" elif not isinstance(body["approve"], bool): success = False message = "The approve value was not a boolean" else: friend_data = get_author_details(body["friend"]) friend_request = FriendRequest.objects.filter(requester=friend_data["id"], friend=get_author_url(pk)) return (success, message, friend_request, friend_data)
def get_friends_with_any_body(userId, friends): return { "query": "friends", "author": get_author_url(str(userId)), "authors": friends }
def handle_friend_request_response(self, request, pk): try: author = Author.objects.get(pk=pk) if ((not request.user.is_authenticated) or request.user.author != author): return Response({ "query": "friendrequest", "success": False, "message": "You must be authenticated as the requested user to perform this action." }, status=status.HTTP_403_FORBIDDEN) except: return Response({ "query": "friendrequest", "success": False, "message": "Invalid author ID specified" }, status=404) try: message = "The request body could not be parsed" body = request.data success, message, friend_request, friend_data = validate_friend_request_response(body, pk) except: return Response({ "query": "friendrequest", "success": False, "message": message }, status=400) if not success: return Response({ "query": "friendrequest", "success": False, "message": message }, status=400) if not friend_request: return Response({ "query": "friendrequest", "success": False, "message": "Could not find a friend request from the specified author" }, status=404) # check if this is an external friendship localAuthorUrl = get_author_url(pk) if localAuthorUrl.split("/author/")[0] != friend_data["url"].split("/author/")[0]: xServerAuthorUrl = friend_data["url"] xServerBody = { "query": "friendrequest", "friend": friend_data, "author": { "displayName": author.displayName, "host": localAuthorUrl.split("/author/")[0], "id": localAuthorUrl, "url": localAuthorUrl } } print(xServerBody) server = ServerUtil(authorUrl=xServerAuthorUrl) # if we fail to notify the external server we can't proceed with the friendship if not server.is_valid() or not server.notify_server_of_friendship(xServerBody): return Response({ "query": "friendrequest", "success": False, "message": "Failed to notify external server." }, status=500) if (body["approve"]): Follow.objects.create(follower=get_author_url(pk), followed=friend_data["url"]) friend_request.delete() response = { "message": message, "success": success, "query": "friendrequest" } return Response(response, status=200)
def setUp(self): self.author1 = get_author_url(str(setupUser("one").id)) self.author2 = get_author_url(str(setupUser("two").id)) self.author3 = get_author_url(str(setupUser("three").id))
def makeFriends(author1, author2): Follow.objects.create(follower=get_author_url(str(author1.pk)), followed=get_author_url(str(author2.pk))) Follow.objects.create(follower=get_author_url(str(author2.pk)), followed=get_author_url(str(author1.pk)))
def setUp(self): self.author1 = get_author_url(str(setupUser("yeet", "password").id)) self.author2 = get_author_url(str(setupUser("yaw", "password").id)) self.author3 = get_author_url(str(setupUser("yaw2", "password").id)) self.client.login(username="******", password="******")