Skip to content

New Group Sync for GN#350

Open
f-necas wants to merge 3 commits intogeorchestra-gn4.4.xfrom
roles-org
Open

New Group Sync for GN#350
f-necas wants to merge 3 commits intogeorchestra-gn4.4.xfrom
roles-org

Conversation

@f-necas
Copy link
Copy Markdown
Collaborator

@f-necas f-necas commented Dec 18, 2025

I introduce here a new synchronization for geonetwork in georchestra in order to solve the current situation of:

  • Org based sync is not granular enough to be able to give a user rights in different groups
  • Role based sync is not understable enough and we easily loose track of what is sync between georchestra's console and GN

How does it work

This new sync works (almost) like Org based sync. Organization in georchestra are mapped to GN groups.

But, we can also create new roles in georchestra like PSC:GN_REVIEWER which will allow user to be REVIEWER of PSC group.

In this way, we can set multiple and differents rights for user like PSC:GN_REVIEWER, C2C:GN_EDITOR and so on.

Top level roles like GN_EDITOR, GN_REVIEWER set this role for organization/group where user belongs to (just as org based sync).

Requires: georchestra/georchestra#4616
and georchestra/georchestra#4656

Examples

User Profile in GN Roles in GN
Organization PSC, Roles: ROLE_USER Guest No group attribution
No Organization , Roles: GN_EDITOR Editor No group attribution
Organization PSC , Roles: GN_EDITOR Editor Editor in PSC Group
Organization PSC , Roles: GN_EDITOR, C3P:GN_REVIEWER Reviewer Editor in PSC Group, Reviewer in C3P Group
No Org, Roles: GN_ADMIN Administrator Admin in all groups

GIP: georchestra/improvement-proposals#16

@f-necas f-necas changed the title roles-org New Group Sync for GN Dec 18, 2025
@f-necas f-necas requested review from a team, jeanpommier, landryb and pmauduit December 19, 2025 07:13
@f-necas f-necas marked this pull request as ready for review December 19, 2025 07:13
@fvanderbiest
Copy link
Copy Markdown
Member

I like the fact that this is an extension of the org-based sync.
As I understand it, it changes nothing for those who do not need it.

Can you explain whether this will be be a new option for the geonetwork.syncMode setting or a replacement of the orgs sync mode ?
https://github.com/georchestra/datadir/blob/92dd72bd7911f38da0779ea8474572af89abed9a/geonetwork/geonetwork.properties#L25-L32

@fvanderbiest
Copy link
Copy Markdown
Member

If the community agrees on this move, it has to be properly documented in addition to this PR.

@jeanpommier
Copy link
Copy Markdown
Member

Looks very interesting indeed.
Sorry, I won't have time to look at the code right now

@landryb
Copy link
Copy Markdown
Member

landryb commented Dec 19, 2025

i dont understand much of it but i dont have the time to properly look, but shouldnt the 'profile in GN' be Editor in the 4th line in the example ? per 'Organization PSC , Roles: GN_EDITOR, C3P:GN_REVIEWER'.. or it has to be 'the higher profile' ?

@f-necas
Copy link
Copy Markdown
Collaborator Author

f-necas commented Dec 19, 2025

i dont understand much of it but i dont have the time to properly look, but shouldnt the 'profile in GN' be Editor in the 4th line in the example ? per 'Organization PSC , Roles: GN_EDITOR, C3P:GN_REVIEWER'.. or it has to be 'the higher profile' ?

We compute the highest profile in order for user to have the good minimal profile acording to his roles.

If the community agrees on this move, it has to be properly documented in addition to this PR.

Yep wanted to do so but I didn't remembered some things I did. It was a shot i gave a few months ago...

Can you explain whether this will be be a new option for the geonetwork.syncMode setting or a replacement of the orgs sync mode ? georchestra/datadir@92dd72b/geonetwork/geonetwork.properties#L25-L32

Sure will do, if community agrees

@fvanderbiest
Copy link
Copy Markdown
Member

