Skip to content

Commit

Permalink
wip 5
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiught committed Apr 17, 2023
1 parent 75fe21e commit 5fa32f8
Show file tree
Hide file tree
Showing 3 changed files with 376 additions and 0 deletions.
219 changes: 219 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
package config

import (
"fmt"
"os"
"path"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestDefaultPath(t *testing.T) {
homeDir, err := os.UserHomeDir()
require.NoError(t, err)

expectedPath := path.Join(homeDir, ".config", "auth0", "config.json")

actualPath := defaultPath()

assert.Equal(t, expectedPath, actualPath)
}

func TestConfig_LoadFromDisk(t *testing.T) {
t.Run("it fails to load a non existent config file", func(t *testing.T) {
config := &Config{Path: "i-am-a-non-existent-config.json"}
err := config.loadFromDisk()
assert.EqualError(t, err, "config.json file is missing")
})

t.Run("it fails to load config if path is a directory", func(t *testing.T) {
dirPath, err := os.MkdirTemp("", "")
require.NoError(t, err)
t.Cleanup(func() {
err := os.Remove(dirPath)
require.NoError(t, err)
})

config := &Config{Path: dirPath}
err = config.loadFromDisk()

assert.EqualError(t, err, fmt.Sprintf("read %s: is a directory", dirPath))
})

t.Run("it fails to load an empty config file", func(t *testing.T) {
tempFile := createTempConfigFile(t, []byte(""))

config := &Config{Path: tempFile}
err := config.loadFromDisk()

assert.EqualError(t, err, "unexpected end of JSON input")
})

t.Run("it can successfully load a config file with a logged in tenant", func(t *testing.T) {
tempFile := createTempConfigFile(t, []byte(`
{
"install_id": "3998b053-dd7f-4bfe-bb10-c4f3a96a0180",
"default_tenant": "auth0-cli.eu.auth0.com",
"tenants": {
"auth0-cli.eu.auth0.com": {
"name": "auth0-cli",
"domain": "auth0-cli.eu.auth0.com",
"access_token": "eyfSaswe",
"expires_at": "2023-04-18T11:18:07.998809Z",
"client_id": "secret"
}
}
}
`))

expectedConfig := &Config{
Path: tempFile,
InstallID: "3998b053-dd7f-4bfe-bb10-c4f3a96a0180",
DefaultTenant: "auth0-cli.eu.auth0.com",
Tenants: Tenants{
"auth0-cli.eu.auth0.com": Tenant{
Name: "auth0-cli",
Domain: "auth0-cli.eu.auth0.com",
AccessToken: "eyfSaswe",
ExpiresAt: time.Date(2023, time.April, 18, 11, 18, 7, 998809000, time.UTC),
ClientID: "secret",
},
},
}

config := &Config{Path: tempFile}
err := config.loadFromDisk()

assert.NoError(t, err)
assert.Equal(t, expectedConfig, config)
})

t.Run("it can successfully load a config file with no logged in tenants", func(t *testing.T) {
tempFile := createTempConfigFile(t, []byte(`
{
"install_id": "3998b053-dd7f-4bfe-bb10-c4f3a96a0180",
"default_tenant": "",
"tenants": {}
}
`))

expectedConfig := &Config{
Path: tempFile,
InstallID: "3998b053-dd7f-4bfe-bb10-c4f3a96a0180",
Tenants: map[string]Tenant{},
}

config := &Config{Path: tempFile}
err := config.loadFromDisk()

assert.NoError(t, err)
assert.Equal(t, expectedConfig, config)
})
}

func TestConfig_SaveToDisk(t *testing.T) {
var testCases = []struct {
name string
config *Config
expectedOutput string
}{
{
name: "valid config with a logged in tenant",
config: &Config{
InstallID: "3998b053-dd7f-4bfe-bb10-c4f3a96a0180",
DefaultTenant: "auth0-cli.eu.auth0.com",
Tenants: Tenants{
"auth0-cli.eu.auth0.com": Tenant{
Name: "auth0-cli",
Domain: "auth0-cli.eu.auth0.com",
AccessToken: "eyfSaswe",
ExpiresAt: time.Date(2023, time.April, 18, 11, 18, 7, 998809000, time.UTC),
ClientID: "secret",
},
},
},
expectedOutput: `{
"install_id": "3998b053-dd7f-4bfe-bb10-c4f3a96a0180",
"default_tenant": "auth0-cli.eu.auth0.com",
"tenants": {
"auth0-cli.eu.auth0.com": {
"name": "auth0-cli",
"domain": "auth0-cli.eu.auth0.com",
"access_token": "eyfSaswe",
"expires_at": "2023-04-18T11:18:07.998809Z",
"client_id": "secret"
}
}
}`,
},
{
name: "valid config with no logged in tenants",
config: &Config{
InstallID: "3998b053-dd7f-4bfe-bb10-c4f3a96a0180",
Tenants: map[string]Tenant{},
},
expectedOutput: `{
"install_id": "3998b053-dd7f-4bfe-bb10-c4f3a96a0180",
"default_tenant": "",
"tenants": {}
}`,
},
}

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
t.Cleanup(func() {
err := os.RemoveAll(tmpDir)
require.NoError(t, err)
})

testCase.config.Path = path.Join(tmpDir, "auth0", "config.json")

err = testCase.config.saveToDisk()
assert.NoError(t, err)

fileContent, err := os.ReadFile(testCase.config.Path)
assert.NoError(t, err)
assert.Equal(t, string(fileContent), testCase.expectedOutput)
})
}

