* feat(auth): automatic User Group assignment from Login Provider groups
Add per-Login-Provider "Auto Assignment" rules to the User Group editor so a
user is added to (or removed from) a Cacti User Group at every login based on
the groups their Login Provider reports.
User Group editor (user_group_admin.php):
- Add General / Auto Assignment / Rights spacers to the General tab and rename
"General Group Options" to "Group Options".
- Render one text field per eligible Login Provider under Auto Assignment
(label = provider name). The admin enters a plain Group Name (e.g.
"Cacti Admins"), never a DN.
- Store the rules as JSON in the new user_auth_group.auto_assignments column,
keyed by login_providers.id.
Group resolution:
- SAML2 and OpenID match the configured name against the group attribute /
group claim values.
- LDAP/AD resolve membership live against the directory using a raw group name
plus the Search Base, trying AD (memberOf + nested LDAP_MATCHING_RULE_IN_CHAIN),
FreeIPA / 389 DS (groupOfNames/groupOfUniqueNames) and RFC2307 posixGroup
shapes so one plain name works everywhere. This requires a directory-read
bind, so the field is only offered for Anonymous Searching or Specific
Searching with a stored service account.
- A directory that cannot be reached returns "undetermined" so existing
memberships are left untouched rather than revoked on a transient outage.
Lifecycle:
- auto_assignments is reconciled on every login (lib/auth.php credential flow
and login_sso.php redirect flow).
- Deleting a Login Provider strips its key from every group's auto_assignments.
Schema: add user_auth_group.auto_assignments (cacti.sql + 1_3_0 upgrade).
* ci: document free-text group-name validateInput for constraint ratchet
* fix(auth): address PR review on automatic User Group assignment
- LDAP: add an Active Directory bare-name query (objectClass=group with the
member:1.2.840.113556.1.4.1941 nested-membership rule) so a plain group
name resolves on AD, which neither the posixGroup nor groupOfNames probe
could match.
- LDAP: make single-group membership tri-state and propagate an "undetermined"
result, so a search that fails after binding (timeout/dropped connection) no
longer looks like confirmed non-membership and revokes assignments.
- SAML2/OpenID: supportsAutoAssignment() now requires a configured group
claim, and applyAutoAssignments() skips any provider that is not currently
capable, so a blank claim can never strip users from their groups.
- Only reset the permission cache when a membership row actually changed
(accumulate db_affected_rows) rather than on every login that has a rule.
- Add unit tests for the reconciliation boundaries (add / remove / no-op /
incapable / undetermined) and for the LDAP group-query construction
(AD plain + nested, posixGroup, groupOfNames, DN mode).
- Regenerate locales/po/cacti.pot for the new UI strings.
* fix(phpstan): resolve level-8 errors from auto group assignment
- Guard supportsAutoAssignment() behind an instanceof AbstractLoginProvider
check so it is not called on the base LoginProviderInterface.
- Assign the injected field list to a variable (inject_form_variables takes it
by reference) and pass an is_array-guarded group row.
- Bump the lib/ldap.php ldap_get_entries baseline count 3->6 for the three new
group-membership query helpers.