Problem/Motivation

Mapillary already depends on Entity API (entity:entity in mapillary.info.yml), but none of its handlers are used. All Mapillary entity types rely on core handlers only, which makes access all-or-nothing.

  • The five content entity types (mapillary_image, mapillary_sequence, mapillary_organization, mapillary_map_feature, mapillary_detection) use the core EntityAccessControlHandler with an admin_permission. Every operation, including view, requires administer mapillary_*, which is a "restrict access" permission. There is no way to let an editor, or an anonymous visitor, view Mapillary entities without granting full administration.
  • There are no bundle-level permissions, although four of the five types are bundleable (image, sequence, map feature, detection).
  • Permissions for the admin listings are inconsistent. The list builder collection routes (/admin/content/mapillary/image, ...) require the per-type administer mapillary_* permission, while the Views listings (/admin/content/mapillary/images, ...) require the global administer mapillary permission.
  • Entity pages (canonical, edit, delete) have no View/Edit/Delete local tasks. The existing *.links.task.yml files only cover the settings pages.

Entity API provides exactly these pieces, and the module already requires it.

Steps to reproduce

  1. Create a role without any administer mapillary_* permission and give it to a user.
  2. As that user, open an imported Mapillary image at /admin/content/mapillary/image/{id}: access denied.
  3. On /admin/people/permissions, look for a permission to view Mapillary images or a single bundle of them: there is none.

Proposed resolution

1. Content entity types

Add or replace these handlers on the five content entity types:

  • permission_provider: Drupal\entity\EntityPermissionProvider. Generates per-type and per-bundle permissions.
  • access: Drupal\entity\EntityAccessControlHandler. Checks {operation} {entity_type} or {operation} {bundle} {entity_type}; the admin permission still allows everything. Requires the permission provider above.
  • query_access: Drupal\entity\QueryAccess\QueryAccessHandler. Filters access-checked entity queries and Views on Mapillary base tables to the bundles the user may view.
  • route_provider.html: Drupal\entity\Routing\AdminHtmlRouteProvider. The collection route accepts access {entity_type} overview in addition to the admin permission.
  • local_task_provider.default: Drupal\entity\Menu\DefaultEntityLocalTaskProvider. Adds View/Edit/Delete tabs on entity pages.

Set permission_granularity to 'bundle' on the four bundleable types and keep the default ('entity_type') on mapillary_organization.

Mapillary entities have no owner (EntityOwnerInterface) and no published status (EntityPublishedInterface), so the generated permissions are plain view/update/delete, without "own/any" or "unpublished" variants:

  • administer {entity_type}: all types, existing name.
  • access {entity_type} overview: all types.
  • view {entity_type}: all types.
  • view {bundle} {entity_type}, update {bundle} {entity_type}, delete {bundle} {entity_type}: bundleable types.
  • update {entity_type}, delete {entity_type}: mapillary_organization.
  • create {bundle} {entity_type} / create {entity_type}: generated, but without effect (see below).

The provider builds the admin permission name from admin_permission, so administer mapillary_image and the other four keep their names and their providing module. The five administer mapillary_* entries can be removed from the *.permissions.yml files, and existing role assignments keep working without an update.

The entity types define no add-form or add-page link templates (entities are imported from the Mapillary API, not created in the UI), so the generated create permissions do not open any route.

2. Bundle config entity types

Use Drupal\entity\BundleEntityAccessControlHandler as the access handler for mapillary_image_type, mapillary_sequence_type, mapillary_map_feature_type and mapillary_detection_type. Users who may view any bundle of the content type get view label access on the bundle entity (exposed bundle filters, bundle labels in listings).

3. Admin Views listings

Change the access setting of the five admin Views (mapillary_images, mapillary_sequences, mapillary_organizations, mapillary_map_features, mapillary_detections) from administer mapillary to the matching access {entity_type} overview permission. Rows are then filtered by query access, so a user sees only the bundles they may view.

Existing sites need a post-update hook for this, because the views are installed from config/install. It should only touch views whose access setting still has the shipped value.

Behaviour changes to decide on

  • Today, a role with only the global administer mapillary permission sees every row in the five Views listings. After this change, Views rows are filtered by query access, so that role would see no rows unless it also has the per-type admin or view permissions. Proposal: the post-update hook grants access {entity_type} overview and view {entity_type} to roles that have administer mapillary, so current visibility is preserved. Feedback welcome, since this grants permissions automatically.
  • Mapillary's own data access is not affected. The importers use load() and loadByProperties(), which do not check access. The explicit entity queries (detection cascade queuer, computed image count field, install/update hooks) all use accessCheck(FALSE).
  • No database schema change; only entity type handler definitions change. A cache rebuild (as run by drush updb) is enough.

Out of scope

  • EntityCollectionLocalActionProvider: there are no add forms to link to.
  • Bundle plugins, entity and bundle duplication, revision UI, delete-multiple forms: they do not match how Mapillary data is created (imported, not authored).
  • Field formatters: mapillary_street_level_viewer, mapillary_sequence_id, mapillary_map_feature_summary and mapillary_leaflet_split_viewer render the referenced entity without a view access check. Whether they should respect the new view permissions is a question for a follow-up issue.
  • JSON:API filtering: Entity API's hook_jsonapi_entity_filter_access() only maps owner and published conditions, so bundle-based conditions do not open JSON:API filtering. Nothing changes there for Mapillary entities.

