AuthN: fetch final state of signed in user (#62854)
* AuthN: add a hook we can use to fetch final state of user
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
|
||||
var errFetchingSignedInUser = errutil.NewBase(errutil.StatusInternal, "user.sync.fetch", errutil.WithPublicMessage("Insufficient information to authenticate user"))
|
||||
|
||||
func ProvideFetchUserSync(service user.Service) *FetchUserSync {
|
||||
return &FetchUserSync{service}
|
||||
}
|
||||
|
||||
type FetchUserSync struct {
|
||||
userService user.Service
|
||||
}
|
||||
|
||||
func (s *FetchUserSync) FetchSyncedUserHook(ctx context.Context, identity *authn.Identity, r *authn.Request) error {
|
||||
if !identity.ClientParams.FetchSyncedUser {
|
||||
return nil
|
||||
}
|
||||
namespace, id := identity.NamespacedID()
|
||||
if namespace != authn.NamespaceUser {
|
||||
return nil
|
||||
}
|
||||
|
||||
usr, err := s.userService.GetSignedInUserWithCacheCtx(ctx, &user.GetSignedInUserQuery{
|
||||
UserID: id,
|
||||
OrgID: r.OrgID,
|
||||
})
|
||||
if err != nil {
|
||||
return errFetchingSignedInUser.Errorf("failed to resolve user: %w", err)
|
||||
}
|
||||
|
||||
syncSignedInUserToIdentity(usr, identity)
|
||||
return nil
|
||||
}
|
||||
|
||||
func syncSignedInUserToIdentity(usr *user.SignedInUser, identity *authn.Identity) {
|
||||
identity.Name = usr.Name
|
||||
identity.Login = usr.Login
|
||||
identity.Email = usr.Email
|
||||
identity.OrgID = usr.OrgID
|
||||
identity.OrgName = usr.OrgName
|
||||
identity.OrgCount = usr.OrgCount
|
||||
identity.OrgRoles = map[int64]org.RoleType{identity.OrgID: usr.OrgRole}
|
||||
identity.HelpFlags1 = usr.HelpFlags1
|
||||
identity.Teams = usr.Teams
|
||||
identity.LastSeenAt = usr.LastSeenAt
|
||||
identity.IsDisabled = usr.IsDisabled
|
||||
identity.IsGrafanaAdmin = &usr.IsGrafanaAdmin
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
)
|
||||
|
||||
func TestFetchUserSync_FetchSyncedUserHook(t *testing.T) {
|
||||
type testCase struct {
|
||||
desc string
|
||||
req *authn.Request
|
||||
identity *authn.Identity
|
||||
expectedErr error
|
||||
}
|
||||
|
||||
tests := []testCase{
|
||||
{
|
||||
desc: "should skip hook when flag is not enabled",
|
||||
req: &authn.Request{},
|
||||
identity: &authn.Identity{ClientParams: authn.ClientParams{FetchSyncedUser: false}},
|
||||
},
|
||||
{
|
||||
desc: "should skip hook when identity is not a user",
|
||||
req: &authn.Request{},
|
||||
identity: &authn.Identity{ID: "apikey:1", ClientParams: authn.ClientParams{FetchSyncedUser: true}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
s := ProvideFetchUserSync(nil)
|
||||
err := s.FetchSyncedUserHook(context.Background(), tt.identity, tt.req)
|
||||
require.ErrorIs(t, err, tt.expectedErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user