Skip to content

Commit

Permalink
APIGOV-23624 Rotate application credentials (#107)
Browse files Browse the repository at this point in the history
* APIGOV-23624 update

* APIGOV-23624 update

* APIGOV-23624 tests

* APIGOV-23624 - update deps

* APIGOV-23624 update

* APIGOV-23624 - update deps

Co-authored-by: Trevor Johnson <tjohnson@axway.com>
Co-authored-by: Jason Collins <jcollins@axway.com>
  • Loading branch information
3 people authored Sep 23, 2022
1 parent 825defc commit d90d8d2
Show file tree
Hide file tree
Showing 15 changed files with 220 additions and 72 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ module github.com/Axway/agents-mulesoft

go 1.18

// replace github.com/Axway/agent-sdk => /home/ubuntu/go/src/github.com/Axway/agent-sdk
//replace github.com/Axway/agent-sdk => /home/ubuntu/go/src/github.com/Axway/agent-sdk

require (
github.com/Axway/agent-sdk v1.1.32
github.com/Axway/agent-sdk v1.1.35-0.20220923023651-3b9feb426670
github.com/elastic/beats/v7 v7.17.5
github.com/getkin/kin-openapi v0.76.0
github.com/sirupsen/logrus v1.8.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Axway/agent-sdk v1.1.32 h1:sYcV2muSOY6QkHHdT/fQbr0VoTxCesqArVFHTunN28M=
github.com/Axway/agent-sdk v1.1.32/go.mod h1:X65UL9Tulf0Gyq4UQzLZtn/A6B+B07sCI65JSZLH9sg=
github.com/Axway/agent-sdk v1.1.35-0.20220923023651-3b9feb426670 h1:XU2ywR8+z+gjMH4XPeciLo8cIYw8Oi8i0DRwLKzuJkI=
github.com/Axway/agent-sdk v1.1.35-0.20220923023651-3b9feb426670/go.mod h1:dj9d7QcFV6kAh4I2dApyCVi/LxT9hVVAD/Bmh15rn4g=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-autorest v12.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
Expand Down
8 changes: 8 additions & 0 deletions pkg/anypoint/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type Client interface {
OnConfigChange(mulesoftConfig *config.MulesoftConfig)
DeleteContract(apiID string, contractID string) error
RevokeContract(apiID, contractID string) error
ResetAppSecret(appID int64) (*Application, error)
}

type AnalyticsClient interface {
Expand Down Expand Up @@ -413,6 +414,13 @@ func (c *AnypointClient) CreateClientApplication(apiInstanceID string, app *AppR
return &application, nil
}

func (c *AnypointClient) ResetAppSecret(appID int64) (*Application, error) {
url := fmt.Sprintf("%s/exchange/api/v2/organizations/%s/applications/%v/secret/reset", c.baseURL, c.auth.GetOrgID(), appID)
application := &Application{}
err := c.invokeJSONPost(url, nil, []byte{}, application)
return application, err
}

func (c *AnypointClient) DeleteClientApplication(appID int64) error {
url := fmt.Sprintf("%s/exchange/api/v2/organizations/%s/applications/%v", c.baseURL, c.auth.GetOrgID(), appID)

Expand Down
4 changes: 4 additions & 0 deletions pkg/anypoint/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,7 @@ func (m *MockAnypointClient) RevokeContract(apiID, contractID string) error {
args := m.Called()
return args.Error(0)
}

func (m *MockAnypointClient) ResetAppSecret(appID int64) (*Application, error) {
return nil, nil
}
48 changes: 15 additions & 33 deletions pkg/cmd/discovery/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package discovery
import (
"fmt"

prov "github.com/Axway/agent-sdk/pkg/apic/provisioning"
"github.com/Axway/agent-sdk/pkg/migrate"
"github.com/Axway/agent-sdk/pkg/util"
"github.com/Axway/agent-sdk/pkg/util/log"
Expand Down Expand Up @@ -71,17 +70,28 @@ func initConfig(centralConfig corecfg.CentralConfig) (interface{}, error) {

if util.IsNotTest() {
client = anypoint.NewClient(conf.MulesoftConfig)
sm, err := initSubscriptionManager(client, agent.GetCentralClient())
if err != nil {
return nil, fmt.Errorf("error while initializing the subscription manager %s", err)
muleSubClient := subs.NewMuleSubscriptionClient(client)
entry := logrus.NewEntry(log.Get())
var sm *subs.Manager

if centralConfig.IsMarketplaceSubsEnabled() {
agent.RegisterProvisioner(subs.NewProvisioner(muleSubClient, entry))
agent.NewAPIKeyAccessRequestBuilder().Register()
agent.NewOAuthCredentialRequestBuilder(agent.WithCRDOAuthSecret()).IsRenewable().Register()
} else {
var err error
sm, err = initUCSubscriptionManager(client, agent.GetCentralClient())
if err != nil {
return nil, fmt.Errorf("error while initializing the subscription manager %s", err)
}
}

discoveryAgent = discovery.NewAgent(conf, client, sm)
}
return conf, nil
}

func initSubscriptionManager(apc anypoint.Client, central apic.Client) (*subs.Manager, error) {
func initUCSubscriptionManager(apc anypoint.Client, central apic.Client) (*subs.Manager, error) {
entry := logrus.NewEntry(log.Get())
muleSubClient := subs.NewMuleSubscriptionClient(apc)
clientID := subs.NewClientIDContract()
Expand All @@ -103,33 +113,5 @@ func initSubscriptionManager(apc anypoint.Client, central apic.Client) (*subs.Ma
// start polling for subscriptions
subManager.Start()

agent.RegisterProvisioner(subs.NewProvisioner(muleSubClient, entry))
agent.NewAPIKeyAccessRequestBuilder().Register()
newCredentialReq().Register()

return sm, nil
}

func newCredentialReq() prov.CredentialRequestBuilder {
id := prov.NewSchemaPropertyBuilder().
SetName(common.ClientID).
SetLabel(common.ClientIDLabel).
SetRequired().
IsString().
IsEncrypted()

secret := prov.NewSchemaPropertyBuilder().
SetName(common.ClientSecret).
SetLabel(common.ClientSecretLabel).
SetRequired().
IsString().
IsEncrypted()

return agent.NewCredentialRequestBuilder().
SetName(prov.APIKeyCRD).
SetProvisionSchema(
prov.NewSchemaBuilder().
AddProperty(id).
AddProperty(secret),
)
}
4 changes: 2 additions & 2 deletions pkg/cmd/discovery/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func Test_initSubscriptionManager(t *testing.T) {
return &apic.MockSubscriptionManager{}
}

manager, err := initSubscriptionManager(mc, cc)
manager, err := initUCSubscriptionManager(mc, cc)
assert.NotNil(t, manager)

assert.Nil(t, err)
Expand All @@ -59,6 +59,6 @@ func Test_initSubscriptionManager(t *testing.T) {
cc.RegisterSubscriptionSchemaMock = func(_ apic.SubscriptionSchema, _ bool) error {
return fmt.Errorf("failed")
}
_, err = initSubscriptionManager(mc, cc)
_, err = initUCSubscriptionManager(mc, cc)
assert.NotNil(t, err)
}
7 changes: 7 additions & 0 deletions pkg/discovery/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"
"syscall"

"github.com/Axway/agent-sdk/pkg/util"
"github.com/Axway/agents-mulesoft/pkg/subscription"

coreAgent "github.com/Axway/agent-sdk/pkg/agent"
Expand Down Expand Up @@ -50,6 +51,12 @@ func NewAgent(cfg *config.AgentConfig, client anypoint.Client, sm subscription.S
cache: c,
}

if util.IsNil(sm) {
svcHandler.mode = marketplace
} else {
svcHandler.mode = catalog
}

disc := &discovery{
apiChan: apiChan,
cache: c,
Expand Down
1 change: 1 addition & 0 deletions pkg/discovery/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,6 @@ func BuildServiceBody(service *ServiceDetail) (apic.ServiceBody, error) {
SetURL(service.URL).
SetVersion(service.Version).
SetAccessRequestDefinitionName(service.AccessRequestDefinition, false).
SetCredentialRequestDefinitions(service.CRDs).
Build()
}
69 changes: 46 additions & 23 deletions pkg/discovery/servicehandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strconv"
"strings"

"github.com/Axway/agent-sdk/pkg/apic/provisioning"
"github.com/Axway/agent-sdk/pkg/cache"
"github.com/Axway/agent-sdk/pkg/util/oas"
"github.com/Axway/agents-mulesoft/pkg/common"
Expand All @@ -27,6 +28,11 @@ import (
"sigs.k8s.io/yaml"
)

const (
marketplace = "marketplace"
catalog = "unified-catalog"
)

// ServiceHandler converts a mulesoft asset to an array of ServiceDetails
type ServiceHandler interface {
ToServiceDetails(asset *anypoint.Asset) []*ServiceDetail
Expand All @@ -40,6 +46,7 @@ type serviceHandler struct {
client anypoint.Client
schemas subs.SchemaStore
cache cache.Cache
mode string
}

func (s *serviceHandler) OnConfigChange(cfg *config.MulesoftConfig) {
Expand Down Expand Up @@ -94,7 +101,7 @@ func (s *serviceHandler) getServiceDetail(asset *anypoint.Asset, api *anypoint.A
if err != nil {
return nil, err
}
authPolicy, configuration, isSLABased := getAuthPolicy(policies)
authPolicy, configuration, isSLABased := getAuthPolicy(policies, s.mode)
logger = logger.WithField("policy", authPolicy)

isAlreadyPublished, checksum := isPublished(api, authPolicy, s.cache)
Expand All @@ -114,27 +121,37 @@ func (s *serviceHandler) getServiceDetail(asset *anypoint.Asset, api *anypoint.A

apiID := strconv.FormatInt(api.ID, 10)

subSchName := s.schemas.GetSubscriptionSchemaName(common.PolicyDetail{
Policy: authPolicy,
IsSLABased: isSLABased,
APIId: apiID,
})

// If the API has a new SLA Tier policy, create a new subscription schema for it
if subSchName == "" && isSLABased {
// Get details of the SLA tiers
tiers, err1 := s.client.GetSLATiers(api.ID)
if err1 != nil {
return nil, err1
}
schema, err1 := s.createSLATierSchema(apiID, tiers, agent.GetCentralClient())
if err1 != nil {
return nil, err1
var crds []string
subSchName := ""
ard := ""
if s.mode == marketplace {
if authPolicy == apic.Oauth {
ard = provisioning.APIKeyARD
crds = []string{provisioning.OAuthSecretCRD}
}
} else {
subSchName = s.schemas.GetSubscriptionSchemaName(common.PolicyDetail{
Policy: authPolicy,
IsSLABased: isSLABased,
APIId: apiID,
})

// If the API has a new SLA Tier policy, create a new subscription schema for it
if subSchName == "" && isSLABased {
// Get details of the SLA tiers
tiers, err1 := s.client.GetSLATiers(api.ID)
if err1 != nil {
return nil, err1
}
schema, err1 := s.createSLATierSchema(apiID, tiers, agent.GetCentralClient())
if err1 != nil {
return nil, err1
}

logger.Infof("schema registered")
logger.Infof("schema registered")

subSchName = schema.GetSubscriptionName()
subSchName = schema.GetSubscriptionName()
}
}

exchangeAsset, err := s.client.GetExchangeAsset(api.GroupID, api.AssetID, api.AssetVersion)
Expand Down Expand Up @@ -179,7 +196,8 @@ func (s *serviceHandler) getServiceDetail(asset *anypoint.Asset, api *anypoint.A
}

return &ServiceDetail{
AccessRequestDefinition: subSchName,
AccessRequestDefinition: ard,
CRDs: crds,
APIName: api.AssetID,
APISpec: modifiedSpec,
AuthPolicy: authPolicy,
Expand Down Expand Up @@ -349,16 +367,21 @@ func getSpecType(file *anypoint.ExchangeFile, specContent []byte) (string, error
}

// getAuthPolicy gets the authentication policy type.
func getAuthPolicy(policies anypoint.Policies) (string, map[string]interface{}, bool) {
func getAuthPolicy(policies anypoint.Policies, mode string) (string, map[string]interface{}, bool) {
authPolicy := apic.Apikey
if mode == marketplace {
authPolicy = apic.Oauth
}

for _, policy := range policies.Policies {
if policy.Template.AssetID == common.ClientIDEnforcement {
conf := getMapFromInterface(policy.Configuration)
return apic.Apikey, conf, false
return authPolicy, conf, false
}

if strings.Contains(policy.Template.AssetID, common.SLABased) {
conf := getMapFromInterface(policy.Configuration)
return apic.Apikey, conf, true
return authPolicy, conf, true
}

if policy.Template.AssetID == common.ExternalOauth {
Expand Down
2 changes: 1 addition & 1 deletion pkg/discovery/servicehandler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ func Test_getAuthPolicy(t *testing.T) {
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
policy, conf, _ := getAuthPolicy(tc.policies)
policy, conf, _ := getAuthPolicy(tc.policies, catalog)
assert.Equal(t, policy, tc.expected)
assert.NotNil(t, conf)
})
Expand Down
1 change: 1 addition & 0 deletions pkg/discovery/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type ServiceDetail struct {
APISpec []byte
APIUpdateSeverity string
AuthPolicy string
CRDs []string
Description string
Documentation []byte
ID string
Expand Down
12 changes: 9 additions & 3 deletions pkg/subscription/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import (
)

type MockMuleSubscriptionClient struct {
app *anypoint.Application
err error
contract *anypoint.Contract
app *anypoint.Application
newApp *anypoint.Application
err error
rotateErr error
contract *anypoint.Contract
}

func (m *MockMuleSubscriptionClient) CreateApp(appName, apiID, description string) (*anypoint.Application, error) {
Expand All @@ -29,3 +31,7 @@ func (m *MockMuleSubscriptionClient) DeleteContract(apiID, contractID string) er
func (m *MockMuleSubscriptionClient) GetApp(id string) (*anypoint.Application, error) {
return m.app, m.err
}

func (m *MockMuleSubscriptionClient) ResetAppSecret(appID int64) (*anypoint.Application, error) {
return m.newApp, m.rotateErr
}
6 changes: 6 additions & 0 deletions pkg/subscription/mulesubscriptionclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type MuleSubscriptionClient interface {
DeleteApp(appID int64) error
DeleteContract(apiID, contractID string) error
GetApp(id string) (*anypoint.Application, error)
ResetAppSecret(appID int64) (*anypoint.Application, error)
}

type muleSubscription struct {
Expand All @@ -26,6 +27,11 @@ func NewMuleSubscriptionClient(client anypoint.Client) MuleSubscriptionClient {
}
}

// ResetAppSecret resets the secret for an app
func (c muleSubscription) ResetAppSecret(appID int64) (*anypoint.Application, error) {
return c.client.ResetAppSecret(appID)
}

// GetApp gets a mulesoft app by id
func (c muleSubscription) GetApp(id string) (*anypoint.Application, error) {
return c.client.GetClientApplication(id)
Expand Down
Loading

0 comments on commit d90d8d2

Please sign in to comment.