I think this deserves a GIP :-)

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new role_per_org group synchronization strategy for GeoNetwork/geOrchestra integration to support granular per-organization group privileges via prefixed roles (e.g. PSC:GN_REVIEWER), while keeping org-based mapping for “root” roles.

Changes:

  • Introduces GroupSyncMode.role_per_org and a new RolePerOrgBasedGroupSynchronizer implementation, wired via GroupSynchronizerProxy.
  • Deprecates the existing roles-based group synchronizer class (annotation) and adjusts integration test support to compute user profile using “root roles” provided by the active synchronizer.
  • Adds a new integration test covering the new sync mode.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
georchestra-integration/externalized-accounts/src/test/java/org/geonetwork/security/external/integration/RolePerOrgBasedSynchronizationIT.java New integration test exercising the new sync mode and prefixed role behavior.
georchestra-integration/externalized-accounts/src/test/java/org/geonetwork/security/external/integration/IntegrationTestSupport.java Updates profile assertion to use synchronizer-provided “root roles” for profile resolution.
georchestra-integration/externalized-accounts/src/main/java/org/geonetwork/security/external/model/GroupSyncMode.java Adds the role_per_org sync mode (and marks roles as deprecated in a comment).
georchestra-integration/externalized-accounts/src/main/java/org/geonetwork/security/external/integration/RolesBasedGroupSynchronizer.java Marks the old roles-based synchronizer class as @Deprecated.
georchestra-integration/externalized-accounts/src/main/java/org/geonetwork/security/external/integration/RolePerOrgBasedGroupSynchronizer.java New synchronizer implementing org group mapping + prefixed-role per-group privileges.
georchestra-integration/externalized-accounts/src/main/java/org/geonetwork/security/external/integration/IntegrationConfiguration.java Registers the new synchronizer as a Spring bean.
georchestra-integration/externalized-accounts/src/main/java/org/geonetwork/security/external/integration/GroupSynchronizerProxy.java Routes role_per_org mode to the new synchronizer and delegates getRootRolesForUser.
georchestra-integration/externalized-accounts/src/main/java/org/geonetwork/security/external/integration/GroupSynchronizer.java Adds getRootRolesForUser to the interface.
georchestra-integration/externalized-accounts/src/main/java/org/geonetwork/security/external/integration/AbstractGroupSynchronizer.java Provides a default getRootRolesForUser implementation (returns raw roles).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +65 to +70
if (!StringUtils.hasLength(orgName)) {
return Collections.emptyList();
}
Stream<String> groupsName = userRoles(user)
.map(r -> r.contains(separator) ? r.split(separator)[0] : orgName
).distinct();
.map(r -> r.contains(separator) ? r.split(separator)[0] : orgName
).distinct();
Stream<CanonicalGroup> roleGroups = groupsName.map(role -> this.externalGroupLinks.findByName(role)//
.map(GroupLink::getCanonical)
Comment on lines +93 to +96
private Stream<String> userRoles(CanonicalUser user) {
return user.getRoles().stream()
.filter(r -> Pattern.compile(".+" + separator + ".+").matcher(r).matches() || config.getProfiles().getRolemappings().keySet().contains(r));
}
} else {
return group.getName().equals(user.getOrganization());
}
}) //e.g filter roles for this group PSC:GN_REVIEWER and GN_EDITOR //e.g filter roles for this group PSC:GN_REVIEWER and GN_EDITOR

@Override
public List<String> getRootRolesForUser(CanonicalUser user) {
// Not used in RolePerOrg mode
Comment on lines +126 to +127
if (role.contains(separator)) {
return role.split(separator)[1];
Comment on lines +75 to +76
* Returns the names of the root roles (non group-specific) assigned to the given user in the
* external system.
@@ -237,4 +237,8 @@ private Profile resolveUserProfile(@NonNull List<String> roles) {
return profileMappings.resolveHighestProfileFromRoleNames(roles);
}

CanonicalGroup org = orgGroups.get(0); // just to be explicit
CanonicalGroup role = super.createRole("PSC:GN_REVIEWER"); // role name in external repo
List<CanonicalGroup> orgs = new ArrayList<>(super.defaultGroups);
orgs.add(org);
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.

5 participants