diff --git a/README.md b/README.md
index 6b9f030..5652c8a 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,12 @@ imscli authorize user help
The complete documentation of the project is available in the [DOCUMENTATION.md](DOCUMENTATION.md) file.
+## Development Notes
+
+### PersistentPreRunE and subcommands
+
+The root command defines a `PersistentPreRunE` that loads configuration from flags, environment variables, and config files (see `cmd/root.go`). In cobra, if a subcommand defines its own `PersistentPreRunE`, it **overrides** the parent's — the root's `PersistentPreRunE` will not run for that subcommand or its children. If you need to add a `PersistentPreRunE` to a subcommand, you must explicitly call the parent's first.
+
## Contributing
Contributions are welcomed! Read the [Contributing Guide](CONTRIBUTING.md) for more information.
diff --git a/cmd/refresh.go b/cmd/refresh.go
index 648b59b..877dfb7 100644
--- a/cmd/refresh.go
+++ b/cmd/refresh.go
@@ -33,10 +33,10 @@ func refreshCmd(imsConfig *ims.Config) *cobra.Command {
return fmt.Errorf("error during the token refresh: %w", err)
}
if imsConfig.FullOutput {
- data := map[string]interface{}{
- "access_token": resp.AccessToken,
- "refresh_token": resp.RefreshToken,
- }
+ data := struct {
+ AccessToken string `json:"access_token"`
+ RefreshToken string `json:"refresh_token"`
+ }{resp.AccessToken, resp.RefreshToken}
jsonData, err := json.MarshalIndent(data, "", " ")
if err != nil {
return fmt.Errorf("error marshalling full JSON response: %w", err)
diff --git a/ims/authz_user.go b/ims/authz_user.go
index 83e252e..07685e2 100644
--- a/ims/authz_user.go
+++ b/ims/authz_user.go
@@ -24,8 +24,6 @@ import (
"github.com/pkg/browser"
)
-const defaultPort = 8888
-
// Validate that:
// - the ims.Config struct has the necessary parameters for AuthorizeUser
// - the provided environment exists
@@ -42,6 +40,8 @@ func (i Config) validateAuthorizeUserConfig() error {
return fmt.Errorf("missing client id parameter")
case i.Organization == "":
return fmt.Errorf("missing organization parameter")
+ case i.Port <= 0:
+ return fmt.Errorf("missing or invalid port parameter")
case i.ClientSecret == "":
if i.PublicClient {
log.Println("all needed parameters verified not empty")
@@ -72,12 +72,6 @@ func (i Config) authorizeUser(pkce bool) (string, error) {
return "", fmt.Errorf("invalid parameters for login user: %w", err)
}
- // Use default port if not specified
- port := i.Port
- if port == 0 {
- port = defaultPort
- }
-
c, err := i.newIMSClient()
if err != nil {
return "", fmt.Errorf("error creating the IMS client: %w", err)
@@ -89,7 +83,7 @@ func (i Config) authorizeUser(pkce bool) (string, error) {
ClientSecret: i.ClientSecret,
Scope: i.Scopes,
UsePKCE: pkce,
- RedirectURI: fmt.Sprintf("http://localhost:%d", port),
+ RedirectURI: fmt.Sprintf("http://localhost:%d", i.Port),
OnError: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, `
An error occurred
@@ -107,15 +101,15 @@ func (i Config) authorizeUser(pkce bool) (string, error) {
return "", fmt.Errorf("create authorization server: %w", err)
}
- listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
+ listener, err := net.Listen("tcp", fmt.Sprintf(":%d", i.Port))
if err != nil {
- return "", fmt.Errorf("unable to listen at port %d", port)
+ return "", fmt.Errorf("unable to listen at port %d", i.Port)
}
defer listener.Close()
log.Println("Local server successfully launched and contacted.")
- localUrl := fmt.Sprintf("http://localhost:%d/", port)
+ localUrl := fmt.Sprintf("http://localhost:%d/", i.Port)
// Suppress "Opening in existing browser session." messages from chromium-based
// browsers. The CLI token output goes to stdout, so stray browser messages
diff --git a/ims/config.go b/ims/config.go
index 371f468..8500eca 100644
--- a/ims/config.go
+++ b/ims/config.go
@@ -52,7 +52,6 @@ type Config struct {
// Access token information
type TokenInfo struct {
AccessToken string
- Expires int //(response.ExpiresIn * time.Millisecond),
Valid bool
Info string
}
diff --git a/ims/config_test.go b/ims/config_test.go
index ced8041..c628844 100644
--- a/ims/config_test.go
+++ b/ims/config_test.go
@@ -224,6 +224,7 @@ func TestValidateAuthorizeUserConfig(t *testing.T) {
ClientSecret: "s",
Organization: "org",
Scopes: []string{"openid"},
+ Port: 8888,
}
tests := []struct {
name string
@@ -239,6 +240,7 @@ func TestValidateAuthorizeUserConfig(t *testing.T) {
{name: "missing clientID", config: withField(validConfig, func(c *Config) { c.ClientID = "" }), wantErr: "missing client id"},
{name: "missing organization", config: withField(validConfig, func(c *Config) { c.Organization = "" }), wantErr: "missing organization"},
{name: "missing secret non-public", config: withField(validConfig, func(c *Config) { c.ClientSecret = "" }), wantErr: "missing client secret"},
+ {name: "missing port", config: withField(validConfig, func(c *Config) { c.Port = 0 }), wantErr: "missing or invalid port"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
diff --git a/ims/exchange.go b/ims/exchange.go
index 6bd1d43..c1405d4 100644
--- a/ims/exchange.go
+++ b/ims/exchange.go
@@ -12,7 +12,6 @@ package ims
import (
"fmt"
- "time"
"github.com/adobe/ims-go/ims"
)
@@ -62,6 +61,5 @@ func (i Config) ClusterExchange() (TokenInfo, error) {
return TokenInfo{
AccessToken: r.AccessToken,
- Expires: int(r.ExpiresIn * time.Millisecond),
}, nil
}
diff --git a/ims/jwt_exchange.go b/ims/jwt_exchange.go
index a30277b..42a91f4 100644
--- a/ims/jwt_exchange.go
+++ b/ims/jwt_exchange.go
@@ -13,6 +13,7 @@ package ims
import (
"fmt"
"os"
+ "strings"
"time"
"github.com/adobe/ims-go/ims"
@@ -29,14 +30,20 @@ func (i Config) AuthorizeJWTExchange() (TokenInfo, error) {
if err != nil {
return TokenInfo{}, fmt.Errorf("error read private key file: %s, %w", i.PrivateKeyPath, err)
}
+ defer func() {
+ for i := range key {
+ key[i] = 0
+ }
+ }()
// Metascopes are passed as generic claims with the format map[string]interface{}
// where the strings are in the form: baseIMSUrl/s/metascope
// and the interface{} is 'true'
+ baseURL := strings.TrimRight(i.URL, "/")
claims := make(map[string]interface{})
for _, metascope := range i.Metascopes {
- claims[fmt.Sprintf("%s/s/%s", i.URL, metascope)] = true
+ claims[fmt.Sprintf("%s/s/%s", baseURL, metascope)] = true
}
r, err := c.ExchangeJWT(&ims.ExchangeJWTRequest{
@@ -54,6 +61,5 @@ func (i Config) AuthorizeJWTExchange() (TokenInfo, error) {
return TokenInfo{
AccessToken: r.AccessToken,
- Expires: int(r.ExpiresIn * time.Millisecond),
}, nil
}
diff --git a/ims/organizations.go b/ims/organizations.go
index 5853c9b..c9fadc4 100644
--- a/ims/organizations.go
+++ b/ims/organizations.go
@@ -22,7 +22,7 @@ func (i Config) validateGetOrganizationsConfig() error {
switch i.OrgsAPIVersion {
case "v1", "v2", "v3", "v4", "v5", "v6":
default:
- return fmt.Errorf("invalid API version parameter, use something like v5")
+ return fmt.Errorf("invalid API version parameter, latest version is v6")
}
switch {
diff --git a/ims/refresh.go b/ims/refresh.go
index 185a258..82ce08e 100644
--- a/ims/refresh.go
+++ b/ims/refresh.go
@@ -12,8 +12,8 @@ package ims
import (
"fmt"
+
"github.com/adobe/ims-go/ims"
- "time"
)
func (i Config) validateRefreshConfig() error {
@@ -56,7 +56,6 @@ func (i Config) Refresh() (RefreshInfo, error) {
return RefreshInfo{
TokenInfo: TokenInfo{
AccessToken: r.AccessToken,
- Expires: int(r.ExpiresIn * time.Second),
},
RefreshToken: r.RefreshToken,
}, nil