Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource to enable the token exchange idp permission #318

Merged
merged 8 commits into from
Jun 29, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ services:
ports:
- 8389:389
keycloak:
image: jboss/keycloak:8.0.1
command: -b 0.0.0.0 -Dkeycloak.profile.feature.upload_scripts=enabled
image: jboss/keycloak:10.0.2
command: -b 0.0.0.0 -Dkeycloak.profile.feature.upload_scripts=enabled -Dkeycloak.profile.feature.admin_fine_grained_authz=enabled -Dkeycloak.profile.feature.token_exchange=enabled
depends_on:
- postgres
- openldap
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# keycloak_identity_provider_token_exchange_scope_permission

Allows you to manage Identity Provider "Token exchange" Scope Based Permissions.

This is part of a preview keycloak feature. You need to enable this feature to be able to use this resource.
More information about enabling the preview feature can be found here: https://www.keycloak.org/docs/latest/securing_apps/index.html#_token-exchange

When enabling Identity Provider Permissions, Keycloak does several things automatically:
1. Enable Authorization on build-in realm-management client
1. Create a "token-exchange" scope
1. Create a resource representing the identity provider
1. Create a scope based permission for the "token-exchange" scope and identity provider resource

The only thing that is missing is a policy set on the permission.
As the policy lives within the context of the realm-management client, you cannot create a policy resource and link to from with your _.tf_ file. This would also cause an implicit cycle dependency.
Thus, the only way to manage this in terraform is to create and manage the policy internally from within this terraform resource itself.
At the moment only a client policy type is supported. The client policy will automatically be created for the clients parameter.

### Example Usage

```hcl
resource "keycloak_realm" "token-exchange_realm" {
realm = "token-exchange_destination_realm"
enabled = true
}

resource keycloak_oidc_identity_provider token-exchange_my_oidc_idp {
realm = keycloak_realm.token-exchange_realm.id
alias = "myIdp"
authorization_url = "http://localhost:8080/auth/realms/someRealm/protocol/openid-connect/auth"
token_url = "http://localhost:8080/auth/realms/someRealm/protocol/openid-connect/token"
client_id = "clientId"
client_secret = "secret"
default_scopes = "openid"
}

resource "keycloak_openid_client" "token-exchange_webapp_client" {
realm_id = keycloak_realm.token-exchange_realm.id
name = "webapp_client"
client_id = "webapp_client"
client_secret = "secret"
description = "a webapp client on the destination realm"
access_type = "CONFIDENTIAL"
standard_flow_enabled = true
valid_redirect_uris = [
"http://localhost:8080/*",
]
}

//relevant part
resource "keycloak_identity_provider_token_exchange_scope_permission" "oidc_idp_permission" {
realm_id = keycloak_realm.token-exchange_realm.id
provider_alias = keycloak_oidc_identity_provider.token-exchange_my_oidc_idp.alias
policy_type = "client"
clients = [keycloak_openid_client.token-exchange_webapp_client.id]
}
```

### Argument Reference

The following arguments are supported:

- `realm_id` - (Required) The realm this group exists in.
- `provider_alias` - (Required) Alias of the identity provider.
- `policy_type` - (Optional) Defaults to "client" This is also the only value policy type supported by this provider.
- `clients` - (Required) Ids of the clients for which a policy will be created and set on scope based token exchange permission.
- `policy_id` - (Computed) Policy id that will be set on the scope based token exchange permission automatically created by enabling permissions on the reference identity provider.
tomrutsaert marked this conversation as resolved.
Show resolved Hide resolved
- `authorization_resource_server_id` - (Computed) Resource server id representing the realm management client on which this permission is managed.
- `authorization_idp_resource_id` - (Computed) Resource id representing the identity provider, this automatically created by keycloak.
- `authorization_token_exchange_scope_permission_id` - (Computed) Permission id representing the Permission with scope 'Token Exchange' and the resource 'authorization_idp_resource_id', this automatically created by keycloak, the policy id will be set on this permission.

### Import

This resource can be imported using the format
`{{realm_id}}/{{provider_alias}}`, where `provider_alias` is the alias that you assign to the identity provider upon creation.

Example:

```bash
$ terraform import keycloak_identity_provider_token_exchange_scope_permission.my_permission my-realm/my_idp
```

57 changes: 57 additions & 0 deletions example/external_token_exchange_example.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
resource "keycloak_realm" "token-exchange_source_realm" {
realm = "token-exchange_source_realm"
enabled = true
}