t.Run("it fails to save config if file path is a read only directory", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
t.Cleanup(func() {
err := os.RemoveAll(tmpDir)
require.NoError(t, err)
})

err = os.Chmod(tmpDir, 0555)
require.NoError(t, err)

config := &Config{Path: path.Join(tmpDir, "auth0", "config.json")}

err = config.saveToDisk()
assert.EqualError(t, err, fmt.Sprintf("mkdir %s/auth0: permission denied", tmpDir))
})
}

func createTempConfigFile(t *testing.T, data []byte) string {
t.Helper()

tempFile, err := os.CreateTemp("", "config.json")
require.NoError(t, err)

t.Cleanup(func() {
err := os.Remove(tempFile.Name())
require.NoError(t, err)
})

_, err = tempFile.Write(data)
require.NoError(t, err)

return tempFile.Name()
}
12 changes: 12 additions & 0 deletions internal/config/tenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ import (
const accessTokenExpThreshold = 5 * time.Minute

type (
// Tenants keeps track of all the tenants we
// logged into. The key is the tenant domain.
Tenants map[string]Tenant

// Tenant keeps track of auth0 config for the tenant.
Tenant struct {
Name string `json:"name"`
Domain string `json:"domain"`
Expand All @@ -40,6 +43,8 @@ func (t *Tenant) HasAllRequiredScopes() bool {
return true
}

// GetExtraRequestedScopes retrieves any extra scopes requested
// for the tenant when logging in through the device code flow.
func (t *Tenant) GetExtraRequestedScopes() []string {
additionallyRequestedScopes := make([]string, 0)

Expand All @@ -61,18 +66,24 @@ func (t *Tenant) GetExtraRequestedScopes() []string {
return additionallyRequestedScopes
}

// IsAuthenticatedWithClientCredentials checks to see if the
// tenant has been authenticated through client credentials.
func (t *Tenant) IsAuthenticatedWithClientCredentials() bool {
return t.ClientID != ""
}

// IsAuthenticatedWithDeviceCodeFlow checks to see if the
// tenant has been authenticated through device code flow.
func (t *Tenant) IsAuthenticatedWithDeviceCodeFlow() bool {
return t.ClientID == ""
}

// HasExpiredToken checks whether the tenant has an expired token.
func (t *Tenant) HasExpiredToken() bool {
return time.Now().Add(accessTokenExpThreshold).After(t.ExpiresAt)
}

// GetAccessToken retrieves the tenant's access token.
func (t *Tenant) GetAccessToken() string {
accessToken, err := keyring.GetAccessToken(t.Domain)
if err == nil && accessToken != "" {
Expand All @@ -82,6 +93,7 @@ func (t *Tenant) GetAccessToken() string {
return t.AccessToken
}

// RegenerateAccessToken regenerates the access token for the tenant.
func (t *Tenant) RegenerateAccessToken(ctx context.Context) error {
if t.IsAuthenticatedWithClientCredentials() {
clientSecret, err := keyring.GetClientSecret(t.Domain)
Expand Down
Loading

0 comments on commit 5fa32f8

Please sign in to comment.