og-8.x-1.x-dev/src/OgAccess.php

src/OgAccess.php
<?php

declare(strict_types=1);

namespace Drupal\og;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\og\Entity\OgRole;
use Drupal\og\Event\GroupContentEntityOperationAccessEvent;
use Drupal\user\EntityOwnerInterface;
use Drupal\user\UserInterface;
use Psr\EventDispatcher\EventDispatcherInterface;

/**
 * The service that determines if users have access to groups and group content.
 */
class OgAccess implements OgAccessInterface {

  /**
   * Group level permission that grants full access to the group.
   *
   * Not to be confused with the 'administer organic groups' global permission
   * which is intended for site builders and gives full access to _all_ groups.
   */
  const ADMINISTER_GROUP_PERMISSION = 'administer group';

  /**
   * Group level permission that allows the user to delete the group entity.
   */
  const DELETE_GROUP_PERMISSION = 'delete group';

  /**
   * Group level permission that allows the user to update the group entity.
   */
  const UPDATE_GROUP_PERMISSION = 'update group';

  /**
   * Maps entity operations performed on groups to group level permissions.
   */
  const OPERATION_GROUP_PERMISSION_MAPPING = [
    'delete' => self::DELETE_GROUP_PERMISSION,
    'update' => self::UPDATE_GROUP_PERMISSION,
  ];

  public function __construct(
    protected readonly ConfigFactoryInterface $configFactory,
    protected readonly AccountProxyInterface $accountProxy,
    protected readonly ModuleHandlerInterface $moduleHandler,
    protected readonly GroupTypeManagerInterface $groupTypeManager,
    protected readonly PermissionManagerInterface $permissionManager,
    protected readonly MembershipManagerInterface $membershipManager,
    protected readonly EventDispatcherInterface $dispatcher,
  ) {}

  /**
   * {@inheritdoc}
   */
  public function userAccess(EntityInterface $group, string $permission, ?AccountInterface $user = NULL, bool $skip_alter = FALSE): AccessResultInterface {
    $group_type_id = $group->getEntityTypeId();
    $bundle = $group->bundle();
    // As Og::isGroup depends on this config, we retrieve it here and set it as
    // the minimal caching data.
    $config = $this->configFactory->get('og.settings');
    $cacheable_metadata = (new CacheableMetadata())
      ->addCacheableDependency($config);

    if (!$this->groupTypeManager->isGroup($group_type_id, $bundle)) {
      // Not a group.
      return AccessResult::neutral()->addCacheableDependency($cacheable_metadata);
    }

    if (!isset($user)) {
      $user = $this->accountProxy->getAccount();
    }

    // From this point on, every result also depends on the user so check
    // whether it is the current. See https://www.drupal.org/node/2628870
    // @todo This doesn't really vary by user but by the user's roles inside of
    //   the group. We should create a cache context for OgRole entities.
    // @see https://github.com/amitaibu/og/issues/219
    if ($user->id() == $this->accountProxy->id()) {
      $cacheable_metadata->addCacheContexts(['user']);
    }

    // User ID 1 has all privileges.
    if ($user->id() == 1) {
      return AccessResult::allowed()->addCacheableDependency($cacheable_metadata);
    }

    // Check if the user has a global permission to administer all groups. This
    // gives full access.
    $user_access = AccessResult::allowedIfHasPermission($user, 'administer organic groups');
    if ($user_access->isAllowed()) {
      return $user_access->addCacheableDependency($cacheable_metadata);
    }

    if ($config->get('group_manager_full_access') && $user->isAuthenticated() && $group instanceof EntityOwnerInterface) {
      $cacheable_metadata->addCacheableDependency($group);
      if ($group->getOwnerId() == $user->id()) {
        return AccessResult::allowed()->addCacheableDependency($cacheable_metadata);
      }
    }

    $permissions = [];
    $user_is_group_admin = FALSE;
    if ($membership = $this->membershipManager->getMembership($group, $user->id())) {
      foreach ($membership->getRoles() as $role) {
        // Check for the is_admin flag.
        if ($role->isAdmin()) {
          $user_is_group_admin = TRUE;
          break;
        }

        $permissions = array_merge($permissions, $role->getPermissions());
      }
    }
    elseif (!$this->membershipManager->isMember($group, $user->id(), [OgMembershipInterface::STATE_BLOCKED])) {
      // User is a non-member or has a pending membership.
      $role = OgRole::loadByGroupAndName($group, OgRoleInterface::ANONYMOUS);
      if ($role) {
        $permissions = $role->getPermissions();
      }
    }

    $permissions = array_unique($permissions);

    if (!$skip_alter && !in_array($permission, $permissions)) {
      // Let modules alter the permissions.
      $context = [
        'permission' => $permission,
        'group' => $group,
        'user' => $user,
      ];
      $this->moduleHandler->alter('og_user_access', $permissions, $cacheable_metadata, $context);
    }

    // Check if the user is a group admin and who has access to all the group
    // permissions.
    // @todo It should be possible for modules to alter the permissions even if
    //   the user is a group admin, UID 1 or has 'administer group' permission.
    if ($user_is_group_admin || in_array($permission, $permissions)) {
      // User is a group admin, and we do not ignore this special permission
      // that grants access to all the group permissions.
      return AccessResult::allowed()->addCacheableDependency($cacheable_metadata);
    }

    return AccessResult::neutral()->addCacheableDependency($cacheable_metadata);
  }

