Skip to content

Commit

Permalink
feat(auth): on startup allow binding env variable on static client se…
Browse files Browse the repository at this point in the history
…crets (#33)

* feat(auth): on startup allow binding environment variable on static client secrets

* feat: add codeowners

* feat: simplify test
  • Loading branch information
Dav-14 authored Nov 19, 2024
1 parent e5de0bb commit 66c84cb
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* @formancehq/backend
* @formancehq/devops
10 changes: 10 additions & 0 deletions cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import (

"github.com/formancehq/go-libs/aws/iam"
"github.com/formancehq/go-libs/bun/bunconnect"
"github.com/formancehq/go-libs/collectionutils"
"github.com/formancehq/go-libs/licence"
"github.com/formancehq/go-libs/logging"

"github.com/formancehq/go-libs/otlp"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
Expand Down Expand Up @@ -128,6 +130,14 @@ func newServeCommand() *cobra.Command {
}
}

o.Clients = collectionutils.Map(o.Clients, func(client auth.StaticClient) auth.StaticClient {
c, err := client.FromEnvironment()
if err != nil {
logging.FromContext(cmd.Context()).Errorf("error while loading secrets from environment: %v", err)
}
return c
})

zLogging.SetOutput(cmd.OutOrStdout())

connectionOptions, err := bunconnect.ConnectionOptionsFromFlags(cmd)
Expand Down
24 changes: 24 additions & 0 deletions pkg/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"crypto/sha256"
"encoding/base64"
"errors"
"os"
"strings"

"github.com/uptrace/bun"

Expand Down Expand Up @@ -109,11 +111,33 @@ func (c *Client) GetScopes() []string {
return c.Scopes
}

var (
ErrEnvironmentVariableNotFound = errors.New("environment variable not found")
)

type StaticClient struct {
ClientOptions `mapstructure:",squash" yaml:",inline"`
Secrets []string `json:"secrets" yaml:"secrets"`
}

func (c StaticClient) FromEnvironment() (StaticClient, error) {
for i, secret := range c.Secrets {
environmentVariable, found := strings.CutPrefix(secret, "$")
if !found {
continue
}

value := os.Getenv(environmentVariable)
if value == "" {
return c, ErrEnvironmentVariableNotFound
}

c.Secrets[i] = value
}

return c, nil
}

func (s StaticClient) ValidateSecret(secret string) error {
for _, clientSecret := range s.Secrets {
if clientSecret == secret {
Expand Down
61 changes: 61 additions & 0 deletions pkg/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package auth_test

import (
"os"
"testing"

auth "github.com/formancehq/auth/pkg"
"github.com/stretchr/testify/require"
)

func TestStaticClientFromEnvironment(t *testing.T) {
type testCase struct {
staticClients []auth.StaticClient
environmentVariables map[string]string
expectedErr error
}

testCases := []testCase{
{
staticClients: []auth.StaticClient{
{
Secrets: []string{"$SECRET"},
},
},
environmentVariables: map[string]string{
"SECRET": "secret",
},
expectedErr: nil,
},
{
staticClients: []auth.StaticClient{
{
Secrets: []string{"$ERROR"},
},
},
environmentVariables: map[string]string{},
expectedErr: auth.ErrEnvironmentVariableNotFound,
},
}

for _, tc := range testCases {
t.Run("TestStaticClientFromEnvironment", func(t *testing.T) {
for key, value := range tc.environmentVariables {
os.Setenv(key, value)
}

for _, staticClient := range tc.staticClients {
loadedClient, err := staticClient.FromEnvironment()
if tc.expectedErr != nil {
require.Error(t, err)
require.ErrorIs(t, err, tc.expectedErr)
return
}
for _, secret := range loadedClient.Secrets {
require.NotContains(t, secret, "$")
require.NotEmpty(t, secret)
}
}
})
}
}

0 comments on commit 66c84cb

Please sign in to comment.