Commit Graph

4 Commits

Author SHA1 Message Date
Juan José Mata
02af8463f6 Administer invitations in /admin/users (#1185)
* Add invited users with delete button to admin users page

Shows pending invitations per family below active users in /admin/users/.
Each invitation row has a red Delete button aligned with the role column.
Alt/option-clicking any Delete button changes all invitation button labels
to "Delete All" and destroys all pending invitations for that family.

- Add admin routes: DELETE /admin/invitations/:id and DELETE /admin/families/:id/invitations
- Add Admin::InvitationsController with destroy and destroy_all actions
- Load pending invitations grouped by family in users controller index
- Render invitation rows in a dashed-border tbody below active user rows
- Add admin-invitation-delete Stimulus controller for alt-click behavior
- Add i18n strings for invitation UI and flash messages

https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm

* Fix destroy_all using params[:id] from member route

The member route /admin/families/:id/invitations sets params[:id],
not params[:family_id], so Family.find was always receiving nil.

https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm

* Fix translation key in destroy_all to match locale

t(".success_all") looked up a nonexistent key; the locale defines
admin.invitations.destroy_all.success, so t(".success") is correct.

https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm

* Scope bulk delete to pending invitations and allow re-inviting emails

- destroy_all now uses family.invitations.pending.destroy_all so accepted
  and expired invitation history is preserved
- Replace blanket email uniqueness validation with a custom check scoped
  to pending invitations only, so the same email can be invited again
  after an invitation is deleted or expires

https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm

* Drop unconditional unique DB index on invitations(email, family_id)

The model-level uniqueness check was already scoped to pending
invitations, but the blanket unique index on (email, family_id)
still caused ActiveRecord::RecordNotUnique when re-inviting an
email that had any historical invitation record in the same family
(e.g. after an accepted invite or after an account deletion).

Replace it with no DB-level unique constraint — the
no_duplicate_pending_invitation_in_family model validation is the
sole enforcer and correctly scopes uniqueness to pending rows only.

https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm

* Replace blanket unique index with partial unique index on pending invitations

Instead of dropping the DB-level uniqueness constraint entirely, replace
the unconditional unique index on (email, family_id) with a partial unique
index scoped to WHERE accepted_at IS NULL. This enforces the invariant at
the DB layer (no two non-accepted invitations for the same email in a
family) while allowing re-invites once a prior invitation has been accepted.

https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm

* Fix migration version and make remove_index reversible

- Change Migration[8.0] to Migration[7.2] to match the rest of the codebase
- Pass column names to remove_index so Rails can reconstruct the old index on rollback

https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm

---------

Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-14 11:32:33 +01:00
Juan José Mata
7fce804c89 Group users by family in /admin/users (#1139)
* Display user admins grouped

* Start family/groups collapsed

* Sort by number of transactions

* Display subscription status

* Fix tests

* Use Stimulus
2026-03-06 23:38:25 +01:00
Juan José Mata
06fedb34f3 Add new columns and sorting to admin users list (#1004)
* Add trial end date to admin users list

* Add new columns

* Regression
2026-02-16 20:10:14 +01:00
Josh Waldrep
14993d871c feat: comprehensive SSO/OIDC upgrade with enterprise features
Multi-provider SSO support:
   - Database-backed SSO provider management with admin UI
   - Support for OpenID Connect, Google OAuth2, GitHub, and SAML 2.0
   - Flipper feature flag (db_sso_providers) for dynamic provider loading
   - ProviderLoader service for YAML or database configuration

   Admin functionality:
   - Admin::SsoProvidersController for CRUD operations
   - Admin::UsersController for super_admin role management
   - Pundit policies for authorization
   - Test connection endpoint for validating provider config

   User provisioning improvements:
   - JIT (just-in-time) account creation with configurable default role
   - Changed default JIT role from admin to member (security)
   - User attribute sync on each SSO login
   - Group/role mapping from IdP claims

   SSO identity management:
   - Settings::SsoIdentitiesController for users to manage connected accounts
   - Issuer validation for OIDC identities
   - Unlink protection when no password set

   Audit logging:
   - SsoAuditLog model tracking login, logout, link, unlink, JIT creation
   - Captures IP address, user agent, and metadata

   Advanced OIDC features:
   - Custom scopes per provider
   - Configurable prompt parameter (login, consent, select_account, none)
   - RP-initiated logout (federated logout to IdP)
   - id_token storage for logout

   SAML 2.0 support:
   - omniauth-saml gem integration
   - IdP metadata URL or manual configuration
   - Certificate and fingerprint validation
   - NameID format configuration
2026-01-03 17:56:42 -05:00