  /**
   * {@inheritdoc}
   */
  public function userAccessEntity(string $permission, EntityInterface $entity, ?AccountInterface $user = NULL): AccessResultInterface {
    $result = AccessResult::neutral();

    $entity_type = $entity->getEntityType();
    $entity_type_id = $entity_type->id();
    $bundle = $entity->bundle();

    if ($this->groupTypeManager->isGroup($entity_type_id, $bundle)) {
      // An entity can be a group and group content in the same time. If the
      // group returns a neutral result the user still might have access to
      // the permission in group content context. So if we get a neutral result
      // we will continue with the group content access check below.
      $result = $this->userAccess($entity, $permission, $user);
      if (!$result->isNeutral()) {
        return $result;
      }
    }

    if ($this->groupTypeManager->isGroupContent($entity_type_id, $bundle)) {
      $result->addCacheTags($entity_type->getListCacheTags());

      // The entity might be a user or a non-user entity.
      $groups = $entity instanceof UserInterface ? $this->membershipManager->getUserGroups($entity->id()) : $this->membershipManager->getGroups($entity);

      if ($groups) {
        foreach ($groups as $entity_groups) {
          foreach ($entity_groups as $group) {
            $result = $result->orIf($this->userAccess($group, $permission, $user));
          }
        }
      }
    }

    return $result;
  }

  /**
   * {@inheritdoc}
   */
  public function userAccessEntityOperation(string $operation, EntityInterface $entity, ?AccountInterface $user = NULL): AccessResultInterface {
    $result = AccessResult::neutral();

    $entity_type = $entity->getEntityType();
    $entity_type_id = $entity_type->id();
    $bundle = $entity->bundle();

    if ($this->groupTypeManager->isGroup($entity_type_id, $bundle)) {
      // We are performing an entity operation on a group entity. Map the
      // operation to the corresponding group level permission.
      if (array_key_exists($operation, self::OPERATION_GROUP_PERMISSION_MAPPING)) {
        $permission = self::OPERATION_GROUP_PERMISSION_MAPPING[$operation];

        // An entity can be a group and group content in the same time. If the
        // group returns a neutral result the user still might have access to
        // the permission in group content context. So if we get a neutral
        // result we will continue with the group content access check below.
        $result = $this->userAccess($entity, $permission, $user);
        if (!$result->isNeutral()) {
          return $result;
        }
      }
    }

    if ($this->groupTypeManager->isGroupContent($entity_type_id, $bundle)) {
      $result->addCacheTags($entity_type->getListCacheTags());

      // The entity might be a user or a non-user entity.
      $groups = $entity instanceof UserInterface ? $this->membershipManager->getUserGroups($entity->id()) : $this->membershipManager->getGroups($entity);

      if ($groups) {
        foreach ($groups as $entity_groups) {
          foreach ($entity_groups as $group) {
            $result = $result->orIf($this->userAccessGroupContentEntityOperation($operation, $group, $entity, $user));
          }
        }
      }
    }

    return $result;
  }

  /**
   * {@inheritdoc}
   */
  public function userAccessGroupContentEntityOperation(string $operation, EntityInterface $group_entity, EntityInterface $group_content_entity, ?AccountInterface $user = NULL): AccessResultInterface {
    // Default to the current user.
    $user = $user ?: $this->accountProxy->getAccount();

    $event = new GroupContentEntityOperationAccessEvent($operation, $group_entity, $group_content_entity, $user);

    // @todo This doesn't really vary by user but by the user's roles inside of
    //   the group. We should create a cache context for OgRole entities.
    // @see https://github.com/amitaibu/og/issues/219
    $event->addCacheableDependency($group_content_entity);
    if ($user->id() == $this->accountProxy->id()) {
      $event->addCacheContexts(['user']);
    }

    $this->dispatcher->dispatch($event, GroupContentEntityOperationAccessEvent::EVENT_NAME);

    return $event->getAccessResult();
  }

  /**
   * {@inheritdoc}
   */
  public function reset(): void {
    @trigger_error('OgAccessInterface::reset() is deprecated in og:8.1.0-alpha6 and is removed from og:8.1.0-beta1. The static cache has been removed and this method no longer serves any purpose. Any calls to this method can safely be removed. See https://github.com/Gizra/og/issues/654', E_USER_DEPRECATED);
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc