diff --git a/go.mod b/go.mod index bf478c1..95df55f 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( require ( cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect + github.com/alecthomas/kong v0.8.1 // indirect github.com/alecthomas/repr v0.3.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 // indirect @@ -32,8 +33,10 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 // indirect github.com/aws/smithy-go v1.19.0 // indirect + github.com/chzyer/readline v1.5.1 // indirect github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -44,8 +47,13 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect + github.com/kataras/tableprinter v0.0.0-20201125135848-89e81fc956e7 // indirect + github.com/kataras/tablewriter v0.0.0-20180708051242-e063d29b7c23 // indirect github.com/kr/pretty v0.3.1 // indirect + github.com/lensesio/tableprinter v0.0.0-20201125135848-89e81fc956e7 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.4.4 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/stretchr/objx v0.5.1 // indirect diff --git a/go.sum b/go.sum index 4ad89e0..aaa9b9e 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2Aawl github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/alecthomas/assert/v2 v2.4.1 h1:mwPZod/d35nlaCppr6sFP0rbCL05WH9fIo7lvsf47zo= github.com/alecthomas/assert/v2 v2.4.1/go.mod h1:fw5suVxB+wfYJ3291t0hRTqtGzFYdSwstnRQdaQx2DM= +github.com/alecthomas/kong v0.8.1 h1:acZdn3m4lLRobeh3Zi2S2EpnXTd1mOL6U7xVml+vfkY= +github.com/alecthomas/kong v0.8.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8= github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= github.com/alecthomas/repr v0.3.0 h1:NeYzUPfjjlqHY4KtzgKJiWd6sVq2eNUPTi34PiFGjY8= @@ -72,6 +74,10 @@ github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= @@ -81,6 +87,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -142,6 +150,10 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo github.com/hhsnopek/etag v0.0.0-20171206181245-aea95f647346 h1:Odeq5rB6OZSkib5gqTG+EM1iF0bUVjYYd33XB1ULv00= github.com/hhsnopek/etag v0.0.0-20171206181245-aea95f647346/go.mod h1:4ggHM2qnyyZjenBb7RpwVzIj+JMsu9kHCVxMjB30hGs= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/kataras/tableprinter v0.0.0-20201125135848-89e81fc956e7 h1:dhFs0QH1feL9RtTVyt2O8/SKjvREjq6evemrmBVIX/k= +github.com/kataras/tableprinter v0.0.0-20201125135848-89e81fc956e7/go.mod h1:DZxCfbuCvJAMfpR2wyUnyTr+XKusTbX9THYM1wBMTas= +github.com/kataras/tablewriter v0.0.0-20180708051242-e063d29b7c23 h1:M8exrBzuhWcU6aoHJlHWPe4qFjVKzkMGRal78f5jRRU= +github.com/kataras/tablewriter v0.0.0-20180708051242-e063d29b7c23/go.mod h1:kBSna6b0/RzsOcOZf515vAXwSsXYusl2U7SA0XP09yI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -149,11 +161,18 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lensesio/tableprinter v0.0.0-20201125135848-89e81fc956e7 h1:k/1ku0yehLCPqERCHkIHMDqDg1R02AcCScRuHbamU3s= +github.com/lensesio/tableprinter v0.0.0-20201125135848-89e81fc956e7/go.mod h1:YR/zYthNdWfO8+0IOyHDcIDBBBS2JMnYUIwSsnwmRqU= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= @@ -225,6 +244,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= diff --git a/pkg/hexapolicy/hexa_policy.go b/pkg/hexapolicy/hexa_policy.go index e9816da..8ad864b 100644 --- a/pkg/hexapolicy/hexa_policy.go +++ b/pkg/hexapolicy/hexa_policy.go @@ -18,7 +18,7 @@ const ( SSamlAuth string = "saml" SCidr string = "net" - IDQL_VERSION string = "0.6" + IdqlVersion string = "0.6" ) type Policies struct { diff --git a/providers/aws/avp/avpClient/avpTestSupport/aws_api_clientvalues.go b/providers/aws/avp/avpClient/avpTestSupport/aws_api_clientvalues.go index 14c1758..0a55b05 100644 --- a/providers/aws/avp/avpClient/avpTestSupport/aws_api_clientvalues.go +++ b/providers/aws/avp/avpClient/avpTestSupport/aws_api_clientvalues.go @@ -50,7 +50,7 @@ func AwsCredentialsForTest() []byte { } func IntegrationInfo() PolicyProvider.IntegrationInfo { - return PolicyProvider.IntegrationInfo{Name: "avp", Key: AwsCredentialsForTest()} + return PolicyProvider.IntegrationInfo{Name: PolicyProvider.PROVIDER_TYPE_AVP, Key: AwsCredentialsForTest()} } func AppInfo() PolicyProvider.ApplicationInfo { diff --git a/providers/aws/avp/avp_provider.go b/providers/aws/avp/avp_provider.go index cceb789..4496fcb 100644 --- a/providers/aws/avp/avp_provider.go +++ b/providers/aws/avp/avp_provider.go @@ -30,7 +30,7 @@ func MapAvpMeta(item types.PolicyItem) hexapolicy.MetaInfo { data[ParamPolicyType] = string(types.PolicyTypeStatic) return hexapolicy.MetaInfo{ - Version: hexapolicy.IDQL_VERSION, + Version: hexapolicy.IdqlVersion, ProviderType: ProviderTypeAvp, Created: item.CreatedDate, Modified: item.LastUpdatedDate, @@ -44,7 +44,7 @@ func MapAvpTemplate(item *verifiedpermissions.GetPolicyTemplateOutput) hexapolic data := map[string]interface{}{} data[ParamPolicyType] = string(types.PolicyTypeTemplateLinked) return hexapolicy.MetaInfo{ - Version: hexapolicy.IDQL_VERSION, + Version: hexapolicy.IdqlVersion, ProviderType: ProviderTypeAvp, Created: item.CreatedDate, Modified: item.LastUpdatedDate, diff --git a/sdk/options.go b/sdk/options.go new file mode 100644 index 0000000..37ed95a --- /dev/null +++ b/sdk/options.go @@ -0,0 +1,34 @@ +package sdk + +import ( + "net/http" + + "github.com/hexa-org/policy-mapper/api/PolicyProvider" +) + +type Options struct { + // The HTTP client to invoke API calls with. Defaults to client's default HTTP + // implementation if nil. + HTTPClient http.Client `json:"-"` + Info *PolicyProvider.IntegrationInfo `json:"integrationInfo"` + + ProviderOpts interface{} `json:"-"` +} + +func WithHttpClient(client http.Client) func(*Options) { + return func(o *Options) { + o.HTTPClient = client + } +} + +func WithProviderOptions(options interface{}) func(*Options) { + return func(o *Options) { + o.ProviderOpts = options + } +} + +func WithIntegrationInfo(info PolicyProvider.IntegrationInfo) func(options *Options) { + return func(o *Options) { + o.Info = &info + } +} diff --git a/sdk/provderTools.go b/sdk/provderTools.go new file mode 100644 index 0000000..f6c1669 --- /dev/null +++ b/sdk/provderTools.go @@ -0,0 +1,189 @@ +package sdk + +import ( + "errors" + "fmt" + "net/http" + + "github.com/hexa-org/policy-mapper/api/PolicyProvider" + "github.com/hexa-org/policy-mapper/pkg/hexapolicy" + "github.com/hexa-org/policy-mapper/providers/aws/avp" + "github.com/hexa-org/policy-mapper/providers/aws/awscommon" +) + +var PROVIDER_TYPE_AVP = "avp" + +type Integration struct { + Alias string `json:"alias"` + Opts Options `json:"options"` + Apps map[string]PolicyProvider.ApplicationInfo `json:"apps"` + provider PolicyProvider.Provider `json:"-"` +} + +/* +OpenIntegration accepts a json byte stream and parses into an IntegrationInfo which can be used to invoke the +provider features. An IntegrationInfo struct consists of: + +- integrationInfo - PolicyProvider.IntegrationInfo information defining the provider type and the credential used to access. If not provided, it must be provided using Options + +- options - one or more configuration functions for configuring provider See: sdk.Options +*/ +func OpenIntegration(integrationInfo *PolicyProvider.IntegrationInfo, options ...func(*Options)) (*Integration, error) { + + i := &Integration{ + Opts: Options{}, + } + + if integrationInfo != nil { + i.Opts.Info = integrationInfo + } + for _, opt := range options { + opt(&i.Opts) + } + + _, err := i.open() + + return i, err +} + +/* +Open causes the provider to be instantiated and a handle to the Provider interface is returned. Note: providers +are automatically opened if Open is not called. Primary purpose is to expose the underlying Provider interface for +some integrations such as Orchestrator. +*/ +func (i *Integration) open() (PolicyProvider.Provider, error) { + info := i.Opts.Info + if info == nil { + return nil, errors.New("missing PolicyProvider.IntegrationInfo object") + } + switch info.Name { + case PROVIDER_TYPE_AVP: + if i.provider == nil { + opts := awscommon.AWSClientOptions{DisableRetry: true} + if i.Opts.ProviderOpts != nil { + switch v := i.Opts.ProviderOpts.(type) { + case awscommon.AWSClientOptions: + opts = v + default: + } + } + i.provider = &avp.AmazonAvpProvider{AwsClientOpts: opts} + } + return i.provider, nil + default: + return nil, errors.New("provider not available in hexa policy SDK") + } +} + +// GetType returns the type of underlying provider. See: sdk.PROVIDER_TYPE_ values. If not defined, "ERROR" is returned. +func (i *Integration) GetType() string { + if i.Opts.Info == nil { + return "ERROR" + } + return i.Opts.Info.Name +} + +func (i *Integration) checkOpen() { + if i.provider == nil { + i.open() + } +} + +/* +GetPolicyApplicationPoints invokes Provider.DiscoverApplications method to locate applications or policy application +points available within a platform Integration. The 'aliasGen' func parameter is used to generate a local alias for the +application. If 'nil' is passed, the ObjectId value from ApplicationInfo is used as the alias. +*/ +func (i *Integration) GetPolicyApplicationPoints(aliasGen func() string) ([]PolicyProvider.ApplicationInfo, error) { + i.checkOpen() + + aliasMap := map[string]string{} + if i.Apps != nil { + for k, app := range i.Apps { + aliasMap[app.ObjectID] = k + } + } + apps, err := i.provider.DiscoverApplications(*i.Opts.Info) + if err != nil { + return nil, err + } + + newAppMap := make(map[string]PolicyProvider.ApplicationInfo, len(apps)) + for _, app := range apps { + // default to objectID + alias := app.ObjectID + + // if the application had a previous alias, use it + oldAlias, exist := aliasMap[app.ObjectID] + if exist { + alias = oldAlias + } else { + if aliasGen != nil { + alias = aliasGen() + } + } + newAppMap[alias] = app + } + i.Apps = newAppMap + + return apps, nil +} + +func (i *Integration) GetApplicationInfo(papAlias string) (*PolicyProvider.ApplicationInfo, error) { + if i.Apps == nil { + _, err := i.GetPolicyApplicationPoints(nil) + if err != nil { + return nil, err + } + } + info, exist := i.Apps[papAlias] + if !exist { + return nil, errors.New(fmt.Sprintf("alias [%s] not found", papAlias)) + } + return &info, nil +} + +/* +GetPolicies queries the designated 'pap' and returns a set of mapped hexapolicy.PolicyInfo policies. +*/ +func (i *Integration) GetPolicies(papAlias string) ([]hexapolicy.PolicyInfo, error) { + i.checkOpen() + app, err := i.GetApplicationInfo(papAlias) + if err != nil { + return nil, err + } + return i.provider.GetPolicyInfo(*i.Opts.Info, *app) +} + +/* +SetPolicyInfo applies the specified set of policies to the integrations 'pap'. Depending on the underlying provider, +set replaces all policies or does a reconciliation and performs the necessary changes to make the 'pap' have the same set of policies. +Note: SetPolicyInfo does not support the setting of an individual policy. +*/ +func (i *Integration) SetPolicyInfo(papAlias string, policies []hexapolicy.PolicyInfo) (int, error) { + i.checkOpen() + app, err := i.GetApplicationInfo(papAlias) + if err != nil { + return http.StatusInternalServerError, err + } + return i.provider.SetPolicyInfo(*i.Opts.Info, *app, policies) +} + +/* +ReconcilePolicy returns the set of differences between the supplied policies and the policies reported by the specified 'pap'. +Setting 'diffsOnly' to false will return results that include matched and unsupported policies (e.g. templates). If the +provider implementation does not support reconcile, an error is returned. +*/ +func (i *Integration) ReconcilePolicy(papAlias string, policies []hexapolicy.PolicyInfo, diffsOnly bool) ([]hexapolicy.PolicyDif, error) { + i.checkOpen() + app, err := i.GetApplicationInfo(papAlias) + if err != nil { + return []hexapolicy.PolicyDif{}, err + } + switch rp := i.provider.(type) { + case PolicyProvider.V2Provider: + return rp.Reconcile(*i.Opts.Info, *app, policies, diffsOnly) + default: + return []hexapolicy.PolicyDif{}, errors.New("provider does not support reconcile") + } +} diff --git a/sdk/providerTools_test.go b/sdk/providerTools_test.go new file mode 100644 index 0000000..a66bae0 --- /dev/null +++ b/sdk/providerTools_test.go @@ -0,0 +1,171 @@ +package sdk + +import ( + "fmt" + "log" + "net/http" + "os" + "slices" + "testing" + "time" + + "github.com/hexa-org/policy-mapper/api/PolicyProvider" + "github.com/hexa-org/policy-mapper/pkg/hexapolicy" + "github.com/hexa-org/policy-mapper/providers/aws/avp" + "github.com/hexa-org/policy-mapper/providers/aws/avp/avpClient/avpTestSupport" + "github.com/hexa-org/policy-mapper/providers/aws/awscommon" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +var testLog = log.New(os.Stdout, "SDK-TEST: ", log.Ldate|log.Ltime) + +type testSuite struct { + suite.Suite + Integration *Integration + Info PolicyProvider.IntegrationInfo + papId string + mockClient *avpTestSupport.MockVerifiedPermissionsHTTPClient +} + +func TestSdk(t *testing.T) { + + mockClient := avpTestSupport.NewMockVerifiedPermissionsHTTPClient() + + options := awscommon.AWSClientOptions{ + HTTPClient: mockClient, + DisableRetry: true, + } + info := avpTestSupport.IntegrationInfo() + integration, err := OpenIntegration(&info, WithProviderOptions(options)) + assert.NoError(t, err, "Check no error opening mock provider") + s := testSuite{ + Info: info, + Integration: integration, + mockClient: mockClient, + } + + suite.Run(t, &s) + + testLog.Println("** SDK Tests Complete **") + +} + +func (s *testSuite) Test1_GetPaps() { + s.mockClient.MockListStores() + + apps, err := s.Integration.GetPolicyApplicationPoints(nil) + assert.NoError(s.T(), err, "Check no error for get PAPs") + assert.Len(s.T(), apps, 1, "Should be 1 app") + assert.Len(s.T(), s.Integration.Apps, len(apps)) + s.papId = apps[0].ObjectID // save for the next test + s.mockClient.VerifyCalled() +} + +func (s *testSuite) Test2_GetPolicies() { + s.mockClient.MockListPoliciesWithHttpStatus(http.StatusOK, 1, 1, nil) + s.mockClient.MockGetPolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"0") + s.mockClient.MockGetPolicyTemplateWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarTemplatePolicyId+"0") + + policies, err := s.Integration.GetPolicies(s.papId) + + assert.NoError(s.T(), err) + assert.NotNil(s.T(), policies) + assert.Len(s.T(), policies, 2, "Should be 2 policies") + assert.True(s.T(), s.mockClient.VerifyCalled()) +} + +func (s *testSuite) Test3_Reconcile() { + s.mockClient.MockListPoliciesWithHttpStatus(http.StatusOK, 5, 1, nil) + s.mockClient.MockGetPolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"0") + s.mockClient.MockGetPolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"1") + s.mockClient.MockGetPolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"2") + s.mockClient.MockGetPolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"3") + s.mockClient.MockGetPolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"4") + s.mockClient.MockGetPolicyTemplateWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarTemplatePolicyId+"0") + + policies, err := s.Integration.GetPolicies(s.papId) + + assert.True(s.T(), s.mockClient.VerifyCalled()) + + // origPolicies := policies[0:] + // This section will cause an update since only action changed + policy := policies[0] + actions := policy.Actions + + actions = append(actions, hexapolicy.ActionInfo{ActionUri: "cedar:hexa_avp::Action::\"UpdateAccount\""}) + + policies[0].Actions = actions + + avpMeta := policies[1].Meta + var avpType string + avpType, exist := avpMeta.SourceData[avp.ParamPolicyType].(string) + assert.True(s.T(), exist, "Check policy type exists") + assert.Equal(s.T(), "TEMPLATE_LINKED", avpType, "Second [1] policy should be template") + + // this should cause a replacement (delete and add) to occur (subject change) + policies[2].Subject.Members = []string{"hexa_avp::User::\"gerry@strata.io\""} + + // this should cause an implied delete by removing policy 5 + policies = append(policies[0:5], policies[6:]...) + + now := time.Now() + // now append a policy by copying and modifying the first + newPolicy := policies[0] + newPolicy.Meta = hexapolicy.MetaInfo{ + Version: "0.5", + Description: "Test New Policy", + Created: &now, + Modified: &now, + } + newPolicy.Subject.Members = []string{"hexa_avp::User::\"nobody@strata.io\""} + + policies = append(policies, newPolicy) + + s.mockClient.MockListPoliciesWithHttpStatus(http.StatusOK, 5, 1, nil) + s.mockClient.MockGetPolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"0") + s.mockClient.MockGetPolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"1") + s.mockClient.MockGetPolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"2") + s.mockClient.MockGetPolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"3") + s.mockClient.MockGetPolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"4") + s.mockClient.MockGetPolicyTemplateWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarTemplatePolicyId+"0") + difs, err := s.Integration.ReconcilePolicy(s.papId, policies, true) + assert.NoError(s.T(), err) + assert.True(s.T(), s.mockClient.VerifyCalled()) + assert.Len(s.T(), difs, 5) + assert.Equal(s.T(), hexapolicy.TYPE_UPDATE, difs[0].Type) + assert.True(s.T(), slices.Equal([]string{"ACTION"}, difs[0].DifTypes)) + assert.Equal(s.T(), hexapolicy.TYPE_IGNORED, difs[1].Type) + assert.Equal(s.T(), hexapolicy.TYPE_UPDATE, difs[2].Type) + assert.True(s.T(), slices.Equal([]string{"SUBJECT"}, difs[2].DifTypes)) + assert.Equal(s.T(), hexapolicy.TYPE_NEW, difs[3].Type) + assert.Equal(s.T(), hexapolicy.TYPE_DELETE, difs[4].Type) + for _, dif := range difs { + fmt.Println(dif.Report()) + } +} + +func (s *testSuite) Test4_SetPolicies() { + s.mockClient.MockListPoliciesWithHttpStatus(http.StatusOK, 1, 1, nil) + s.mockClient.MockGetPolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"0") + s.mockClient.MockGetPolicyTemplateWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarTemplatePolicyId+"0") + policies, err := s.Integration.GetPolicies(s.papId) + assert.True(s.T(), s.mockClient.VerifyCalled()) + + // This section will cause an update since only action changed + policy := policies[0] + actions := policy.Actions + actions = append(actions, hexapolicy.ActionInfo{ActionUri: "cedar:hexa_avp::Action::\"UpdateAccount\""}) + + policies[0].Actions = actions + + s.mockClient.MockListPoliciesWithHttpStatus(http.StatusOK, 1, 1, nil) + s.mockClient.MockGetPolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"0") + s.mockClient.MockGetPolicyTemplateWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarTemplatePolicyId+"0") + s.mockClient.MockUpdatePolicyWithHttpStatus(http.StatusOK, avpTestSupport.TestCedarStaticPolicyId+"0") + status, err := s.Integration.SetPolicyInfo(s.papId, policies) + assert.NoError(s.T(), err) + assert.True(s.T(), s.mockClient.VerifyCalled()) + assert.Equal(s.T(), 200, status, "Should be status 200") + +}