Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Snowflake Oauth security integration to sdk #2830

Merged
merged 7 commits into from
May 24, 2024
Merged
13 changes: 13 additions & 0 deletions pkg/acceptance/helpers/context_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package helpers

import (
"context"
"fmt"
"testing"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
Expand Down Expand Up @@ -71,3 +72,15 @@ func (c *ContextClient) IsRoleInSession(t *testing.T, id sdk.AccountObjectIdenti

return isInSession
}

// ACSURL returns Snowflake Assertion Consumer Service URL
func (c *ContextClient) ACSURL(t *testing.T) string {
t.Helper()
return fmt.Sprintf("https://%s.snowflakecomputing.com/fed/login", c.CurrentAccount(t))
}

// IssuerURL returns a URL containing the EntityID / Issuer for the Snowflake service provider
func (c *ContextClient) IssuerURL(t *testing.T) string {
t.Helper()
return fmt.Sprintf("https://%s.snowflakecomputing.com", c.CurrentAccount(t))
}
33 changes: 26 additions & 7 deletions pkg/acceptance/helpers/random/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"strings"
"testing"
Expand Down Expand Up @@ -34,14 +35,32 @@ func GenerateX509(t *testing.T) string {
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,
})
return encode(t, "CERTIFICATE", caBytes)
}

// GenerateRSA returns an RSA public key without BEGIN and END markers.
func GenerateRSAPublicKey(t *testing.T) string {
t.Helper()
key, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err)

cert := strings.TrimPrefix(certPEM.String(), "-----BEGIN CERTIFICATE-----\n")
cert = strings.TrimSuffix(cert, "-----END CERTIFICATE-----\n")
pub := key.Public()
b, err := x509.MarshalPKIXPublicKey(pub.(*rsa.PublicKey))
require.NoError(t, err)
return encode(t, "RSA PUBLIC KEY", b)
}

func encode(t *testing.T, pemType string, b []byte) string {
t.Helper()
buffer := new(bytes.Buffer)
err := pem.Encode(buffer,
&pem.Block{
Type: pemType,
Bytes: b,
},
)
require.NoError(t, err)
cert := strings.TrimPrefix(buffer.String(), fmt.Sprintf("-----BEGIN %s-----\n", pemType))
cert = strings.TrimSuffix(cert, fmt.Sprintf("-----END %s-----\n", pemType))
return cert
}
2 changes: 1 addition & 1 deletion pkg/acceptance/helpers/security_integration_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (c *SecurityIntegrationClient) DropSecurityIntegrationFunc(t *testing.T, id
ctx := context.Background()

return func() {
err := c.client().Drop(ctx, sdk.NewDropSecurityIntegrationRequest(id).WithIfExists(sdk.Bool(true)))
err := c.client().Drop(ctx, sdk.NewDropSecurityIntegrationRequest(id).WithIfExists(true))
require.NoError(t, err)
}
}
8 changes: 6 additions & 2 deletions pkg/internal/snowflakeroles/snowflake_predefined_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ 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")
SecurityAdmin = sdk.NewAccountObjectIdentifier("SECURITYADMIN")

OktaProvisioner = sdk.NewAccountObjectIdentifier("OKTA_PROVISIONER")
AadProvisioner = sdk.NewAccountObjectIdentifier("AAD_PROVISIONER")
GenericScimProvisioner = sdk.NewAccountObjectIdentifier("GENERIC_SCIM_PROVISIONER")
)
177 changes: 169 additions & 8 deletions pkg/sdk/security_integrations_def.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,68 @@ import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/gen

//go:generate go run ./poc/main.go

type OauthSecurityIntegrationUseSecondaryRolesOption string

const (
OauthSecurityIntegrationUseSecondaryRolesImplicit OauthSecurityIntegrationUseSecondaryRolesOption = "IMPLICIT"
OauthSecurityIntegrationUseSecondaryRolesNone OauthSecurityIntegrationUseSecondaryRolesOption = "NONE"
)

type OauthSecurityIntegrationClientTypeOption string

const (
OauthSecurityIntegrationClientTypePublic OauthSecurityIntegrationClientTypeOption = "PUBLIC"
OauthSecurityIntegrationClientTypeConfidential OauthSecurityIntegrationClientTypeOption = "CONFIDENTIAL"
)

type OauthSecurityIntegrationClientOption string

const (
OauthSecurityIntegrationClientLooker OauthSecurityIntegrationClientOption = "LOOKER"
OauthSecurityIntegrationClientTableauDesktop OauthSecurityIntegrationClientOption = "TABLEAU_DESKTOP"
OauthSecurityIntegrationClientTableauServer OauthSecurityIntegrationClientOption = "TABLEAU_SERVER"
)

type ScimSecurityIntegrationScimClientOption string

