diff --git a/CHANGELOG.md b/CHANGELOG.md index e2851e2..6b3ebcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [0.8.1](https://github.com/JanssenProject/terraform-provider-jans/compare/v0.8.0...v0.8.1) (2023-10-26) + + +### Bug Fixes + +* properly handle agama deployment autoconfigure +* update provider to match latest API +* updates in accordance to latest API changes + ## [0.8.0](https://github.com/JanssenProject/terraform-provider-jans/compare/v0.7.3...v0.8.0) (2023-09-27) diff --git a/docs/data-sources/custom_script_types.md b/docs/data-sources/custom_script_types.md new file mode 100644 index 0000000..0ee8509 --- /dev/null +++ b/docs/data-sources/custom_script_types.md @@ -0,0 +1,23 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "jans_custom_script_types Data Source - terraform-provider-jans" +subcategory: "" +description: |- + Data source for retrieving supported custom script types. +--- + +# jans_custom_script_types (Data Source) + +Data source for retrieving supported custom script types. + + + + +## Schema + +### Read-Only + +- `id` (String) The ID of this resource. +- `types` (List of String) A list of support custom script types. + + diff --git a/docs/resources/app_configuration.md b/docs/resources/app_configuration.md index 80009c7..b5155d1 100644 --- a/docs/resources/app_configuration.md +++ b/docs/resources/app_configuration.md @@ -118,6 +118,7 @@ resource "jans_app_configuration" "global" { Example: id_generation_endpoint, auth_level_mapping, etc. - `display_values_supported` (List of String) A list of the display parameter values that the OpenID Provider supports. One of 'page', 'popup'. - `dn_name` (String) DN of certificate issuer. +- `dpop_jkt_force_for_authorization_code` (Boolean) Demonstration of Proof-of-Possession (DPoP) JWK Thumbprint force for authorization code. - `dpop_jti_cache_time` (Number) Demonstration of Proof-of-Possession (DPoP) cache time. - `dpop_nonce_cache_time` (Number) Demonstration of Proof-of-Possession (DPoP) nonce cache time. - `dpop_signing_alg_values_supported` (List of String) Demonstration of Proof-of-Possession (DPoP) authorization signing algorithms supported. @@ -150,6 +151,7 @@ resource "jans_app_configuration" "global" { - `feature_flags` (List of String) List of feature flags. - `force_id_token_hint_precense` (Boolean) Boolean value specifying whether force id_token_hint parameter presence. - `force_offline_access_scope_to_enable_refresh_token` (Boolean) Boolean value specifying whether force offline_access scope to enable refresh_token grant type. +- `force_ropc_in_authorization_endpoint` (Boolean) Specifies if ROPC is forced in authorization endpoint. - `force_signed_request_object` (Boolean) Boolean value true indicates that signed request object is mandatory. - `front_channel_logout_session_supported` (Boolean) Boolean value to specify support for front channel logout session. - `grant_types_and_response_types_autofix_enabled` (Boolean) Boolean value specifying whether to Grant types and Response types can be auto fixed. diff --git a/examples/data-sources/jans_custom_script_types/data-source.tf b/examples/data-sources/jans_custom_script_types/data-source.tf new file mode 100644 index 0000000..b6f994b --- /dev/null +++ b/examples/data-sources/jans_custom_script_types/data-source.tf @@ -0,0 +1,6 @@ +data "jans_custom_script_types" "script_types" { +} + +output "script_type_client_registration_enabled" { + value = contains(data.jans_custom_script_types.script_types, "client_registration") +} \ No newline at end of file diff --git a/examples/provider/provider.tf b/examples/provider/provider.tf index 6961b61..03af9cb 100644 --- a/examples/provider/provider.tf +++ b/examples/provider/provider.tf @@ -1,3 +1,12 @@ +terraform { + required_version = ">= 0.12.0" + required_providers { + janssen = { + source = "JanssenProject/jans" + version = "0.6.0" + } + } +} provider "jans" { url = "https://test-instnace.jans.io" diff --git a/jans/agama_deployment.go b/jans/agama_deployment.go index 5bfd4c1..5bb7e0b 100644 --- a/jans/agama_deployment.go +++ b/jans/agama_deployment.go @@ -15,8 +15,9 @@ type ProjectMetadata struct { } type DeploymentDetails struct { - Folders []string `json:"folders" schema:"folders"` - Libs []string `json:"libs" schema:"libs"` + Folders []string `json:"folders" schema:"folders"` + Libs []string `json:"libs" schema:"libs"` + Autoconfigure bool `json:"autoconfigure" schema:"autoconfigure"` // FlowsError []string `json:"flowsError" schema:"flows_error"` Error string `json:"error" schema:"error"` ProjectMetadata ProjectMetadata `json:"projectMetadata" schema:"project_metadata"` @@ -97,7 +98,7 @@ func (c *Client) GetAgamaDeployment(ctx context.Context, qname string) (*AgamaDe } // CreateAgamaDeployment creates a new Agama flow. -func (c *Client) CreateAgamaDeployment(ctx context.Context, name string, data []byte) error { +func (c *Client) CreateAgamaDeployment(ctx context.Context, name string, autoconfig bool, data []byte) error { if name == "" { return fmt.Errorf("agama project name may not be empty") @@ -108,7 +109,12 @@ func (c *Client) CreateAgamaDeployment(ctx context.Context, name string, data [] return fmt.Errorf("failed to get token: %w", err) } - if err := c.postZipFile(ctx, "/jans-config-api/api/v1/agama-deployment/"+name, token, data, nil); err != nil { + url := "/jans-config-api/api/v1/agama-deployment/" + name + if autoconfig { + url += "?autoconfigure=true" + } + + if err := c.postZipFile(ctx, url, token, data, nil); err != nil { return fmt.Errorf("post request failed: %w", err) } diff --git a/jans/agama_deployment_test.go b/jans/agama_deployment_test.go index 046b07d..a20e501 100644 --- a/jans/agama_deployment_test.go +++ b/jans/agama_deployment_test.go @@ -2,7 +2,7 @@ package jans import ( "context" - "io/ioutil" + "io" "os" "path/filepath" "runtime" @@ -40,7 +40,7 @@ func TestAgamaDeployment(t *testing.T) { } // read file into byte array - contents, err := ioutil.ReadAll(zipFile) + contents, err := io.ReadAll(zipFile) if err != nil { t.Fatalf("failed to read test file: %v", err) } @@ -51,7 +51,7 @@ func TestAgamaDeployment(t *testing.T) { }) // upload test file - if err = client.CreateAgamaDeployment(ctx, "testDeployment", contents); err != nil { + if err = client.CreateAgamaDeployment(ctx, "testDeployment", true, contents); err != nil { t.Fatalf("failed to create test deployment: %v", err) } @@ -65,6 +65,15 @@ func TestAgamaDeployment(t *testing.T) { t.Errorf("expected deployment name to be 'testDeployment', got '%s'", deployment.Name) } + deployments, err = client.GetAgamaDeployments(ctx) + if err != nil { + t.Error(err) + } + + if len(deployments) != 1 { + t.Errorf("expected 1 deployment, got %d", len(deployments)) + } + // delete test deployment err = client.DeleteAgamaDeployment(ctx, "testDeployment") if err != nil { diff --git a/jans/app_configuration.go b/jans/app_configuration.go index a971bd4..a3c1842 100644 --- a/jans/app_configuration.go +++ b/jans/app_configuration.go @@ -359,7 +359,9 @@ type AppConfiguration struct { DpopJtiCacheTime int `schema:"dpop_jti_cache_time" json:"dpopJtiCacheTime"` DpopUseNonce bool `schema:"dpop_use_nonce" json:"dpopUseNonce"` DpopNonceCacheTime int `schema:"dpop_nonce_cache_time" json:"dpopNonceCacheTime"` + DpopJktForceForAuthorizationCode bool `schema:"dpop_jkt_force_for_authorization_code" json:"dpopJktForceForAuthorizationCode"` AllowIdTokenWithoutImplicitGrantType bool `schema:"allow_id_token_without_implicit_grant_type" json:"allowIdTokenWithoutImplicitGrantType"` + ForceRopcInAuthorizationEndpoint bool `schema:"force_ropc_in_authorization_endpoint" json:"forceRopcInAuthorizationEndpoint"` DiscoveryCacheLifetimeInMinutes int `schema:"discovery_cache_lifetime_in_minutes" json:"discoveryCacheLifetimeInMinutes"` DiscoveryAllowedKeys []string `schema:"discovery_allowed_keys" json:"discoveryAllowedKeys"` DiscoveryDenyKeys []string `schema:"discovery_deny_keys" json:"discoveryDenyKeys"` diff --git a/jans/oidc_client_test.go b/jans/oidc_client_test.go index a06b6d6..972d321 100644 --- a/jans/oidc_client_test.go +++ b/jans/oidc_client_test.go @@ -157,14 +157,21 @@ func TestOIDCClient(t *testing.T) { Disabled: false, AuthorizedOrigins: []string{"https://moabu-21f13b7c-9069-ad58-5685-852e6d236020.gluu.info"}, Attributes: &OidcClientAttribute{ - RunIntrospectionScriptBeforeJwtCreation: false, - KeepClientAuthorizationAfterExpiration: false, - AllowSpontaneousScopes: false, - BackchannelLogoutSessionRequired: false, + RunIntrospectionScriptBeforeJwtCreation: true, + KeepClientAuthorizationAfterExpiration: true, + AllowSpontaneousScopes: true, + BackchannelLogoutSessionRequired: true, ParLifetime: 600, - RequirePar: false, - JansDefaultPromptLogin: false, - MinimumAcrLevel: -1, + RequirePar: true, + DpopBoundAccessToken: true, + JansDefaultPromptLogin: true, + IdTokenLifetime: 300, + AllowOfflineAccessWithoutConsent: true, + MinimumAcrLevel: 3600, + MinimumAcrLevelAutoresolve: true, + AdditionalTokenEndpointAuthMethods: []string{"client_secret_jwt"}, + MinimumAcrPriorityList: []string{"basic"}, + RequestedLifetime: 300, }, Description: "Test client", Organization: "inum=1200.33AFBA,ou=scopes,o=jans", diff --git a/jans/script.go b/jans/script.go index cfdeabc..163d5e6 100644 --- a/jans/script.go +++ b/jans/script.go @@ -156,3 +156,19 @@ func (c *Client) DeleteScript(ctx context.Context, inum string) error { return nil } + +// GetScriptTypes retrieves the list of supported script types. +func (c *Client) GetScriptTypes(ctx context.Context) ([]string, error) { + + token, err := c.getToken(ctx, "https://jans.io/oauth/config/scripts.readonly") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + ret := []string{} + if err := c.get(ctx, "/jans-config-api/api/v1/config/scripts/types", token, &ret); err != nil { + return nil, fmt.Errorf("get request failed: %w", err) + } + + return ret, nil +} diff --git a/jans/script_test.go b/jans/script_test.go index 315e106..92ddaa8 100644 --- a/jans/script_test.go +++ b/jans/script_test.go @@ -98,3 +98,22 @@ func TestScripts(t *testing.T) { t.Errorf("script location type not updated") } } + +func TestScriptTypes(t *testing.T) { + + client, err := NewInsecureClient(host, user, pass) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + types, err := client.GetScriptTypes(ctx) + if err != nil { + t.Fatal(err) + } + + if len(types) == 0 { + t.Error("expected script types, got none") + } +} diff --git a/provider/data_source_custom_script_types.go b/provider/data_source_custom_script_types.go new file mode 100644 index 0000000..2809766 --- /dev/null +++ b/provider/data_source_custom_script_types.go @@ -0,0 +1,46 @@ +package provider + +import ( + "context" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/jans/terraform-provider-jans/jans" +) + +func dataSourceCustomScriptTypes() *schema.Resource { + + return &schema.Resource{ + Description: "Data source for retrieving supported custom script types.", + ReadContext: dataSourceCustomScriptTypesRead, + Schema: map[string]*schema.Schema{ + "types": { + Type: schema.TypeList, + Computed: true, + Description: "A list of support custom script types.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func dataSourceCustomScriptTypesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + c := meta.(*jans.Client) + + providerConfig, err := c.GetServiceProviderConfig(ctx) + if err != nil { + return diag.FromErr(err) + } + + if err := toSchemaResource(d, providerConfig); err != nil { + return diag.FromErr(err) + } + + d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + + return nil +} diff --git a/provider/provider.go b/provider/provider.go index b06acd9..68adc30 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -99,6 +99,7 @@ func Provider() *schema.Provider { // Resource instances for data sources must have a Read function // and must *not* implement Create, Update or Delete. DataSourcesMap: map[string]*schema.Resource{ + "jans_custom_script_types": dataSourceCustomScriptTypes(), "jans_fido2_configuration": dataSourceFido2Configuration(), "jans_persistence_config": dataSourcePersistenceConfiguration(), "jans_plugins": dataSourcePlugins(), diff --git a/provider/resource_agama_deployment.go b/provider/resource_agama_deployment.go index f8cb82c..d9e8312 100644 --- a/provider/resource_agama_deployment.go +++ b/provider/resource_agama_deployment.go @@ -4,7 +4,7 @@ import ( "context" "errors" "fmt" - "io/ioutil" + "io" "os" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -58,11 +58,13 @@ func resourceAgamaDeployment() *schema.Resource { Required: true, Description: "Path to the deployment file (in zip format)", }, - "deployment_file_hash": { - Type: schema.TypeString, - Required: true, - Description: "Hash of the deployment file, used to detect changes.", - ForceNew: true, + "autoconfigure": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: `Passing 'true' will make this project be configured with the sample configurations + found in the provided binary archive. This param should rarely be passed: use only in controlled + environments where the archive is not shared with third parties`, }, "task_active": { Type: schema.TypeBool, @@ -79,7 +81,7 @@ func resourceAgamaDeploymentCreate(ctx context.Context, d *schema.ResourceData, name := d.Get("name").(string) fileName := d.Get("deployment_file").(string) - fileHash := d.Get("deployment_file_hash").(string) + autoconfig := d.Get("autoconfigure").(bool) // check if file exists and can be accessed if _, err := os.Stat(fileName); err != nil { @@ -94,13 +96,13 @@ func resourceAgamaDeploymentCreate(ctx context.Context, d *schema.ResourceData, defer deploymentFile.Close() // read file into byte array - contents, err := ioutil.ReadAll(deploymentFile) + contents, err := io.ReadAll(deploymentFile) if err != nil { return diag.FromErr(err) } tflog.Debug(ctx, "Creating new agama deployment") - if err := c.CreateAgamaDeployment(ctx, name, contents); err != nil { + if err := c.CreateAgamaDeployment(ctx, name, autoconfig, contents); err != nil { return diag.FromErr(err) } if err := waitForAgamaDeploymetCreation(ctx, c, name); err != nil { @@ -110,7 +112,6 @@ func resourceAgamaDeploymentCreate(ctx context.Context, d *schema.ResourceData, d.SetId(name) d.Set("name", name) - d.Set("deployment_file_hash", fileHash) return resourceAgamaDeploymentRead(ctx, d, meta) } diff --git a/provider/resource_agama_deployment_test.go b/provider/resource_agama_deployment_test.go index 52ff69d..0bad2e0 100644 --- a/provider/resource_agama_deployment_test.go +++ b/provider/resource_agama_deployment_test.go @@ -27,7 +27,6 @@ func TestAccResourceAgamaDeployment_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccResourceCheckAgamaDeploymentExists(resourceName), resource.TestCheckResourceAttr(resourceName, "name", "test-deployment"), - resource.TestCheckResourceAttrSet(resourceName, "deployment_file_hash"), ), }, }, @@ -45,6 +44,7 @@ func testAccResourceAgamaDeploymentConfig_basic() string { resource "jans_agama_deployment" "test" { name = "test-deployment" deployment_file = "` + testFile + `" + autoconfigure = true } ` } diff --git a/provider/resource_app_configuration.go b/provider/resource_app_configuration.go index 7534366..547f946 100644 --- a/provider/resource_app_configuration.go +++ b/provider/resource_app_configuration.go @@ -1939,11 +1939,21 @@ func resourceAppConfiguration() *schema.Resource { Optional: true, Description: "Demonstration of Proof-of-Possession (DPoP) nonce cache time.", }, + "dpop_jkt_force_for_authorization_code": { + Type: schema.TypeBool, + Optional: true, + Description: "Demonstration of Proof-of-Possession (DPoP) JWK Thumbprint force for authorization code.", + }, "allow_id_token_without_implicit_grant_type": { Type: schema.TypeBool, Optional: true, Description: "Specifies if a token without implicit grant types is allowed.", }, + "force_ropc_in_authorization_endpoint": { + Type: schema.TypeBool, + Optional: true, + Description: "Specifies if ROPC is forced in authorization endpoint.", + }, "discovery_cache_lifetime_in_minutes": { Type: schema.TypeInt, Optional: true, diff --git a/provider/resource_oidc_client_test.go b/provider/resource_oidc_client_test.go index 1914758..15afabf 100644 --- a/provider/resource_oidc_client_test.go +++ b/provider/resource_oidc_client_test.go @@ -67,6 +67,8 @@ func TestReourceOidcClient_Mapping(t *testing.T) { KeepClientAuthorizationAfterExpiration: false, AllowSpontaneousScopes: false, BackchannelLogoutSessionRequired: false, + MinimumAcrLevel: -1, + MinimumAcrPriorityList: nil, ParLifetime: 600, RequirePar: false, JansDefaultPromptLogin: false, @@ -74,7 +76,7 @@ func TestReourceOidcClient_Mapping(t *testing.T) { Description: "Test client", Organization: "inum=1200.33AFBA,ou=scopes,o=jans", // Groups: []string{}, - // Ttl: 3600, + Ttl: 3600, DisplayName: "SCIM client", AuthenticationMethod: "client_secret_basic", BaseDn: "inum=1201.d52300ed-8193-510e-b31d-5829f4af346e,ou=clients,o=jans", @@ -136,18 +138,21 @@ resource "jans_oidc_client" "test" { subject_type = "public" attributes { - par_lifetime = 600 - additional_audience = [] - backchannel_logout_uri = [] - consent_gathering_scripts = [] - introspection_scripts = [] - jans_authorized_acr = [] - post_authn_scripts = [] - ropc_scripts = [] - rpt_claims_scripts = [] - spontaneous_scope_script_dns = [] - spontaneous_scopes = [] - update_token_script_dns = [] + par_lifetime = 600 + additional_audience = [] + backchannel_logout_uri = [] + consent_gathering_scripts = [] + introspection_scripts = [] + jans_authorized_acr = [] + post_authn_scripts = [] + ropc_scripts = [] + rpt_claims_scripts = [] + spontaneous_scope_script_dns = [] + spontaneous_scopes = [] + update_token_script_dns = [] + additional_token_endpoint_auth_methods = [] + minimum_acr_level = -1 + minimum_acr_priority_list = [] } lifecycle {