Skip to content

Commit f76c0b5

Browse files
authored
feat: add keycloak_openid_client_authorization_client_scope_policy resource (#1128)
Signed-off-by: Tadas Sutkaitis <[email protected]>
1 parent 319067e commit f76c0b5

5 files changed

+552
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
---
2+
page_title: "keycloak_openid_client_authorization_client_scope_policy Resource"
3+
---
4+
5+
# keycloak\_openid\_client\_authorization\_client\_scope\_policy Resource
6+
7+
Allows you to manage openid Client Authorization Client Scope type Policies.
8+
9+
## Example Usage
10+
11+
```hcl
12+
resource "keycloak_realm" "realm" {
13+
realm = "my-realm"
14+
enabled = true
15+
}
16+
17+
resource "keycloak_openid_client" "test" {
18+
client_id = "client_id"
19+
realm_id = keycloak_realm.realm.id
20+
access_type = "CONFIDENTIAL"
21+
service_accounts_enabled = true
22+
authorization {
23+
policy_enforcement_mode = "ENFORCING"
24+
}
25+
}
26+
27+
resource "keycloak_openid_client_scope" "test1" {
28+
realm_id = keycloak_realm.realm.id
29+
name = "test1"
30+
description = "test1"
31+
}
32+
33+
resource "keycloak_openid_client_scope" "test2" {
34+
realm_id = keycloak_realm.realm.id
35+
name = "test2"
36+
description = "test2"
37+
}
38+
39+
resource "keycloak_openid_client_authorization_client_scope_policy" "test" {
40+
resource_server_id = keycloak_openid_client.test.resource_server_id
41+
realm_id = keycloak_realm.realm.id
42+
name = "test_policy_single"
43+
description = "test"
44+
decision_strategy = "AFFIRMATIVE"
45+
logic = "POSITIVE"
46+
47+
scope {
48+
id = keycloak_openid_client_scope.test1.id
49+
required = false
50+
}
51+
}
52+
53+
resource "keycloak_openid_client_authorization_client_scope_policy" "test_multiple" {
54+
resource_server_id = keycloak_openid_client.test.resource_server_id
55+
realm_id = keycloak_realm.realm.id
56+
name = "test_policy_multiple"
57+
description = "test"
58+
decision_strategy = "AFFIRMATIVE"
59+
logic = "POSITIVE"
60+
61+
scope {
62+
id = keycloak_openid_client_scope.test1.id
63+
required = false
64+
}
65+
66+
scope {
67+
id = keycloak_openid_client_scope.test2.id
68+
required = true
69+
}
70+
}
71+
72+
```
73+
74+
### Argument Reference
75+
76+
The following arguments are supported:
77+
78+
- `realm_id` - (Required) The realm this group exists in.
79+
- `resource_server_id` - (Required) The ID of the resource server.
80+
- `name` - (Required) The name of the policy.
81+
- `description` - (Optional) A description for the authorization policy.
82+
- `decision_strategy` - (Optional) The decision strategy, can be one of `UNANIMOUS`, `AFFIRMATIVE`, or `CONSENSUS`. Defaults to `UNANIMOUS`.
83+
- `logic` - (Optional) The logic, can be one of `POSITIVE` or `NEGATIVE`. Defaults to `POSITIVE`.
84+
- `scope` - An client scope to add [client scope](#scope-arguments). At least one should be defined.
85+
86+
### Scope Arguments
87+
88+
- `id` - (Required) Id of client scope.
89+
- `required` - (Optional) When `true`, then this client scope will be set as required. Defaults to `false`.
90+
91+
### Attributes Reference
92+
93+
In addition to the arguments listed above, the following computed attributes are exported:
94+
95+
- `id` - Policy ID representing the policy.
96+
97+
## Import
98+
99+
Client authorization policies can be imported using the format: `{{realmId}}/{{resourceServerId}}/{{policyId}}`.
100+
101+
Example:
102+
103+
```bash
104+
$ terraform import keycloak_openid_client_authorization_client_scope_policy.test my-realm/3bd4a686-1062-4b59-97b8-e4e3f10b99da/63b3cde8-987d-4cd9-9306-1955579281d9
105+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package keycloak
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
)
8+
9+
type OpenidClientAuthorizationClientScopePolicy struct {
10+
Id string `json:"id,omitempty"`
11+
RealmId string `json:"-"`
12+
ResourceServerId string `json:"-"`
13+
Name string `json:"name"`
14+
DecisionStrategy string `json:"decisionStrategy"`
15+
Logic string `json:"logic"`
16+
Type string `json:"type"`
17+
Scope []OpenidClientAuthorizationClientScope `json:"clientScopes"`
18+
Description string `json:"description"`
19+
}
20+
21+
type OpenidClientAuthorizationClientScope struct {
22+
Id string `json:"id,omitempty"`
23+
Required bool `json:"required,omitempty"`
24+
}
25+
26+
func (keycloakClient *KeycloakClient) NewOpenidClientAuthorizationClientScopePolicy(ctx context.Context, policy *OpenidClientAuthorizationClientScopePolicy) error {
27+
body, _, err := keycloakClient.post(ctx, fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/policy/client-scope", policy.RealmId, policy.ResourceServerId), policy)
28+
if err != nil {
29+
return err
30+
}
31+
err = json.Unmarshal(body, &policy)
32+
if err != nil {
33+
return err
34+
}
35+
return nil
36+
}
37+
38+
func (keycloakClient *KeycloakClient) UpdateOpenidClientAuthorizationClientScopePolicy(ctx context.Context, policy *OpenidClientAuthorizationClientScopePolicy) error {
39+
err := keycloakClient.put(ctx, fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/policy/client-scope/%s", policy.RealmId, policy.ResourceServerId, policy.Id), policy)
40+
if err != nil {
41+
return err
42+
}
43+
return nil
44+
}
45+
46+
func (keycloakClient *KeycloakClient) DeleteOpenidClientAuthorizationClientScopePolicy(ctx context.Context, realmId, resourceServerId, policyId string) error {
47+
return keycloakClient.delete(ctx, fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/policy/client-scope/%s", realmId, resourceServerId, policyId), nil)
48+
}
49+
50+
func (keycloakClient *KeycloakClient) GetOpenidClientAuthorizationClientScopePolicy(ctx context.Context, realmId, resourceServerId, policyId string) (*OpenidClientAuthorizationClientScopePolicy, error) {
51+
52+
policy := OpenidClientAuthorizationClientScopePolicy{
53+
Id: policyId,
54+
ResourceServerId: resourceServerId,
55+
RealmId: realmId,
56+
}
57+
err := keycloakClient.get(ctx, fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/policy/client-scope/%s", realmId, resourceServerId, policyId), &policy, nil)
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
return &policy, nil
63+
}

provider/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ func KeycloakProvider(client *keycloak.KeycloakClient) *schema.Provider {
105105
"keycloak_openid_client_time_policy": resourceKeycloakOpenidClientAuthorizationTimePolicy(),
106106
"keycloak_openid_client_user_policy": resourceKeycloakOpenidClientAuthorizationUserPolicy(),
107107
"keycloak_openid_client_client_policy": resourceKeycloakOpenidClientAuthorizationClientPolicy(),
108+
"keycloak_openid_client_authorization_client_scope_policy": resourceKeycloakOpenidClientAuthorizationClientScopePolicy(),
108109
"keycloak_openid_client_authorization_scope": resourceKeycloakOpenidClientAuthorizationScope(),
109110
"keycloak_openid_client_authorization_permission": resourceKeycloakOpenidClientAuthorizationPermission(),
110111
"keycloak_openid_client_service_account_role": resourceKeycloakOpenidClientServiceAccountRole(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
9+
"github.com/keycloak/terraform-provider-keycloak/keycloak"
10+
)
11+
12+
func resourceKeycloakOpenidClientAuthorizationClientScopePolicy() *schema.Resource {
13+
return &schema.Resource{
14+
CreateContext: resourceKeycloakOpenidClientAuthorizationClientScopePolicyCreate,
15+
ReadContext: resourceKeycloakOpenidClientAuthorizationClientScopePolicyRead,
16+
DeleteContext: resourceKeycloakOpenidClientAuthorizationClientScopePolicyDelete,
17+
UpdateContext: resourceKeycloakOpenidClientAuthorizationClientScopePolicyUpdate,
18+
Importer: &schema.ResourceImporter{
19+
StateContext: genericResourcePolicyImport,
20+
},
21+
Schema: map[string]*schema.Schema{
22+
"resource_server_id": {
23+
Type: schema.TypeString,
24+
Required: true,
25+
},
26+
"realm_id": {
27+
Type: schema.TypeString,
28+
Required: true,
29+
},
30+
"name": {
31+
Type: schema.TypeString,
32+
Required: true,
33+
},
34+
"decision_strategy": {
35+
Type: schema.TypeString,
36+
Optional: true,
37+
ValidateFunc: validation.StringInSlice(keycloakOpenidClientResourcePermissionDecisionStrategies, false),
38+
Default: "UNANIMOUS",
39+
},
40+
"logic": {
41+
Type: schema.TypeString,
42+
Optional: true,
43+
ValidateFunc: validation.StringInSlice(keycloakPolicyLogicTypes, false),
44+
Default: "POSITIVE",
45+
},
46+
"description": {
47+
Type: schema.TypeString,
48+
Optional: true,
49+
},
50+
"scope": {
51+
Type: schema.TypeSet,
52+
Required: true,
53+
MinItems: 1,
54+
Elem: &schema.Resource{
55+
Schema: map[string]*schema.Schema{
56+
"id": {
57+
Type: schema.TypeString,
58+
Required: true,
59+
},
60+
"required": {
61+
Type: schema.TypeBool,
62+
Optional: true,
63+
Default: false,
64+
},
65+
},
66+
},
67+
},
68+
},
69+
}
70+
}
71+
72+
func getOpenidClientAuthorizationClientScopePolicyResourceFromData(data *schema.ResourceData) *keycloak.OpenidClientAuthorizationClientScopePolicy {
73+
var clientScopes []keycloak.OpenidClientAuthorizationClientScope
74+
75+
if v, ok := data.Get("scope").(*schema.Set); ok {
76+
for _, clientScope := range v.List() { // Use List() for TypeSet
77+
clientScopeMap := clientScope.(map[string]interface{})
78+
clientScopes = append(clientScopes, keycloak.OpenidClientAuthorizationClientScope{
79+
Id: clientScopeMap["id"].(string),
80+
Required: clientScopeMap["required"].(bool),
81+
})
82+
}
83+
}
84+
85+
resource := keycloak.OpenidClientAuthorizationClientScopePolicy{
86+
Id: data.Id(),
87+
ResourceServerId: data.Get("resource_server_id").(string),
88+
RealmId: data.Get("realm_id").(string),
89+
DecisionStrategy: data.Get("decision_strategy").(string),
90+
Logic: data.Get("logic").(string),
91+
Name: data.Get("name").(string),
92+
Type: "client-scope",
93+
Scope: clientScopes,
94+
Description: data.Get("description").(string),
95+
}
96+
97+
return &resource
98+
}
99+
100+
func setOpenidClientAuthorizationClientScopePolicyResourceData(ctx context.Context, keycloakClient *keycloak.KeycloakClient, policy *keycloak.OpenidClientAuthorizationClientScopePolicy, data *schema.ResourceData) error {
101+
data.SetId(policy.Id)
102+
103+
data.Set("resource_server_id", policy.ResourceServerId)
104+
data.Set("realm_id", policy.RealmId)
105+
data.Set("name", policy.Name)
106+
data.Set("decision_strategy", policy.DecisionStrategy)
107+
data.Set("logic", policy.Logic)
108+
data.Set("description", policy.Description)
109+
110+
// Convert scope slice to a set for consistent Terraform state
111+
clientScopesSet := make([]interface{}, len(policy.Scope))
112+
for i, g := range policy.Scope {
113+
clientScopesSet[i] = map[string]interface{}{
114+
"id": g.Id,
115+
"required": g.Required,
116+
}
117+
}
118+
119+
if err := data.Set("scope", clientScopesSet); err != nil {
120+
return err
121+
}
122+
123+
return nil
124+
}
125+
126+
func resourceKeycloakOpenidClientAuthorizationClientScopePolicyCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
127+
keycloakClient := meta.(*keycloak.KeycloakClient)
128+
129+
resource := getOpenidClientAuthorizationClientScopePolicyResourceFromData(data)
130+
131+
err := keycloakClient.NewOpenidClientAuthorizationClientScopePolicy(ctx, resource)
132+
if err != nil {
133+
return diag.FromErr(err)
134+
}
135+
136+
err = setOpenidClientAuthorizationClientScopePolicyResourceData(ctx, keycloakClient, resource, data)
137+
if err != nil {
138+
return diag.FromErr(err)
139+
}
140+
141+
return resourceKeycloakOpenidClientAuthorizationClientScopePolicyRead(ctx, data, meta)
142+
}
143+
144+
func resourceKeycloakOpenidClientAuthorizationClientScopePolicyRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
145+
keycloakClient := meta.(*keycloak.KeycloakClient)
146+
147+
realmId := data.Get("realm_id").(string)
148+
resourceServerId := data.Get("resource_server_id").(string)
149+
id := data.Id()
150+
151+
resource, err := keycloakClient.GetOpenidClientAuthorizationClientScopePolicy(ctx, realmId, resourceServerId, id)
152+
if err != nil {
153+
return handleNotFoundError(ctx, err, data)
154+
}
155+
156+
err = setOpenidClientAuthorizationClientScopePolicyResourceData(ctx, keycloakClient, resource, data)
157+
if err != nil {
158+
return diag.FromErr(err)
159+
}
160+
161+
return nil
162+
}
163+
164+
func resourceKeycloakOpenidClientAuthorizationClientScopePolicyUpdate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
165+
keycloakClient := meta.(*keycloak.KeycloakClient)
166+
167+
resource := getOpenidClientAuthorizationClientScopePolicyResourceFromData(data)
168+
169+
err := keycloakClient.UpdateOpenidClientAuthorizationClientScopePolicy(ctx, resource)
170+
if err != nil {
171+
return diag.FromErr(err)
172+
}
173+
174+
err = setOpenidClientAuthorizationClientScopePolicyResourceData(ctx, keycloakClient, resource, data)
175+
if err != nil {
176+
return diag.FromErr(err)
177+
}
178+
179+
return nil
180+
}
181+
182+
func resourceKeycloakOpenidClientAuthorizationClientScopePolicyDelete(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
183+
keycloakClient := meta.(*keycloak.KeycloakClient)
184+
185+
realmId := data.Get("realm_id").(string)
186+
resourceServerId := data.Get("resource_server_id").(string)
187+
id := data.Id()
188+
189+
return diag.FromErr(keycloakClient.DeleteOpenidClientAuthorizationClientScopePolicy(ctx, realmId, resourceServerId, id))
190+
}

0 commit comments

Comments
 (0)