AuthN: Use typed namespace id inside authn package (#86048)

* authn: Use typed namespace id inside package
This commit is contained in:
Karl Persson
2024-04-24 09:57:34 +02:00
committed by GitHub
parent 804c726413
commit 0fa983ad8e
36 changed files with 189 additions and 203 deletions
+2 -2
View File
@@ -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},
+7 -7
View File
@@ -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},
},
+2 -2
View File
@@ -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",
+12 -2
View File
@@ -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,
+2 -2
View File
@@ -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,
+6 -6
View File
@@ -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 {
+13 -18
View File
@@ -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 {
-2
View File
@@ -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",
+4 -4
View File
@@ -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",
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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) {
+2 -2
View File
@@ -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},
+2 -2
View File
@@ -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},
},
+1 -1
View File
@@ -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,
+4 -4
View File
@@ -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{