Skip to content

Commit

Permalink
feat: Add SCIM and SAML2 security integrations to sdk (#2799)
Browse files Browse the repository at this point in the history
<!-- Feel free to delete comments as you fill this in -->

<!-- summary of changes -->
Add security integrations to SDK.
Changes in follow ups: Add remaining integration types. Use SDK in
Terraform resources. Make sure related bugs are fixed.
## Test Plan
<!-- detail ways in which this PR has been tested or needs to be tested
-->
* [ ] acceptance tests
<!-- add more below if you think they are relevant -->
* [ ] …

## References
<!-- issues documentation links, etc  -->

https://docs.snowflake.com/en/sql-reference/sql/create-security-integration

https://docs.snowflake.com/en/sql-reference/sql/alter-security-integration
  • Loading branch information
sfc-gh-jmichalak authored May 20, 2024
1 parent f20940c commit 1312ff1
Show file tree
Hide file tree
Showing 17 changed files with 2,337 additions and 64 deletions.
47 changes: 47 additions & 0 deletions pkg/acceptance/helpers/random/certs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package random

import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"strings"
"testing"
"time"

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

// Generate X509 returns base64 encoded certificate on a single line without the leading -----BEGIN CERTIFICATE----- and ending -----END CERTIFICATE----- markers.
func GenerateX509(t *testing.T) string {
t.Helper()
ca := &x509.Certificate{
SerialNumber: big.NewInt(1658),
Subject: pkix.Name{
Organization: []string{"Company, INC."},
},
NotAfter: time.Now().AddDate(10, 0, 0),
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
}

caPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err)

caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey)
require.NoError(t, err)

certPEM := new(bytes.Buffer)
err = pem.Encode(certPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
})
require.NoError(t, err)

cert := strings.TrimPrefix(certPEM.String(), "-----BEGIN CERTIFICATE-----\n")
cert = strings.TrimSuffix(cert, "-----END CERTIFICATE-----\n")
return cert
}
13 changes: 13 additions & 0 deletions pkg/acceptance/helpers/role_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,19 @@ func (c *RoleClient) GrantRoleToUser(t *testing.T, id sdk.AccountObjectIdentifie
require.NoError(t, err)
}

func (c *RoleClient) GrantRoleToCurrentRole(t *testing.T, id sdk.AccountObjectIdentifier) {
t.Helper()
ctx := context.Background()

currentRole, err := c.context.client.ContextFunctions.CurrentRole(ctx)
require.NoError(t, err)

err = c.client().Grant(ctx, sdk.NewGrantRoleRequest(id, sdk.GrantRole{
Role: sdk.Pointer(currentRole),
}))
require.NoError(t, err)
}