resource "keycloak_openid_client" "token-exchange_destination_client" {
realm_id = keycloak_realm.token-exchange_source_realm.id
name = "destination_client"
client_id = "destination_client"
client_secret = "secret"
description = "a client used by the destination realm"
access_type = "CONFIDENTIAL"
standard_flow_enabled = true
valid_redirect_uris = [
"http://localhost:8080/*",
]
}

resource "keycloak_realm" "token-exchange_destination_realm" {
realm = "token-exchange_destination_realm"
enabled = true
}

resource keycloak_oidc_identity_provider token-exchange_source_oidc_idp {
realm = keycloak_realm.token-exchange_destination_realm.id
alias = "source"
authorization_url = "http://localhost:8080/auth/realms/${keycloak_realm.token-exchange_source_realm.id}/protocol/openid-connect/auth"
token_url = "http://localhost:8080/auth/realms/${keycloak_realm.token-exchange_source_realm.id}/protocol/openid-connect/token"
user_info_url = "http://localhost:8080/auth/realms/${keycloak_realm.token-exchange_source_realm.id}/protocol/openid-connect/userinfo"
jwks_url = "http://localhost:8080/auth/realms/${keycloak_realm.token-exchange_source_realm.id}/protocol/openid-connect/certs"
validate_signature = true
client_id = keycloak_openid_client.token-exchange_destination_client.client_id
client_secret = keycloak_openid_client.token-exchange_destination_client.client_secret
default_scopes = "openid"
}

resource "keycloak_openid_client" "token-exchange_webapp_client" {
realm_id = keycloak_realm.token-exchange_destination_realm.id
name = "webapp_client"
client_id = "webapp_client"
client_secret = "secret"
description = "a webapp client on the destination realm"
access_type = "CONFIDENTIAL"
standard_flow_enabled = true
valid_redirect_uris = [
"http://localhost:8080/*",
]
}

//token exchange feature enabler
resource "keycloak_identity_provider_token_exchange_scope_permission" "source_oidc_idp_permission" {
realm_id = keycloak_realm.token-exchange_destination_realm.id
provider_alias = keycloak_oidc_identity_provider.token-exchange_source_oidc_idp.alias
policy_type = "client"
clients = [keycloak_openid_client.token-exchange_webapp_client.id]
}

46 changes: 46 additions & 0 deletions keycloak/identity_provider_permissions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package keycloak

import (
"fmt"
)

type IdentityProviderPermissionsInput struct {
Enabled bool `json:"enabled"`
}

type IdentityProviderPermissions struct {
RealmId string `json:"-"`
ProviderAlias string `json:"-"`
Enabled bool `json:"enabled"`
Resource string `json:"resource"`
ScopePermissions map[string]interface{} `json:"scopePermissions"`
}

func (keycloakClient *KeycloakClient) EnableIdentityProviderPermissions(realmId, providerAlias string) error {
return keycloakClient.put(fmt.Sprintf("/realms/%s/identity-provider/instances/%s/management/permissions", realmId, providerAlias), IdentityProviderPermissionsInput{Enabled: true})
}

func (keycloakClient *KeycloakClient) DisableIdentityProviderPermissions(realmId, providerAlias string) error {
return keycloakClient.put(fmt.Sprintf("/realms/%s/identity-provider/instances/%s/management/permissions", realmId, providerAlias), IdentityProviderPermissionsInput{Enabled: false})
}

func (keycloakClient *KeycloakClient) GetIdentityProviderPermissions(realmId, providerAlias string) (*IdentityProviderPermissions, error) {
var identityProviderPermissions IdentityProviderPermissions
identityProviderPermissions.RealmId = realmId
identityProviderPermissions.ProviderAlias = providerAlias

err := keycloakClient.get(fmt.Sprintf("/realms/%s/identity-provider/instances/%s/management/permissions", realmId, providerAlias), &identityProviderPermissions, nil)
if err != nil {
return nil, err
}

return &identityProviderPermissions, nil
}

func (identityProviderPermissions *IdentityProviderPermissions) GetTokenExchangeScopedPermissionId() (string, error) {
if identityProviderPermissions.Enabled {
return identityProviderPermissions.ScopePermissions["token-exchange"].(string), nil
} else {
return "", fmt.Errorf("identity provider permissions are not enabled, thus can not return the linked 'token-exchange' scope based permission")
}
}
Loading