AuthN: Use typed namespace id inside authn package (#86048)
* authn: Use typed namespace id inside package
This commit is contained in:
@@ -256,7 +256,7 @@ func validateApiKey(orgID int64, key *apikey.APIKey) error {
|
||||
|
||||
func newAPIKeyIdentity(key *apikey.APIKey) *authn.Identity {
|
||||
return &authn.Identity{
|
||||
ID: authn.NamespacedID(authn.NamespaceAPIKey, key.ID),
|
||||
ID: authn.NewNamespaceIDUnchecked(authn.NamespaceAPIKey, key.ID),
|
||||
OrgID: key.OrgID,
|
||||
OrgRoles: map[int64]org.RoleType{key.OrgID: key.Role},
|
||||
ClientParams: authn.ClientParams{SyncPermissions: true},
|
||||
@@ -266,7 +266,7 @@ func newAPIKeyIdentity(key *apikey.APIKey) *authn.Identity {
|
||||
|
||||
func newServiceAccountIdentity(key *apikey.APIKey) *authn.Identity {
|
||||
return &authn.Identity{
|
||||
ID: authn.NamespacedID(authn.NamespaceServiceAccount, *key.ServiceAccountId),
|
||||
ID: authn.NewNamespaceIDUnchecked(authn.NamespaceServiceAccount, *key.ServiceAccountId),
|
||||
OrgID: key.OrgID,
|
||||
AuthenticatedBy: login.APIKeyAuthModule,
|
||||
ClientParams: authn.ClientParams{FetchSyncedUser: true, SyncPermissions: true},
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestAPIKey_Authenticate(t *testing.T) {
|
||||
Role: org.RoleAdmin,
|
||||
},
|
||||
expectedIdentity: &authn.Identity{
|
||||
ID: "api-key:1",
|
||||
ID: authn.MustParseNamespaceID("api-key:1"),
|
||||
OrgID: 1,
|
||||
OrgRoles: map[int64]org.RoleType{1: org.RoleAdmin},
|
||||
ClientParams: authn.ClientParams{
|
||||
@@ -70,7 +70,7 @@ func TestAPIKey_Authenticate(t *testing.T) {
|
||||
ServiceAccountId: intPtr(1),
|
||||
},
|
||||
expectedIdentity: &authn.Identity{
|
||||
ID: "service-account:1",
|
||||
ID: authn.MustParseNamespaceID("service-account:1"),
|
||||
OrgID: 1,
|
||||
ClientParams: authn.ClientParams{
|
||||
FetchSyncedUser: true,
|
||||
@@ -205,7 +205,7 @@ func TestAPIKey_GetAPIKeyIDFromIdentity(t *testing.T) {
|
||||
ServiceAccountId: intPtr(1),
|
||||
},
|
||||
expectedIdentity: &authn.Identity{
|
||||
ID: "service-account:1",
|
||||
ID: authn.MustParseNamespaceID("service-account:1"),
|
||||
OrgID: 1,
|
||||
Name: "test",
|
||||
AuthenticatedBy: login.APIKeyAuthModule,
|
||||
@@ -221,7 +221,7 @@ func TestAPIKey_GetAPIKeyIDFromIdentity(t *testing.T) {
|
||||
Key: hash,
|
||||
},
|
||||
expectedIdentity: &authn.Identity{
|
||||
ID: "api-key:2",
|
||||
ID: authn.MustParseNamespaceID("api-key:2"),
|
||||
OrgID: 1,
|
||||
Name: "test",
|
||||
AuthenticatedBy: login.APIKeyAuthModule,
|
||||
@@ -237,7 +237,7 @@ func TestAPIKey_GetAPIKeyIDFromIdentity(t *testing.T) {
|
||||
Key: hash,
|
||||
},
|
||||
expectedIdentity: &authn.Identity{
|
||||
ID: "user:2",
|
||||
ID: authn.MustParseNamespaceID("user:2"),
|
||||
OrgID: 1,
|
||||
Name: "test",
|
||||
AuthenticatedBy: login.APIKeyAuthModule,
|
||||
@@ -253,7 +253,7 @@ func TestAPIKey_GetAPIKeyIDFromIdentity(t *testing.T) {
|
||||
Key: hash,
|
||||
},
|
||||
expectedIdentity: &authn.Identity{
|
||||
ID: "service-account:2",
|
||||
ID: authn.MustParseNamespaceID("service-account:2"),
|
||||
OrgID: 1,
|
||||
Name: "test",
|
||||
AuthenticatedBy: login.APIKeyAuthModule,
|
||||
@@ -351,7 +351,7 @@ func TestAPIKey_ResolveIdentity(t *testing.T) {
|
||||
expectedIdenity: &authn.Identity{
|
||||
OrgID: 1,
|
||||
OrgRoles: map[int64]org.RoleType{1: org.RoleEditor},
|
||||
ID: "api-key:1",
|
||||
ID: authn.MustParseNamespaceID("api-key:1"),
|
||||
AuthenticatedBy: login.APIKeyAuthModule,
|
||||
ClientParams: authn.ClientParams{SyncPermissions: true},
|
||||
},
|
||||
|
||||
@@ -24,8 +24,8 @@ func TestBasic_Authenticate(t *testing.T) {
|
||||
{
|
||||
desc: "should success when password client return identity",
|
||||
req: &authn.Request{HTTPRequest: &http.Request{Header: map[string][]string{authorizationHeaderName: {encodeBasicAuth("user", "password")}}}},
|
||||
client: authntest.FakePasswordClient{ExpectedIdentity: &authn.Identity{ID: "user:1"}},
|
||||
expectedIdentity: &authn.Identity{ID: "user:1"},
|
||||
client: authntest.FakePasswordClient{ExpectedIdentity: &authn.Identity{ID: authn.MustParseNamespaceID("user:1")}},
|
||||
expectedIdentity: &authn.Identity{ID: authn.MustParseNamespaceID("user:1")},
|
||||
},
|
||||
{
|
||||
desc: "should fail when basic auth header could not be decoded",
|
||||
|
||||
@@ -109,8 +109,13 @@ func (s *ExtendedJWT) authenticateAsUser(idTokenClaims,
|
||||
return nil, errJWTInvalid.Errorf("Failed to parse sub: %w", err)
|
||||
}
|
||||
|
||||
id, err := authn.ParseNamespaceID(idTokenClaims.Subject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &authn.Identity{
|
||||
ID: idTokenClaims.Subject,
|
||||
ID: id,
|
||||
OrgID: s.getDefaultOrgID(),
|
||||
AuthenticatedBy: login.ExtendedJWTModule,
|
||||
AuthID: accessTokenClaims.Subject,
|
||||
@@ -129,8 +134,13 @@ func (s *ExtendedJWT) authenticateAsService(claims *ExtendedJWTClaims) (*authn.I
|
||||
return nil, errJWTInvalid.Errorf("Failed to parse sub: %s", "invalid subject format")
|
||||
}
|
||||
|
||||
id, err := authn.ParseNamespaceID(claims.Subject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &authn.Identity{
|
||||
ID: claims.Subject,
|
||||
ID: id,
|
||||
OrgID: s.getDefaultOrgID(),
|
||||
AuthenticatedBy: login.ExtendedJWTModule,
|
||||
AuthID: claims.Subject,
|
||||
|
||||
@@ -166,7 +166,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
|
||||
orgID: 1,
|
||||
want: &authn.Identity{OrgID: 1, OrgName: "",
|
||||
OrgRoles: map[int64]roletype.RoleType(nil),
|
||||
ID: "access-policy:this-uid", Login: "", Name: "", Email: "",
|
||||
ID: authn.MustParseNamespaceID("access-policy:this-uid"), Login: "", Name: "", Email: "",
|
||||
IsGrafanaAdmin: (*bool)(nil), AuthenticatedBy: "extendedjwt",
|
||||
AuthID: "access-policy:this-uid", IsDisabled: false, HelpFlags1: 0x0,
|
||||
LastSeenAt: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||
@@ -196,7 +196,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
|
||||
}
|
||||
},
|
||||
want: &authn.Identity{OrgID: 1, OrgName: "",
|
||||
OrgRoles: map[int64]roletype.RoleType(nil), ID: "user:2",
|
||||
OrgRoles: map[int64]roletype.RoleType(nil), ID: authn.MustParseNamespaceID("user:2"),
|
||||
Login: "", Name: "", Email: "",
|
||||
IsGrafanaAdmin: (*bool)(nil), AuthenticatedBy: "extendedjwt",
|
||||
AuthID: "access-policy:this-uid", IsDisabled: false, HelpFlags1: 0x0,
|
||||
|
||||
@@ -104,12 +104,12 @@ func (c *Grafana) AuthenticatePassword(ctx context.Context, r *authn.Request, us
|
||||
return nil, errInvalidPassword.Errorf("invalid password")
|
||||
}
|
||||
|
||||
signedInUser, err := c.userService.GetSignedInUserWithCacheCtx(ctx, &user.GetSignedInUserQuery{OrgID: r.OrgID, UserID: usr.ID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return authn.IdentityFromSignedInUser(authn.NamespacedID(authn.NamespaceUser, signedInUser.UserID), signedInUser, authn.ClientParams{SyncPermissions: true}, login.PasswordAuthModule), nil
|
||||
return &authn.Identity{
|
||||
ID: authn.NewNamespaceIDUnchecked(authn.NamespaceUser, usr.ID),
|
||||
OrgID: r.OrgID,
|
||||
ClientParams: authn.ClientParams{FetchSyncedUser: true, SyncPermissions: true},
|
||||
AuthenticatedBy: login.PasswordAuthModule,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func comparePassword(password, salt, hash string) bool {
|
||||
|
||||
@@ -125,29 +125,25 @@ func TestGrafana_AuthenticateProxy(t *testing.T) {
|
||||
|
||||
func TestGrafana_AuthenticatePassword(t *testing.T) {
|
||||
type testCase struct {
|
||||
desc string
|
||||
username string
|
||||
password string
|
||||
findUser bool
|
||||
expectedErr error
|
||||
expectedIdentity *authn.Identity
|
||||
expectedSignedInUser *user.SignedInUser
|
||||
desc string
|
||||
username string
|
||||
password string
|
||||
findUser bool
|
||||
expectedErr error
|
||||
expectedIdentity *authn.Identity
|
||||
}
|
||||
|
||||
tests := []testCase{
|
||||
{
|
||||
desc: "should successfully authenticate user with correct password",
|
||||
username: "user",
|
||||
password: "password",
|
||||
findUser: true,
|
||||
expectedSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, OrgRole: "Viewer"},
|
||||
desc: "should successfully authenticate user with correct password",
|
||||
username: "user",
|
||||
password: "password",
|
||||
findUser: true,
|
||||
expectedIdentity: &authn.Identity{
|
||||
ID: "user:1",
|
||||
ID: authn.MustParseNamespaceID("user:1"),
|
||||
OrgID: 1,
|
||||
OrgRoles: map[int64]org.RoleType{1: "Viewer"},
|
||||
IsGrafanaAdmin: boolPtr(false),
|
||||
ClientParams: authn.ClientParams{SyncPermissions: true},
|
||||
AuthenticatedBy: login.PasswordAuthModule,
|
||||
ClientParams: authn.ClientParams{FetchSyncedUser: true, SyncPermissions: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -169,8 +165,7 @@ func TestGrafana_AuthenticatePassword(t *testing.T) {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
hashed, _ := util.EncodePassword("password", "salt")
|
||||
userService := &usertest.FakeUserService{
|
||||
ExpectedSignedInUser: tt.expectedSignedInUser,
|
||||
ExpectedUser: &user.User{Password: user.Password(hashed), Salt: "salt"},
|
||||
ExpectedUser: &user.User{ID: 1, Password: user.Password(hashed), Salt: "salt"},
|
||||
}
|
||||
|
||||
if !tt.findUser {
|
||||
|
||||
@@ -40,7 +40,6 @@ func TestAuthenticateJWT(t *testing.T) {
|
||||
OrgName: "",
|
||||
OrgRoles: map[int64]roletype.RoleType{1: roletype.RoleAdmin},
|
||||
Groups: []string{"foo", "bar"},
|
||||
ID: "",
|
||||
Login: "eai-doe",
|
||||
Name: "Eai Doe",
|
||||
Email: "eai.doe@cor.po",
|
||||
@@ -92,7 +91,6 @@ func TestAuthenticateJWT(t *testing.T) {
|
||||
OrgID: 0,
|
||||
OrgName: "",
|
||||
OrgRoles: map[int64]roletype.RoleType{1: roletype.RoleAdmin},
|
||||
ID: "",
|
||||
Login: "eai-doe",
|
||||
Groups: []string{},
|
||||
Name: "Eai Doe",
|
||||
|
||||
@@ -29,16 +29,16 @@ func TestPassword_AuthenticatePassword(t *testing.T) {
|
||||
username: "test",
|
||||
password: "test",
|
||||
req: &authn.Request{},
|
||||
clients: []authn.PasswordClient{authntest.FakePasswordClient{ExpectedIdentity: &authn.Identity{ID: "user:1"}}},
|
||||
expectedIdentity: &authn.Identity{ID: "user:1"},
|
||||
clients: []authn.PasswordClient{authntest.FakePasswordClient{ExpectedIdentity: &authn.Identity{ID: authn.MustParseNamespaceID("user:1")}}},
|
||||
expectedIdentity: &authn.Identity{ID: authn.MustParseNamespaceID("user:1")},
|
||||
},
|
||||
{
|
||||
desc: "should success when found in second client",
|
||||
username: "test",
|
||||
password: "test",
|
||||
req: &authn.Request{},
|
||||
clients: []authn.PasswordClient{authntest.FakePasswordClient{ExpectedErr: errIdentityNotFound}, authntest.FakePasswordClient{ExpectedIdentity: &authn.Identity{ID: "user:2"}}},
|
||||
expectedIdentity: &authn.Identity{ID: "user:2"},
|
||||
clients: []authn.PasswordClient{authntest.FakePasswordClient{ExpectedErr: errIdentityNotFound}, authntest.FakePasswordClient{ExpectedIdentity: &authn.Identity{ID: authn.MustParseNamespaceID("user:2")}}},
|
||||
expectedIdentity: &authn.Identity{ID: authn.MustParseNamespaceID("user:2")},
|
||||
},
|
||||
{
|
||||
desc: "should fail for empty password",
|
||||
|
||||
@@ -125,7 +125,7 @@ func (c *Proxy) retrieveIDFromCache(ctx context.Context, cacheKey string, r *aut
|
||||
}
|
||||
|
||||
return &authn.Identity{
|
||||
ID: authn.NamespacedID(authn.NamespaceUser, uid),
|
||||
ID: authn.NewNamespaceIDUnchecked(authn.NamespaceUser, uid),
|
||||
OrgID: r.OrgID,
|
||||
// FIXME: This does not match the actual auth module used, but should not have any impact
|
||||
// Maybe caching the auth module used with the user ID would be a good idea
|
||||
|
||||
@@ -202,8 +202,8 @@ func TestProxy_Hook(t *testing.T) {
|
||||
proxyFieldRole: "X-Role",
|
||||
}
|
||||
cache := &fakeCache{data: make(map[string][]byte)}
|
||||
userId := 1
|
||||
userID := fmt.Sprintf("%s:%d", authn.NamespaceUser, userId)
|
||||
userId := int64(1)
|
||||
userID := authn.MustNewNamespaceID(authn.NamespaceUser, userId)
|
||||
|
||||
// withRole creates a test case for a user with a specific role.
|
||||
withRole := func(role string) func(t *testing.T) {
|
||||
|
||||
@@ -42,7 +42,7 @@ func (c *Render) Authenticate(ctx context.Context, r *authn.Request) (*authn.Ide
|
||||
|
||||
if renderUsr.UserID <= 0 {
|
||||
return &authn.Identity{
|
||||
ID: authn.NamespacedID(authn.NamespaceRenderService, 0),
|
||||
ID: authn.NewNamespaceIDUnchecked(authn.NamespaceRenderService, 0),
|
||||
OrgID: renderUsr.OrgID,
|
||||
OrgRoles: map[int64]org.RoleType{renderUsr.OrgID: org.RoleType(renderUsr.OrgRole)},
|
||||
ClientParams: authn.ClientParams{SyncPermissions: true},
|
||||
@@ -52,7 +52,7 @@ func (c *Render) Authenticate(ctx context.Context, r *authn.Request) (*authn.Ide
|
||||
}
|
||||
|
||||
return &authn.Identity{
|
||||
ID: authn.NamespacedID(authn.NamespaceUser, renderUsr.UserID),
|
||||
ID: authn.NewNamespaceIDUnchecked(authn.NamespaceUser, renderUsr.UserID),
|
||||
LastSeenAt: time.Now(),
|
||||
AuthenticatedBy: login.RenderModule,
|
||||
ClientParams: authn.ClientParams{FetchSyncedUser: true, SyncPermissions: true},
|
||||
|
||||
@@ -35,7 +35,7 @@ func TestRender_Authenticate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expectedIdentity: &authn.Identity{
|
||||
ID: "render:0",
|
||||
ID: authn.MustParseNamespaceID("render:0"),
|
||||
OrgID: 1,
|
||||
OrgRoles: map[int64]org.RoleType{1: org.RoleViewer},
|
||||
AuthenticatedBy: login.RenderModule,
|
||||
@@ -56,7 +56,7 @@ func TestRender_Authenticate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expectedIdentity: &authn.Identity{
|
||||
ID: "user:1",
|
||||
ID: authn.MustParseNamespaceID("user:1"),
|
||||
AuthenticatedBy: login.RenderModule,
|
||||
ClientParams: authn.ClientParams{FetchSyncedUser: true, SyncPermissions: true},
|
||||
},
|
||||
|
||||
@@ -57,7 +57,7 @@ func (s *Session) Authenticate(ctx context.Context, r *authn.Request) (*authn.Id
|
||||
}
|
||||
|
||||
ident := &authn.Identity{
|
||||
ID: authn.NamespacedID(authn.NamespaceUser, token.UserId),
|
||||
ID: authn.NewNamespaceIDUnchecked(authn.NamespaceUser, token.UserId),
|
||||
SessionToken: token,
|
||||
ClientParams: authn.ClientParams{
|
||||
FetchSyncedUser: true,
|
||||
|
||||
@@ -96,7 +96,7 @@ func TestSession_Authenticate(t *testing.T) {
|
||||
},
|
||||
args: args{r: &authn.Request{HTTPRequest: validHTTPReq}},
|
||||
wantID: &authn.Identity{
|
||||
ID: "user:1",
|
||||
ID: authn.MustParseNamespaceID("user:1"),
|
||||
SessionToken: validToken,
|
||||
ClientParams: authn.ClientParams{
|
||||
SyncPermissions: true,
|
||||
@@ -129,7 +129,7 @@ func TestSession_Authenticate(t *testing.T) {
|
||||
},
|
||||
args: args{r: &authn.Request{HTTPRequest: validHTTPReq}},
|
||||
wantID: &authn.Identity{
|
||||
ID: "user:1",
|
||||
ID: authn.MustParseNamespaceID("user:1"),
|
||||
SessionToken: validToken,
|
||||
ClientParams: authn.ClientParams{
|
||||
SyncPermissions: true,
|
||||
@@ -148,7 +148,7 @@ func TestSession_Authenticate(t *testing.T) {
|
||||
},
|
||||
args: args{r: &authn.Request{HTTPRequest: validHTTPReq}},
|
||||
wantID: &authn.Identity{
|
||||
ID: "user:1",
|
||||
ID: authn.MustParseNamespaceID("user:1"),
|
||||
AuthID: "1",
|
||||
AuthenticatedBy: "oauth_azuread",
|
||||
SessionToken: validToken,
|
||||
@@ -170,7 +170,7 @@ func TestSession_Authenticate(t *testing.T) {
|
||||
},
|
||||
args: args{r: &authn.Request{HTTPRequest: validHTTPReq}},
|
||||
wantID: &authn.Identity{
|
||||
ID: "user:1",
|
||||
ID: authn.MustParseNamespaceID("user:1"),
|
||||
SessionToken: validToken,
|
||||
|
||||
ClientParams: authn.ClientParams{
|
||||
|
||||
Reference in New Issue
Block a user