// TODO: move later to grants client
func (c *RoleClient) GrantOwnershipOnAccountObject(t *testing.T, roleId sdk.AccountObjectIdentifier, objectId sdk.AccountObjectIdentifier, objectType sdk.ObjectType) {
t.Helper()
Expand Down
72 changes: 72 additions & 0 deletions pkg/acceptance/helpers/security_integration_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package helpers

import (
"context"
"testing"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
"github.com/stretchr/testify/require"
)

type SecurityIntegrationClient struct {
context *TestClientContext
ids *IdsGenerator
}

func NewSecurityIntegrationClient(context *TestClientContext, idsGenerator *IdsGenerator) *SecurityIntegrationClient {
return &SecurityIntegrationClient{
context: context,
ids: idsGenerator,
}
}

func (c *SecurityIntegrationClient) client() sdk.SecurityIntegrations {
return c.context.client.SecurityIntegrations
}

func (c *SecurityIntegrationClient) CreateSaml2(t *testing.T, id sdk.AccountObjectIdentifier) (*sdk.SecurityIntegration, func()) {
t.Helper()
return c.CreateSaml2WithRequest(t, sdk.NewCreateSaml2SecurityIntegrationRequest(id, false, c.ids.Alpha(), "https://example.com", "Custom", random.GenerateX509(t)))
}

func (c *SecurityIntegrationClient) CreateSaml2WithRequest(t *testing.T, request *sdk.CreateSaml2SecurityIntegrationRequest) (*sdk.SecurityIntegration, func()) {
t.Helper()
ctx := context.Background()

err := c.client().CreateSaml2(ctx, request)
require.NoError(t, err)

si, err := c.client().ShowByID(ctx, request.GetName())
require.NoError(t, err)

return si, c.DropSecurityIntegrationFunc(t, request.GetName())
}

func (c *SecurityIntegrationClient) CreateScim(t *testing.T) (*sdk.SecurityIntegration, func()) {
t.Helper()
return c.CreateScimWithRequest(t, sdk.NewCreateScimSecurityIntegrationRequest(c.ids.RandomAccountObjectIdentifier(), false, sdk.ScimSecurityIntegrationScimClientGeneric, sdk.ScimSecurityIntegrationRunAsRoleGenericScimProvisioner))
}

func (c *SecurityIntegrationClient) CreateScimWithRequest(t *testing.T, request *sdk.CreateScimSecurityIntegrationRequest) (*sdk.SecurityIntegration, func()) {
t.Helper()
ctx := context.Background()

err := c.client().CreateScim(ctx, request)
require.NoError(t, err)

si, err := c.client().ShowByID(ctx, request.GetName())
require.NoError(t, err)

return si, c.DropSecurityIntegrationFunc(t, request.GetName())
}

func (c *SecurityIntegrationClient) DropSecurityIntegrationFunc(t *testing.T, id sdk.AccountObjectIdentifier) func() {
t.Helper()
ctx := context.Background()

return func() {
err := c.client().Drop(ctx, sdk.NewDropSecurityIntegrationRequest(id).WithIfExists(sdk.Bool(true)))
require.NoError(t, err)
}
}
122 changes: 62 additions & 60 deletions pkg/acceptance/helpers/test_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,37 @@ type TestClient struct {

Ids *IdsGenerator

Account *AccountClient
Alert *AlertClient
ApiIntegration *ApiIntegrationClient
Application *ApplicationClient
ApplicationPackage *ApplicationPackageClient
Context *ContextClient
Database *DatabaseClient
DatabaseRole *DatabaseRoleClient
DynamicTable *DynamicTableClient
FailoverGroup *FailoverGroupClient
FileFormat *FileFormatClient
MaskingPolicy *MaskingPolicyClient
MaterializedView *MaterializedViewClient
NetworkPolicy *NetworkPolicyClient
Parameter *ParameterClient
PasswordPolicy *PasswordPolicyClient
Pipe *PipeClient
ResourceMonitor *ResourceMonitorClient
Role *RoleClient
RowAccessPolicy *RowAccessPolicyClient
Schema *SchemaClient
SessionPolicy *SessionPolicyClient
Share *ShareClient
Stage *StageClient
Table *TableClient
Tag *TagClient
Task *TaskClient
User *UserClient
View *ViewClient
Warehouse *WarehouseClient
Account *AccountClient
Alert *AlertClient
ApiIntegration *ApiIntegrationClient
Application *ApplicationClient
ApplicationPackage *ApplicationPackageClient
Context *ContextClient
Database *DatabaseClient
DatabaseRole *DatabaseRoleClient
DynamicTable *DynamicTableClient
FailoverGroup *FailoverGroupClient
FileFormat *FileFormatClient
MaskingPolicy *MaskingPolicyClient
MaterializedView *MaterializedViewClient
NetworkPolicy *NetworkPolicyClient
Parameter *ParameterClient
PasswordPolicy *PasswordPolicyClient
Pipe *PipeClient
ResourceMonitor *ResourceMonitorClient
Role *RoleClient
RowAccessPolicy *RowAccessPolicyClient
Schema *SchemaClient
SecurityIntegration *SecurityIntegrationClient
SessionPolicy *SessionPolicyClient
Share *ShareClient
Stage *StageClient
Table *TableClient
Tag *TagClient
Task *TaskClient
User *UserClient
View *ViewClient
Warehouse *WarehouseClient
}

func NewTestClient(c *sdk.Client, database string, schema string, warehouse string, testObjectSuffix string) *TestClient {
Expand All @@ -55,36 +56,37 @@ func NewTestClient(c *sdk.Client, database string, schema string, warehouse stri

Ids: idsGenerator,

Account: NewAccountClient(context),
Alert: NewAlertClient(context, idsGenerator),
ApiIntegration: NewApiIntegrationClient(context, idsGenerator),
Application: NewApplicationClient(context, idsGenerator),
ApplicationPackage: NewApplicationPackageClient(context, idsGenerator),
Context: NewContextClient(context),
Database: NewDatabaseClient(context, idsGenerator),
DatabaseRole: NewDatabaseRoleClient(context, idsGenerator),
DynamicTable: NewDynamicTableClient(context, idsGenerator),
FailoverGroup: NewFailoverGroupClient(context, idsGenerator),
FileFormat: NewFileFormatClient(context, idsGenerator),
MaskingPolicy: NewMaskingPolicyClient(context, idsGenerator),
MaterializedView: NewMaterializedViewClient(context, idsGenerator),
NetworkPolicy: NewNetworkPolicyClient(context, idsGenerator),
Parameter: NewParameterClient(context),
PasswordPolicy: NewPasswordPolicyClient(context, idsGenerator),
Pipe: NewPipeClient(context, idsGenerator),
ResourceMonitor: NewResourceMonitorClient(context, idsGenerator),
Role: NewRoleClient(context, idsGenerator),
RowAccessPolicy: NewRowAccessPolicyClient(context, idsGenerator),
Schema: NewSchemaClient(context, idsGenerator),
SessionPolicy: NewSessionPolicyClient(context, idsGenerator),
Share: NewShareClient(context, idsGenerator),
Stage: NewStageClient(context, idsGenerator),
Table: NewTableClient(context, idsGenerator),
Tag: NewTagClient(context, idsGenerator),
Task: NewTaskClient(context, idsGenerator),
User: NewUserClient(context, idsGenerator),
View: NewViewClient(context, idsGenerator),
Warehouse: NewWarehouseClient(context, idsGenerator),
Account: NewAccountClient(context),
Alert: NewAlertClient(context, idsGenerator),
ApiIntegration: NewApiIntegrationClient(context, idsGenerator),
Application: NewApplicationClient(context, idsGenerator),
ApplicationPackage: NewApplicationPackageClient(context, idsGenerator),
Context: NewContextClient(context),
Database: NewDatabaseClient(context, idsGenerator),
DatabaseRole: NewDatabaseRoleClient(context, idsGenerator),
DynamicTable: NewDynamicTableClient(context, idsGenerator),
FailoverGroup: NewFailoverGroupClient(context, idsGenerator),
FileFormat: NewFileFormatClient(context, idsGenerator),
MaskingPolicy: NewMaskingPolicyClient(context, idsGenerator),
MaterializedView: NewMaterializedViewClient(context, idsGenerator),
NetworkPolicy: NewNetworkPolicyClient(context, idsGenerator),
Parameter: NewParameterClient(context),
PasswordPolicy: NewPasswordPolicyClient(context, idsGenerator),
Pipe: NewPipeClient(context, idsGenerator),
ResourceMonitor: NewResourceMonitorClient(context, idsGenerator),
Role: NewRoleClient(context, idsGenerator),
RowAccessPolicy: NewRowAccessPolicyClient(context, idsGenerator),
Schema: NewSchemaClient(context, idsGenerator),
SecurityIntegration: NewSecurityIntegrationClient(context, idsGenerator),
SessionPolicy: NewSessionPolicyClient(context, idsGenerator),
Share: NewShareClient(context, idsGenerator),
Stage: NewStageClient(context, idsGenerator),
Table: NewTableClient(context, idsGenerator),
Tag: NewTagClient(context, idsGenerator),
Task: NewTaskClient(context, idsGenerator),
User: NewUserClient(context, idsGenerator),
View: NewViewClient(context, idsGenerator),
Warehouse: NewWarehouseClient(context, idsGenerator),
}
}

Expand Down
5 changes: 3 additions & 2 deletions pkg/internal/snowflakeroles/snowflake_predefined_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package snowflakeroles
import "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"

var (
Orgadmin = sdk.NewAccountObjectIdentifier("ORGADMIN")
Accountadmin = sdk.NewAccountObjectIdentifier("ACCOUNTADMIN")
Orgadmin = sdk.NewAccountObjectIdentifier("ORGADMIN")
Accountadmin = sdk.NewAccountObjectIdentifier("ACCOUNTADMIN")
GenericScimProvisioner = sdk.NewAccountObjectIdentifier("GENERIC_SCIM_PROVISIONER")
)
2 changes: 2 additions & 0 deletions pkg/sdk/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type Client struct {
Roles Roles
RowAccessPolicies RowAccessPolicies
Schemas Schemas
SecurityIntegrations SecurityIntegrations
Sequences Sequences
SessionPolicies SessionPolicies
Sessions Sessions
Expand Down Expand Up @@ -226,6 +227,7 @@ func (c *Client) initialize() {
c.Roles = &roles{client: c}
c.RowAccessPolicies = &rowAccessPolicies{client: c}
c.Schemas = &schemas{client: c}
c.SecurityIntegrations = &securityIntegrations{client: c}
c.Sequences = &sequences{client: c}
c.SessionPolicies = &sessionPolicies{client: c}
c.Sessions = &sessions{client: c}
Expand Down
9 changes: 9 additions & 0 deletions pkg/sdk/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ func (parameters *parameters) SetAccountParameter(ctx context.Context, parameter
return fmt.Errorf("CLIENT_ENCRYPTION_KEY_SIZE session parameter is an integer, got %v", value)
}
opts.Set.Parameters.AccountParameters.ClientEncryptionKeySize = Pointer(v)
case AccountParameterEnableIdentifierFirstLogin:
b, err := parseBooleanParameter(string(parameter), value)
if err != nil {
return err
}
opts.Set.Parameters.AccountParameters.EnableIdentifierFirstLogin = b
case AccountParameterEnableInternalStagesPrivatelink:
b, err := parseBooleanParameter(string(parameter), value)
if err != nil {
Expand Down Expand Up @@ -328,6 +334,7 @@ const (
AccountParameterAllowClientMFACaching AccountParameter = "ALLOW_CLIENT_MFA_CACHING"
AccountParameterAllowIDToken AccountParameter = "ALLOW_ID_TOKEN" // #nosec G101
AccountParameterClientEncryptionKeySize AccountParameter = "CLIENT_ENCRYPTION_KEY_SIZE"
AccountParameterEnableIdentifierFirstLogin AccountParameter = "ENABLE_IDENTIFIER_FIRST_LOGIN"
AccountParameterEnableInternalStagesPrivatelink AccountParameter = "ENABLE_INTERNAL_STAGES_PRIVATELINK"
AccountParameterEnableTriSecretAndRekeyOptOutForImageRepository AccountParameter = "ENABLE_TRI_SECRET_AND_REKEY_OPT_OUT_FOR_IMAGE_REPOSITORY" // #nosec G101
AccountParameterEnableTriSecretAndRekeyOptOutForSpcsBlockStorage AccountParameter = "ENABLE_TRI_SECRET_AND_REKEY_OPT_OUT_FOR_SPCS_BLOCK_STORAGE" // #nosec G101
Expand Down Expand Up @@ -524,6 +531,7 @@ type AccountParameters struct {
AllowClientMFACaching *bool `ddl:"parameter" sql:"ALLOW_CLIENT_MFA_CACHING"`
AllowIDToken *bool `ddl:"parameter" sql:"ALLOW_ID_TOKEN"`
ClientEncryptionKeySize *int `ddl:"parameter" sql:"CLIENT_ENCRYPTION_KEY_SIZE"`
EnableIdentifierFirstLogin *bool `ddl:"parameter" sql:"ENABLE_IDENTIFIER_FIRST_LOGIN"`
EnableInternalStagesPrivatelink *bool `ddl:"parameter" sql:"ENABLE_INTERNAL_STAGES_PRIVATELINK"`
EnableUnredactedQuerySyntaxError *bool `ddl:"parameter" sql:"ENABLE_UNREDACTED_QUERY_SYNTAX_ERROR"`
EnableTriSecretAndRekeyOptOutForImageRepository *bool `ddl:"parameter" sql:"ENABLE_TRI_SECRET_AND_REKEY_OPT_OUT_FOR_IMAGE_REPOSITORY"`
Expand Down Expand Up @@ -567,6 +575,7 @@ type AccountParametersUnset struct {
AllowClientMFACaching *bool `ddl:"keyword" sql:"ALLOW_CLIENT_MFA_CACHING"`
AllowIDToken *bool `ddl:"keyword" sql:"ALLOW_ID_TOKEN"`
ClientEncryptionKeySize *bool `ddl:"keyword" sql:"CLIENT_ENCRYPTION_KEY_SIZE"`
EnableIdentifierFirstLogin *bool `ddl:"keyword" sql:"ENABLE_IDENTIFIER_FIRST_LOGIN"`
EnableInternalStagesPrivatelink *bool `ddl:"keyword" sql:"ENABLE_INTERNAL_STAGES_PRIVATELINK"`
EnableTriSecretAndRekeyOptOutForImageRepository *bool `ddl:"keyword" sql:"ENABLE_TRI_SECRET_AND_REKEY_OPT_OUT_FOR_IMAGE_REPOSITORY"`
EnableTriSecretAndRekeyOptOutForSpcsBlockStorage *bool `ddl:"keyword" sql:"ENABLE_TRI_SECRET_AND_REKEY_OPT_OUT_FOR_SPCS_BLOCK_STORAGE"`
Expand Down
4 changes: 2 additions & 2 deletions pkg/sdk/poc/generator/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,6 @@ func (i *Interface) DescribeOperation(describeKind DescriptionMappingKind, doc s
return i
}

func (i *Interface) CustomOperation(kind string, doc string, queryStruct *QueryStruct) *Interface {
return i.newSimpleOperation(kind, doc, queryStruct)
func (i *Interface) CustomOperation(kind string, doc string, queryStruct *QueryStruct, helperStructs ...IntoField) *Interface {
return i.newSimpleOperation(kind, doc, queryStruct, helperStructs...)
}
1 change: 1 addition & 0 deletions pkg/sdk/poc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ var definitionMapping = map[string]*generator.Interface{
"external_functions_def.go": sdk.ExternalFunctionsDef,
"streamlits_def.go": sdk.StreamlitsDef,
"network_rule_def.go": sdk.NetworkRuleDef,
"security_integrations_def.go": sdk.SecurityIntegrationsDef,
}

func main() {
Expand Down
Loading

0 comments on commit 1312ff1

Please sign in to comment.