diff --git src/bp-activity/bp-activity-functions.php src/bp-activity/bp-activity-functions.php
index 6fa46dbef..052a294eb 100644
--- src/bp-activity/bp-activity-functions.php
+++ src/bp-activity/bp-activity-functions.php
@@ -3077,6 +3077,62 @@ function bp_activity_get_permalink( $activity_id, $activity_obj = false ) {
 	return apply_filters_ref_array( 'bp_activity_get_permalink', array( $link, &$activity_obj ) );
 }
 
+/**
+ * Can a user see a particular activity item?
+ *
+ * @since 3.0.0
+ *
+ * @param  object  $activity Activity object.
+ * @param  integer $user_id  User ID.
+ * @return boolean True on success, false on failure.
+ */
+function bp_activity_permalink_access( $activity, $user_id = 0 ) {
+	$bp     = buddypress();
+	$retval = false;
+
+	// Fallback.
+	if ( empty( $user_id ) ) {
+		$user_id = bp_loggedin_user_id();
+	}
+
+	// Admins and moderators can see everything.
+	if ( bp_current_user_can( 'bp_moderate' ) ) {
+		$retval = true;
+	}
+
+	// If activity is from a group, do an extra cap check.
+	if ( bp_is_active( 'groups' ) && $activity->component === $bp->groups->id ) {
+		$group_id = $activity->item_id;
+
+		// Check to see if the user has access to the activity's parent group.
+		$group = groups_get_group( $group_id );
+		if ( $group ) {
+			$retval = $group->user_has_access;
+		}
+
+		// Group admins and mods have access as well.
+		if ( groups_is_user_admin( $user_id, $group_id ) || groups_is_user_mod( $user_id, $group_id ) ) {
+			$retval = true;
+		}
+	}
+
+	// If activity author match user, allow access as well.
+	if ( $user_id === $activity->user_id ) {
+		$retval = true;
+	}
+
+	/**
+	 * Filters whether the current user can has access to an activity item.
+	 *
+	 * @since 3.0.0
+	 *
+	 * @param bool   $retval   Return value.
+	 * @param int    $user_id  Current user ID.
+	 * @param object $activity Activity obhect.
+	 */
+	return apply_filters( 'bp_activity_permalink_access', $retval, $user_id, $activity );
+}
+
 /**
  * Hide a user's activity.
  *
diff --git src/bp-activity/bp-activity-screens.php src/bp-activity/bp-activity-screens.php
index 7de6980a4..43ebdd45c 100644
--- src/bp-activity/bp-activity-screens.php
+++ src/bp-activity/bp-activity-screens.php
@@ -195,19 +195,25 @@ add_action( 'bp_activity_screen_mentions', 'bp_activity_reset_my_new_mentions' )
  *
  * @since 1.2.0
  *
+ * @return bool|string Boolean on false or the template for a single activity item on success.
  */
 function bp_activity_screen_single_activity_permalink() {
-	$bp = buddypress();
-
 	// No displayed user or not viewing activity component.
-	if ( !bp_is_activity_component() )
+	if ( ! bp_is_activity_component() ) {
 		return false;
+	}
 
-	if ( ! bp_current_action() || !is_numeric( bp_current_action() ) )
+	$action = bp_current_action();
+	if ( ! $action || ! is_numeric( $action ) ) {
 		return false;
+	}
 
 	// Get the activity details.
-	$activity = bp_activity_get_specific( array( 'activity_ids' => bp_current_action(), 'show_hidden' => true, 'spam' => 'ham_only', ) );
+	$activity = bp_activity_get_specific( array(
+		'activity_ids' => $action,
+		'show_hidden'  => true,
+		'spam'         => 'ham_only',
+	) );
 
 	// 404 if activity does not exist
 	if ( empty( $activity['activities'][0] ) || bp_action_variables() ) {
@@ -218,37 +224,12 @@ function bp_activity_screen_single_activity_permalink() {
 		$activity = $activity['activities'][0];
 	}
 
-	// Default access is true.
-	$has_access = true;
-
-	// If activity is from a group, do an extra cap check.
-	if ( isset( $bp->groups->id ) && $activity->component == $bp->groups->id ) {
-
-		// Activity is from a group, but groups is currently disabled.
-		if ( !bp_is_active( 'groups') ) {
-			bp_do_404();
-			return;
-		}
-
-		// Check to see if the user has access to to the activity's parent group.
-		if ( $group = groups_get_group( $activity->item_id ) ) {
-			$has_access = $group->user_has_access;
-		}
-	}
-
-	// If activity author does not match displayed user, block access.
-	if ( true === $has_access && bp_displayed_user_id() !== $activity->user_id ) {
-		$has_access = false;
-	}
-
 	/**
-	 * Filters the access permission for a single activity view.
+	 * Check user access to the activity item.
 	 *
-	 * @since 1.2.0
-	 *
-	 * @param array $access Array holding the current $has_access value and current activity item instance.
+	 * @since 3.0.0
 	 */
-	$has_access = apply_filters_ref_array( 'bp_activity_permalink_access', array( $has_access, &$activity ) );
+	$has_access = bp_activity_permalink_access( $activity, bp_displayed_user_id() );
 
 	/**
 	 * Fires before the loading of a single activity template file.
@@ -273,7 +254,7 @@ function bp_activity_screen_single_activity_permalink() {
 		} else {
 			$url = sprintf(
 				wp_login_url( 'wp-login.php?redirect_to=%s' ),
-				esc_url_raw( bp_activity_get_permalink( bp_current_action() ) )
+				esc_url_raw( bp_activity_get_permalink( $action ) )
 			);
 		}
 
@@ -287,7 +268,10 @@ function bp_activity_screen_single_activity_permalink() {
 	 *
 	 * @param string $template Path to the activity template to load.
 	 */
-	bp_core_load_template( apply_filters( 'bp_activity_template_profile_activity_permalink', 'members/single/activity/permalink' ) );
+	$template = apply_filters( 'bp_activity_template_profile_activity_permalink', 'members/single/activity/permalink' );
+
+	// Load the template.
+	bp_core_load_template( $template );
 }
 add_action( 'bp_screens', 'bp_activity_screen_single_activity_permalink' );
 
diff --git tests/phpunit/testcases/activity/functions.php tests/phpunit/testcases/activity/functions.php
index a4b831d0d..eabade4ea 100644
--- tests/phpunit/testcases/activity/functions.php
+++ tests/phpunit/testcases/activity/functions.php
@@ -1475,6 +1475,81 @@ Bar!';
 		$this->assertSame( array(), $found['activities'] );
 	}
 
