Security update for permissions_by_term
[yaffs-website] / web / modules / contrib / permissions_by_term / src / Service / AccessStorage.php
similarity index 59%
rename from web/modules/contrib/permissions_by_term/src/AccessStorage.php
rename to web/modules/contrib/permissions_by_term/src/Service/AccessStorage.php
index 50961cef7e35ea6598788c17826fbed6435e0834..2342e898837846e100e24e21878f80d75175c55f 100644 (file)
@@ -1,31 +1,26 @@
 <?php
 
-namespace Drupal\permissions_by_term;
+namespace Drupal\permissions_by_term\Service;
 
-use Drupal\Core\Database\Driver\mysql\Connection;
+use Drupal\Core\Database\Connection;
 use Drupal\Component\Utility\Tags;
 use Drupal\Core\Form\FormState;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\user\Entity\User;
 
 /**
  * Class AccessStorage.
  *
- * Defines an API to the database in the term access context.
- *
- * The "protected" class methods are meant for protection regarding Drupal's
- * forms and presentation layer.
- *
- * The "public" class methods can be used for extensions.
- *
  * @package Drupal\permissions_by_term
  */
-class AccessStorage implements AccessStorageInterface {
+class AccessStorage {
 
   /**
-   * Drupal\Core\Database\Driver\mysql\Connection definition.
+   * The database connection.
    *
-   * @var Drupal\Core\Database\Driver\mysql\Connection
+   * @var Connection
    */
-  protected $oDatabase;
+  protected $database;
 
   /**
    * The term name for which the access is set.
@@ -48,14 +43,32 @@ class AccessStorage implements AccessStorageInterface {
    */
   protected $aSubmittedRolesGrantedAccess;
 
+  /**
+   * @var Term
+   */
+  protected $term;
+
+  /**
+   * @var string
+   */
+  const NODE_ACCESS_REALM = 'permissions_by_term';
+
+  /**
+   * @var AccessCheck
+   */
+  protected $accessCheck;
+
   /**
    * AccessStorageService constructor.
    *
-   * @param \Drupal\Core\Database\Driver\mysql\Connection $database
-   *   The connection to the database.
+   * @param Connection  $database
+   * @param Term        $term
+   * @param AccessCheck $accessCheck
    */
-  public function __construct(Connection $database) {
-    $this->oDatabase  = $database;
+  public function __construct(Connection $database, Term $term, AccessCheck $accessCheck) {
+    $this->database  = $database;
+    $this->term = $term;
+    $this->accessCheck = $accessCheck;
   }
 
   /**
@@ -76,7 +89,7 @@ class AccessStorage implements AccessStorageInterface {
   }
 
   /**
-   * {@inheritdoc}
+   * @param FormState $form_state
    */
   public function checkIfUsersExists(FormState $form_state) {
     $sAllowedUsers = $form_state->getValue('access')['user'];
@@ -94,10 +107,12 @@ class AccessStorage implements AccessStorageInterface {
   }
 
   /**
-   * {@inheritdoc}
+   * @param int $term_id
+   *
+   * @return array
    */
-  public function getExistingUserTermPermissionsByTid($term_id) {
-    return $this->oDatabase->select('permissions_by_term_user', 'pu')
+  public function getUserTermPermissionsByTid($term_id) {
+    return $this->database->select('permissions_by_term_user', 'pu')
       ->condition('tid', $term_id)
       ->fields('pu', ['uid'])
       ->execute()
@@ -105,10 +120,57 @@ class AccessStorage implements AccessStorageInterface {
   }
 
   /**
-   * {@inheritdoc}
+   * @param int   $uid
+   * @param array $rids
+   *
+   * @return array
+   */
+  public function getPermittedTids($uid, $rids) {
+    $permittedTids = $this->database->select('permissions_by_term_user', 'pu')
+      ->condition('uid', $uid)
+      ->fields('pu', ['tid'])
+      ->execute()
+      ->fetchCol();
+
+    foreach ($rids as $rid) {
+      $permittedTidsByRid = $this->database->select('permissions_by_term_role', 'pr')
+        ->condition('rid', $rid)
+        ->fields('pr', ['tid'])
+        ->execute()
+        ->fetchCol();
+
+      $permittedTids = array_merge($permittedTidsByRid, $permittedTids);
+    }
+
+    return array_unique($permittedTids);
+  }
+
+  /**
+   * @param array $tids
+   *
+   * @return array
+   */
+  public function getUserTermPermissionsByTids($tids) {
+    $uids = [];
+
+    foreach ($tids as $tid) {
+      if (!empty($tmpUids = $this->getUserTermPermissionsByTid($tid))) {
+        foreach ($tmpUids as $tmpUid) {
+          $uids[] = $tmpUid;
+        }
+      }
+    }
+
+    return $uids;
+  }
+
+  /**
+   * @param int $term_id
+   *
+   * @return array
    */
-  public function getExistingRoleTermPermissionsByTid($term_id) {
-    return $this->oDatabase->select('permissions_by_term_role', 'pr')
+  public function getRoleTermPermissionsByTid($term_id) {
+    return $this->database->select('permissions_by_term_role', 'pr')
       ->condition('tid', $term_id)
       ->fields('pr', ['rid'])
       ->execute()
@@ -116,10 +178,32 @@ class AccessStorage implements AccessStorageInterface {
   }
 
   /**
-   * {@inheritdoc}
+   * @param array $tids
+   *
+   * @return array
+   */
+  public function getRoleTermPermissionsByTids($tids) {
+    $rids = [];
+
+    foreach ($tids as $tid) {
+      $tmpRids = $this->getRoleTermPermissionsByTid($tid);
+      if (!empty($tmpRids)) {
+        foreach ($tmpRids as $tmpRid) {
+          $rids[] = $tmpRid;
+        }
+      }
+    }
+
+    return $rids;
+  }
+
+  /**
+   * @param string $sUsername
+   *
+   * @return int
    */
   public function getUserIdByName($sUsername) {
-    return $this->oDatabase->select('users_field_data', 'ufd')
+    return $this->database->select('users_field_data', 'ufd')
       ->condition('name', $sUsername)
       ->fields('ufd', ['uid'])
       ->execute()
@@ -127,7 +211,9 @@ class AccessStorage implements AccessStorageInterface {
   }
 
   /**
-   * {@inheritdoc}
+   * @param array $aUserNames
+   *
+   * @return array
    */
   public function getUserIdsByNames($aUserNames) {
     $aUserIds = [];
@@ -139,10 +225,12 @@ class AccessStorage implements AccessStorageInterface {
   }
 
   /**
-   * {@inheritdoc}
+   * @param int $term_id
+   *
+   * @return array
    */
   public function getAllowedUserIds($term_id) {
-    $query = $this->oDatabase->select('permissions_by_term_user', 'p')
+    $query = $this->database->select('permissions_by_term_user', 'p')
       ->fields('p', ['uid'])
       ->condition('p.tid', $term_id);
 
@@ -152,11 +240,12 @@ class AccessStorage implements AccessStorageInterface {
   }
 
   /**
-   * {@inheritdoc}
+   * @param array $aUserIdsAccessRemove
+   * @param int   $term_id
    */
   public function deleteTermPermissionsByUserIds($aUserIdsAccessRemove, $term_id) {
     foreach ($aUserIdsAccessRemove as $iUserId) {
-      $this->oDatabase->delete('permissions_by_term_user')
+      $this->database->delete('permissions_by_term_user')
         ->condition('uid', $iUserId, '=')
         ->condition('tid', $term_id, '=')
         ->execute();
@@ -164,11 +253,12 @@ class AccessStorage implements AccessStorageInterface {
   }
 
   /**
-   * {@inheritdoc}
+   * @param array $aRoleIdsAccessRemove
+   * @param int   $term_id
    */
   public function deleteTermPermissionsByRoleIds($aRoleIdsAccessRemove, $term_id) {
     foreach ($aRoleIdsAccessRemove as $sRoleId) {
-      $this->oDatabase->delete('permissions_by_term_role')
+      $this->database->delete('permissions_by_term_role')
         ->condition('rid', $sRoleId, '=')
         ->condition('tid', $term_id, '=')
         ->execute();
@@ -176,47 +266,42 @@ class AccessStorage implements AccessStorageInterface {
   }
 
   /**
-   * {@inheritdoc}
+   * @param int $userId
+   */
+  public function deleteAllTermPermissionsByUserId($userId) {
+    $this->database->delete('permissions_by_term_user')
+      ->condition('uid', $userId, '=')
+      ->execute();
+  }
+
+  /**
+   * @param array $aUserIdsGrantedAccess
+   * @param int   $term_id
+   *
+   * @throws \Exception
    */
   public function addTermPermissionsByUserIds($aUserIdsGrantedAccess, $term_id) {
     foreach ($aUserIdsGrantedAccess as $iUserIdGrantedAccess) {
-      $this->oDatabase->insert('permissions_by_term_user')
+      $this->database->insert('permissions_by_term_user')
         ->fields(['tid', 'uid'], [$term_id, $iUserIdGrantedAccess])
         ->execute();
     }
   }
 
   /**
-   * {@inheritdoc}
+   * @param array $aRoleIdsGrantedAccess
+   * @param int   $term_id
+   *
+   * @throws \Exception
    */
   public function addTermPermissionsByRoleIds($aRoleIdsGrantedAccess, $term_id) {
     foreach ($aRoleIdsGrantedAccess as $sRoleIdGrantedAccess) {
-      $this->oDatabase->insert('permissions_by_term_role')
+      $this->database->insert('permissions_by_term_role')
         ->fields(['tid', 'rid'], [$term_id, $sRoleIdGrantedAccess])
         ->execute();
     }
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getTermIdByName($sTermName) {
-    $aTermId = \Drupal::entityQuery('taxonomy_term')
-      ->condition('name', $sTermName)
-      ->execute();
-    return key($aTermId);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getTermNameById($term_id) {
-    $term_name = \Drupal::entityQuery('taxonomy_term')
-      ->condition('id', $term_id)
-      ->execute();
-    return key($term_name);
-  }
-
   /**
    * Gets the user ids which have been submitted by form.
    *
@@ -264,13 +349,21 @@ class AccessStorage implements AccessStorageInterface {
   }
 
   /**
-   * {@inheritdoc}
+   * Saves term permissions by users.
+   *
+   * Opposite to save term permission by roles.
+   *
+   * @param FormState $form_state
+   * @param int       $term_id
+   *
+   * @return array
+   *   Data for database queries.
    */
   public function saveTermPermissions(FormState $form_state, $term_id) {
-    $aExistingUserPermissions       = $this->getExistingUserTermPermissionsByTid($term_id);
+    $aExistingUserPermissions       = $this->getUserTermPermissionsByTid($term_id);
     $aSubmittedUserIdsGrantedAccess = $this->getSubmittedUserIds();
 
-    $aExistingRoleIdsGrantedAccess = $this->getExistingRoleTermPermissionsByTid($term_id);
+    $aExistingRoleIdsGrantedAccess = $this->getRoleTermPermissionsByTid($term_id);
     $aSubmittedRolesGrantedAccess  = $this->getSubmittedRolesGrantedAccess($form_state);
 
     $aRet = $this->getPreparedDataForDatabaseQueries($aExistingUserPermissions,
@@ -340,7 +433,19 @@ class AccessStorage implements AccessStorageInterface {
   }
 
   /**
-   * {@inheritdoc}
+   * Prepares the data which has to be applied to the database.
+   *
+   * @param array $aExistingUserPermissions
+   *   The permissions for existing user.
+   * @param array $aSubmittedUserIdsGrantedAccess
+   *   The user ids which get access.
+   * @param array $aExistingRoleIdsGrantedAccess
+   *   The existing role ids.
+   * @param array $aSubmittedRolesGrantedAccess
+   *   The user roles which get access.
+   *
+   * @return array
+   *   User ID and role data.
    */
   public function getPreparedDataForDatabaseQueries($aExistingUserPermissions,
                                                     $aSubmittedUserIdsGrantedAccess,
@@ -370,7 +475,13 @@ class AccessStorage implements AccessStorageInterface {
   }
 
   /**
-   * {@inheritdoc}
+   * The form value for allowed users as string to be shown to the user.
+   *
+   * @param User[] $aAllowedUsers
+   *   An array with the allowed users.
+   *
+   * @return null|string
+   *   Either null or the user name.
    */
   public function getUserFormValue($aAllowedUsers) {
 
@@ -381,7 +492,7 @@ class AccessStorage implements AccessStorageInterface {
       foreach ($aAllowedUsers as $oUser) {
         $iUid = intval($oUser->id());
         if ($iUid !== 0) {
-          $sUsername = $oUser->getUsername();
+          $sUsername = $oUser->getDisplayName();
         }
         else {
           $sUsername = t('Anonymous User');
@@ -402,16 +513,21 @@ class AccessStorage implements AccessStorageInterface {
    */
   public function getAllNids()
   {
-    $query = $this->oDatabase->select('node', 'n')
+    $query = $this->database->select('node', 'n')
         ->fields('n', ['nid']);
 
     return $query->execute()
         ->fetchCol();
   }
 
+  /**
+   * @param $nid
+   *
+   * @return array
+   */
   public function getTidsByNid($nid)
   {
-    $node = $this->entityManager->getStorage('node')->load($nid);
+    $node = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
     $tids = [];
 
     foreach ($node->getFields() as $field) {
@@ -430,6 +546,9 @@ class AccessStorage implements AccessStorageInterface {
     return $tids;
   }
 
+  /**
+   * @return array
+   */
   public function getAllUids()
   {
     $nodes = \Drupal::entityQuery('user')
@@ -438,9 +557,14 @@ class AccessStorage implements AccessStorageInterface {
     return array_values($nodes);
   }
 
+  /**
+   * @param $nid
+   *
+   * @return array
+   */
   public function getNodeType($nid)
   {
-    $query = $this->oDatabase->select('node', 'n')
+    $query = $this->database->select('node', 'n')
       ->fields('n', ['type'])
       ->condition('n.nid', $nid);
 
@@ -448,9 +572,14 @@ class AccessStorage implements AccessStorageInterface {
       ->fetchAssoc()['type'];
   }
 
+  /**
+   * @param $nid
+   *
+   * @return array
+   */
   public function getLangCode($nid)
   {
-    $query = $this->oDatabase->select('node', 'n')
+    $query = $this->database->select('node', 'n')
       ->fields('n', ['langcode'])
       ->condition('n.nid', $nid);
 
@@ -458,24 +587,80 @@ class AccessStorage implements AccessStorageInterface {
       ->fetchAssoc()['langcode'];
   }
 
-  public function getGidsByRealm($realm)
+  /**
+   * @param AccountInterface $user
+   *
+   * @return array
+   */
+  public function getGids(AccountInterface $user)
   {
-    $query = $this->oDatabase->select('node_access', 'na')
-      ->fields('na', ['gid'])
-      ->condition('na.realm', $realm);
+    $grants = null;
+
+    if (!empty($permittedNids = $this->computePermittedTids($user))) {
+      $query = $this->database->select('node_access', 'na')
+        ->fields('na', ['gid'])
+        ->condition('na.nid', $permittedNids, 'IN')
+        ->condition('na.realm', self::NODE_ACCESS_REALM);
 
-    $gids = $query->execute()->fetchCol();
+      $gids = $query->execute()->fetchCol();
 
-    foreach ($gids as $gid) {
-      $grants[$realm][] = $gid;
+      foreach ($gids as $gid) {
+        $grants[self::NODE_ACCESS_REALM][] = $gid;
+      }
     }
 
     return $grants;
   }
 
+  private function computePermittedTids(AccountInterface $user)
+  {
+    $nidsWithNoTidRestriction = $this->getNidsWithNoTidRestriction();
+    $nidsByTids = $this->term->getNidsByTids($this->getPermittedTids($user->id(), $user->getRoles()));
+
+    if (\Drupal::config('permissions_by_term.settings.single_term_restriction')->get('value')) {
+      $permittedNids = [];
+      foreach ($nidsByTids as $nid) {
+        if($this->accessCheck->canUserAccessByNodeId($nid)) {
+          $permittedNids[] = $nid;
+        }
+      }
+      $nidsByTids = $permittedNids;
+    }
+
+    if (!empty($nidsByTids)) {
+      return array_merge(
+        $this->getNidsWithNoTidRestriction(),
+        $nidsByTids
+      );
+    }
+
+    return $nidsWithNoTidRestriction;
+  }
+
+  private function getNidsWithNoTidRestriction() {
+    $queryNidsNoRestriction = $this->database->select('node', 'n')
+      ->fields('n', ['nid']);
+
+    $queryNidsNoRestriction->leftJoin('taxonomy_index', 'ti', 'n.nid = ti.nid');
+    $queryNidsNoRestriction->leftJoin('permissions_by_term_user', 'ptu', 'ptu.tid = ti.tid');
+    $queryNidsNoRestriction->condition('ptu.tid', NULL, 'IS');
+    $queryNidsNoRestriction->leftJoin('permissions_by_term_role', 'ptr', 'ptr.tid = ti.tid');
+    $queryNidsNoRestriction->condition('ptr.tid', NULL, 'IS');
+
+     return $queryNidsNoRestriction
+      ->execute()
+      ->fetchCol();
+  }
+
+
+  /**
+   * @param $uid
+   *
+   * @return array
+   */
   public function getAllNidsUserCanAccess($uid)
   {
-    $query = $this->oDatabase->select('node_access', 'na')
+    $query = $this->database->select('node_access', 'na')
       ->fields('na', ['nid'])
       ->condition('na.realm', 'permissions_by_term__uid_' . $uid);
 
@@ -483,9 +668,14 @@ class AccessStorage implements AccessStorageInterface {
       ->fetchCol();
   }
 
+  /**
+   * @param $tid
+   *
+   * @return array
+   */
   public function getNidsByTid($tid)
   {
-      $query = $this->oDatabase->select('taxonomy_index', 'ti')
+      $query = $this->database->select('taxonomy_index', 'ti')
         ->fields('ti', ['nid'])
         ->condition('ti.tid', $tid);