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

Add IAM SAML Federations, Federation Certificates #286

Merged
merged 10 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/selectel/dbaas-go v0.12.1
github.com/selectel/domains-go v1.0.2
github.com/selectel/go-selvpcclient/v3 v3.1.1
github.com/selectel/iam-go v0.3.0
github.com/selectel/iam-go v0.4.1
github.com/selectel/mks-go v0.15.0
github.com/selectel/secretsmanager-go v0.2.1
github.com/stretchr/testify v1.8.4
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ github.com/selectel/domains-go v1.0.2 h1:Si6iGaMnTFJxwiJVI50DOdZnwcxc87kqaWrVQYW
github.com/selectel/domains-go v1.0.2/go.mod h1:SugRKfq4sTpnOHquslCpzda72wV8u0cMBHx0C0l+bzA=
github.com/selectel/go-selvpcclient/v3 v3.1.1 h1:C1q2LqqosiapoLpnGITGmysg0YCSQYDo2Gh69CioevM=
github.com/selectel/go-selvpcclient/v3 v3.1.1/go.mod h1:NM7IXhh1IzqZ88DOw1Qc5Ez3tULLViXo95l5+rKPuyQ=
github.com/selectel/iam-go v0.3.0 h1:HRoxSBXwvASE9v/A4WgeEDeMvomARIWyj2essV4LmYc=
github.com/selectel/iam-go v0.3.0/go.mod h1:OIAkW7MZK97YUm+uvUgYbgDhkI9SdzTCxwd4yZoOR1o=
github.com/selectel/iam-go v0.4.1 h1:grncCGkPVCM6nwqSTk+q15M5ZO6S/Pe0AIbbmKtm6gU=
github.com/selectel/iam-go v0.4.1/go.mod h1:OIAkW7MZK97YUm+uvUgYbgDhkI9SdzTCxwd4yZoOR1o=
github.com/selectel/mks-go v0.15.0 h1:0ytV5DiQAgbojKA0ukBjtwfWBSQh658nF3mhjZTrWj8=
github.com/selectel/mks-go v0.15.0/go.mod h1:VxtV3dzwgOEzZc+9VMQb9DvxfSlej2ZQ8jnT8kqIGgU=
github.com/selectel/secretsmanager-go v0.2.1 h1:OSBrA/07lm/Ecpwg59IJHFAoUHZR29oyfwUgTpr/dos=
Expand Down
82 changes: 43 additions & 39 deletions selectel/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,47 @@ const (
)

const (
objectACL = "acl"
objectFloatingIP = "floating IP"
objectKeypair = "keypair"
objectLicense = "license"
objectProject = "project"
objectProjectQuotas = "quotas for project"
objectRole = "role"
objectSubnet = "subnet"
objectToken = "token"
objectTopic = "topic"
objectUser = "user"
objectServiceUser = "service user"
objectS3Credentials = "s3 credentials"
objectGroup = "group"
objectGroupMembership = "group-membership"
objectCluster = "cluster"
objectKubeConfig = "kubeconfig"
objectKubeVersions = "kube-versions"
objectNodegroup = "nodegroup"
objectDomain = "domain"
objectRecord = "record"
objectZone = "zone"
objectRRSet = "rrset"
objectDatastore = "datastore"
objectDatabase = "database"
objectGrant = "grant"
objectExtension = "extension"
objectDatastoreTypes = "datastore-types"
objectAvailableExtensions = "available-extensions"
objectFlavors = "flavors"
objectConfigurationParameters = "configuration-parameters"
objectPrometheusMetricToken = "prometheus-metric-token"
objectFeatureGates = "feature-gates"
objectAdmissionControllers = "admission-controllers"
objectLogicalReplicationSlot = "logical-replication-slot"
objectRegistry = "registry"
objectRegistryToken = "registry token"
objectSecret = "secret"
objectCertificate = "certificate"
objectACL = "acl"
objectFloatingIP = "floating IP"
objectKeypair = "keypair"
objectLicense = "license"
objectProject = "project"
objectProjectQuotas = "quotas for project"
objectRole = "role"
objectSubnet = "subnet"
objectToken = "token"
objectTopic = "topic"
objectUser = "user"
objectServiceUser = "service user"
objectS3Credentials = "s3 credentials"
objectSAMLFederation = "saml federation"
objectSAMLFederationCertificate = "saml federation certificate"
objectGroup = "group"
objectGroupMembership = "group-membership"
objectCluster = "cluster"
objectKubeConfig = "kubeconfig"
objectKubeVersions = "kube-versions"
objectNodegroup = "nodegroup"
objectDomain = "domain"
objectRecord = "record"
objectZone = "zone"
objectRRSet = "rrset"
objectDatastore = "datastore"
objectDatabase = "database"
objectGrant = "grant"
objectExtension = "extension"
objectDatastoreTypes = "datastore-types"
objectAvailableExtensions = "available-extensions"
objectFlavors = "flavors"
objectConfigurationParameters = "configuration-parameters"
objectPrometheusMetricToken = "prometheus-metric-token"
objectFeatureGates = "feature-gates"
objectAdmissionControllers = "admission-controllers"
objectLogicalReplicationSlot = "logical-replication-slot"
objectRegistry = "registry"
objectRegistryToken = "registry token"
objectSecret = "secret"
objectCertificate = "certificate"
)