+	/**
+	 * @group bp_activity_permalink_access
+	 */
+	public function test_user_can_access_their_own_activity() {
+		$u = self::factory()->user->create();
+
+		$a = self::factory()->activity->create( array(
+			'user_id' => $u,
+		) );
+
+		$o = self::factory()->activity->get_object_by_id( $a );
+
+		$this->assertTrue( bp_activity_permalink_access( $o, $u ) );
+	}
+
+	/**
+	 * @group bp_activity_permalink_access
+	 */
+	public function test_user_cant_access_someone_elses_activity() {
+		$u = self::factory()->user->create();
+		$u2 = self::factory()->user->create();
+
+		$a = self::factory()->activity->create( array(
+			'user_id' => $u2,
+		) );
+
+		$o = self::factory()->activity->get_object_by_id( $a );
+
+		$this->assertFalse( bp_activity_permalink_access( $o, $u ) );
+	}
+
+	/**
+	 * @group bp_activity_permalink_access
+	 */
+	public function test_group_admin_access_to_someone_elses_activity() {
+		$u = self::factory()->user->create();
+
+		$g = self::factory()->group->create( array(
+			'status' => 'public',
+		) );
+
+		$a = self::factory()->activity->create( array(
+			'component' => buddypress()->groups->id,
+			'user_id'   => $u,
+			'item_id'   => $g,
+		) );
+
+		$o = self::factory()->activity->get_object_by_id( $a );
+
+		$c = self::factory()->user->create();
+		self::add_user_to_group( $c, $g );
+
+		$m1 = new BP_Groups_Member( $c, $g );
+		$m1->promote( 'admin' );
+
+		$this->assertTrue( bp_activity_permalink_access( $o, $c ) );
+	}
+
+	/**
+	 * @group bp_activity_permalink_access
+	 */
+	public function test_admin_access_to_someone_elses_activity() {
+		$u = self::factory()->user->create();
+		$u2 = self::factory()->user->create( array( 'role' => 'administrator' ) );
+
+		$a = self::factory()->activity->create( array(
+			'user_id' => $u,
+		) );
+
+		$o = self::factory()->activity->get_object_by_id( $a );
+
+		$this->set_current_user( $u2 );
+		$this->assertTrue( bp_activity_permalink_access( $o, $u2 ) );
+	}
+
 	public function check_activity_caches() {
 		foreach ( $this->acaches as $k => $v ) {
 			$this->acaches[ $k ] = wp_cache_get( $k, 'bp_activity' );
