-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
use the adal library for spn when --legacy is specified (#338)
* use the adal library for spn when --legacy is specified * simplified validation * addressed feedbacks * added basic validation UTs
- Loading branch information
Showing
4 changed files
with
149 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package token | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/Azure/go-autorest/autorest/adal" | ||
) | ||
|
||
var errInvalidOAuthConfig = errors.New("OAuthConfig needs to be configured with api-version=1.0") | ||
|
||
type legacyServicePrincipalToken struct { | ||
clientID string | ||
clientSecret string | ||
clientCert string | ||
clientCertPassword string | ||
resourceID string | ||
tenantID string | ||
oAuthConfig adal.OAuthConfig | ||
} | ||
|
||
func newLegacyServicePrincipalToken(oAuthConfig adal.OAuthConfig, clientID, clientSecret, clientCert, clientCertPassword, resourceID, tenantID string) (TokenProvider, error) { | ||
if err := validateOAuthConfig(oAuthConfig); err != nil { | ||
return nil, err | ||
} | ||
if clientID == "" { | ||
return nil, errors.New("clientID cannot be empty") | ||
} | ||
if clientSecret == "" && clientCert == "" { | ||
return nil, errors.New("both clientSecret and clientcert cannot be empty") | ||
} | ||
if clientSecret != "" && clientCert != "" { | ||
return nil, errors.New("client secret and client certificate cannot be set at the same time. Only one has to be specified") | ||
} | ||
if resourceID == "" { | ||
return nil, errors.New("resourceID cannot be empty") | ||
} | ||
if tenantID == "" { | ||
return nil, errors.New("tenantID cannot be empty") | ||
} | ||
|
||
return &legacyServicePrincipalToken{ | ||
clientID: clientID, | ||
clientSecret: clientSecret, | ||
clientCert: clientCert, | ||
clientCertPassword: clientCertPassword, | ||
resourceID: resourceID, | ||
tenantID: tenantID, | ||
oAuthConfig: oAuthConfig, | ||
}, nil | ||
} | ||
|
||
func (p *legacyServicePrincipalToken) Token() (adal.Token, error) { | ||
emptyToken := adal.Token{} | ||
|
||
var ( | ||
spt *adal.ServicePrincipalToken | ||
err error | ||
) | ||
|
||
if p.clientSecret != "" { | ||
spt, err = adal.NewServicePrincipalToken( | ||
p.oAuthConfig, | ||
p.clientID, | ||
p.clientSecret, | ||
p.resourceID) | ||
if err != nil { | ||
return emptyToken, fmt.Errorf("failed to create service principal token using secret: %s", err) | ||
} | ||
} else if p.clientCert != "" { | ||
certData, err := os.ReadFile(p.clientCert) | ||
if err != nil { | ||
return emptyToken, fmt.Errorf("failed to read the certificate file (%s): %w", p.clientCert, err) | ||
} | ||
|
||
// Get the certificate and private key from pfx file | ||
cert, rsaPrivateKey, err := decodePkcs12(certData, p.clientCertPassword) | ||
if err != nil { | ||
return emptyToken, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %w", err) | ||
} | ||
|
||
spt, err = adal.NewServicePrincipalTokenFromCertificate( | ||
p.oAuthConfig, | ||
p.clientID, | ||
cert, | ||
rsaPrivateKey, | ||
p.resourceID) | ||
if err != nil { | ||
return emptyToken, fmt.Errorf("failed to create service principal token using cert: %s", err) | ||
} | ||
} | ||
|
||
err = spt.EnsureFresh() | ||
if err != nil { | ||
return emptyToken, err | ||
} | ||
return spt.Token(), nil | ||
} | ||
|
||
func validateOAuthConfig(config adal.OAuthConfig) error { | ||
v := config.AuthorizeEndpoint.Query().Get("api-version") | ||
if v != "1.0" { | ||
return errInvalidOAuthConfig | ||
} | ||
|
||
v = config.TokenEndpoint.Query().Get("api-version") | ||
if v != "1.0" { | ||
return errInvalidOAuthConfig | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package token | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestNewLegacyServicePrincipalToken(t *testing.T) { | ||
t.Run("new spn token provider with legacy should not result in error", func(t *testing.T) { | ||
_, err := newTokenProvider(&Options{ | ||
LoginMethod: ServicePrincipalLogin, | ||
IsLegacy: true, | ||
TenantID: "tenantID", | ||
ClientID: "client-id", | ||
ClientSecret: "foobar", | ||
ServerID: "server-id", | ||
Environment: "AzurePublicCloud", | ||
}) | ||
|
||
require.NoError(t, err) | ||
}) | ||
|
||
t.Run("legacy spn token provider with incorrectly setup oauth config should result in error", func(t *testing.T) { | ||
oathConfig, err := getOAuthConfig("AzurePublicCloud", "tenantID", false) | ||
require.NoError(t, err) | ||
|
||
_, err = newLegacyServicePrincipalToken(*oathConfig, "client-id", "client-secret", "", "", "server-id", "tenant-id") | ||
require.ErrorIs(t, err, errInvalidOAuthConfig) | ||
}) | ||
} |