var (
const (
ScimSecurityIntegrationScimClientOkta ScimSecurityIntegrationScimClientOption = "OKTA"
ScimSecurityIntegrationScimClientAzure ScimSecurityIntegrationScimClientOption = "AZURE"
ScimSecurityIntegrationScimClientGeneric ScimSecurityIntegrationScimClientOption = "GENERIC"
)

type ScimSecurityIntegrationRunAsRoleOption string

var (
const (
ScimSecurityIntegrationRunAsRoleOktaProvisioner ScimSecurityIntegrationRunAsRoleOption = "OKTA_PROVISIONER"
ScimSecurityIntegrationRunAsRoleAadProvisioner ScimSecurityIntegrationRunAsRoleOption = "AAD_PROVISIONER"
ScimSecurityIntegrationRunAsRoleGenericScimProvisioner ScimSecurityIntegrationRunAsRoleOption = "GENERIC_SCIM_PROVISIONER"
)

var (
userDomainDef = g.NewQueryStruct("UserDomain").Text("Domain", g.KeywordOptions().SingleQuotes().Required())
emailPatternDef = g.NewQueryStruct("EmailPattern").Text("Pattern", g.KeywordOptions().SingleQuotes().Required())
userDomainDef = g.NewQueryStruct("UserDomain").Text("Domain", g.KeywordOptions().SingleQuotes().Required())
emailPatternDef = g.NewQueryStruct("EmailPattern").Text("Pattern", g.KeywordOptions().SingleQuotes().Required())
preAuthorizedRolesListDef = g.NewQueryStruct("PreAuthorizedRolesList").
List("PreAuthorizedRolesList", "AccountObjectIdentifier", g.ListOptions().MustParentheses())
blockedRolesListDef = g.NewQueryStruct("BlockedRolesList").
List("BlockedRolesList", "AccountObjectIdentifier", g.ListOptions().MustParentheses())
)

func createSecurityIntegrationOperation(structName string, apply func(qs *g.QueryStruct) *g.QueryStruct) *g.QueryStruct {
func createSecurityIntegrationOperation(structName string, opts func(qs *g.QueryStruct) *g.QueryStruct) *g.QueryStruct {
qs := g.NewQueryStruct(structName).
Create().
OrReplace().
SQL("SECURITY INTEGRATION").
IfNotExists().
Name()
qs = apply(qs)
qs = opts(qs)
return qs.
OptionalComment().
WithValidation(g.ValidIdentifier, "name").
WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists")
}

func alterSecurityIntegrationOperation(structName string, apply func(qs *g.QueryStruct) *g.QueryStruct) *g.QueryStruct {
func alterSecurityIntegrationOperation(structName string, opts func(qs *g.QueryStruct) *g.QueryStruct) *g.QueryStruct {
qs := g.NewQueryStruct(structName).
Alter().
SQL("SECURITY INTEGRATION").
Expand All @@ -48,10 +74,60 @@ func alterSecurityIntegrationOperation(structName string, apply func(qs *g.Query
OptionalSetTags().
OptionalUnsetTags().
WithValidation(g.ValidIdentifier, "name")
qs = apply(qs)
qs = opts(qs)
return qs
}

var oauthForPartnerApplicationsIntegrationSetDef = g.NewQueryStruct("OauthForPartnerApplicationsIntegrationSet").
OptionalBooleanAssignment("ENABLED", g.ParameterOptions()).
OptionalBooleanAssignment("OAUTH_ISSUE_REFRESH_TOKENS", g.ParameterOptions()).
OptionalTextAssignment("OAUTH_REDIRECT_URI", g.ParameterOptions().SingleQuotes()).
OptionalNumberAssignment("OAUTH_REFRESH_TOKEN_VALIDITY", g.ParameterOptions()).
OptionalAssignment(
"OAUTH_USE_SECONDARY_ROLES",
g.KindOfT[OauthSecurityIntegrationUseSecondaryRolesOption](),
g.ParameterOptions(),
).
OptionalQueryStructField("BlockedRolesList", blockedRolesListDef, g.ParameterOptions().SQL("BLOCKED_ROLES_LIST").Parentheses()).
OptionalComment().
WithValidation(g.AtLeastOneValueSet, "Enabled", "OauthIssueRefreshTokens", "OauthRedirectUri", "OauthRefreshTokenValidity", "OauthUseSecondaryRoles",
"BlockedRolesList", "Comment")

var oauthForPartnerApplicationsIntegrationUnsetDef = g.NewQueryStruct("OauthForPartnerApplicationsIntegrationUnset").
OptionalSQL("ENABLED").
sfc-gh-asawicki marked this conversation as resolved.
Show resolved Hide resolved
OptionalSQL("OAUTH_USE_SECONDARY_ROLES").
WithValidation(g.AtLeastOneValueSet, "Enabled", "OauthUseSecondaryRoles")

var oauthForCustomClientsIntegrationSetDef = g.NewQueryStruct("OauthForCustomClientsIntegrationSet").
OptionalBooleanAssignment("ENABLED", g.ParameterOptions()).
OptionalTextAssignment("OAUTH_REDIRECT_URI", g.ParameterOptions().SingleQuotes()).
OptionalBooleanAssignment("OAUTH_ALLOW_NON_TLS_REDIRECT_URI", g.ParameterOptions()).
OptionalBooleanAssignment("OAUTH_ENFORCE_PKCE", g.ParameterOptions()).
OptionalQueryStructField("PreAuthorizedRolesList", preAuthorizedRolesListDef, g.ParameterOptions().SQL("PRE_AUTHORIZED_ROLES_LIST").Parentheses()).
OptionalQueryStructField("BlockedRolesList", blockedRolesListDef, g.ParameterOptions().SQL("BLOCKED_ROLES_LIST").Parentheses()).
OptionalBooleanAssignment("OAUTH_ISSUE_REFRESH_TOKENS", g.ParameterOptions()).
OptionalNumberAssignment("OAUTH_REFRESH_TOKEN_VALIDITY", g.ParameterOptions()).
OptionalAssignment(
sfc-gh-asawicki marked this conversation as resolved.
Show resolved Hide resolved
"OAUTH_USE_SECONDARY_ROLES",
g.KindOfT[OauthSecurityIntegrationUseSecondaryRolesOption](),
g.ParameterOptions(),
).
OptionalIdentifier("NetworkPolicy", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Equals().SQL("NETWORK_POLICY")).
OptionalTextAssignment("OAUTH_CLIENT_RSA_PUBLIC_KEY", g.ParameterOptions().SingleQuotes()).
OptionalTextAssignment("OAUTH_CLIENT_RSA_PUBLIC_KEY_2", g.ParameterOptions().SingleQuotes()).
OptionalComment().
WithValidation(g.AtLeastOneValueSet, "Enabled", "OauthRedirectUri", "OauthAllowNonTlsRedirectUri", "OauthEnforcePkce", "PreAuthorizedRolesList",
"BlockedRolesList", "OauthIssueRefreshTokens", "OauthRefreshTokenValidity", "OauthUseSecondaryRoles", "NetworkPolicy", "OauthClientRsaPublicKey",
"OauthClientRsaPublicKey2", "Comment")

var oauthForCustomClientsIntegrationUnsetDef = g.NewQueryStruct("OauthForCustomClientsIntegrationUnset").
OptionalSQL("ENABLED").
OptionalSQL("NETWORK_POLICY").
OptionalSQL("OAUTH_CLIENT_RSA_PUBLIC_KEY").
OptionalSQL("OAUTH_CLIENT_RSA_PUBLIC_KEY_2").
sfc-gh-asawicki marked this conversation as resolved.
Show resolved Hide resolved
OptionalSQL("OAUTH_USE_SECONDARY_ROLES").
WithValidation(g.AtLeastOneValueSet, "Enabled", "NetworkPolicy", "OauthUseSecondaryRoles", "OauthClientRsaPublicKey", "OauthClientRsaPublicKey2")

var saml2IntegrationSetDef = g.NewQueryStruct("Saml2IntegrationSet").
OptionalBooleanAssignment("ENABLED", g.ParameterOptions()).
OptionalTextAssignment("SAML2_ISSUER", g.ParameterOptions().SingleQuotes()).
Expand Down Expand Up @@ -100,6 +176,61 @@ var SecurityIntegrationsDef = g.NewInterface(
"SecurityIntegration",
g.KindOfT[AccountObjectIdentifier](),
).
CustomOperation(
"CreateOauthForPartnerApplications",
"https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-oauth-snowflake",
createSecurityIntegrationOperation("CreateOauthForPartnerApplications", func(qs *g.QueryStruct) *g.QueryStruct {
return qs.
PredefinedQueryStructField("integrationType", "string", g.StaticOptions().SQL("TYPE = OAUTH")).
Assignment(
"OAUTH_CLIENT",
g.KindOfT[OauthSecurityIntegrationClientOption](),
g.ParameterOptions().Required(),
).
OptionalTextAssignment("OAUTH_REDIRECT_URI", g.ParameterOptions().SingleQuotes()).
sfc-gh-asawicki marked this conversation as resolved.
Show resolved Hide resolved
OptionalBooleanAssignment("ENABLED", g.ParameterOptions()).
OptionalBooleanAssignment("OAUTH_ISSUE_REFRESH_TOKENS", g.ParameterOptions()).
OptionalNumberAssignment("OAUTH_REFRESH_TOKEN_VALIDITY", g.ParameterOptions()).
OptionalAssignment(
"OAUTH_USE_SECONDARY_ROLES",
g.KindOfT[OauthSecurityIntegrationUseSecondaryRolesOption](),
g.ParameterOptions(),
).
OptionalQueryStructField("BlockedRolesList", blockedRolesListDef, g.ParameterOptions().SQL("BLOCKED_ROLES_LIST").Parentheses())
}),
preAuthorizedRolesListDef,
blockedRolesListDef,
).
CustomOperation(
"CreateOauthForCustomClients",
"https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-oauth-snowflake",
createSecurityIntegrationOperation("CreateOauthForCustomClients", func(qs *g.QueryStruct) *g.QueryStruct {
return qs.
PredefinedQueryStructField("integrationType", "string", g.StaticOptions().SQL("TYPE = OAUTH")).
PredefinedQueryStructField("oauthClient", "string", g.StaticOptions().SQL("OAUTH_CLIENT = CUSTOM")).
Assignment(
"OAUTH_CLIENT_TYPE",
g.KindOfT[OauthSecurityIntegrationClientTypeOption](),
g.ParameterOptions().Required().SingleQuotes(),
).
TextAssignment("OAUTH_REDIRECT_URI", g.ParameterOptions().Required().SingleQuotes()).
OptionalBooleanAssignment("ENABLED", g.ParameterOptions()).
OptionalBooleanAssignment("OAUTH_ALLOW_NON_TLS_REDIRECT_URI", g.ParameterOptions()).
OptionalBooleanAssignment("OAUTH_ENFORCE_PKCE", g.ParameterOptions()).
OptionalAssignment(
"OAUTH_USE_SECONDARY_ROLES",
g.KindOfT[OauthSecurityIntegrationUseSecondaryRolesOption](),
g.ParameterOptions(),
).
OptionalQueryStructField("PreAuthorizedRolesList", preAuthorizedRolesListDef, g.ParameterOptions().SQL("PRE_AUTHORIZED_ROLES_LIST").Parentheses()).
OptionalQueryStructField("BlockedRolesList", blockedRolesListDef, g.ParameterOptions().SQL("BLOCKED_ROLES_LIST").Parentheses()).
OptionalBooleanAssignment("OAUTH_ISSUE_REFRESH_TOKENS", g.ParameterOptions()).
OptionalNumberAssignment("OAUTH_REFRESH_TOKEN_VALIDITY", g.ParameterOptions()).
OptionalIdentifier("NetworkPolicy", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Equals().SQL("NETWORK_POLICY")).
OptionalTextAssignment("OAUTH_CLIENT_RSA_PUBLIC_KEY", g.ParameterOptions().SingleQuotes()).
OptionalTextAssignment("OAUTH_CLIENT_RSA_PUBLIC_KEY_2", g.ParameterOptions().SingleQuotes())
sfc-gh-asawicki marked this conversation as resolved.
Show resolved Hide resolved
}),
).
CustomOperation(
"CreateSaml2",
"https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-saml2",
Expand Down Expand Up @@ -147,6 +278,36 @@ var SecurityIntegrationsDef = g.NewInterface(
OptionalBooleanAssignment("SYNC_PASSWORD", g.ParameterOptions())
}),
).
CustomOperation(
"AlterOauthForPartnerApplications",
"https://docs.snowflake.com/en/sql-reference/sql/alter-security-integration-oauth-snowflake",
alterSecurityIntegrationOperation("AlterOauthForPartnerApplications", func(qs *g.QueryStruct) *g.QueryStruct {
return qs.OptionalQueryStructField(
"Set",
oauthForPartnerApplicationsIntegrationSetDef,
g.ListOptions().NoParentheses().SQL("SET"),
).OptionalQueryStructField(
"Unset",
oauthForPartnerApplicationsIntegrationUnsetDef,
g.ListOptions().NoParentheses().SQL("UNSET"),
).WithValidation(g.ExactlyOneValueSet, "Set", "Unset", "SetTags", "UnsetTags")
}),
).
CustomOperation(
"AlterOauthForCustomClients",
"https://docs.snowflake.com/en/sql-reference/sql/alter-security-integration-oauth-snowflake",
alterSecurityIntegrationOperation("AlterOauthForCustomClients", func(qs *g.QueryStruct) *g.QueryStruct {
return qs.OptionalQueryStructField(
"Set",
oauthForCustomClientsIntegrationSetDef,
g.ListOptions().NoParentheses().SQL("SET"),
).OptionalQueryStructField(
"Unset",
oauthForCustomClientsIntegrationUnsetDef,
g.ListOptions().NoParentheses().SQL("UNSET"),
).WithValidation(g.ExactlyOneValueSet, "Set", "Unset", "SetTags", "UnsetTags")
}),
).
CustomOperation(
"AlterSaml2",
"https://docs.snowflake.com/en/sql-reference/sql/alter-security-integration-saml2",
Expand Down
Loading
Loading