From 96764d0596f3266c600201ac3bdb993927eca4a7 Mon Sep 17 00:00:00 2001 From: Michael Parker Date: Tue, 4 Oct 2022 15:07:35 -0500 Subject: [PATCH] feat: new data source - keycloak_openid_client_scope (#743) --- docs/data-sources/openid_client_scope.md | 34 ++++++++++ keycloak/openid_client.go | 4 +- keycloak/openid_client_scope.go | 4 +- .../data_source_keycloak_openid_client.go | 5 +- ...ata_source_keycloak_openid_client_scope.go | 62 ++++++++++++++++++ ...ource_keycloak_openid_client_scope_test.go | 63 +++++++++++++++++++ provider/provider.go | 1 + .../resource_keycloak_openid_client_scope.go | 14 ++--- 8 files changed, 173 insertions(+), 14 deletions(-) create mode 100644 docs/data-sources/openid_client_scope.md create mode 100644 provider/data_source_keycloak_openid_client_scope.go create mode 100644 provider/data_source_keycloak_openid_client_scope_test.go diff --git a/docs/data-sources/openid_client_scope.md b/docs/data-sources/openid_client_scope.md new file mode 100644 index 00000000..c210c124 --- /dev/null +++ b/docs/data-sources/openid_client_scope.md @@ -0,0 +1,34 @@ +--- +page_title: "keycloak_openid_client_scope Data Source" +--- + +# keycloak_openid_client_scope Data Source + +This data source can be used to fetch properties of a Keycloak OpenID client scope for usage with other resources. + +## Example Usage + +```hcl +data "keycloak_openid_client_scope" "offline_access" { + realm_id = "my-realm" + name = "offline_access" +} + +# use the data source +resource "keycloak_openid_audience_protocol_mapper" "audience_mapper" { + realm_id = data.keycloak_openid_client_scope.offline_access.realm_id + client_scope_id = data.keycloak_openid_client_scope.offline_access.id + name = "audience-mapper" + + included_custom_audience = "foo" +} +``` + +## Argument Reference + +- `realm_id` - (Required) The realm id. +- `name` - (Required) The name of the client scope. + +## Attributes Reference + +See the docs for the `keycloak_openid_client_scope` resource for details on the exported attributes. diff --git a/keycloak/openid_client.go b/keycloak/openid_client.go index 07ba61cf..4589b90d 100644 --- a/keycloak/openid_client.go +++ b/keycloak/openid_client.go @@ -287,7 +287,7 @@ func (keycloakClient *KeycloakClient) attachOpenidClientScopes(ctx context.Conte return fmt.Errorf("validation error: client with id %s uses access type BEARER-ONLY which does not use scopes", clientId) } - allOpenidClientScopes, err := keycloakClient.ListOpenidClientScopesWithFilter(ctx, realmId, includeOpenidClientScopesMatchingNames(scopeNames)) + allOpenidClientScopes, err := keycloakClient.ListOpenidClientScopesWithFilter(ctx, realmId, IncludeOpenidClientScopesMatchingNames(scopeNames)) if err != nil { return err } @@ -336,7 +336,7 @@ func (keycloakClient *KeycloakClient) AttachOpenidClientOptionalScopes(ctx conte } func (keycloakClient *KeycloakClient) detachOpenidClientScopes(ctx context.Context, realmId, clientId, t string, scopeNames []string) error { - allOpenidClientScopes, err := keycloakClient.ListOpenidClientScopesWithFilter(ctx, realmId, includeOpenidClientScopesMatchingNames(scopeNames)) + allOpenidClientScopes, err := keycloakClient.ListOpenidClientScopesWithFilter(ctx, realmId, IncludeOpenidClientScopesMatchingNames(scopeNames)) if err != nil { return err } diff --git a/keycloak/openid_client_scope.go b/keycloak/openid_client_scope.go index 0dbe6c42..77413cf7 100644 --- a/keycloak/openid_client_scope.go +++ b/keycloak/openid_client_scope.go @@ -101,6 +101,8 @@ func (keycloakClient *KeycloakClient) ListOpenidClientScopesWithFilter(ctx conte scope := new(OpenidClientScope) *scope = clientScope + scope.RealmId = realmId + openidClientScopes = append(openidClientScopes, scope) } } @@ -108,7 +110,7 @@ func (keycloakClient *KeycloakClient) ListOpenidClientScopesWithFilter(ctx conte return openidClientScopes, nil } -func includeOpenidClientScopesMatchingNames(scopeNames []string) OpenidClientScopeFilterFunc { +func IncludeOpenidClientScopesMatchingNames(scopeNames []string) OpenidClientScopeFilterFunc { return func(scope *OpenidClientScope) bool { for _, scopeName := range scopeNames { if scopeName == scope.Name { diff --git a/provider/data_source_keycloak_openid_client.go b/provider/data_source_keycloak_openid_client.go index 570ffa8a..146eaec1 100644 --- a/provider/data_source_keycloak_openid_client.go +++ b/provider/data_source_keycloak_openid_client.go @@ -244,9 +244,6 @@ func dataSourceKeycloakOpenidClientRead(ctx context.Context, data *schema.Resour } err = setOpenidClientData(ctx, keycloakClient, data, client) - if err != nil { - return diag.FromErr(err) - } - return nil + return diag.FromErr(err) } diff --git a/provider/data_source_keycloak_openid_client_scope.go b/provider/data_source_keycloak_openid_client_scope.go new file mode 100644 index 00000000..b6e607ba --- /dev/null +++ b/provider/data_source_keycloak_openid_client_scope.go @@ -0,0 +1,62 @@ +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" +) + +func dataSourceKeycloakOpenidClientScope() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceKeycloakOpenidClientScopeRead, + + Schema: map[string]*schema.Schema{ + "realm_id": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "consent_screen_text": { + Type: schema.TypeString, + Computed: true, + }, + "include_in_token_scope": { + Type: schema.TypeBool, + Computed: true, + }, + "gui_order": { + Type: schema.TypeInt, + Computed: true, + }, + }, + } +} + +func dataSourceKeycloakOpenidClientScopeRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + name := data.Get("name").(string) + + scopes, err := keycloakClient.ListOpenidClientScopesWithFilter(ctx, realmId, keycloak.IncludeOpenidClientScopesMatchingNames([]string{name})) + if err != nil { + return diag.FromErr(err) + } + + if len(scopes) != 1 { + return diag.Errorf("expected provided client scope name to match 1 scope, but matched %d scopes", len(scopes)) + } + + setOpenidClientScopeData(data, scopes[0]) + + return nil +} diff --git a/provider/data_source_keycloak_openid_client_scope_test.go b/provider/data_source_keycloak_openid_client_scope_test.go new file mode 100644 index 00000000..ba0c0ecc --- /dev/null +++ b/provider/data_source_keycloak_openid_client_scope_test.go @@ -0,0 +1,63 @@ +package provider + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccKeycloakDataSourceOpenidClientScope_basic(t *testing.T) { + t.Parallel() + clientScopeName := acctest.RandomWithPrefix("tf-acc-test") + dataSourceName := "data.keycloak_openid_client_scope.test" + resourceName := "keycloak_openid_client_scope.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccKeycloakOpenidClientScopeConfig(clientScopeName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, "realm_id", resourceName, "realm_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(dataSourceName, "description", resourceName, "description"), + resource.TestCheckResourceAttrPair(dataSourceName, "consent_screen_text", resourceName, "consent_screen_text"), + resource.TestCheckResourceAttrPair(dataSourceName, "include_in_token_scope", resourceName, "include_in_token_scope"), + ), + }, + }, + }) +} + +func testAccKeycloakOpenidClientScopeConfig(name string) string { + return fmt.Sprintf(` +data "keycloak_realm" "realm" { + realm = "%s" +} + +resource "keycloak_openid_client_scope" "test" { + name = "%s" + realm_id = data.keycloak_realm.realm.id + + description = "%s" + consent_screen_text = "%s" + include_in_token_scope = %t +} + +data "keycloak_openid_client_scope" "test" { + name = keycloak_openid_client_scope.test.name + realm_id = data.keycloak_realm.realm.id +} + +resource "keycloak_openid_audience_protocol_mapper" "audience_mapper" { + realm_id = data.keycloak_realm.realm.id + client_scope_id = data.keycloak_openid_client_scope.test.id + name = "audience-mapper" + + included_custom_audience = "foo" +} +`, testAccRealm.Realm, name, acctest.RandString(10), acctest.RandString(10), randomBool()) +} diff --git a/provider/provider.go b/provider/provider.go index 68a3576e..e2561e14 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -16,6 +16,7 @@ func KeycloakProvider(client *keycloak.KeycloakClient) *schema.Provider { "keycloak_group": dataSourceKeycloakGroup(), "keycloak_openid_client": dataSourceKeycloakOpenidClient(), "keycloak_openid_client_authorization_policy": dataSourceKeycloakOpenidClientAuthorizationPolicy(), + "keycloak_openid_client_scope": dataSourceKeycloakOpenidClientScope(), "keycloak_openid_client_service_account_user": dataSourceKeycloakOpenidClientServiceAccountUser(), "keycloak_realm": dataSourceKeycloakRealm(), "keycloak_realm_keys": dataSourceKeycloakRealmKeys(), diff --git a/provider/resource_keycloak_openid_client_scope.go b/provider/resource_keycloak_openid_client_scope.go index 82a47040..5c61944a 100644 --- a/provider/resource_keycloak_openid_client_scope.go +++ b/provider/resource_keycloak_openid_client_scope.go @@ -52,7 +52,7 @@ func resourceKeycloakOpenidClientScope() *schema.Resource { } } -func getClientScopeFromData(data *schema.ResourceData) *keycloak.OpenidClientScope { +func getOpenidClientScopeFromData(data *schema.ResourceData) *keycloak.OpenidClientScope { clientScope := &keycloak.OpenidClientScope{ Id: data.Id(), RealmId: data.Get("realm_id").(string), @@ -77,7 +77,7 @@ func getClientScopeFromData(data *schema.ResourceData) *keycloak.OpenidClientSco return clientScope } -func setClientScopeData(data *schema.ResourceData, clientScope *keycloak.OpenidClientScope) { +func setOpenidClientScopeData(data *schema.ResourceData, clientScope *keycloak.OpenidClientScope) { data.SetId(clientScope.Id) data.Set("realm_id", clientScope.RealmId) @@ -97,14 +97,14 @@ func setClientScopeData(data *schema.ResourceData, clientScope *keycloak.OpenidC func resourceKeycloakOpenidClientScopeCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics { keycloakClient := meta.(*keycloak.KeycloakClient) - clientScope := getClientScopeFromData(data) + clientScope := getOpenidClientScopeFromData(data) err := keycloakClient.NewOpenidClientScope(ctx, clientScope) if err != nil { return diag.FromErr(err) } - setClientScopeData(data, clientScope) + setOpenidClientScopeData(data, clientScope) return resourceKeycloakOpenidClientScopeRead(ctx, data, meta) } @@ -120,7 +120,7 @@ func resourceKeycloakOpenidClientScopeRead(ctx context.Context, data *schema.Res return handleNotFoundError(ctx, err, data) } - setClientScopeData(data, clientScope) + setOpenidClientScopeData(data, clientScope) return nil } @@ -128,14 +128,14 @@ func resourceKeycloakOpenidClientScopeRead(ctx context.Context, data *schema.Res func resourceKeycloakOpenidClientScopeUpdate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics { keycloakClient := meta.(*keycloak.KeycloakClient) - clientScope := getClientScopeFromData(data) + clientScope := getOpenidClientScopeFromData(data) err := keycloakClient.UpdateOpenidClientScope(ctx, clientScope) if err != nil { return diag.FromErr(err) } - setClientScopeData(data, clientScope) + setOpenidClientScopeData(data, clientScope) return nil }