Files
sm-user-plugin/classes/access_revoked.go
Jakub Zych 970719ce75 fix: access-revoked event on ban and password changes, banned users lose their link, token-specific MCP revoke
- AccessRevokedEvent and FireAccessRevoked: fired in the write's transaction
  by the ban action (newly banned users only), the password reset, an
  administrator setting a password and the self-service password change;
  a revoked subject event per user after commit
- LinkedBackendUser answers no administrator for a banned user
- FireSubjectEvent / FireSubjectEventAfterCommit carry TokenID or ClientID;
  DELETE /tokens/{id} ends only that token's MCP sessions
- the MCP personal-token path stamps last_used_ip from towel.ClientIP
- BackendLinksFor replaces the duplicate lookups; requestUsable uses
  IsUsableAt
2026-10-23 16:47:56 +02:00

58 lines
2.0 KiB
Go

package classes
import (
"context"
"fmt"
"time"
"git.golem15.com/golem15/summercms/modules/backpack"
"git.golem15.com/golem15/summercms/modules/pact"
"gorm.io/gorm"
)
// Reasons FireAccessRevoked is called with by this plugin.
const (
AccessRevokedBan = "ban"
AccessRevokedPasswordReset = "password_reset"
AccessRevokedPasswordSetByAdmin = "password_set_by_admin"
AccessRevokedPasswordChanged = "password_changed"
)
// FireAccessRevoked tells the application that userIDs lost every
// credential issued before before (reason names why: a ban, a password reset
// or change). It fires *AccessRevokedEvent in tx at once, so a listener
// error is returned and the caller's transaction rolls back, and then one
// "revoked" *pact.MCPSubjectChanged per user once tx commits, so MCP
// sessions of those users end on every instance. A nil app or bus, or an
// empty id list, does nothing. A nil tx fires the subject events at once.
//
// The plugin calls it from the ban action, the password reset, an
// administrator setting a password and the self-service password change.
// RevokeUserAccess, BanUsers and DeactivateUsers do not fire it: an
// application that calls RevokeUserAccess or BanUsers directly calls
// FireAccessRevoked too, in the same transaction.
func FireAccessRevoked(ctx context.Context, app *backpack.App, tx *gorm.DB, userIDs []uint, before time.Time, reason string) error {
if app == nil || app.Events == nil || len(userIDs) == 0 {
return nil
}
if ctx == nil {
ctx = context.Background()
}
ids := make([]uint, 0, len(userIDs))
for _, id := range userIDs {
if id != 0 {
ids = append(ids, id)
}
}
if len(ids) == 0 {
return nil
}
if err := app.Events.Fire(ctx, &AccessRevokedEvent{UserIDs: ids, Before: before, Reason: reason, Tx: tx}); err != nil {
return fmt.Errorf("%s: %w", AccessRevokedEventName, err)
}
for _, id := range ids {
FireSubjectChangedAfterCommit(ctx, app, tx, SubjectGuardUser, id, pact.MCPSubjectRevoked)
}
return nil
}