diff --git a/go.mod b/go.mod index d8dfa94f..6b5f15ed 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 6df326b6..0eec4931 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/selectel/provider.go b/selectel/provider.go index e141d7dc..dcae49dd 100644 --- a/selectel/provider.go +++ b/selectel/provider.go @@ -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. @@ -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 diff --git a/selectel/resource_selectel_iam_saml_federation_certificate_v1.go b/selectel/resource_selectel_iam_saml_federation_certificate_v1.go new file mode 100644 index 00000000..7c6c2ed4 --- /dev/null +++ b/selectel/resource_selectel_iam_saml_federation_certificate_v1.go @@ -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 +} diff --git a/selectel/resource_selectel_iam_saml_federation_certificate_v1_test.go b/selectel/resource_selectel_iam_saml_federation_certificate_v1_test.go new file mode 100644 index 00000000..879db834 --- /dev/null +++ b/selectel/resource_selectel_iam_saml_federation_certificate_v1_test.go @@ -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) +} diff --git a/selectel/resource_selectel_iam_saml_federation_v1.go b/selectel/resource_selectel_iam_saml_federation_v1.go new file mode 100644 index 00000000..d0eec7fd --- /dev/null +++ b/selectel/resource_selectel_iam_saml_federation_v1.go @@ -0,0 +1,165 @@ +package selectel + +import ( + "context" + "errors" + "log" + + "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" +) + +func resourceIAMSAMLFederationV1() *schema.Resource { + return &schema.Resource{ + Description: "Represents a SAML Federation in IAM API", + CreateContext: resourceIAMSAMLFederationV1Create, + ReadContext: resourceIAMSAMLFederationV1Read, + UpdateContext: resourceIAMSAMLFederationV1Update, + DeleteContext: resourceIAMSAMLFederationV1Delete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Name of the Federation.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: "Description of the Federation.", + }, + "issuer": { + Type: schema.TypeString, + Required: true, + Description: "ID of the credential provider.", + }, + "sso_url": { + Type: schema.TypeString, + Required: true, + Description: "Single sign-on endpoint URL.", + }, + "sign_authn_requests": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Should sign authentication requests.", + }, + "session_max_age_hours": { + Type: schema.TypeInt, + Required: true, + Description: "Session lifetime.", + }, + "force_authn": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Enable forced authentication at every login.", + }, + "account_id": { + Type: schema.TypeString, + Computed: true, + Description: "Account ID", + }, + }, + } +} + +func resourceIAMSAMLFederationV1Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + iamClient, diagErr := getIAMClient(meta) + if diagErr != nil { + return diagErr + } + + opts := saml.CreateRequest{ + Name: d.Get("name").(string), + Description: d.Get("description").(string), + Issuer: d.Get("issuer").(string), + SSOUrl: d.Get("sso_url").(string), + SignAuthnRequests: d.Get("sign_authn_requests").(bool), + ForceAuthn: d.Get("force_authn").(bool), + SessionMaxAgeHours: d.Get("session_max_age_hours").(int), + } + log.Print(msgCreate(objectSAMLFederation, opts)) + + federation, err := iamClient.SAMLFederations.Create(ctx, opts) + if err != nil { + return diag.FromErr(errCreatingObject(objectSAMLFederation, err)) + } + + d.SetId(federation.ID) + + return resourceIAMSAMLFederationV1Read(ctx, d, meta) +} + +func resourceIAMSAMLFederationV1Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + iamClient, diagErr := getIAMClient(meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgGet(objectSAMLFederation, d.Id())) + federation, err := iamClient.SAMLFederations.Get(ctx, d.Id()) + if err != nil { + return diag.FromErr(errGettingObject(objectSAMLFederation, d.Id(), err)) + } + + d.Set("account_id", federation.AccountID) + d.Set("name", federation.Name) + d.Set("description", federation.Description) + d.Set("issuer", federation.Issuer) + d.Set("sso_url", federation.SSOUrl) + d.Set("sign_authn_requests", federation.SignAuthnRequests) + d.Set("force_authn", federation.ForceAuthn) + d.Set("session_max_age_hours", federation.SessionMaxAgeHours) + + return nil +} + +func resourceIAMSAMLFederationV1Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + iamClient, diagErr := getIAMClient(meta) + if diagErr != nil { + return diagErr + } + + signAuthnRequests := d.Get("sign_authn_requests").(bool) + forceAuthn := d.Get("force_authn").(bool) + description := d.Get("description").(string) + + opts := saml.UpdateRequest{ + Name: d.Get("name").(string), + Description: &description, + Issuer: d.Get("issuer").(string), + SSOUrl: d.Get("sso_url").(string), + SignAuthnRequests: &signAuthnRequests, + ForceAuthn: &forceAuthn, + SessionMaxAgeHours: d.Get("session_max_age_hours").(int), + } + + log.Print(msgUpdate(objectSAMLFederation, d.Id(), opts)) + err := iamClient.SAMLFederations.Update(ctx, d.Id(), opts) + if err != nil { + return diag.FromErr(errUpdatingObject(objectSAMLFederation, d.Id(), err)) + } + + return resourceIAMSAMLFederationV1Read(ctx, d, meta) +} + +func resourceIAMSAMLFederationV1Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + iamClient, diagErr := getIAMClient(meta) + if diagErr != nil { + return diagErr + } + + log.Print(msgDelete(objectSAMLFederation, d.Id())) + err := iamClient.SAMLFederations.Delete(ctx, d.Id()) + if err != nil && !errors.Is(err, iamerrors.ErrFederationNotFound) { + return diag.FromErr(errDeletingObject(objectSAMLFederation, d.Id(), err)) + } + + return nil +} diff --git a/selectel/resource_selectel_iam_saml_federation_v1_test.go b/selectel/resource_selectel_iam_saml_federation_v1_test.go new file mode 100644 index 00000000..1e71b5e3 --- /dev/null +++ b/selectel/resource_selectel_iam_saml_federation_v1_test.go @@ -0,0 +1,92 @@ +package selectel + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccIAMV1SAMLFederationBasic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccSelectelPreCheck(t) }, + ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccIAMV1SAMLFederationBasic(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "id"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "name", "federation name"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "description", "simple description"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "issuer", "http://localhost:8080/realms/master"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "sso_url", "http://localhost:8080/realms/master/protocol/saml"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "sign_authn_requests", "true"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "force_authn", "true"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "session_max_age_hours", "24"), + ), + }, + }, + }) +} + +func TestAccIAMV1SAMLFederationUpdate(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccSelectelPreCheck(t) }, + ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccIAMV1SAMLFederationBasic(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "id"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "name", "federation name"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "description", "simple description"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "issuer", "http://localhost:8080/realms/master"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "sso_url", "http://localhost:8080/realms/master/protocol/saml"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "sign_authn_requests", "true"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "force_authn", "true"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "session_max_age_hours", "24"), + ), + }, + { + Config: testAccIAMV1SAMLFederationUpdate(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "id"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "name", "federation name 2"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "description", "simple description 2"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "issuer", "http://localhost:8080/realms/master"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "sso_url", "http://localhost:8080/realms/master/protocol/saml"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "sign_authn_requests", "true"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "force_authn", "true"), + resource.TestCheckResourceAttr("selectel_iam_saml_federation_v1.federation_tf_acc_test_1", "session_max_age_hours", "24"), + ), + }, + }, + }) +} + +func testAccIAMV1SAMLFederationBasic() string { + return ` +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 +} +` +} + +func testAccIAMV1SAMLFederationUpdate() string { + return ` +resource "selectel_iam_saml_federation_v1" "federation_tf_acc_test_1" { + name = "federation name 2" + description = "simple description 2" + 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 +} +` +} diff --git a/website/docs/r/iam_saml_federation_certificate_v1.html.markdown b/website/docs/r/iam_saml_federation_certificate_v1.html.markdown new file mode 100644 index 00000000..d0333d0b --- /dev/null +++ b/website/docs/r/iam_saml_federation_certificate_v1.html.markdown @@ -0,0 +1,69 @@ +--- +layout: "selectel" +page_title: "Selectel: selectel_iam_saml_federation_certificate_v1" +sidebar_current: "docs-selectel-resource-iam-saml-federation-certificate-v1" +description: |- + Creates and manages SAML Federation Certificates for Selectel products using public API v1. +--- + +# selectel\_iam\_saml\_federation\_certificate\_v1 + +Manages SAML Federation Certificates for Selectel products using public API v1. +Selectel products support Identity and Access Management (IAM). +For more information about Federation Certificates, see the [official Selectel documentation](https://docs.selectel.ru/en/control-panel-actions/users-and-roles/federations/certificates/). + +## Example Usage + +```hcl +resource "selectel_iam_saml_federation_certificate_v1" "certificate" { + federation_id = selectel_iam_saml_federation_v1.federation_1.id + name = "certificate name" + description = "simple description" + data = file("${path.module}/federation_cert.crt") +} + +``` + +## Argument Reference + +* `federation_id` - (Required) Federation ID. + +* `name` - (Required) Certificate name. + +* `description` - (Optional) Certificate description. + +* `data` - (Required) Certificate data. It must begin with -----BEGIN CERTIFICATE----- and end with -----END CERTIFICATE-----. + +## Attributes Reference + +* `account_id` - Federation account ID. + +* `not_before` - Issuing date. + +* `not_after` - End date. + +* `fingerprint` - Fingerprint. + +## Import + +You can import a certificate: + +```shell +export OS_DOMAIN_NAME= +export OS_USERNAME= +export OS_PASSWORD= +export OS_SAML_FEDERATION_ID= +terraform import selectel_iam_saml_federation_certificate_v1.certificate_1 +``` + +where: + +* `` — Selectel account ID. The account ID is in the top right corner of the [Control panel](https://my.selectel.ru/). Learn more about [Registration](https://docs.selectel.ru/en/control-panel-actions/account/registration/). + +* `` — Name of the service user. To get the name, in the [Control panel](https://my.selectel.ru/iam/users_management/users?type=service), go to **Identity & Access Management** ⟶ **User management** ⟶ the **Service users** tab ⟶ copy the name of the required user. Learn more about [Service Users](https://docs.selectel.ru/en/control-panel-actions/users-and-roles/user-types-and-roles/). + +* `` — Password of the service user. + +* `` — Unique identifier of the Federation, which has this certificate, for example, `abc1bb378ac84e1234b869b77aadd2ab`. To get the federation ID, use either [Control Panel](https://my.selectel.ru/iam/federations) or [IAM API](https://developers.selectel.ru/docs/control-panel/iam/) + +* `` — Unique identifier of the Certificate. diff --git a/website/docs/r/iam_saml_federation_v1.html.markdown b/website/docs/r/iam_saml_federation_v1.html.markdown new file mode 100644 index 00000000..7a76a799 --- /dev/null +++ b/website/docs/r/iam_saml_federation_v1.html.markdown @@ -0,0 +1,66 @@ +--- +layout: "selectel" +page_title: "Selectel: selectel_iam_saml_federation_v1" +sidebar_current: "docs-selectel-resource-iam-saml-federation-v1" +description: |- + Creates and manages SAML Federation for Selectel products using public API v1. +--- + +# selectel\_iam\_saml\_federation\_v1 + +Manages SAML Federation for Selectel products using public API v1. +Selectel products support Identity and Access Management (IAM). +For more information about Federations, see the [official Selectel documentation](https://docs.selectel.ru/en/control-panel-actions/users-and-roles/federations/). + +## Example Usage + +```hcl +resource "selectel_iam_saml_federation_v1" "federation_1" { + name = "federation name" + description = "simple description" + issuer = "http://localhost:8080/realms/master" + sso_url = "http://localhost:8080/realms/master/protocol/saml" + session_max_age_hours = 24 +} +``` + +## Argument Reference + +* `name` - (Required) Federation name. + +* `description` - (Optional) Federation description. + +* `issuer` - (Required) ID of the credential provider. + +* `sso_url` - (Required) Link to the credential provider login page. + +* `sign_authn_requests` - (Optional) Enable authentication requests signing. + +* `force_authn` - (Optional) Require users to authenticate via SSO every time they log in. + +* `session_max_age_hours` - (Required) Session lifetime. + +## Attributes Reference + +* `account_id` - Federation account ID. + +## Import + +You can import a federation: + +```shell +export OS_DOMAIN_NAME= +export OS_USERNAME= +export OS_PASSWORD= +terraform import selectel_iam_saml_federation_v1.federation_1 +``` + +where: + +* `` — Selectel account ID. The account ID is in the top right corner of the [Control panel](https://my.selectel.ru/). Learn more about [Registration](https://docs.selectel.ru/en/control-panel-actions/account/registration/). + +* `` — Name of the service user. To get the name, in the [Control panel](https://my.selectel.ru/iam/users_management/users?type=service), go to **Identity & Access Management** ⟶ **User management** ⟶ the **Service users** tab ⟶ copy the name of the required user. Learn more about [Service Users](https://docs.selectel.ru/en/control-panel-actions/users-and-roles/user-types-and-roles/). + +* `` — Password of the service user. + +* `` — Unique identifier of the federation, for example, `abc1bb378ac84e1234b869b77aadd2ab`. To get the federation ID, use either [Control Panel](https://my.selectel.ru/iam/federations) or [IAM API](https://developers.selectel.ru/docs/control-panel/iam/).