Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resolvers: use the role ID rather than name to resolve the entity #140

Closed
wants to merge 2 commits into from

Conversation

max-moser
Copy link
Contributor

The GroupResolver already uses the role ID rather than their name for creating reference dictionaries.

More context

More context, as taken from the Discord server:

hey, i've been trying out tu-graz-library/invenio-curations for a bit and it looks like the creation of curation requests is failing, as that uses roles (groups) as receivers
digging a little bit deeper, it looks like the root cause is a mismatch between using role.id and role.name for creating reference dicts and resolution of them later:

the creation of reference dicts for groups uses entity.id: https://github.com/inveniosoftware/invenio-users-resources/blob/master/invenio_users_resources/entity_resolvers.py#L176
but the resolution queries the database based on Role.name: https://github.com/inveniosoftware/invenio-users-resources/blob/master/invenio_users_resources/entity_resolvers.py#L127

this entails that the basic axiom of resolve(reference(x)) == x fails:

In [17]: from invenio_requests.services.results import ResolverRegistry

In [18]: admin_role
Out[18]: <Role 1>

In [19]: ResolverRegistry.resolve_entity(ResolverRegistry.reference_entity(admin_role))
Out[19]: {}

In [20]: ResolverRegistry.resolve_entity(ResolverRegistry.reference_entity(a)) == admin_role
Out[20]: False

the version in use for our local install is invenio-users-resources v5.2.0, but the above linked code is the same for later versions

* the GroupResolver already uses the role ID rather than their name for
  creating reference dictionaries
@max-moser
Copy link
Contributor Author

Note: The tests for both App-RDM as well as RDM-Records pass with these changes installed.

Are there any other places where this may be used, e.g. custom modules for Zenodo?

@max-moser
Copy link
Contributor Author

It looks like the groups service still resolves roles by name.
This is relevant for creating access grants for groups for RDMRecords (which is the way Invenio-Curations works), where the following steps are performed:

  1. create a data dictionary specifying the grants to be created [1]
  2. call rdm_records_service.access.bulk_create_grants(system_identity, recid, data) [2]
  3. the access service validates the "role" subject [3] via the groups service
  4. the groups service looks up the group by name [4,5] rather than by ID

[1] e.g. {'grants': [{'permission': 'preview', 'subject': {'type': 'role', 'id': '1'}, 'origin': 'request:0e85afe9-47fc-4a4f-9558-a1b8fe96c89e'}]}
[2] e.g. invenio_curations.requests.curation
[3] invenio_rdm_records.services.access.service
[4] invenio_users_resources.services.groups.service
[5] invenio_users_resources.records.api

@max-moser
Copy link
Contributor Author

Closing this, as the full transition seems to be WIP: inveniosoftware/invenio-access#205
Currently, the use of id and name seem to be a bit mixed up, which doesn't matter for newly created roles as the ID is generally set to the same value as the name.
For roles that have existed on an older version of InvenioRDM and were upgraded to v12, this may be problematic.
The following basic script can be used to alleviate this pain point:

from invenio_access.models import ActionRoles
from invenio_accounts.models import Role, User
from invenio_db import db

# remember each user's roles, and the existing ActionRoles
user_roles = {u: [r.name for r in u.roles] for u in User.query.all()}
action_roles = [
    {
        "id": ar.id,
        "action": ar.action,
        "exclude": ar.exclude,
        "argument": ar.argument,
        "role_id": ar.role.name,
    }
    for ar in ActionRoles.query.all()
]

# basically clear the `accounts_userrole` table to avoid FK violations
for u in user_roles:
    u.roles.clear()
db.session.flush()

# similar, avoid FK violations
for ar in ActionRoles.query.all():
    db.session.delete(ar)
db.session.flush()

# perform the actual update
for r in Role.query.all():
    r.id = r.name
db.session.flush()

# restore the roles for each user
for u, rns in user_roles.items():
    for rn in rns:
        u.roles.append(Role.query.get(rn))
db.session.flush()

# recreate the ActionRoles, including their old IDs
for ar_dict in action_roles:
    db.session.add(ActionRoles(**ar_dict))
db.session.flush()

db.session.commit()

@max-moser max-moser closed this Aug 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant