Skip to content

Commit

Permalink
PMM-13315 Move SanitizeSAName to global utils.
Browse files Browse the repository at this point in the history
  • Loading branch information
JiriCtvrtka committed Oct 8, 2024
1 parent b14fef8 commit c646f67
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 49 deletions.
18 changes: 5 additions & 13 deletions api-tests/server/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
package server

import (
"bytes"
"crypto/md5" //nolint:gosec
"bytes" //nolint:gosec
"encoding/json"
"fmt"
"io"
Expand All @@ -36,7 +35,8 @@ import (
pmmapitests "github.com/percona/pmm/api-tests"
serverClient "github.com/percona/pmm/api/server/v1/json/client"
server "github.com/percona/pmm/api/server/v1/json/client/server_service"
stringsgen "github.com/percona/pmm/managed/utils/strings"
"github.com/percona/pmm/utils/grafana"
stringsgen "github.com/percona/pmm/utils/strings"
)

const (
Expand Down Expand Up @@ -523,7 +523,7 @@ func createServiceAccountWithRole(t *testing.T, role, nodeName string) int {

name := fmt.Sprintf("%s-%s", pmmServiceAccountName, nodeName)
data, err := json.Marshal(map[string]string{
"name": sanitizeSAName(name),
"name": grafana.SanitizeSAName(name),
"role": role,
})
require.NoError(t, err)
Expand Down Expand Up @@ -585,7 +585,7 @@ func createServiceToken(t *testing.T, serviceAccountID int, nodeName string) (in

name := fmt.Sprintf("%s-%s", pmmServiceTokenName, nodeName)
data, err := json.Marshal(map[string]string{
"name": sanitizeSAName(name),
"name": grafana.SanitizeSAName(name),
})
require.NoError(t, err)

Expand Down Expand Up @@ -620,11 +620,3 @@ func deleteServiceToken(t *testing.T, serviceAccountID, serviceTokenID int) {

require.Equalf(t, http.StatusOK, resp.StatusCode, "failed to delete service token, status code: %d, response: %s", resp.StatusCode, b)
}

func sanitizeSAName(name string) string {
if len(name) <= 180 {
return name
}

return fmt.Sprintf("%s%x", name[:148], md5.Sum([]byte(name[148:]))) //nolint:gosec
}
30 changes: 8 additions & 22 deletions managed/services/grafana/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ package grafana

import (
"bytes"
"context"
"crypto/md5" //nolint:gosec
"context" //nolint:gosec
"encoding/json"
"fmt"
"io"
Expand All @@ -41,6 +40,7 @@ import (
"github.com/percona/pmm/managed/services"
"github.com/percona/pmm/managed/utils/auth"
"github.com/percona/pmm/managed/utils/irt"
"github.com/percona/pmm/utils/grafana"
)

// ErrFailedToGetToken means it failed to get user's token. Most likely due to the fact user is not logged in using Percona Account.
Expand Down Expand Up @@ -351,7 +351,7 @@ type serviceAccountSearch struct {

func (c *Client) getServiceAccountIDFromName(ctx context.Context, nodeName string, authHeaders http.Header) (int, error) {
var res serviceAccountSearch
serviceAccountName := sanitizeSAName(fmt.Sprintf("%s-%s", pmmServiceAccountName, nodeName))
serviceAccountName := grafana.SanitizeSAName(fmt.Sprintf("%s-%s", pmmServiceAccountName, nodeName))
if err := c.do(ctx, http.MethodGet, "/api/serviceaccounts/search", fmt.Sprintf("query=%s", serviceAccountName), authHeaders, nil, &res); err != nil {
return 0, err
}
Expand Down Expand Up @@ -384,7 +384,7 @@ func (c *Client) getNotPMMAgentTokenCountForServiceAccount(ctx context.Context,
count := 0
for _, token := range tokens {
serviceTokenName := fmt.Sprintf("%s-%s", pmmServiceTokenName, nodeName)
if !strings.HasPrefix(token.Name, sanitizeSAName(serviceTokenName)) {
if !strings.HasPrefix(token.Name, grafana.SanitizeSAName(serviceTokenName)) {
count++
}
}
Expand Down Expand Up @@ -673,27 +673,13 @@ type serviceToken struct {
Role string `json:"role"`
}

// Max length of service account name is 190 chars (limit in Grafana Postgres DB).
// However, prefix added by grafana is counted too. Prefix is sa-{orgID}-.
// Bare minimum is 5 chars reserved (orgID is <10, like sa-1-) and could be more depends
// on orgID number. Let's reserve 10 chars. It will cover almost one million orgIDs.
// Sanitizing, ensure its length by hashing postfix when length is exceeded.
// MD5 is used because it has fixed length 32 chars.
func sanitizeSAName(name string) string {
if len(name) <= 180 {
return name
}

return fmt.Sprintf("%s%x", name[:148], md5.Sum([]byte(name[148:]))) //nolint:gosec
}

func (c *Client) createServiceAccount(ctx context.Context, role role, nodeName string, reregister bool, authHeaders http.Header) (int, error) {
if role == none {
return 0, errors.New("you cannot create service account with empty role")
}

serviceAccountName := fmt.Sprintf("%s-%s", pmmServiceAccountName, nodeName)
b, err := json.Marshal(serviceAccount{Name: sanitizeSAName(serviceAccountName), Role: role.String(), Force: reregister})
b, err := json.Marshal(serviceAccount{Name: grafana.SanitizeSAName(serviceAccountName), Role: role.String(), Force: reregister})
if err != nil {
return 0, errors.WithStack(err)
}
Expand Down Expand Up @@ -727,7 +713,7 @@ func (c *Client) createServiceToken(ctx context.Context, serviceAccountID int, n
}
}

b, err := json.Marshal(serviceToken{Name: sanitizeSAName(serviceTokenName), Role: admin.String()})
b, err := json.Marshal(serviceToken{Name: grafana.SanitizeSAName(serviceTokenName), Role: admin.String()})
if err != nil {
return 0, "", errors.WithStack(err)
}
Expand All @@ -750,7 +736,7 @@ func (c *Client) serviceTokenExists(ctx context.Context, serviceAccountID int, n

serviceTokenName := fmt.Sprintf("%s-%s", pmmServiceTokenName, nodeName)
for _, token := range tokens {
if !strings.HasPrefix(token.Name, sanitizeSAName(serviceTokenName)) {
if !strings.HasPrefix(token.Name, grafana.SanitizeSAName(serviceTokenName)) {
continue
}

Expand All @@ -768,7 +754,7 @@ func (c *Client) deletePMMAgentServiceToken(ctx context.Context, serviceAccountI

serviceTokenName := fmt.Sprintf("%s-%s", pmmServiceTokenName, nodeName)
for _, token := range tokens {
if strings.HasPrefix(token.Name, sanitizeSAName(serviceTokenName)) {
if strings.HasPrefix(token.Name, grafana.SanitizeSAName(serviceTokenName)) {
if err := c.do(ctx, "DELETE", fmt.Sprintf("/api/serviceaccounts/%d/tokens/%d", serviceAccountID, token.ID), "", authHeaders, nil, nil); err != nil {
return err
}
Expand Down
15 changes: 1 addition & 14 deletions managed/services/grafana/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

stringsgen "github.com/percona/pmm/managed/utils/strings"
stringsgen "github.com/percona/pmm/utils/strings"
)

func TestClient(t *testing.T) {
Expand Down Expand Up @@ -238,16 +238,3 @@ func TestClient(t *testing.T) {
require.NoError(t, err)
})
}

func Test_sanitizeSAName(t *testing.T) {
// max possible length without hashing
len180, err := stringsgen.GenerateRandomString(180)
require.NoError(t, err)
require.Equal(t, len180, sanitizeSAName(len180))

// too long length - postfix hashed
len200, err := stringsgen.GenerateRandomString(200)
require.NoError(t, err)
len200sanitized := sanitizeSAName(len200)
require.Equal(t, fmt.Sprintf("%s%s", len200[:148], len200sanitized[148:]), len200sanitized)
}
21 changes: 21 additions & 0 deletions utils/grafana/serviceaccounts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package grafana

import (
"crypto/md5"
"fmt"
)

// SanitizeSAName is used for sanitize name and it's length for service accounts.
// Max length of service account name is 190 chars (limit in Grafana Postgres DB).
// However, prefix added by grafana is counted too. Prefix is sa-{orgID}-.
// Bare minimum is 5 chars reserved (orgID is <10, like sa-1-) and could be more depends
// on orgID number. Let's reserve 10 chars. It will cover almost one million orgIDs.
// Sanitizing, ensure its length by hashing postfix when length is exceeded.
// MD5 is used because it has fixed length 32 chars.
func SanitizeSAName(name string) string {
if len(name) <= 180 {
return name
}

return fmt.Sprintf("%s%x", name[:148], md5.Sum([]byte(name[148:]))) //nolint:gosec
}
23 changes: 23 additions & 0 deletions utils/grafana/serviceaccounts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package grafana

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"

stringsgen "github.com/percona/pmm/utils/strings"
)

func Test_sanitizeSAName(t *testing.T) {
// max possible length without hashing
len180, err := stringsgen.GenerateRandomString(180)
require.NoError(t, err)
require.Equal(t, len180, SanitizeSAName(len180))

// too long length - postfix hashed
len200, err := stringsgen.GenerateRandomString(200)
require.NoError(t, err)
len200sanitized := SanitizeSAName(len200)
require.Equal(t, fmt.Sprintf("%s%s", len200[:148], len200sanitized[148:]), len200sanitized)
}
File renamed without changes.

0 comments on commit c646f67

Please sign in to comment.