-
Notifications
You must be signed in to change notification settings - Fork 324
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: new resource - keycloak_openid_audience_resolve_protocol_mapper (…
- Loading branch information
Showing
6 changed files
with
558 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
--- | ||
page_title: "keycloak_openid_audience_resolve_protocol_mapper Resource" | ||
--- | ||
|
||
# keycloak\_openid\_audience\_resolve\_protocol\_mapper Resource | ||
|
||
Allows for creating the "Audience Resolve" OIDC protocol mapper within Keycloak. | ||
|
||
This protocol mapper is useful to avoid manual management of audiences, instead relying on the presence of client roles | ||
to imply which audiences are appropriate for the token. See the | ||
[Keycloak docs](https://www.keycloak.org/docs/latest/server_admin/#_audience_resolve) for more details. | ||
|
||
## Example Usage (Client) | ||
|
||
```hcl | ||
resource "keycloak_realm" "realm" { | ||
realm = "my-realm" | ||
enabled = true | ||
} | ||
resource "keycloak_openid_client" "openid_client" { | ||
realm_id = keycloak_realm.realm.id | ||
client_id = "client" | ||
name = "client" | ||
enabled = true | ||
access_type = "CONFIDENTIAL" | ||
valid_redirect_uris = [ | ||
"http://localhost:8080/openid-callback" | ||
] | ||
} | ||
resource "keycloak_openid_audience_resolve_protocol_mapper" "audience_mapper" { | ||
realm_id = keycloak_realm.realm.id | ||
client_id = keycloak_openid_client.openid_client.id | ||
name = "my-audience-resolve-mapper" | ||
} | ||
``` | ||
|
||
## Example Usage (Client Scope) | ||
|
||
```hcl | ||
resource "keycloak_realm" "realm" { | ||
realm = "my-realm" | ||
enabled = true | ||
} | ||
resource "keycloak_openid_client_scope" "client_scope" { | ||
realm_id = keycloak_realm.realm.id | ||
name = "test-client-scope" | ||
} | ||
resource "keycloak_openid_audience_protocol_mapper" "audience_mapper" { | ||
realm_id = keycloak_realm.realm.id | ||
client_scope_id = keycloak_openid_client_scope.client_scope.id | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
- `realm_id` - (Required) The realm this protocol mapper exists within. | ||
- `name` - (Optional) The display name of this protocol mapper in the GUI. Defaults to "audience resolve". | ||
- `client_id` - (Optional) The client this protocol mapper should be attached to. Conflicts with `client_scope_id`. One of `client_id` or `client_scope_id` must be specified. | ||
- `client_scope_id` - (Optional) The client scope this protocol mapper should be attached to. Conflicts with `client_id`. One of `client_id` or `client_scope_id` must be specified. | ||
|
||
## Import | ||
|
||
Protocol mappers can be imported using one of the following formats: | ||
- Client: `{{realm_id}}/client/{{client_keycloak_id}}/{{protocol_mapper_id}}` | ||
- Client Scope: `{{realm_id}}/client-scope/{{client_scope_keycloak_id}}/{{protocol_mapper_id}}` | ||
|
||
Example: | ||
|
||
```bash | ||
$ terraform import keycloak_openid_audience_protocol_mapper.audience_mapper my-realm/client/a7202154-8793-4656-b655-1dd18c181e14/71602afa-f7d1-4788-8c49-ef8fd00af0f4 | ||
$ terraform import keycloak_openid_audience_protocol_mapper.audience_mapper my-realm/client-scope/b799ea7e-73ee-4a73-990a-1eafebe8e20a/71602afa-f7d1-4788-8c49-ef8fd00af0f4 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package keycloak | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
type OpenIdAudienceResolveProtocolMapper struct { | ||
Id string | ||
Name string | ||
RealmId string | ||
ClientId string | ||
ClientScopeId string | ||
} | ||
|
||
func (mapper *OpenIdAudienceResolveProtocolMapper) convertToGenericProtocolMapper() *protocolMapper { | ||
return &protocolMapper{ | ||
Id: mapper.Id, | ||
Name: mapper.Name, | ||
Protocol: "openid-connect", | ||
ProtocolMapper: "oidc-audience-resolve-mapper", | ||
Config: map[string]string{}, | ||
} | ||
} | ||
|
||
func (protocolMapper *protocolMapper) convertToOpenIdAudienceResolveProtocolMapper(realmId, clientId, clientScopeId string) (*OpenIdAudienceResolveProtocolMapper, error) { | ||
return &OpenIdAudienceResolveProtocolMapper{ | ||
Id: protocolMapper.Id, | ||
Name: protocolMapper.Name, | ||
RealmId: realmId, | ||
ClientId: clientId, | ||
ClientScopeId: clientScopeId, | ||
}, nil | ||
} | ||
|
||
func (keycloakClient *KeycloakClient) GetOpenIdAudienceResolveProtocolMapper(realmId, clientId, clientScopeId, mapperId string) (*OpenIdAudienceResolveProtocolMapper, error) { | ||
var protocolMapper *protocolMapper | ||
|
||
err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return protocolMapper.convertToOpenIdAudienceResolveProtocolMapper(realmId, clientId, clientScopeId) | ||
} | ||
|
||
func (keycloakClient *KeycloakClient) DeleteOpenIdAudienceResolveProtocolMapper(realmId, clientId, clientScopeId, mapperId string) error { | ||
return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), nil) | ||
} | ||
|
||
func (keycloakClient *KeycloakClient) NewOpenIdAudienceResolveProtocolMapper(mapper *OpenIdAudienceResolveProtocolMapper) error { | ||
path := protocolMapperPath(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId) | ||
|
||
_, location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
mapper.Id = getIdFromLocationHeader(location) | ||
|
||
return nil | ||
} | ||
|
||
func (keycloakClient *KeycloakClient) UpdateOpenIdAudienceResolveProtocolMapper(mapper *OpenIdAudienceResolveProtocolMapper) error { | ||
path := individualProtocolMapperPath(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId, mapper.Id) | ||
|
||
return keycloakClient.put(path, mapper.convertToGenericProtocolMapper()) | ||
} | ||
|
||
func (keycloakClient *KeycloakClient) ValidateOpenIdAudienceResolveProtocolMapper(mapper *OpenIdAudienceResolveProtocolMapper) error { | ||
if mapper.ClientId == "" && mapper.ClientScopeId == "" { | ||
return fmt.Errorf("validation error: one of ClientId or ClientScopeId must be set") | ||
} | ||
|
||
protocolMappers, err := keycloakClient.listGenericProtocolMappers(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, protocolMapper := range protocolMappers { | ||
if protocolMapper.Name == mapper.Name && protocolMapper.Id != mapper.Id { | ||
return fmt.Errorf("validation error: a protocol mapper with name %s already exists for this client", mapper.Name) | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 120 additions & 0 deletions
120
provider/resource_keycloak_openid_audience_resolve_protocol_mapper.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package provider | ||
|
||
import ( | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/mrparkers/terraform-provider-keycloak/keycloak" | ||
) | ||
|
||
func resourceKeycloakOpenIdAudienceResolveProtocolMapper() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceKeycloakOpenIdAudienceResolveProtocolMapperCreate, | ||
Read: resourceKeycloakOpenIdAudienceResolveProtocolMapperRead, | ||
//Update: resourceKeycloakOpenIdAudienceResolveProtocolMapperUpdate, | ||
Delete: resourceKeycloakOpenIdAudienceResolveProtocolMapperDelete, | ||
Importer: &schema.ResourceImporter{ | ||
// import a mapper tied to a client: | ||
// {{realmId}}/client/{{clientId}}/{{protocolMapperId}} | ||
// or a client scope: | ||
// {{realmId}}/client-scope/{{clientScopeId}}/{{protocolMapperId}} | ||
State: genericProtocolMapperImport, | ||
}, | ||
Schema: map[string]*schema.Schema{ | ||
"name": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
Description: "A human-friendly name that will appear in the Keycloak console.", | ||
Default: "audience resolve", | ||
}, | ||
"realm_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
Description: "The realm id where the associated client or client scope exists.", | ||
}, | ||
"client_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
Description: "The mapper's associated client. Cannot be used at the same time as client_scope_id.", | ||
ConflictsWith: []string{"client_scope_id"}, | ||
}, | ||
"client_scope_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
Description: "The mapper's associated client scope. Cannot be used at the same time as client_id.", | ||
ConflictsWith: []string{"client_id"}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func mapFromDataToOpenIdAudienceResolveProtocolMapper(data *schema.ResourceData) *keycloak.OpenIdAudienceResolveProtocolMapper { | ||
return &keycloak.OpenIdAudienceResolveProtocolMapper{ | ||
Id: data.Id(), | ||
Name: data.Get("name").(string), | ||
RealmId: data.Get("realm_id").(string), | ||
ClientId: data.Get("client_id").(string), | ||
ClientScopeId: data.Get("client_scope_id").(string), | ||
} | ||
} | ||
|
||
func mapFromOpenIdAudienceResolveMapperToData(mapper *keycloak.OpenIdAudienceResolveProtocolMapper, data *schema.ResourceData) { | ||
data.SetId(mapper.Id) | ||
data.Set("name", mapper.Name) | ||
data.Set("realm_id", mapper.RealmId) | ||
|
||
if mapper.ClientId != "" { | ||
data.Set("client_id", mapper.ClientId) | ||
} else { | ||
data.Set("client_scope_id", mapper.ClientScopeId) | ||
} | ||
} | ||
|
||
func resourceKeycloakOpenIdAudienceResolveProtocolMapperCreate(data *schema.ResourceData, meta interface{}) error { | ||
keycloakClient := meta.(*keycloak.KeycloakClient) | ||
|
||
openIdAudienceResolveMapper := mapFromDataToOpenIdAudienceResolveProtocolMapper(data) | ||
|
||
err := keycloakClient.ValidateOpenIdAudienceResolveProtocolMapper(openIdAudienceResolveMapper) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = keycloakClient.NewOpenIdAudienceResolveProtocolMapper(openIdAudienceResolveMapper) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
mapFromOpenIdAudienceResolveMapperToData(openIdAudienceResolveMapper, data) | ||
|
||
return resourceKeycloakOpenIdAudienceResolveProtocolMapperRead(data, meta) | ||
} | ||
|
||
func resourceKeycloakOpenIdAudienceResolveProtocolMapperRead(data *schema.ResourceData, meta interface{}) error { | ||
keycloakClient := meta.(*keycloak.KeycloakClient) | ||
|
||
realmId := data.Get("realm_id").(string) | ||
clientId := data.Get("client_id").(string) | ||
clientScopeId := data.Get("client_scope_id").(string) | ||
|
||
openIdAudienceResolveMapper, err := keycloakClient.GetOpenIdAudienceResolveProtocolMapper(realmId, clientId, clientScopeId, data.Id()) | ||
if err != nil { | ||
return handleNotFoundError(err, data) | ||
} | ||
|
||
mapFromOpenIdAudienceResolveMapperToData(openIdAudienceResolveMapper, data) | ||
|
||
return nil | ||
} | ||
|
||
func resourceKeycloakOpenIdAudienceResolveProtocolMapperDelete(data *schema.ResourceData, meta interface{}) error { | ||
keycloakClient := meta.(*keycloak.KeycloakClient) | ||
|
||
realmId := data.Get("realm_id").(string) | ||
clientId := data.Get("client_id").(string) | ||
clientScopeId := data.Get("client_scope_id").(string) | ||
|
||
return keycloakClient.DeleteOpenIdAudienceResolveProtocolMapper(realmId, clientId, clientScopeId, data.Id()) | ||
} |
Oops, something went wrong.