mirror of
https://github.com/grafana/grafana.git
synced 2026-07-30 00:08:10 -05:00
AuthN: Add last seen sync hooks for user and api keys (#61571)
* AUthN: Add last seen sync hooks for user / service account and move api key last seen to own hook * ContextHandler: only run sync for last seen if auth.Service is not enabled
This commit is contained in:
@@ -108,6 +108,8 @@ func ProvideService(
|
||||
orgUserSyncService := sync.ProvideOrgSync(userService, orgService, accessControlService)
|
||||
s.RegisterPostAuthHook(userSyncService.SyncUser)
|
||||
s.RegisterPostAuthHook(orgUserSyncService.SyncOrgUser)
|
||||
s.RegisterPostAuthHook(sync.ProvideUserLastSeenSync(userService).SyncLastSeen)
|
||||
s.RegisterPostAuthHook(sync.ProvideAPIKeyLastSeenSync(apikeyService).SyncLastSeen)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package usersync
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/apikey"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
)
|
||||
|
||||
func ProvideAPIKeyLastSeenSync(service apikey.Service) *APIKeyLastSeenSync {
|
||||
return &APIKeyLastSeenSync{log.New("apikeylastseen.sync"), service}
|
||||
}
|
||||
|
||||
type APIKeyLastSeenSync struct {
|
||||
log log.Logger
|
||||
service apikey.Service
|
||||
}
|
||||
|
||||
func (s *APIKeyLastSeenSync) SyncLastSeen(ctx context.Context, identity *authn.Identity, _ *authn.Request) error {
|
||||
namespace, id := identity.NamespacedID()
|
||||
if namespace != authn.NamespaceAPIKey {
|
||||
return nil
|
||||
}
|
||||
|
||||
go func(apikeyID int64) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
s.log.Error("panic during user last seen sync", "err", err)
|
||||
}
|
||||
}()
|
||||
if err := s.service.UpdateAPIKeyLastUsedDate(context.Background(), apikeyID); err != nil {
|
||||
s.log.Warn("failed to update last use date for api key", "id", apikeyID)
|
||||
}
|
||||
}(id)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package usersync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
func ProvideUserLastSeenSync(service user.Service) *UserLastSeenSync {
|
||||
return &UserLastSeenSync{log.New("userlastseen.sync"), service}
|
||||
}
|
||||
|
||||
type UserLastSeenSync struct {
|
||||
log log.Logger
|
||||
service user.Service
|
||||
}
|
||||
|
||||
func (s *UserLastSeenSync) SyncLastSeen(ctx context.Context, identity *authn.Identity, _ *authn.Request) error {
|
||||
namespace, id := identity.NamespacedID()
|
||||
|
||||
if namespace != authn.NamespaceUser && namespace != authn.NamespaceServiceAccount {
|
||||
// skip sync
|
||||
return nil
|
||||
}
|
||||
|
||||
if !shouldUpdateLastSeen(identity.LastSeenAt) {
|
||||
return nil
|
||||
}
|
||||
|
||||
go func(userID int64) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
s.log.Error("panic during user last seen sync", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := s.service.UpdateLastSeenAt(context.Background(), &user.UpdateUserLastSeenAtCommand{UserID: userID}); err != nil {
|
||||
s.log.Error("failed to update last_seen_at", "err", err, "userId", userID)
|
||||
}
|
||||
}(id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func shouldUpdateLastSeen(t time.Time) bool {
|
||||
return time.Since(t) > time.Minute*5
|
||||
}
|
||||
@@ -56,17 +56,6 @@ func (s *APIKey) Authenticate(ctx context.Context, r *authn.Request) (*authn.Ide
|
||||
return nil, errAPIKeyRevoked.Errorf("Api key is revoked")
|
||||
}
|
||||
|
||||
go func(id int64) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
s.log.Error("api key authentication panic", "err", err)
|
||||
}
|
||||
}()
|
||||
if err := s.apiKeyService.UpdateAPIKeyLastUsedDate(context.Background(), id); err != nil {
|
||||
s.log.Warn("failed to update last use date for api key", "id", id)
|
||||
}
|
||||
}(apiKey.Id)
|
||||
|
||||
// if the api key don't belong to a service account construct the identity and return it
|
||||
if apiKey.ServiceAccountId == nil || *apiKey.ServiceAccountId < 1 {
|
||||
return &authn.Identity{
|
||||
|
||||
@@ -180,11 +180,14 @@ func (h *ContextHandler) Middleware(next http.Handler) http.Handler {
|
||||
{Num: reqContext.UserID}},
|
||||
)
|
||||
|
||||
// update last seen every 5min
|
||||
if reqContext.ShouldUpdateLastSeenAt() {
|
||||
reqContext.Logger.Debug("Updating last user_seen_at", "user_id", reqContext.UserID)
|
||||
if err := h.userService.UpdateLastSeenAt(mContext.Req.Context(), &user.UpdateUserLastSeenAtCommand{UserID: reqContext.UserID}); err != nil {
|
||||
reqContext.Logger.Error("Failed to update last_seen_at", "error", err)
|
||||
// when using authn service this is implemented as a post auth hook
|
||||
if !h.features.IsEnabled(featuremgmt.FlagAuthnService) {
|
||||
// update last seen every 5min
|
||||
if reqContext.ShouldUpdateLastSeenAt() {
|
||||
reqContext.Logger.Debug("Updating last user_seen_at", "user_id", reqContext.UserID)
|
||||
if err := h.userService.UpdateLastSeenAt(mContext.Req.Context(), &user.UpdateUserLastSeenAtCommand{UserID: reqContext.UserID}); err != nil {
|
||||
reqContext.Logger.Error("Failed to update last_seen_at", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user