diff --git a/pkg/services/authn/identity.go b/pkg/services/authn/identity.go index 2b6e7bfe533..6e246114bcc 100644 --- a/pkg/services/authn/identity.go +++ b/pkg/services/authn/identity.go @@ -156,6 +156,9 @@ func (i *Identity) GetExtra() map[string][]string { if i.GetOrgRole().IsValid() { extra["user-instance-role"] = []string{string(i.GetOrgRole())} } + if i.AccessTokenClaims != nil && i.AccessTokenClaims.Rest.ServiceIdentity != "" { + extra[authn.ServiceIdentityKey] = []string{i.AccessTokenClaims.Rest.ServiceIdentity} + } return extra } diff --git a/pkg/services/authn/identity_test.go b/pkg/services/authn/identity_test.go new file mode 100644 index 00000000000..2a869d553c4 --- /dev/null +++ b/pkg/services/authn/identity_test.go @@ -0,0 +1,140 @@ +package authn + +import ( + "testing" + + authnlib "github.com/grafana/authlib/authn" + "github.com/grafana/authlib/types" + "github.com/grafana/grafana/pkg/services/org" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestIdentity_GetExtra(t *testing.T) { + tests := []struct { + name string + identity *Identity + expected map[string][]string + }{ + { + name: "returns empty map when no extra fields are set", + identity: &Identity{ + ID: "1", + Type: types.TypeUser, + }, + expected: map[string][]string{ + "user-instance-role": {"None"}, + }, + }, + { + name: "returns id-token when IDToken is set", + identity: &Identity{ + ID: "1", + Type: types.TypeUser, + IDToken: "test-id-token", + }, + expected: map[string][]string{ + "id-token": {"test-id-token"}, + "user-instance-role": {"None"}, + }, + }, + { + name: "returns user-instance-role when OrgRole is valid", + identity: &Identity{ + ID: "1", + Type: types.TypeUser, + OrgID: 1, + OrgRoles: map[int64]org.RoleType{1: "Admin"}, + }, + expected: map[string][]string{ + "user-instance-role": {"Admin"}, + }, + }, + { + name: "returns service-identity when AccessTokenClaims contains ServiceIdentity", + identity: &Identity{ + ID: "1", + Type: types.TypeAccessPolicy, + AccessTokenClaims: &authnlib.Claims[authnlib.AccessTokenClaims]{ + Rest: authnlib.AccessTokenClaims{ + ServiceIdentity: "secrets-manager", + }, + }, + }, + expected: map[string][]string{ + string(authnlib.ServiceIdentityKey): {"secrets-manager"}, + "user-instance-role": {"None"}, + }, + }, + { + name: "returns all extra fields when multiple are set", + identity: &Identity{ + ID: "1", + Type: types.TypeUser, + OrgID: 1, + IDToken: "test-id-token", + OrgRoles: map[int64]org.RoleType{1: "Editor"}, + AccessTokenClaims: &authnlib.Claims[authnlib.AccessTokenClaims]{ + Rest: authnlib.AccessTokenClaims{ + ServiceIdentity: "custom-service", + }, + }, + }, + expected: map[string][]string{ + "id-token": {"test-id-token"}, + "user-instance-role": {"Editor"}, + string(authnlib.ServiceIdentityKey): {"custom-service"}, + }, + }, + { + name: "does not include service-identity when AccessTokenClaims is nil", + identity: &Identity{ + ID: "1", + Type: types.TypeUser, + AccessTokenClaims: nil, + }, + expected: map[string][]string{ + "user-instance-role": {"None"}, + }, + }, + { + name: "does not include service-identity when ServiceIdentity is empty", + identity: &Identity{ + ID: "1", + Type: types.TypeUser, + AccessTokenClaims: &authnlib.Claims[authnlib.AccessTokenClaims]{ + Rest: authnlib.AccessTokenClaims{ + ServiceIdentity: "", + }, + }, + }, + expected: map[string][]string{ + "user-instance-role": {"None"}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + extra := tt.identity.GetExtra() + assert.Equal(t, tt.expected, extra) + }) + } +} + +func TestIdentity_GetExtra_ServiceIdentityKey(t *testing.T) { + // Test that the ServiceIdentityKey constant matches authlib's constant + identity := &Identity{ + ID: "1", + Type: types.TypeAccessPolicy, + AccessTokenClaims: &authnlib.Claims[authnlib.AccessTokenClaims]{ + Rest: authnlib.AccessTokenClaims{ + ServiceIdentity: "test-service", + }, + }, + } + + extra := identity.GetExtra() + require.Contains(t, extra, string(authnlib.ServiceIdentityKey)) + assert.Equal(t, []string{"test-service"}, extra[string(authnlib.ServiceIdentityKey)]) +}