// This is a global MutexKV for use within this plugin.
Expand Down Expand Up @@ -137,6 +139,8 @@ func Provider() *schema.Provider {
"selectel_iam_serviceuser_v1": resourceIAMServiceUserV1(),
"selectel_iam_user_v1": resourceIAMUserV1(),
"selectel_iam_s3_credentials_v1": resourceIAMS3CredentialsV1(),
"selectel_iam_saml_federation_v1": resourceIAMSAMLFederationV1(),
"selectel_iam_saml_federation_certificate_v1": resourceIAMSAMLFederationCertificateV1(),
"selectel_iam_group_v1": resourceIAMGroupV1(),
"selectel_iam_group_membership_v1": resourceIAMGroupMembershipV1(),
"selectel_vpc_vrrp_subnet_v2": resourceVPCVRRPSubnetV2(), // DEPRECATED
Expand Down
166 changes: 166 additions & 0 deletions selectel/resource_selectel_iam_saml_federation_certificate_v1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package selectel

import (
"context"
"errors"
"fmt"
"log"
"os"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/selectel/iam-go/iamerrors"
"github.com/selectel/iam-go/service/federations/saml/certificates"
)

func resourceIAMSAMLFederationCertificateV1() *schema.Resource {
return &schema.Resource{
Description: "Represents a SAML Federation Certificate in IAM API",
CreateContext: resourceIAMSAMLFederationCertificateV1Create,
ReadContext: resourceIAMSAMLFederationCertificateV1Read,
UpdateContext: resourceIAMSAMLFederationCertificateV1Update,
DeleteContext: resourceIAMSAMLFederationCertificateV1Delete,
Importer: &schema.ResourceImporter{
StateContext: resourceIAMS3SAMLFederationCertificateV1ImportState,
},
Schema: map[string]*schema.Schema{
"federation_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Federation ID to create Certificate for.",
},
"name": {
Type: schema.TypeString,
Required: true,
Description: "Name of the Certificate.",
},
"description": {
Type: schema.TypeString,
Optional: true,
Default: "",
Description: "Description of the Certificate.",
},
"data": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Certificate issued on the provider side. It must begin with -----BEGIN CERTIFICATE----- and end with -----END CERTIFICATE-----.",
},
"account_id": {
Type: schema.TypeString,
Computed: true,
Description: "Account ID.",
},
"not_before": {
Type: schema.TypeString,
Computed: true,
Description: "Certificate lifetime left bound.",
},
"not_after": {
Type: schema.TypeString,
Computed: true,
Description: "Certificate lifetime right bound.",
},
"fingerprint": {
Type: schema.TypeString,
Computed: true,
Description: "Fingerprint.",
},
},
}
}