Remaining tasks

  • Add the handlers and permission_granularity to the five content entity types.
  • Add BundleEntityAccessControlHandler to the four bundle config entity types.
  • Remove the administer mapillary_* entries from the *.permissions.yml files.
  • Update the access setting of the five admin Views in config/install.
  • Post-update hook: update the views access setting and, if agreed, grant the overview and view permissions to roles with administer mapillary.
  • Kernel tests: generated permission list per type and bundle; access matrix (anonymous, bundle view only, per-type admin) for view/update/delete; access-checked entity query filtered by bundle; existing role assignments unchanged.
  • Functional tests: collection route reachable with the overview permission; Views listing rows filtered by bundle permission; View/Edit/Delete tabs present.
  • Update path test for the post-update hook.
  • README and CHANGELOG.

User interface changes

  • New permissions on /admin/people/permissions for every Mapillary content entity type and bundle.
  • View/Edit/Delete tabs on Mapillary entity pages.
  • Admin listings show only the rows the user may view.

API changes

  • Entity type handler definitions change for nine entity types.
  • The administer mapillary_* permissions move from YAML to the Entity API permission provider. Names and providing modules stay the same.

Data model changes

None.

Issue fork mapillary-3627415

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

orkutmuratyilmaz created an issue. See original summary.

orkutmuratyilmaz’s picture

Issue summary: View changes

orkutmuratyilmaz’s picture

The 3627415-use-entity-api branch implements the proposed resolution. Three points differ from the issue summary, all found while testing against the Entity API code.

Views access for the admin listings

The summary proposes the core perm access plugin with access {entity_type} overview. That plugin checks one permission only, so a role with administer mapillary_detection but without the overview permission got a 403 on /admin/content/mapillary/detections, while the entity list route from AdminHtmlRouteProvider accepts either permission (administer {entity_type}+access {entity_type} overview).

The branch adds a Views access plugin, mapillary_entity_overview (Drupal\mapillary\Plugin\views\access\EntityOverviewAccess). It grants access with the admin or the overview permission of the configured entity type, and the five admin Views use it. It lives in the base module because mapillary_organization does not depend on mapillary_entity.

Local tasks

Entity pages get View and Edit tabs from DefaultEntityLocalTaskProvider. There is no Delete tab: like core, Entity API leaves it out on purpose (#1834002: Configuration delete operations are all over the place). Deleting still works through the delete-form route (/admin/content/mapillary/image/{id}/delete), linked from the Delete link at the bottom of the edit form and from the Operations column of the list builder and the admin Views. Both are shown only with delete access.

Bundle labels

view label is the access operation for showing only the label of an entity. For the bundle entities (mapillary_image_type and the others) it decides who sees a bundle name, for example in a bundle column or an exposed bundle filter added to a view. With BundleEntityAccessControlHandler, a user without the admin permission sees a bundle name only if they may view entities of that bundle: view default mapillary_image shows the name of the default image type but not of the other image types, view mapillary_image shows all of them. The admin Views shipped by Mapillary have no bundle column or filter, so they look the same.

Post-update hooks

mapillary_entity, mapillary_map_feature, mapillary_detection and mapillary_organization each get two post-update hooks, sharing Drupal\mapillary\Update\EntityAccessUpdater:

  • *_post_update_entity_api_views_access() switches the view displays whose access setting is still the shipped perm / administer mapillary to the new plugin. Customised displays are left alone.
  • *_post_update_entity_api_grant_overview_permissions() grants access {entity_type} overview and view {entity_type} to roles with administer mapillary, except admin roles and except for the entity types whose admin permission the role already has.

The grant is a separate function, so it can be dropped if automatic grants are not wanted (the open question in the summary).

Checked

  • The generated permissions match the list in the summary. administer mapillary_* keep their names, providing modules and restrict access.
  • Query access applies to the five admin Views on every display (Entity API alters Views queries on the entity base table) and to access-checked entity queries. Importers and cascade queuers are not affected.
  • No add-form or add-page route is generated, so the create permissions open nothing.
  • The view YAML files in config/install are installed as trusted data, so their dependencies are written by hand; a kernel test compares them with the calculated dependencies.

Tests

  • Kernel: handlers and generated permissions per entity type, routes, access matrix (anonymous, bundle viewer, bundle editor, type viewer, overview only, type admin, administer mapillary), bundle-filtered entity queries, bundle view label, Organization access, both post-update hooks and the shipped view dependencies.
  • Functional: collection route with the overview permission, Views rows filtered by bundle permission, View and Edit tabs.
  • MapillaryAdminViewsTest and MapillaryOrganizationsAdminViewTest logged in with administer mapillary only. They now use the overview permissions and assert that administer mapillary alone gets a 403, which is the intended behaviour change.

The module test suite passes (178 tests), PHPCS reports no new errors and PHPStan (level 6) no new errors.

orkutmuratyilmaz’s picture

Status: Active » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.