func resourceIAMSAMLFederationCertificateV1Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
iamClient, diagErr := getIAMClient(meta)
if diagErr != nil {
return diagErr
}

opts := certificates.CreateRequest{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
Data: d.Get("data").(string),
}
log.Print(msgCreate(objectSAMLFederationCertificate, opts))

certificate, err := iamClient.SAMLFederations.Certificates.Create(ctx, d.Get("federation_id").(string), opts)
if err != nil {
return diag.FromErr(errCreatingObject(objectSAMLFederationCertificate, err))
}

d.SetId(certificate.ID)

return resourceIAMSAMLFederationCertificateV1Read(ctx, d, meta)
}

func resourceIAMSAMLFederationCertificateV1Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
iamClient, diagErr := getIAMClient(meta)
if diagErr != nil {
return diagErr
}

log.Print(msgGet(objectSAMLFederationCertificate, d.Id()))
certificate, err := iamClient.SAMLFederations.Certificates.Get(ctx, d.Get("federation_id").(string), d.Id())
if err != nil {
return diag.FromErr(errGettingObject(objectSAMLFederationCertificate, d.Id(), err))
}

d.Set("account_id", certificate.AccountID)
d.Set("name", certificate.Name)
d.Set("description", certificate.Description)
d.Set("not_before", certificate.NotBefore)
d.Set("not_after", certificate.NotAfter)
d.Set("fingerprint", certificate.Fingerprint)
d.Set("data", certificate.Data)

return nil
}

func resourceIAMSAMLFederationCertificateV1Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
iamClient, diagErr := getIAMClient(meta)
if diagErr != nil {
return diagErr
}

desc := d.Get("description").(string)

opts := certificates.UpdateRequest{
Name: d.Get("name").(string),
Description: &desc,
}

log.Print(msgUpdate(objectSAMLFederationCertificate, d.Id(), opts))
_, err := iamClient.SAMLFederations.Certificates.Update(ctx, d.Get("federation_id").(string), d.Id(), opts)
if err != nil {
return diag.FromErr(errUpdatingObject(objectSAMLFederationCertificate, d.Id(), err))
}

return resourceIAMSAMLFederationCertificateV1Read(ctx, d, meta)
}

func resourceIAMSAMLFederationCertificateV1Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
iamClient, diagErr := getIAMClient(meta)
if diagErr != nil {
return diagErr
}

log.Print(msgDelete(objectSAMLFederationCertificate, d.Id()))
err := iamClient.SAMLFederations.Certificates.Delete(ctx, d.Get("federation_id").(string), d.Id())
if err != nil && !errors.Is(err, iamerrors.ErrFederationCertificateNotFound) {
return diag.FromErr(errDeletingObject(objectSAMLFederationCertificate, d.Id(), err))
}

return nil
}

func resourceIAMS3SAMLFederationCertificateV1ImportState(_ context.Context, d *schema.ResourceData, _ interface{}) ([]*schema.ResourceData, error) {
var v string
if v = os.Getenv("OS_SAML_FEDERATION_ID"); v == "" {
return nil, fmt.Errorf("no OS_SAML_FEDERATION_ID environment variable was found, provide one to use import")
}

d.Set("federation_id", v)

return []*schema.ResourceData{d}, nil
}
109 changes: 109 additions & 0 deletions selectel/resource_selectel_iam_saml_federation_certificate_v1_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package selectel

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

var cert = `-----BEGIN CERTIFICATE-----\nMIICmzCCAYMCBgGI6ANFczANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjMwNjIzMTEyNjQ4WhcNMzMwNjIzMTEyODI4WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC04rOaDpre/MucE3HXVCnAnpqIqQOeMn696AW2FATnI26x1BsxVAGjcrheAOIu+CxC28m48Ah4+SiTEk/u2X/WbGTd/1GZooz37cge0AWMQGyh8ysZRd6q06kg4QGD1iUtdQyHioMbSr9pPne2QQgSX5/gM9XDuA6dpG9Yv0PIPLFlk3BIUL1qEfUiYbDlrunkN/y4XromJaJPpgXKWraH194bqcgXGQLrCqicKwsRBoQJHg3ODWHjHFOwYODJ1XBsRcAue4J88PKiPV1tZNPVczMptrkqGBYTgOYGjKXGe5EH50RJE4/3Ynurz2s34DSDVJhJOYtGwpfeSuU3i3mVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGAweCuWJmJXMUdRtgoFIiu6BGotDX5sA/VOm4CRsEXV7/qnBagrAPkRz86KGm4lOPL0X+I13JQh4/OB1gxnPN+BXhNtCWCoj1wA3/BWjs1ow/gaVXzwdy+1mbc/sUBudsLq2Yqs54GgeYsTBKMVpSLKiRg1NebEFlqFmG2hjPzYg1QHL4VBusMQgqt7TTnOfGtdT3Ss9TKGRQ+iwfNL0BtSAKaTRdhNVU4lDYUs788Kw5od/uJj0wTICKO5/PrkX7Uy42+fyU+4SvJynPOy+M+z+s08JC9+eYXixfeeFG1nNWR+DIKXcXaSwNQW+8RweGbOJxQ2BoUKtl0NCHrvxJw=\n-----END CERTIFICATE-----`

func TestAccIAMV1SAMLFederationCertificateBasic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccSelectelPreCheck(t) },
ProviderFactories: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccIAMV1SAMLFederationCertificateBasic(),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "id"),
resource.TestCheckResourceAttr("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "name", "cert"),
resource.TestCheckResourceAttr("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "description", "simple description"),
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "data"),
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "not_before"),
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "not_after"),
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "fingerprint"),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func TestAccIAMV1SAMLFederationCertificateUpdate(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccSelectelPreCheck(t) },
ProviderFactories: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccIAMV1SAMLFederationCertificateBasic(),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "id"),
resource.TestCheckResourceAttr("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "name", "cert"),
resource.TestCheckResourceAttr("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "description", "simple description"),
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "data"),
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "not_before"),
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "not_after"),
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "fingerprint"),
),
ExpectNonEmptyPlan: true,
},
{
Config: testAccIAMV1SAMLFederationCertificateUpdate(),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "id"),
resource.TestCheckResourceAttr("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "name", "cert 2"),
resource.TestCheckResourceAttr("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "description", "simple description 2"),
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "data"),
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "not_before"),
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "not_after"),
resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_certificate_v1.certificate_tf_acc_test_1", "fingerprint"),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func testAccIAMV1SAMLFederationCertificateBasic() string {
return fmt.Sprintf(`
resource "selectel_iam_saml_federation_v1" "federation_tf_acc_test_1" {
name = "federation name"
description = "simple description"
issuer = "http://localhost:8080/realms/master"
sso_url = "http://localhost:8080/realms/master/protocol/saml"
sign_authn_requests = true
force_authn = true
session_max_age_hours = 24
}

resource "selectel_iam_saml_federation_certificate_v1" "certificate_tf_acc_test_1" {
federation_id = selectel_iam_saml_federation_v1.federation_tf_acc_test_1.id
name = "cert"
description = "simple description"
data = "%s"
}
`, cert)
}

func testAccIAMV1SAMLFederationCertificateUpdate() string {
return fmt.Sprintf(`
resource "selectel_iam_saml_federation_v1" "federation_tf_acc_test_1" {
name = "federation name"
description = "simple description"
issuer = "http://localhost:8080/realms/master"
sso_url = "http://localhost:8080/realms/master/protocol/saml"
sign_authn_requests = true
force_authn = true
session_max_age_hours = 24
}

resource "selectel_iam_saml_federation_certificate_v1" "certificate_tf_acc_test_1" {
federation_id = selectel_iam_saml_federation_v1.federation_tf_acc_test_1.id
name = "cert 2"
description = "simple description 2"
data = "%s"
}
`, cert)
}
Loading